@scrylog/cli 0.2.0 → 0.3.0

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.
Files changed (94) hide show
  1. package/README.md +46 -7
  2. package/package.json +13 -10
  3. package/src/api/docs/sessions.ts +3 -2
  4. package/src/api/docs/system.ts +3 -0
  5. package/src/api/docs/usage.ts +2 -2
  6. package/src/api/routes/sessions.ts +6 -2
  7. package/src/api/routes/system.ts +4 -1
  8. package/src/api/routes/webhooks/claude.ts +8 -0
  9. package/src/api/routes/webhooks/opencode.ts +1 -0
  10. package/src/api/schemas/common.ts +8 -0
  11. package/src/api/schemas/sessions.ts +15 -1
  12. package/src/api/schemas/usage.ts +1 -0
  13. package/src/cli/commands.ts +85 -3
  14. package/src/cli/index.ts +18 -0
  15. package/src/cli/program.ts +175 -0
  16. package/src/daemon/collectors/claude/collector.ts +1 -1
  17. package/src/daemon/collectors/claude/discovery.ts +2 -23
  18. package/src/daemon/collectors/codex/collector.ts +3 -3
  19. package/src/daemon/collectors/git.ts +60 -2
  20. package/src/daemon/collectors/herdr/collector.ts +128 -0
  21. package/src/daemon/collectors/herdr/index.ts +2 -0
  22. package/src/daemon/collectors/herdr/normalization.ts +111 -0
  23. package/src/daemon/collectors/opencode-db.ts +17 -5
  24. package/src/daemon/collectors/tmux/collector.ts +21 -1
  25. package/src/daemon/collectors/tmux/enrichment.ts +1 -0
  26. package/src/daemon/collectors/usage.ts +115 -49
  27. package/src/daemon/index.ts +2 -0
  28. package/src/daemon/store.ts +44 -2
  29. package/src/postinstall.cjs +11 -0
  30. package/src/shared/config.ts +33 -0
  31. package/src/shared/session-exclusions.ts +5 -0
  32. package/src/shared/session-view.ts +67 -6
  33. package/src/shared/terminal-capabilities.ts +130 -0
  34. package/src/shared/types.ts +33 -2
  35. package/src/shared/version.ts +44 -0
  36. package/src/tui/App.tsx +240 -0
  37. package/src/tui/assets/providers/SOURCES.md +21 -0
  38. package/src/tui/assets/providers/claude-terminal.png +0 -0
  39. package/src/tui/assets/providers/claude-terminal.svg +62 -0
  40. package/src/tui/assets/providers/claude.png +0 -0
  41. package/src/tui/assets/providers/copilot.png +0 -0
  42. package/src/tui/assets/providers/copilot.svg +44 -0
  43. package/src/tui/assets/providers/openai-terminal.png +0 -0
  44. package/src/tui/assets/providers/openai-terminal.svg +62 -0
  45. package/src/tui/assets/providers/openai.png +0 -0
  46. package/src/tui/assets/providers/opencode-terminal.png +0 -0
  47. package/src/tui/assets/providers/opencode-terminal.svg +62 -0
  48. package/src/tui/assets/providers/opencode.png +0 -0
  49. package/src/tui/assets/providers/shell.png +0 -0
  50. package/src/tui/assets/providers/shell.svg +23 -0
  51. package/src/tui/assets/providers/unknown.png +0 -0
  52. package/src/tui/assets/providers/unknown.svg +23 -0
  53. package/src/tui/client.ts +22 -8
  54. package/src/tui/components/BindingsModal.tsx +82 -0
  55. package/src/tui/components/DashboardHeader.tsx +256 -0
  56. package/src/tui/components/KeyBar.tsx +67 -0
  57. package/src/tui/components/SessionDetailModal.tsx +169 -0
  58. package/src/tui/components/SessionsView.tsx +184 -0
  59. package/src/tui/components/TabBar.tsx +40 -0
  60. package/src/tui/components/UsageSummaryPane.tsx +272 -0
  61. package/src/tui/components/UsageView.tsx +242 -0
  62. package/src/tui/components/tool-icons.ts +24 -0
  63. package/src/tui/index.tsx +106 -0
  64. package/src/tui/keybindings.ts +49 -0
  65. package/src/tui/model.ts +295 -0
  66. package/src/tui/state.ts +77 -0
  67. package/src/tui/theme.ts +110 -0
  68. package/src/usage/README.md +3 -3
  69. package/src/usage/clients/anthropic-oauth-usage.ts +100 -20
  70. package/src/usage/clients/anthropic-web-usage.ts +133 -49
  71. package/src/usage/clients/claude-cli-probe.ts +458 -132
  72. package/src/usage/clients/codex-app-server.ts +354 -0
  73. package/src/usage/clients/codex-oauth-usage.ts +143 -16
  74. package/src/usage/clients/opencode-go-api.ts +100 -0
  75. package/src/usage/clients/opencode-go-usage.ts +163 -27
  76. package/src/usage/errors.ts +33 -0
  77. package/src/usage/local/opencode-go-local.ts +194 -0
  78. package/src/usage/mappers.ts +24 -15
  79. package/src/usage/planner.ts +89 -25
  80. package/src/usage/providers/claude.ts +203 -83
  81. package/src/usage/providers/codex.ts +92 -11
  82. package/src/usage/providers/opencode-go.ts +94 -16
  83. package/src/usage/resolvers/browser-cookies.ts +407 -69
  84. package/src/usage/resolvers/claude-auth.ts +140 -40
  85. package/src/usage/resolvers/claude-desktop-auth.ts +15 -5
  86. package/src/usage/resolvers/claude-web.ts +72 -6
  87. package/src/usage/resolvers/codex-auth.ts +74 -17
  88. package/src/usage/resolvers/keychain-coordinator.ts +397 -0
  89. package/src/usage/resolvers/opencode-go-web.ts +47 -6
  90. package/src/usage/types.ts +37 -2
  91. package/src/usage/web-session-cache.ts +37 -0
  92. package/src/neo-blessed.d.ts +0 -4
  93. package/src/tui/app.ts +0 -920
  94. package/src/tui/index.ts +0 -217
