okstra 0.30.2 → 0.30.3
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/okstra
CHANGED
|
@@ -17,6 +17,7 @@ const COMMANDS = new Map([
|
|
|
17
17
|
["render-bundle", () => import("../src/render-bundle.mjs").then((m) => m.run)],
|
|
18
18
|
["render-views", () => import("../src/render-views.mjs").then((m) => m.run)],
|
|
19
19
|
["wizard", () => import("../src/wizard.mjs").then((m) => m.run)],
|
|
20
|
+
["token-usage", () => import("../src/token-usage.mjs").then((m) => m.run)],
|
|
20
21
|
]);
|
|
21
22
|
|
|
22
23
|
const USAGE = `okstra — multi-agent cross-verification orchestrator for Claude Code
|
|
@@ -55,6 +56,9 @@ Introspection commands (JSON output, used by skills to avoid python heredocs):
|
|
|
55
56
|
python3 -m okstra_ctl.run --render-only)
|
|
56
57
|
wizard Drive the okstra-run interactive input state machine
|
|
57
58
|
(init / step / render-args / confirmation)
|
|
59
|
+
token-usage Collect token usage for a run (wraps the installed
|
|
60
|
+
okstra-token-usage.py so skills avoid emitting
|
|
61
|
+
python3 "$HOME/..." invocations).
|
|
58
62
|
|
|
59
63
|
Global options:
|
|
60
64
|
--version Print okstra version and exit
|
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
|
@@ -64,14 +64,10 @@ find <LOGS_ROOT> -type f -path '*/runs/*/prompts/*.log' \
|
|
|
64
64
|
|
|
65
65
|
The columns produced are `size_bytes | mtime_epoch | path`.
|
|
66
66
|
|
|
67
|
-
On macOS, `find -printf` is unavailable. Fall back to
|
|
67
|
+
On macOS, `find -printf` is unavailable. Fall back to `-exec stat` — again substitute the literal `<LOGS_ROOT>`. The `-exec ... {} +` form contains no shell variables and no `$(...)`, so the `Bash(find:*)` permission match holds:
|
|
68
68
|
|
|
69
69
|
```bash
|
|
70
|
-
find <LOGS_ROOT> -type f -path '*/runs/*/prompts/*.log' 2>/dev/null
|
|
71
|
-
| while IFS= read -r p; do
|
|
72
|
-
stat -f '%z%t%m%t%N' "$p"
|
|
73
|
-
done \
|
|
74
|
-
| sort -k1,1nr
|
|
70
|
+
find <LOGS_ROOT> -type f -path '*/runs/*/prompts/*.log' -exec stat -f '%z%t%m%t%N' {} + 2>/dev/null | sort -k1,1nr
|
|
75
71
|
```
|
|
76
72
|
|
|
77
73
|
If the result is empty, report `No wrapper log files found under <projectRoot>` and exit.
|
|
@@ -432,15 +432,13 @@ Token usage is collected from agent session transcripts after the run, NOT from
|
|
|
432
432
|
|
|
433
433
|
### How to Collect
|
|
434
434
|
|
|
435
|
-
At the **start of Phase 7** (persistence), run the helper
|
|
435
|
+
At the **start of Phase 7** (persistence), run the helper via the okstra CLI with the path to this run's `team-state.json`. Substitute `<runDirectoryPath>` with a literal absolute path (no shell variables, no `$(...)`) so the `Bash(okstra:*)` permission match holds:
|
|
436
436
|
|
|
437
437
|
```bash
|
|
438
|
-
|
|
439
|
-
<runDirectoryPath>/state/team-state-<task-type>-<seq>.json \
|
|
440
|
-
--write --summary
|
|
438
|
+
okstra token-usage /abs/path/to/run/state/team-state-<task-type>-<seq>.json --write --summary
|
|
441
439
|
```
|
|
442
440
|
|
|
443
|
-
|
|
441
|
+
`okstra token-usage` is a thin Node-side wrapper around the python helper installed at `~/.okstra/bin/okstra-token-usage.py`. Calling the python script directly with `python3 "$HOME/..."` is forbidden — the `$HOME` expansion breaks the literal-token permission match and forces a confirmation prompt every call.
|
|
444
442
|
|
|
445
443
|
The script reads:
|
|
446
444
|
- `~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl` for the lead and every Claude-side worker (Claude worker, Report writer worker, plus the Claude wrappers around Codex/Gemini workers). Sessions are discovered by `teamName: okstra-<task-id>`, lead is identified by `lead.sessionId`, and other workers are identified by `agentName` (e.g. `claude-worker`, `codex-worker`, `gemini-worker`, `report-writer`).
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { promises as fs } from "node:fs";
|
|
4
|
+
import { resolvePaths } from "./paths.mjs";
|
|
5
|
+
|
|
6
|
+
const USAGE = `okstra token-usage — collect token usage for a run
|
|
7
|
+
|
|
8
|
+
Wraps the python helper (\`okstra-token-usage.py\`) installed under
|
|
9
|
+
\`~/.okstra/bin/\` so skills can call this command without emitting a
|
|
10
|
+
shell-expansion-bearing \`python3 "$HOME/..."\` invocation (which would
|
|
11
|
+
break \`Bash(okstra:*)\` permission matching and force a confirmation
|
|
12
|
+
prompt every call).
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
okstra token-usage <state-file> [--write] [--summary] [...]
|
|
16
|
+
|
|
17
|
+
Arguments and flags after the state-file path are forwarded verbatim to
|
|
18
|
+
the python helper. See \`python3 ~/.okstra/bin/okstra-token-usage.py --help\`
|
|
19
|
+
for the full option list.
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
export async function run(args) {
|
|
23
|
+
if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
24
|
+
process.stdout.write(USAGE);
|
|
25
|
+
return args.length === 0 ? 2 : 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const paths = await resolvePaths();
|
|
29
|
+
const script = join(paths.bin, "okstra-token-usage.py");
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
await fs.access(script);
|
|
33
|
+
} catch {
|
|
34
|
+
process.stderr.write(
|
|
35
|
+
`error: ${script} not found — run 'okstra install' (or 'okstra ensure-installed') first\n`,
|
|
36
|
+
);
|
|
37
|
+
return 1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return await new Promise((resolve) => {
|
|
41
|
+
const child = spawn("python3", [script, ...args], {
|
|
42
|
+
stdio: "inherit",
|
|
43
|
+
env: { ...process.env, PYTHONPATH: paths.pythonpath },
|
|
44
|
+
});
|
|
45
|
+
child.on("error", (err) => {
|
|
46
|
+
process.stderr.write(`error: failed to spawn python3: ${err.message}\n`);
|
|
47
|
+
resolve(1);
|
|
48
|
+
});
|
|
49
|
+
child.on("close", (code) => resolve(typeof code === "number" ? code : 1));
|
|
50
|
+
});
|
|
51
|
+
}
|