@xdfnet/ispeak 1.6.4 → 1.6.5
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/configs/hook-speak.sh +25 -33
- package/package.json +1 -1
package/configs/hook-speak.sh
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# Stop Hook:
|
|
2
|
+
# Stop Hook: 只播报本次停止时的最后一条 assistant 回复
|
|
3
3
|
# iAgent 调用 Claude 时设 ISPEAK_SKIP=1,此时跳过(iAgent 自己播)
|
|
4
4
|
[[ "$ISPEAK_SKIP" == "1" ]] && exit 0
|
|
5
5
|
|
|
@@ -32,87 +32,79 @@ json_value() {
|
|
|
32
32
|
printf "%s" "$input" | sed -n "s/.*\"$key\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p"
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
extract_last_assistant_text() {
|
|
36
36
|
local transcript="$1"
|
|
37
|
-
local cutoff="$2"
|
|
38
37
|
|
|
39
38
|
if command -v node >/dev/null 2>&1; then
|
|
40
39
|
node -e '
|
|
41
40
|
const fs = require("fs");
|
|
42
41
|
const file = process.argv[1];
|
|
43
|
-
|
|
44
|
-
const out = [];
|
|
42
|
+
let last = "";
|
|
45
43
|
|
|
46
44
|
function collectText(content) {
|
|
45
|
+
const out = [];
|
|
47
46
|
if (typeof content === "string") {
|
|
48
|
-
|
|
49
|
-
return;
|
|
47
|
+
return content;
|
|
50
48
|
}
|
|
51
|
-
if (!Array.isArray(content)) return;
|
|
49
|
+
if (!Array.isArray(content)) return "";
|
|
52
50
|
for (const item of content) {
|
|
53
51
|
if (item && typeof item.text === "string") out.push(item.text);
|
|
54
52
|
}
|
|
53
|
+
return out.join(" ");
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
for (const line of fs.readFileSync(file, "utf8").split(/\r?\n/)) {
|
|
58
57
|
if (!line.trim()) continue;
|
|
59
58
|
try {
|
|
60
59
|
const event = JSON.parse(line);
|
|
61
|
-
if (
|
|
62
|
-
if (event.role === "assistant") collectText(event.content);
|
|
63
|
-
if (event.message && event.message.role === "assistant") collectText(event.message.content);
|
|
60
|
+
if (event.role === "assistant") last = collectText(event.content) || last;
|
|
61
|
+
if (event.message && event.message.role === "assistant") last = collectText(event.message.content) || last;
|
|
64
62
|
} catch (_) {}
|
|
65
63
|
}
|
|
66
|
-
process.stdout.write(
|
|
67
|
-
' "$transcript"
|
|
64
|
+
process.stdout.write(last);
|
|
65
|
+
' "$transcript" 2>/dev/null
|
|
68
66
|
return
|
|
69
67
|
fi
|
|
70
68
|
|
|
71
|
-
awk
|
|
69
|
+
awk '
|
|
72
70
|
{
|
|
73
|
-
if (match($0, /"timestamp"[[:space:]]*:[[:space:]]*[0-9]+/)) {
|
|
74
|
-
ts = substr($0, RSTART, RLENGTH)
|
|
75
|
-
gsub(/[^0-9]/, "", ts)
|
|
76
|
-
ts = int(ts)
|
|
77
|
-
if (ts < cutoff) next
|
|
78
|
-
}
|
|
79
|
-
|
|
80
71
|
if (match($0, /"role"[[:space:]]*:[[:space:]]*"assistant"/)) {
|
|
81
72
|
if (match($0, /"content"[[:space:]]*:[[:space:]]*\[/)) {
|
|
82
73
|
gsub(/[^{]*\[/, "", $0)
|
|
83
74
|
gsub(/\].*/, "", $0)
|
|
75
|
+
msg = ""
|
|
84
76
|
while (match($0, /"text"[[:space:]]*:[[:space:]]*"[^"]*"/)) {
|
|
85
77
|
t = substr($0, RSTART, RLENGTH)
|
|
86
78
|
gsub(/"text"[[:space:]]*:[[:space:]]*"/, "", t)
|
|
87
79
|
gsub(/"$/, "", t)
|
|
88
|
-
if (t != "")
|
|
80
|
+
if (t != "") msg = msg " " t
|
|
89
81
|
$0 = substr($0, RSTART + RLENGTH)
|
|
90
82
|
}
|
|
83
|
+
if (msg != "") last = msg
|
|
91
84
|
} else if (match($0, /"content"[[:space:]]*:[[:space:]]*"[^"]*"/)) {
|
|
92
85
|
t = substr($0, RSTART, RLENGTH)
|
|
93
86
|
gsub(/"content"[[:space:]]*:[[:space:]]*"/, "", t)
|
|
94
87
|
gsub(/"$/, "", t)
|
|
95
|
-
if (t != "")
|
|
88
|
+
if (t != "") last = t
|
|
96
89
|
}
|
|
97
90
|
}
|
|
98
91
|
}
|
|
99
|
-
|
|
92
|
+
END {
|
|
93
|
+
gsub(/^[[:space:]]+|[[:space:]]+$/, "", last)
|
|
94
|
+
if (last != "") print last
|
|
95
|
+
}
|
|
96
|
+
' "$transcript" 2>/dev/null
|
|
100
97
|
}
|
|
101
98
|
|
|
102
|
-
# 从 stdin JSON
|
|
99
|
+
# 从 stdin JSON 提取最后一条消息;没有时再从 transcript 取最后一条 assistant
|
|
103
100
|
transcript=$(json_value "transcript_path")
|
|
104
101
|
last_msg=$(json_value "last_assistant_message")
|
|
105
102
|
|
|
106
103
|
all_text="$last_msg"
|
|
107
104
|
|
|
108
|
-
#
|
|
109
|
-
if [[ -n "$transcript" && -f "$transcript" ]]; then
|
|
110
|
-
|
|
111
|
-
cutoff=$(($(date +%s) * 1000 - 30000))
|
|
112
|
-
|
|
113
|
-
# 优先用 JSON parser,Node 不存在时回退到简易 awk。
|
|
114
|
-
extra=$(extract_recent_assistant_text "$transcript" "$cutoff")
|
|
115
|
-
|
|
105
|
+
# Claude Code 部分版本不提供 last_assistant_message,此时只取 transcript 最后一条 assistant。
|
|
106
|
+
if [[ -z "$all_text" && -n "$transcript" && -f "$transcript" ]]; then
|
|
107
|
+
extra=$(extract_last_assistant_text "$transcript")
|
|
116
108
|
if [[ -n "$extra" ]]; then
|
|
117
109
|
all_text="$extra"
|
|
118
110
|
fi
|
package/package.json
CHANGED