package/src/tui/index.ts DELETED
@@ -1,217 +0,0 @@
1
- #!/usr/bin/env bun
2
- import { readFileSync } from "node:fs";
3
- import { join } from "node:path";
4
- import { loadConfig } from "../shared/config";
5
- import type { ScrylogConfig } from "../shared/config";
6
- import { DaemonClient } from "./client";
7
- import { DashboardApp } from "./app";
8
- import { getServiceManagerOrThrow } from "../service/manager";
9
- import type { ServiceStatus } from "../service/manager";
10
- import {
11
- runClaudeHooksInstall,
12
- runCodexHooksInstall,
13
- runOpenCodeHooksInstall,
14
- runConfigCommand,
15
- runDaemonCommand,
16
- runDoctor,
17
- runLogsCommand,
18
- runSetupWizard,
19
- runTokenCommand,
20
- } from "../cli/commands";
21
-
22
- export function printHelp() {
23
- process.stdout.write(`scrylog
24
-
25
- Usage:
26
- scrylog [--config <path>]
27
- scrylog setup [--config <path>]
28
- scrylog doctor [--config <path>]
29
- scrylog daemon <install|uninstall|start|stop|restart|status> [--config <path>]
30
- scrylog config <show|path> [--config <path>]
31
- scrylog token <mcp|api|both> <generate|rotate|clear> [--config <path>]
32
- scrylog claude hooks install [--config <path>]
33
- scrylog codex hooks install [--config <path>]
34
- scrylog opencode hooks install [--config <path>]
35
- scrylog logs [--config <path>]
36
-
37
- Options:
38
- --config <path> Load config from an explicit path
39
- -h, --help Show this help text
40
- -V, --version Show the installed scrylog version
41
-
42
- See also:
43
- scrylogd Background daemon (collects sessions and serves API/MCP)
44
- scrylog-mcp stdio MCP server that proxies daemon data to AI agents
45
- scrylog-claude-hook Claude Code lifecycle hook binary
46
- scrylog-codex-hook Codex CLI lifecycle hook binary
47
-
48
- Notes:
49
- Running scrylog with no subcommand launches the TUI dashboard.
50
- 'scrylog claude hooks install' configures Claude Code to emit lifecycle
51
- hooks that scrylog uses for real-time session state tracking.
52
- 'scrylog codex hooks install' configures Codex CLI to emit lifecycle
53
- hooks that scrylog uses for real-time session state tracking.
54
- 'scrylog opencode hooks install' registers the scrylog plugin in
55
- OpenCode configuration for real-time session state tracking.
56
- `);
57
- }
58
-
59
- export function printVersion() {
60
- const pkg = JSON.parse(readFileSync(join(import.meta.dir, "..", "..", "package.json"), "utf8")) as { version?: string };
61
- process.stdout.write(`scrylog ${pkg.version ?? "unknown"}\n`);
62
- }
63
-
64
- function extractGlobalOptions(argv: string[]) {
65
- const args = [...argv];
66
- let configPath: string | undefined;
67
- const configFlagIndex = args.indexOf("--config");
68
- if (configFlagIndex >= 0) {
69
- configPath = args[configFlagIndex + 1];
70
- args.splice(configFlagIndex, configPath ? 2 : 1);
71
- }
72
- return { args, options: { configPath } };
73
- }
74
-
75
- async function launchTui(configPath?: string): Promise<void> {
76
- const config = loadConfig(configPath);
77
-
78
- const client = new DaemonClient(config);
79
- if (!(await client.isHealthy())) {
80
- printDaemonNotRunning(config, getServiceManagerOrThrow().platform);
81
- process.exit(1);
82
- }
83
-
84
- const app = new DashboardApp(client, {
85
- refreshMs: config.tui.refreshMs,
86
- showStandalone: config.tui.showStandalone,
87
- }, config);
88
-
89
- app.start();
90
- }
91
-
92
- /**
93
- * Print a platform-aware error explaining that the daemon is not running and
94
- * listing the commands to verify, start, and inspect it. Exported for testing.
95
- */
96
- export function printDaemonNotRunning(config: ScrylogConfig, platform: ServiceStatus["platform"]): void {
97
- const logPath = config.daemon.logFilePath;
98
- const hasService = platform === "macos" || platform === "linux";
99
-
100
- process.stderr.write("scrylog: daemon is not running.\n\n");
101
- process.stderr.write("Verify:\n");
102
- process.stderr.write(" scrylog daemon status\n");
103
- process.stderr.write(" scrylog doctor\n\n");
104
- process.stderr.write("Start it (pick one):\n");
105
- if (hasService) {
106
- process.stderr.write(" scrylog daemon start # user service\n");
107
- process.stderr.write(" scrylog daemon install # if the service is not installed yet\n");
108
- }
109
- process.stderr.write(" scrylogd # foreground\n\n");
110
- process.stderr.write(`Logs:\n scrylog logs\n ${logPath}\n`);
111
-
112
- if (!hasService) {
113
- const note =
114
- platform === "wsl"
115
- ? "Service management is unavailable (WSL without systemd). Run scrylogd directly or enable systemd in /etc/wsl.conf."
116
- : platform === "linux-no-systemd"
117
- ? "Service management is unavailable (no systemd user session). Run scrylogd directly."
118
- : "Service management is unavailable on this platform. Run scrylogd directly or use Task Scheduler.";
119
- process.stderr.write(`\n${note}\n`);
120
- }
121
- }
122
-
123
- export const TOKEN_USAGE = "Usage: scrylog token <mcp|api|both> <generate|rotate|clear>";
124
-
125
- export function parseTokenArgs(args: string[]): { target: "mcp" | "api" | "both"; action: string } {
126
- const [target, action] = args;
127
- if (!target) throw new Error(`Missing token target (mcp, api, or both). ${TOKEN_USAGE}`);
128
- if (!action) throw new Error(`Missing token action (generate, rotate, or clear). ${TOKEN_USAGE}`);
129
- if (target !== "mcp" && target !== "api" && target !== "both") {
130
- throw new Error(`Invalid token target: ${target}. Must be mcp, api, or both. ${TOKEN_USAGE}`);
131
- }
132
- return { target, action };
133
- }
134
-
135
- async function main(): Promise<void> {
136
- const { args, options } = extractGlobalOptions(process.argv.slice(2));
137
- if (args.includes("--version") || args.includes("-V")) {
138
- printVersion();
139
- process.exit(0);
140
- }
141
- if (args.includes("--help") || args.includes("-h")) {
142
- printHelp();
143
- process.exit(0);
144
- }
145
-
146
- const [command, subcommand] = args;
147
- if (!command) {
148
- await launchTui(options.configPath ?? process.env.SCRYLOG_CONFIG);
149
- return;
150
- }
151
-
152
- if (command === "setup") {
153
- await runSetupWizard(options);
154
- return;
155
- }
156
-
157
- if (command === "doctor") {
158
- await runDoctor(options);
159
- return;
160
- }
161
-
162
- if (command === "daemon") {
163
- if (!subcommand) throw new Error("Missing daemon action");
164
- await runDaemonCommand(subcommand, options);
165
- return;
166
- }
167
-
168
- if (command === "config") {
169
- if (!subcommand) throw new Error("Missing config action");
170
- runConfigCommand(subcommand, options);
171
- return;
172
- }
173
-
174
- if (command === "token") {
175
- const { target, action } = parseTokenArgs([subcommand, args[2]]);
176
- runTokenCommand(target, action, options);
177
- return;
178
- }
179
-
180
- if (command === "logs") {
181
- await runLogsCommand(options);
182
- return;
183
- }
184
-
185
- if (command === "claude") {
186
- if (subcommand === "hooks" && args[2] === "install") {
187
- await runClaudeHooksInstall(options);
188
- return;
189
- }
190
- throw new Error(`Unknown claude subcommand: ${subcommand ?? "(none)"}`);
191
- }
192
-
193
- if (command === "codex") {
194
- if (subcommand === "hooks" && args[2] === "install") {
195
- await runCodexHooksInstall(options);
196
- return;
197
- }
198
- throw new Error(`Unknown codex subcommand: ${subcommand ?? "(none)"}`);
199
- }
200
-
201
- if (command === "opencode") {
202
- if (subcommand === "hooks" && args[2] === "install") {
203
- await runOpenCodeHooksInstall(options);
204
- return;
205
- }
206
- throw new Error(`Unknown opencode subcommand: ${subcommand ?? "(none)"}`);
207
- }
208
-
209
- throw new Error(`Unknown command: ${command}`);
210
- }
211
-
212
- if (import.meta.main) {
213
- main().catch((err) => {
214
- process.stderr.write(`scrylog: fatal: ${err instanceof Error ? err.message : String(err)}\n`);
215
- process.exit(1);
216
- });
217
- }