@aiyiran/myclaw 1.1.168 → 1.1.169
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/assets/myclaw-inject.js
CHANGED
|
@@ -327,22 +327,32 @@
|
|
|
327
327
|
|
|
328
328
|
function buildTestDonePrompt() {
|
|
329
329
|
var rec = window.myclawLastDebugRecord;
|
|
330
|
-
var lines = [
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
if (rec
|
|
336
|
-
|
|
330
|
+
var lines = ["我刚才已经完成了测试。"];
|
|
331
|
+
|
|
332
|
+
// 调试数据落在 debug-data.js(window.__DEBUG_DATA__ = {...}),AI 直接读这个
|
|
333
|
+
// 数据文件即可,不要读同目录的 debug-record.html(那只是给人看的渲染壳)。
|
|
334
|
+
var dataFile = null;
|
|
335
|
+
if (rec) {
|
|
336
|
+
if (rec.debugRecordPath) {
|
|
337
|
+
dataFile = rec.debugRecordPath.replace(/debug-record\.html$/, "debug-data.js");
|
|
338
|
+
} else if (rec.workspace) {
|
|
339
|
+
dataFile = rec.workspace + "/debug/debug-data.js";
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (dataFile) {
|
|
343
|
+
lines.push("请你读取调试数据文件:" + dataFile);
|
|
344
|
+
} else {
|
|
345
|
+
lines.push("请你读取本次测试的调试数据文件 debug/debug-data.js。");
|
|
337
346
|
}
|
|
347
|
+
lines.push("它是一行 `window.__DEBUG_DATA__ = {...}` 的赋值,里面的 events 数组就是完整运行现场;不要去读同目录的 debug-record.html。");
|
|
338
348
|
if (rec && rec.entryFile) {
|
|
339
349
|
lines.push("我刚才测试的文件是:" + rec.entryFile);
|
|
340
350
|
}
|
|
341
351
|
|
|
342
352
|
lines.push("");
|
|
343
353
|
lines.push("请帮我判断:我们之前的问题是否已经修复。");
|
|
344
|
-
lines.push("
|
|
345
|
-
lines.push("
|
|
354
|
+
lines.push("如果已经修复,请简单说明你从 events 里看到了什么证据。");
|
|
355
|
+
lines.push("如果还没有修复,请指出 events 里最关键的问题,并建议下一步最小修改。");
|
|
346
356
|
|
|
347
357
|
return lines.join("\n");
|
|
348
358
|
}
|
package/package.json
CHANGED
|
@@ -27,38 +27,39 @@ python3 scripts/extract_chat.py '["url1","url2","url3"]' <output-dir>
|
|
|
27
27
|
|
|
28
28
|
## 输出文件
|
|
29
29
|
|
|
30
|
+
**只产出两个文件**,不再为每个会话单独落 js(所有会话的完整数据都汇入 `index.js`):
|
|
31
|
+
|
|
30
32
|
| 文件 | 说明 |
|
|
31
33
|
|---|---|
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
34
|
-
| `...` | |
|
|
35
|
-
| `index.js` | 所有会话的索引列表(仅批量模式生成) |
|
|
36
|
-
| `chat_history.js` | 向后兼容,指向第一个会话 |
|
|
37
|
-
|
|
38
|
-
每个 JS 文件结构:
|
|
39
|
-
|
|
40
|
-
```javascript
|
|
41
|
-
const chatData = {
|
|
42
|
-
"session": "session-name",
|
|
43
|
-
"session_id": "agent:xxx:yyy",
|
|
44
|
-
"total_pairs": N,
|
|
45
|
-
"initiator": "...",
|
|
46
|
-
"conversations": [
|
|
47
|
-
{ "user": "...", "user_time": "...", "ai": "...", "ai_time": "..." },
|
|
48
|
-
...
|
|
49
|
-
]
|
|
50
|
-
};
|
|
51
|
-
```
|
|
34
|
+
| `index.js` | 唯一数据产物:`chatIndex`(会话元数据列表)+ `chatAll`(每个会话的完整对话) |
|
|
35
|
+
| `index.html` | 查看器,复制自 `assets/chat-history-template.html`,只加载 `index.js` |
|
|
52
36
|
|
|
53
37
|
`index.js` 结构:
|
|
54
38
|
|
|
55
39
|
```javascript
|
|
40
|
+
// chatIndex:用于会话切换下拉框的元数据
|
|
56
41
|
const chatIndex = [
|
|
57
|
-
{ "
|
|
58
|
-
|
|
42
|
+
{ "session_id": "agent:xxx:main", "workspace_name": "xxx", "session_name": "main",
|
|
43
|
+
"total_pairs": 10, "first_time": "...", "last_time": "...", "work_url": "" },
|
|
44
|
+
...
|
|
59
45
|
];
|
|
46
|
+
|
|
47
|
+
// chatAll:按 session_id 索引的完整会话数据(HTML 渲染的真正数据源)
|
|
48
|
+
const chatAll = {
|
|
49
|
+
"agent:xxx:main": {
|
|
50
|
+
"workspace_name": "xxx", "session_name": "main", "session_id": "agent:xxx:main",
|
|
51
|
+
"total_pairs": 10, "first_time": "...", "last_time": "...", "work_url": "",
|
|
52
|
+
"conversations": [
|
|
53
|
+
{ "user": "...", "user_time": "...", "ai": "...", "ai_time": "..." },
|
|
54
|
+
...
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
...
|
|
58
|
+
};
|
|
60
59
|
```
|
|
61
60
|
|
|
61
|
+
> 单会话和批量模式产物一致,区别只是 `chatIndex` / `chatAll` 里的会话数量。
|
|
62
|
+
|
|
62
63
|
## 工作流
|
|
63
64
|
|
|
64
65
|
### Step 1: 解析输入
|
|
@@ -85,14 +86,14 @@ URL 示例:`https://claw1.kekouen.cn/chat?session=agent%3Ac108-v1-1811%3Amain`
|
|
|
85
86
|
3. 多条 AI 回复合并为一条
|
|
86
87
|
4. 跳过 `toolResult` 事件和空文本消息
|
|
87
88
|
|
|
88
|
-
### Step 4: 生成
|
|
89
|
+
### Step 4: 生成 index.js
|
|
89
90
|
|
|
90
|
-
|
|
91
|
+
所有会话的数据在内存中汇总,一次性写入唯一的 `index.js`(`chatIndex` + `chatAll`),不为单个会话单独落文件。
|
|
91
92
|
|
|
92
93
|
### Step 5: 渲染
|
|
93
94
|
|
|
94
|
-
将模板 `assets/chat-history-template.html`
|
|
95
|
-
|
|
95
|
+
将模板 `assets/chat-history-template.html` 复制到输出目录为 `index.html`。
|
|
96
|
+
模板只加载 `index.js`,通过 `chatIndex` 渲染会话切换下拉框、从 `chatAll` 取完整对话。
|
|
96
97
|
|
|
97
98
|
模板展示三种时间指标:
|
|
98
99
|
- **课程流逝** — 距首条消息的时间
|
|
@@ -116,8 +117,8 @@ python3 scripts/extract_chat.py "https://claw1.kekouen.cn/chat?session=agent%3Ac
|
|
|
116
117
|
```
|
|
117
118
|
|
|
118
119
|
输出:
|
|
119
|
-
- `
|
|
120
|
-
- `
|
|
120
|
+
- `index.js`(含 1 个会话)
|
|
121
|
+
- `index.html`
|
|
121
122
|
|
|
122
123
|
### 批量处理
|
|
123
124
|
|
|
@@ -131,9 +132,5 @@ python3 scripts/extract_chat.py '[
|
|
|
131
132
|
```
|
|
132
133
|
|
|
133
134
|
输出:
|
|
134
|
-
- `
|
|
135
|
-
- `
|
|
136
|
-
- `03-每天一个小习惯·宠物养成_1813.js`
|
|
137
|
-
- `04-我的金币任务板_1810.js`
|
|
138
|
-
- `index.js`
|
|
139
|
-
- `chat_history.js`(= 01-main.js 的副本)
|
|
135
|
+
- `index.js`(含 4 个会话,全部数据在 `chatAll` 里)
|
|
136
|
+
- `index.html`
|
|
@@ -23,10 +23,11 @@ Usage:
|
|
|
23
23
|
python3 extract_chat.py --scan [--html]
|
|
24
24
|
|
|
25
25
|
输出(默认到当前 workspace /root/.openclaw/workspace-teacher):
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
26
|
+
- index.js:唯一数据产物,含 chatIndex(会话元信息列表,含 work_url 字段)
|
|
27
|
+
+ chatAll(每个会话的完整对话,按 session_id 索引)
|
|
28
|
+
- index.html:查看器,复制自 assets/chat-history-template.html,只加载 index.js
|
|
29
|
+
- 不再为每个会话单独落 js 文件
|
|
30
|
+
- --scan 模式额外生成 作品扫描_<时间>.html(含可点击链接)
|
|
30
31
|
|
|
31
32
|
Host 检测:
|
|
32
33
|
- 自动从 /root/.openclaw/myclaw/server/config.json 读取
|
|
@@ -34,7 +35,7 @@ Host 检测:
|
|
|
34
35
|
|
|
35
36
|
work_url 字段:
|
|
36
37
|
- 默认为空字符串
|
|
37
|
-
- AI
|
|
38
|
+
- AI 可后续编辑 index.js 中对应会话的 work_url 填入学生作品链接
|
|
38
39
|
"""
|
|
39
40
|
|
|
40
41
|
import json
|
|
@@ -166,7 +167,8 @@ def count_conversations(jsonl_path):
|
|
|
166
167
|
messages.append({
|
|
167
168
|
'role': role,
|
|
168
169
|
'text': text.strip(),
|
|
169
|
-
'has_text': bool(text.strip())
|
|
170
|
+
'has_text': bool(text.strip()),
|
|
171
|
+
'timestamp': timestamp
|
|
170
172
|
})
|
|
171
173
|
|
|
172
174
|
# Count conversation pairs (user messages with at least one AI reply)
|
|
@@ -183,9 +185,13 @@ def count_conversations(jsonl_path):
|
|
|
183
185
|
ai_messages.append(messages[j])
|
|
184
186
|
j += 1
|
|
185
187
|
ai_text = '\n\n'.join([m['text'] for m in ai_messages if m['has_text']])
|
|
188
|
+
user_time = msg.get('timestamp', '')
|
|
189
|
+
ai_time = ai_messages[0].get('timestamp', '') if ai_messages else ''
|
|
186
190
|
conversations.append({
|
|
187
191
|
'user': user_text,
|
|
188
192
|
'ai': ai_text,
|
|
193
|
+
'user_time': user_time,
|
|
194
|
+
'ai_time': ai_time,
|
|
189
195
|
})
|
|
190
196
|
i += 1
|
|
191
197
|
else:
|
|
@@ -210,17 +216,26 @@ def build_filename(session_key):
|
|
|
210
216
|
parts = session_key.split(':')
|
|
211
217
|
agent_id = parts[1] if len(parts) > 1 else 'unknown'
|
|
212
218
|
session_name = parts[2] if len(parts) > 2 else 'main'
|
|
219
|
+
# Use last part (UUID or 'main') as unique suffix to avoid collisions
|
|
220
|
+
unique_id = parts[-1] if len(parts) > 3 else session_name
|
|
213
221
|
agent_safe = sanitize_filename(agent_id)
|
|
214
222
|
name_safe = sanitize_filename(session_name)
|
|
215
|
-
|
|
223
|
+
unique_safe = sanitize_filename(unique_id)
|
|
224
|
+
if unique_safe == name_safe:
|
|
225
|
+
return f"{agent_safe}_{name_safe}.js"
|
|
226
|
+
return f"{agent_safe}_{name_safe}_{unique_safe}.js"
|
|
216
227
|
|
|
217
228
|
|
|
218
|
-
def
|
|
229
|
+
def build_chat_data(conversations, session_key, first_ts, last_ts):
|
|
230
|
+
"""构建单个会话的完整数据对象(仅内存,不写盘)。
|
|
231
|
+
|
|
232
|
+
数据最终全部汇入 index.js 的 chatAll,不再为每个会话单独落 js 文件。
|
|
233
|
+
"""
|
|
219
234
|
parts = session_key.split(':')
|
|
220
235
|
workspace_name = parts[1] if len(parts) > 1 else 'unknown'
|
|
221
236
|
session_name = parts[2] if len(parts) > 2 else 'main'
|
|
222
237
|
|
|
223
|
-
|
|
238
|
+
return {
|
|
224
239
|
'workspace_name': workspace_name,
|
|
225
240
|
'session_name': session_name,
|
|
226
241
|
'session_id': session_key,
|
|
@@ -230,15 +245,6 @@ def generate_js(conversations, session_key, first_ts, last_ts, output_path):
|
|
|
230
245
|
'work_url': '',
|
|
231
246
|
'conversations': conversations
|
|
232
247
|
}
|
|
233
|
-
js_content = f'''// Chat History - {session_name}
|
|
234
|
-
// Session Key: {session_key}
|
|
235
|
-
// Generated by chat-history-extractor skill
|
|
236
|
-
|
|
237
|
-
const chatData = {json.dumps(output_data, ensure_ascii=False, indent=2)};
|
|
238
|
-
'''
|
|
239
|
-
with open(output_path, 'w', encoding='utf-8') as f:
|
|
240
|
-
f.write(js_content)
|
|
241
|
-
return len(conversations), session_name
|
|
242
248
|
|
|
243
249
|
|
|
244
250
|
def session_key_to_url(session_key, host=None):
|
|
@@ -387,57 +393,43 @@ def process_one(url_or_key, output_dir, index):
|
|
|
387
393
|
conversations, first_ts, last_ts = parse_jsonl_to_conversations(jsonl_path)
|
|
388
394
|
print(f"[{index}] {len(conversations)} pairs")
|
|
389
395
|
|
|
390
|
-
|
|
391
|
-
js_path = os.path.join(output_dir, js_filename)
|
|
392
|
-
count, _ = generate_js(conversations, session_key, first_ts, last_ts, js_path)
|
|
393
|
-
print(f"[{index}] Generated: {js_filename}")
|
|
396
|
+
data = build_chat_data(conversations, session_key, first_ts, last_ts)
|
|
394
397
|
|
|
395
398
|
return {
|
|
396
399
|
'index': index,
|
|
397
400
|
'session_key': session_key,
|
|
398
401
|
'session_name': session_name,
|
|
399
402
|
'agent_id': agent_id,
|
|
400
|
-
'
|
|
401
|
-
'
|
|
402
|
-
'
|
|
403
|
-
'last_time': parse_time(last_ts) if last_ts else '',
|
|
403
|
+
'total_pairs': data['total_pairs'],
|
|
404
|
+
'first_time': data['first_time'],
|
|
405
|
+
'last_time': data['last_time'],
|
|
404
406
|
'work_url': '',
|
|
407
|
+
'data': data, # 完整会话数据,汇入 index.js 的 chatAll
|
|
405
408
|
}
|
|
406
409
|
|
|
407
410
|
|
|
408
|
-
def generate_index_js(output_dir):
|
|
409
|
-
"""
|
|
411
|
+
def generate_index_js(results, output_dir):
|
|
412
|
+
"""直接用内存中的 results 拼出唯一产物 index.js(chatIndex + chatAll)。
|
|
413
|
+
|
|
414
|
+
不再为每个会话单独落 js 文件——每个会话的完整对话都进 chatAll,
|
|
415
|
+
HTML 只加载 index.js 即可渲染全部会话。
|
|
416
|
+
"""
|
|
410
417
|
all_entries = []
|
|
411
418
|
all_data = {}
|
|
412
419
|
|
|
413
|
-
for
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
data
|
|
425
|
-
|
|
426
|
-
all_data[sid] = data
|
|
427
|
-
all_entries.append({
|
|
428
|
-
'workspace_name': data.get('workspace_name', ''),
|
|
429
|
-
'session_name': data.get('session_name', ''),
|
|
430
|
-
'session_id': sid,
|
|
431
|
-
'js_file': fname,
|
|
432
|
-
'total_pairs': data.get('total_pairs', 0),
|
|
433
|
-
'first_time': data.get('first_time', ''),
|
|
434
|
-
'last_time': data.get('last_time', ''),
|
|
435
|
-
'work_url': data.get('work_url', ''),
|
|
436
|
-
'mtime': mtime,
|
|
437
|
-
})
|
|
438
|
-
except Exception as ex:
|
|
439
|
-
print(' [WARN] Skip %s: %s' % (fname, ex))
|
|
440
|
-
continue
|
|
420
|
+
for r in results:
|
|
421
|
+
data = r['data']
|
|
422
|
+
sid = data['session_id']
|
|
423
|
+
all_data[sid] = data
|
|
424
|
+
all_entries.append({
|
|
425
|
+
'workspace_name': data.get('workspace_name', ''),
|
|
426
|
+
'session_name': data.get('session_name', ''),
|
|
427
|
+
'session_id': sid,
|
|
428
|
+
'total_pairs': data.get('total_pairs', 0),
|
|
429
|
+
'first_time': data.get('first_time', ''),
|
|
430
|
+
'last_time': data.get('last_time', ''),
|
|
431
|
+
'work_url': data.get('work_url', ''),
|
|
432
|
+
})
|
|
441
433
|
|
|
442
434
|
now_ts = int(time.time())
|
|
443
435
|
idx = '// Chat History Index - %d session(s)\n' % len(all_entries)
|
|
@@ -546,7 +538,7 @@ def main():
|
|
|
546
538
|
|
|
547
539
|
os.makedirs(output_dir, exist_ok=True)
|
|
548
540
|
|
|
549
|
-
#
|
|
541
|
+
# 清理旧产物(含历史遗留的单会话 js)后再重新生成
|
|
550
542
|
for old_f in os.listdir(output_dir):
|
|
551
543
|
if old_f.endswith('.js') or old_f == 'index.html':
|
|
552
544
|
try:
|
|
@@ -564,8 +556,8 @@ def main():
|
|
|
564
556
|
print("\nERROR: No sessions processed successfully.")
|
|
565
557
|
sys.exit(1)
|
|
566
558
|
|
|
567
|
-
#
|
|
568
|
-
generate_index_js(output_dir)
|
|
559
|
+
# 唯一数据产物:index.js(chatIndex + chatAll)
|
|
560
|
+
generate_index_js(results, output_dir)
|
|
569
561
|
|
|
570
562
|
# Copy chat-history-template.html as index.html
|
|
571
563
|
import shutil
|