@zachjxyz/moxie 0.2.4 → 0.2.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/bin/moxie +1 -1
- package/lib/phases.sh +2 -2
- package/lib/tokens.sh +46 -20
- package/package.json +1 -1
package/bin/moxie
CHANGED
package/lib/phases.sh
CHANGED
|
@@ -324,11 +324,11 @@ command = 'codex exec -c model_reasoning_effort="xhigh" --dangerously-bypass-app
|
|
|
324
324
|
order = 1
|
|
325
325
|
|
|
326
326
|
[agents.claude]
|
|
327
|
-
command = "claude --dangerously-skip-permissions --effort max -p"
|
|
327
|
+
command = "claude --dangerously-skip-permissions --effort max --output-format json -p"
|
|
328
328
|
order = 2
|
|
329
329
|
|
|
330
330
|
[agents.qwen]
|
|
331
|
-
command = "qwen --yolo"
|
|
331
|
+
command = "qwen --yolo -o json"
|
|
332
332
|
order = 3
|
|
333
333
|
|
|
334
334
|
[settings]
|
package/lib/tokens.sh
CHANGED
|
@@ -14,38 +14,64 @@ extract_tokens() {
|
|
|
14
14
|
return
|
|
15
15
|
fi
|
|
16
16
|
|
|
17
|
-
# ---- Strategy 2:
|
|
18
|
-
#
|
|
17
|
+
# ---- Strategy 2: NDJSON / JSON array with usage block ----
|
|
18
|
+
# Claude (--output-format json) and Qwen (-o json) emit NDJSON or a JSON
|
|
19
|
+
# array. The final "type":"result" object contains a top-level "usage" key.
|
|
20
|
+
# Logs may also contain non-JSON noise (e.g. hook errors on stderr).
|
|
19
21
|
tokens=$(python3 -c "
|
|
20
22
|
import json, sys
|
|
21
23
|
try:
|
|
22
|
-
# The log may have non-JSON preamble; find the JSON object
|
|
23
24
|
with open('$logfile') as f:
|
|
24
25
|
text = f.read()
|
|
25
|
-
|
|
26
|
+
|
|
27
|
+
# Collect candidate JSON objects from three formats:
|
|
28
|
+
# 1) Single JSON object 2) JSON array 3) NDJSON (one obj per line)
|
|
29
|
+
candidates = []
|
|
30
|
+
# 1) Single object
|
|
26
31
|
try:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
obj = json.loads(text)
|
|
33
|
+
if isinstance(obj, list):
|
|
34
|
+
candidates.extend(obj) # 2) JSON array
|
|
35
|
+
elif isinstance(obj, dict):
|
|
36
|
+
candidates.append(obj)
|
|
37
|
+
except Exception:
|
|
38
|
+
# 3) NDJSON — try each line independently (skips noise)
|
|
39
|
+
for line in text.splitlines():
|
|
40
|
+
line = line.strip()
|
|
41
|
+
if not line or line[0] not in ('{', '['):
|
|
42
|
+
continue
|
|
34
43
|
try:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
obj = json.loads(line)
|
|
45
|
+
if isinstance(obj, list):
|
|
46
|
+
candidates.extend(obj)
|
|
47
|
+
elif isinstance(obj, dict):
|
|
48
|
+
candidates.append(obj)
|
|
49
|
+
except Exception:
|
|
38
50
|
continue
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
|
|
52
|
+
# Find the last 'result' object with a usage block
|
|
53
|
+
result_usage = None
|
|
54
|
+
for obj in reversed(candidates):
|
|
55
|
+
if isinstance(obj, dict) and obj.get('type') == 'result' and 'usage' in obj:
|
|
56
|
+
result_usage = obj['usage']
|
|
57
|
+
break
|
|
58
|
+
|
|
59
|
+
# Fallback: any object with a top-level 'usage' dict
|
|
60
|
+
if result_usage is None:
|
|
61
|
+
for obj in reversed(candidates):
|
|
62
|
+
if isinstance(obj, dict) and isinstance(obj.get('usage'), dict):
|
|
63
|
+
result_usage = obj['usage']
|
|
64
|
+
break
|
|
65
|
+
|
|
66
|
+
if result_usage:
|
|
67
|
+
total = result_usage.get('input_tokens', 0) + result_usage.get('output_tokens', 0)
|
|
68
|
+
total += result_usage.get('cache_creation_input_tokens', 0)
|
|
69
|
+
total += result_usage.get('cache_read_input_tokens', 0)
|
|
44
70
|
if total > 0:
|
|
45
71
|
print(total)
|
|
46
72
|
sys.exit(0)
|
|
47
73
|
sys.exit(1)
|
|
48
|
-
except:
|
|
74
|
+
except Exception:
|
|
49
75
|
sys.exit(1)
|
|
50
76
|
" 2>/dev/null)
|
|
51
77
|
if [ -n "$tokens" ] && [ "$tokens" -gt 0 ] 2>/dev/null; then
|
package/package.json
CHANGED