@scrylog/cli 0.3.0 → 0.3.1

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 (195) hide show
  1. package/dist/assets/highlights-eq9cgrbb.scm +604 -0
  2. package/dist/assets/highlights-ghv9g403.scm +205 -0
  3. package/dist/assets/highlights-hk7bwhj4.scm +284 -0
  4. package/dist/assets/highlights-r812a2qc.scm +150 -0
  5. package/dist/assets/highlights-x6tmsnaa.scm +115 -0
  6. package/dist/assets/injections-73j83es3.scm +27 -0
  7. package/dist/assets/parser.worker-xyhf83rf.js +4563 -0
  8. package/dist/assets/tree-sitter-3jzf13jk.wasm +0 -0
  9. package/dist/assets/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
  10. package/dist/assets/tree-sitter-markdown-411r6y9b.wasm +0 -0
  11. package/dist/assets/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
  12. package/dist/assets/tree-sitter-typescript-zxjzwt75.wasm +0 -0
  13. package/dist/assets/tree-sitter-zig-e78zbjpm.wasm +0 -0
  14. package/dist/cli/index.js +499 -0
  15. package/dist/cli/index.js.map +61 -0
  16. package/dist/daemon/index.js +670 -0
  17. package/dist/daemon/index.js.map +1475 -0
  18. package/dist/hooks/claude.js +6 -0
  19. package/dist/hooks/claude.js.map +11 -0
  20. package/dist/hooks/codex.js +10 -0
  21. package/dist/hooks/codex.js.map +11 -0
  22. package/dist/mcp/index.js +100 -0
  23. package/dist/mcp/index.js.map +231 -0
  24. package/dist/oc/index.js +21 -0
  25. package/dist/oc/index.js.map +10 -0
  26. package/package.json +16 -13
  27. package/src/api/docs/sessions.keys.ts +0 -6
  28. package/src/api/docs/sessions.ts +0 -82
  29. package/src/api/docs/system.keys.ts +0 -5
  30. package/src/api/docs/system.ts +0 -69
  31. package/src/api/docs/usage.keys.ts +0 -3
  32. package/src/api/docs/usage.ts +0 -24
  33. package/src/api/docs/webhooks.keys.ts +0 -5
  34. package/src/api/docs/webhooks.ts +0 -78
  35. package/src/api/index.ts +0 -18
  36. package/src/api/lib/auth.ts +0 -43
  37. package/src/api/lib/errors.ts +0 -28
  38. package/src/api/lib/route.ts +0 -18
  39. package/src/api/models/claude.ts +0 -4
  40. package/src/api/models/codex.ts +0 -4
  41. package/src/api/models/opencode.ts +0 -11
  42. package/src/api/routes/sessions.ts +0 -51
  43. package/src/api/routes/system.ts +0 -106
  44. package/src/api/routes/usage.ts +0 -15
  45. package/src/api/routes/webhooks/claude.ts +0 -149
  46. package/src/api/routes/webhooks/codex.ts +0 -175
  47. package/src/api/routes/webhooks/opencode.ts +0 -112
  48. package/src/api/routes.ts +0 -93
  49. package/src/api/schemas/common.ts +0 -29
  50. package/src/api/schemas/sessions.ts +0 -86
  51. package/src/api/schemas/usage.ts +0 -51
  52. package/src/api/schemas/webhooks/claude.ts +0 -32
  53. package/src/api/schemas/webhooks/codex.ts +0 -34
  54. package/src/api/schemas/webhooks/opencode.ts +0 -29
  55. package/src/api/services/claude-session.ts +0 -9
  56. package/src/api/services/codex-session.ts +0 -8
  57. package/src/api/services/opencode-session.ts +0 -73
  58. package/src/classifier.ts +0 -206
  59. package/src/cli/commands.ts +0 -714
  60. package/src/cli/index.ts +0 -18
  61. package/src/cli/program.ts +0 -175
  62. package/src/cli/prompt.ts +0 -44
  63. package/src/daemon/cli.ts +0 -27
  64. package/src/daemon/collectors/claude/README.md +0 -108
  65. package/src/daemon/collectors/claude/collector.ts +0 -423
  66. package/src/daemon/collectors/claude/discovery.ts +0 -127
  67. package/src/daemon/collectors/claude/hooks.ts +0 -181
  68. package/src/daemon/collectors/claude/index.ts +0 -8
  69. package/src/daemon/collectors/claude/jsonl.ts +0 -85
  70. package/src/daemon/collectors/claude/transcript.ts +0 -246
  71. package/src/daemon/collectors/codex/README.md +0 -147
  72. package/src/daemon/collectors/codex/collector.ts +0 -661
  73. package/src/daemon/collectors/codex/discovery.ts +0 -104
  74. package/src/daemon/collectors/codex/hooks.ts +0 -179
  75. package/src/daemon/collectors/codex/index.ts +0 -7
  76. package/src/daemon/collectors/codex/jsonl.ts +0 -108
  77. package/src/daemon/collectors/codex/transcript.ts +0 -299
  78. package/src/daemon/collectors/git.ts +0 -353
  79. package/src/daemon/collectors/herdr/collector.ts +0 -128
  80. package/src/daemon/collectors/herdr/index.ts +0 -2
  81. package/src/daemon/collectors/herdr/normalization.ts +0 -111
  82. package/src/daemon/collectors/opencode/README.md +0 -106
  83. package/src/daemon/collectors/opencode/collector.ts +0 -545
  84. package/src/daemon/collectors/opencode/discovery.ts +0 -257
  85. package/src/daemon/collectors/opencode/index.ts +0 -5
  86. package/src/daemon/collectors/opencode/session-builder.ts +0 -59
  87. package/src/daemon/collectors/opencode-db.ts +0 -294
  88. package/src/daemon/collectors/process.ts +0 -201
  89. package/src/daemon/collectors/tmux/README.md +0 -128
  90. package/src/daemon/collectors/tmux/collector.ts +0 -492
  91. package/src/daemon/collectors/tmux/enrichment.ts +0 -242
  92. package/src/daemon/collectors/tmux/index.ts +0 -4
  93. package/src/daemon/collectors/tmux/parse.ts +0 -128
  94. package/src/daemon/collectors/usage.ts +0 -259
  95. package/src/daemon/index.ts +0 -209
  96. package/src/daemon/scheduler.ts +0 -86
  97. package/src/daemon/store.ts +0 -630
  98. package/src/detect.ts +0 -143
  99. package/src/hooks/claude.ts +0 -596
  100. package/src/hooks/codex.ts +0 -517
  101. package/src/legacy/classifier.ts +0 -215
  102. package/src/legacy/detect.ts +0 -143
  103. package/src/mcp/client-server.ts +0 -21
  104. package/src/mcp/http-handler.ts +0 -126
  105. package/src/mcp/index.ts +0 -61
  106. package/src/mcp/resources/config-current.ts +0 -19
  107. package/src/mcp/resources/index.ts +0 -11
  108. package/src/mcp/resources/sessions-active.ts +0 -19
  109. package/src/mcp/resources/sessions-all.ts +0 -19
  110. package/src/mcp/schemas/sessions.ts +0 -3
  111. package/src/mcp/server.ts +0 -17
  112. package/src/mcp/services/api-adapter.ts +0 -48
  113. package/src/mcp/services/data-source.ts +0 -22
  114. package/src/mcp/services/store-adapter.ts +0 -40
  115. package/src/mcp/tools/get-active-sessions.ts +0 -16
  116. package/src/mcp/tools/get-session-children.ts +0 -24
  117. package/src/mcp/tools/get-session-messages.ts +0 -24
  118. package/src/mcp/tools/get-session.ts +0 -23
  119. package/src/mcp/tools/index.ts +0 -15
  120. package/src/mcp/tools/list-sessions.ts +0 -16
  121. package/src/oc/index.ts +0 -302
  122. package/src/service/manager.ts +0 -284
  123. package/src/shared/config.ts +0 -511
  124. package/src/shared/ipc.ts +0 -80
  125. package/src/shared/logger.ts +0 -248
  126. package/src/shared/paths.ts +0 -73
  127. package/src/shared/platform.ts +0 -110
  128. package/src/shared/session-exclusions.ts +0 -5
  129. package/src/shared/session-view.ts +0 -302
  130. package/src/shared/terminal-capabilities.ts +0 -130
  131. package/src/shared/types.ts +0 -330
  132. package/src/shared/utils.ts +0 -339
  133. package/src/shared/version.ts +0 -44
  134. package/src/tui/App.tsx +0 -240
  135. package/src/tui/client.ts +0 -163
  136. package/src/tui/components/BindingsModal.tsx +0 -82
  137. package/src/tui/components/DashboardHeader.tsx +0 -256
  138. package/src/tui/components/KeyBar.tsx +0 -67
  139. package/src/tui/components/SessionDetailModal.tsx +0 -169
  140. package/src/tui/components/SessionsView.tsx +0 -184
  141. package/src/tui/components/TabBar.tsx +0 -40
  142. package/src/tui/components/UsageSummaryPane.tsx +0 -272
  143. package/src/tui/components/UsageView.tsx +0 -242
  144. package/src/tui/components/tool-icons.ts +0 -24
  145. package/src/tui/index.tsx +0 -106
  146. package/src/tui/keybindings.ts +0 -49
  147. package/src/tui/model.ts +0 -295
  148. package/src/tui/state.ts +0 -77
  149. package/src/tui/theme.ts +0 -110
  150. package/src/types.ts +0 -113
  151. package/src/usage/README.md +0 -323
  152. package/src/usage/api.ts +0 -99
  153. package/src/usage/client.ts +0 -3
  154. package/src/usage/clients/anthropic-oauth-usage.ts +0 -258
  155. package/src/usage/clients/anthropic-web-usage.ts +0 -329
  156. package/src/usage/clients/claude-cli-probe.ts +0 -513
  157. package/src/usage/clients/codex-app-server.ts +0 -354
  158. package/src/usage/clients/codex-oauth-usage.ts +0 -240
  159. package/src/usage/clients/opencode-go-api.ts +0 -100
  160. package/src/usage/clients/opencode-go-usage.ts +0 -577
  161. package/src/usage/errors.ts +0 -33
  162. package/src/usage/index.ts +0 -20
  163. package/src/usage/local/opencode-go-local.ts +0 -194
  164. package/src/usage/mappers.ts +0 -282
  165. package/src/usage/planner.ts +0 -118
  166. package/src/usage/providers/claude.ts +0 -233
  167. package/src/usage/providers/codex.ts +0 -115
  168. package/src/usage/providers/opencode-go.ts +0 -115
  169. package/src/usage/resolver.ts +0 -4
  170. package/src/usage/resolvers/browser-cookies.ts +0 -808
  171. package/src/usage/resolvers/claude-auth.ts +0 -208
  172. package/src/usage/resolvers/claude-cli.ts +0 -24
  173. package/src/usage/resolvers/claude-desktop-auth.ts +0 -170
  174. package/src/usage/resolvers/claude-web.ts +0 -82
  175. package/src/usage/resolvers/codex-auth.ts +0 -174
  176. package/src/usage/resolvers/keychain-coordinator.ts +0 -397
  177. package/src/usage/resolvers/opencode-go-web.ts +0 -55
  178. package/src/usage/types.ts +0 -225
  179. package/src/usage/web-session-cache.ts +0 -37
  180. /package/{src/tui → dist/cli}/assets/providers/SOURCES.md +0 -0
  181. /package/{src/tui → dist/cli}/assets/providers/claude-terminal.png +0 -0
  182. /package/{src/tui → dist/cli}/assets/providers/claude-terminal.svg +0 -0
  183. /package/{src/tui → dist/cli}/assets/providers/claude.png +0 -0
  184. /package/{src/tui → dist/cli}/assets/providers/copilot.png +0 -0
  185. /package/{src/tui → dist/cli}/assets/providers/copilot.svg +0 -0
  186. /package/{src/tui → dist/cli}/assets/providers/openai-terminal.png +0 -0
  187. /package/{src/tui → dist/cli}/assets/providers/openai-terminal.svg +0 -0
  188. /package/{src/tui → dist/cli}/assets/providers/openai.png +0 -0
  189. /package/{src/tui → dist/cli}/assets/providers/opencode-terminal.png +0 -0
  190. /package/{src/tui → dist/cli}/assets/providers/opencode-terminal.svg +0 -0
  191. /package/{src/tui → dist/cli}/assets/providers/opencode.png +0 -0
  192. /package/{src/tui → dist/cli}/assets/providers/shell.png +0 -0
  193. /package/{src/tui → dist/cli}/assets/providers/shell.svg +0 -0
  194. /package/{src/tui → dist/cli}/assets/providers/unknown.png +0 -0
  195. /package/{src/tui → dist/cli}/assets/providers/unknown.svg +0 -0
@@ -1,511 +0,0 @@
1
- import { existsSync, readFileSync, renameSync, writeFileSync } from "node:fs";
2
- import { resolve } from "node:path";
3
- import type { LogFormat, LogLevel } from "./logger";
4
- import { ensureConfigDir, ensureParentDir, getDaemonLogPath, getDefaultConfigPath, getLegacyConfigPath } from "./paths";
5
- import { getHooksDir, IS_WINDOWS } from "./platform";
6
-
7
- // ── Config schema ─────────────────────────────────────────────────────────────
8
-
9
- export interface DaemonConfig {
10
- /** Interval in ms between tmux/process collection cycles (default: 10000) */
11
- pollIntervalMs: number;
12
- /** Override Unix socket path for local IPC on macOS/Linux */
13
- socketPath?: string;
14
- /** Timeout in ms for daemon-owned external commands (default: 5000) */
15
- commandTimeoutMs: number;
16
- /** Timeout in ms for daemon-owned network requests (default: 10000) */
17
- networkTimeoutMs: number;
18
- /** Minimum log level written to stderr (default: "info") */
19
- logLevel: LogLevel;
20
- /** stderr log format (default: "auto") */
21
- logFormat: LogFormat;
22
- /** Whether scrylogd writes its own JSONL log file (default: true) */
23
- logFileEnabled: boolean;
24
- /** Path to the JSONL daemon log file */
25
- logFilePath: string;
26
- /** Max active log file size in bytes before rotation */
27
- logFileMaxBytes: number;
28
- /** Number of log files to retain including the active file */
29
- logFileMaxFiles: number;
30
- /** Legacy API TCP port retained for config compatibility. */
31
- port?: number;
32
- /** Legacy API TCP host retained for config compatibility. */
33
- host?: string;
34
- }
35
-
36
- export interface ApiConfig {
37
- /** Whether to expose the REST API on TCP (default: true on non-Windows) */
38
- tcpEnabled: boolean;
39
- /** Hostname for the TCP API listener (default: "127.0.0.1") */
40
- host: string;
41
- /** TCP port for the REST API listener (default: 7788) */
42
- port: number;
43
- /** Bearer token for API HTTP auth (required for remote access) */
44
- bearerToken?: string;
45
- /** SSE keepalive comment-frame interval in ms; 0 disables keepalive (default: 15000) */
46
- sseKeepaliveMs: number;
47
- }
48
-
49
- export interface OpenCodeConfig {
50
- /** Which OpenCode ingestion path to use (default: "hooks") */
51
- mode: "hooks" | "sse";
52
- /** Explicit list of OpenCode instance URLs to monitor (auto-discover if empty) */
53
- instances: string[];
54
- /** Whether to enable auto-discovery via process scanning (default: true) */
55
- autoDiscover: boolean;
56
- }
57
-
58
- export interface ClaudeConfig {
59
- /** Override path to ~/.claude directory (default: auto-detected) */
60
- claudeDir?: string;
61
- /** Enable hooks integration for enriched data (default: false) */
62
- hooksEnabled: boolean;
63
- /** Directory where hook scripts write their output (default: /tmp/scrylog-claude-hooks) */
64
- hooksDir: string;
65
- }
66
-
67
- export interface CodexConfig {
68
- /** Override path to ~/.codex directory (default: auto-detect, respects $CODEX_HOME) */
69
- codexDir?: string;
70
- /** Enable hooks integration for enriched data (default: false) */
71
- hooksEnabled: boolean;
72
- /** Directory where hook scripts write their output (default: /tmp/scrylog-codex-hooks) */
73
- hooksDir: string;
74
- }
75
-
76
- export interface TmuxConfig {
77
- /** Number of lines to capture for preview (default: 50) */
78
- previewLines: number;
79
- /** Number of lines to capture for detail view (default: 200) */
80
- detailLines: number;
81
- }
82
-
83
- export interface TuiConfig {
84
- /** Refresh interval in ms for polling-based updates (default: 5000) */
85
- refreshMs: number;
86
- /** Whether to show standalone sessions section (default: true) */
87
- showStandalone: boolean;
88
- /** Whether to connect to daemon via SSE for push updates (default: true) */
89
- usePushUpdates: boolean;
90
- /** Hide standalone sessions with no activity in the last N hours (default: 6, 0 = show all) */
91
- standaloneMaxAgeHours: number;
92
- /** Debounce window in ms for coalescing push-event bursts into a single refresh (default: 120, 0 = disabled) */
93
- pushDebounceMs: number;
94
- /**
95
- * Provider icon rendering mode (default: blocks):
96
- * - "blocks": terminal pixel art, works everywhere including tmux
97
- * - "graphics": full artwork negotiated automatically (kitty → sixel → blocks)
98
- * - "kitty" / "sixel": force a specific native graphics protocol, ignoring detection
99
- * - "text": colored provider initials
100
- */
101
- providerIcons: "blocks" | "graphics" | "kitty" | "sixel" | "text";
102
- }
103
-
104
- type ProviderIconsMode = TuiConfig["providerIcons"];
105
-
106
- const PROVIDER_ICON_MODES: readonly ProviderIconsMode[] = ["blocks", "graphics", "kitty", "sixel", "text"];
107
-
108
- export interface McpConfig {
109
- /** Whether the local stdio MCP binary should be allowed (default: true) */
110
- stdioEnabled: boolean;
111
- /** Whether to expose MCP over HTTP (default: false) */
112
- httpEnabled: boolean;
113
- /** Host for the MCP HTTP listener (default: 127.0.0.1) */
114
- host: string;
115
- /** Port for the MCP HTTP transport (default: 7789) */
116
- port: number;
117
- /** Bearer token for MCP HTTP auth (required for remote access) */
118
- bearerToken?: string;
119
- }
120
-
121
- export interface CorsConfig {
122
- /** Whether to send CORS headers on API responses (default: false) */
123
- enabled: boolean;
124
- /** Allowed origins — use ["*"] for public, or explicit URLs (default: []) */
125
- origins: string[];
126
- /** Allowed HTTP methods (default: ["GET", "POST", "OPTIONS"]) */
127
- methods: string[];
128
- /** Allowed request headers (default: ["Content-Type", "Authorization"]) */
129
- allowedHeaders: string[];
130
- /** Whether to expose headers to the browser (default: []) */
131
- exposedHeaders: string[];
132
- /** Whether to include credentials in CORS requests (default: false) */
133
- credentials: boolean;
134
- /** Preflight cache duration in seconds (default: 600) */
135
- maxAge: number;
136
- }
137
-
138
- export interface UsageClaudeConfig {
139
- /** Explicit data source override (default: "auto") */
140
- dataSource: "auto" | "oauth" | "web" | "cli";
141
- /** Whether to enrich OAuth/CLI with Web API extras (default: true) */
142
- webExtrasEnabled: boolean;
143
- }
144
-
145
- export interface UsageCodexConfig {
146
- /** Explicit data source override (default: "auto") */
147
- dataSource: "auto" | "oauth";
148
- /** Override for ChatGPT API base URL (reads from ~/.codex/config.toml if not set) */
149
- chatgptBaseUrl?: string;
150
- }
151
-
152
- export interface UsageOpenCodeGoConfig {
153
- /** Server function ID for workspace resolution (default: hardcoded) */
154
- serverFnId: string;
155
- }
156
-
157
- export interface UsageAuthConfig {
158
- /**
159
- * Whether background usage polls may read foreign Keychain secrets (macOS).
160
- * `false` keeps the daemon prompt-free but disables Keychain-backed credential sources.
161
- */
162
- allowKeychainPrompts: boolean;
163
- }
164
-
165
- export interface UsageConfig {
166
- /** Whether usage collection is enabled (default: true) */
167
- enabled: boolean;
168
- /** Poll interval in ms (default: 300000 = 5 minutes) */
169
- pollIntervalMs: number;
170
- /** Which providers to poll (default: ["claude", "opencode-go"]) */
171
- providers: string[];
172
- claude: UsageClaudeConfig;
173
- codex: UsageCodexConfig;
174
- opencodeGo: UsageOpenCodeGoConfig;
175
- auth: UsageAuthConfig;
176
- }
177
-
178
- export interface RetentionConfig {
179
- /** Maximum sessions retained in memory before pruning inactive oldest sessions */
180
- maxSessions: number;
181
- /** Maximum inactive standalone session age in hours (0 = disabled) */
182
- standaloneMaxAgeHours: number;
183
- /** TTL for excluded Claude local-command IDs in hours */
184
- localCommandTtlHours: number;
185
- /** Maximum entries retained by bounded in-memory caches */
186
- cacheMaxEntries: number;
187
- /** Maximum Claude transcript bytes to parse from the tail of large files */
188
- transcriptMaxBytes: number;
189
- }
190
-
191
- export interface ScrylogConfig {
192
- daemon: DaemonConfig;
193
- api: ApiConfig;
194
- cors: CorsConfig;
195
- opencode: OpenCodeConfig;
196
- claude: ClaudeConfig;
197
- codex: CodexConfig;
198
- tmux: TmuxConfig;
199
- tui: TuiConfig;
200
- mcp: McpConfig;
201
- usage: UsageConfig;
202
- retention: RetentionConfig;
203
- }
204
-
205
- // ── Defaults ──────────────────────────────────────────────────────────────────
206
-
207
- export const CONFIG_DEFAULTS: ScrylogConfig = {
208
- daemon: {
209
- pollIntervalMs: 10_000,
210
- commandTimeoutMs: 5_000,
211
- networkTimeoutMs: 10_000,
212
- logLevel: "info",
213
- logFormat: "auto",
214
- logFileEnabled: true,
215
- logFilePath: getDaemonLogPath(),
216
- logFileMaxBytes: 5 * 1024 * 1024,
217
- logFileMaxFiles: 3,
218
- },
219
- api: {
220
- tcpEnabled: IS_WINDOWS,
221
- host: "127.0.0.1",
222
- port: 7788,
223
- sseKeepaliveMs: 15_000,
224
- },
225
- cors: {
226
- enabled: false,
227
- origins: [],
228
- methods: ["GET", "POST", "OPTIONS"],
229
- allowedHeaders: ["Content-Type", "Authorization"],
230
- exposedHeaders: [],
231
- credentials: false,
232
- maxAge: 600,
233
- },
234
- opencode: {
235
- mode: "hooks",
236
- instances: [],
237
- autoDiscover: true,
238
- },
239
- claude: {
240
- hooksEnabled: false,
241
- hooksDir: getHooksDir("claude-hooks"),
242
- },
243
- codex: {
244
- hooksEnabled: false,
245
- hooksDir: getHooksDir("codex-hooks"),
246
- },
247
- tmux: {
248
- previewLines: 50,
249
- detailLines: 200,
250
- },
251
- tui: {
252
- refreshMs: 5_000,
253
- showStandalone: true,
254
- usePushUpdates: true,
255
- standaloneMaxAgeHours: 6,
256
- pushDebounceMs: 120,
257
- providerIcons: "blocks",
258
- },
259
- mcp: {
260
- stdioEnabled: true,
261
- httpEnabled: false,
262
- host: "127.0.0.1",
263
- port: 7789,
264
- },
265
- usage: {
266
- enabled: true,
267
- pollIntervalMs: 300_000,
268
- providers: ["claude", "opencode-go", "codex"],
269
- claude: {
270
- dataSource: "auto",
271
- webExtrasEnabled: true,
272
- },
273
- codex: {
274
- dataSource: "auto",
275
- },
276
- opencodeGo: {
277
- serverFnId: "def39973159c7f0483d8793a822b8dbb10d067e12c65455fcb4608459ba0234f",
278
- },
279
- auth: {
280
- allowKeychainPrompts: true,
281
- },
282
- },
283
- retention: {
284
- maxSessions: 500,
285
- standaloneMaxAgeHours: 168,
286
- localCommandTtlHours: 72,
287
- cacheMaxEntries: 1_000,
288
- transcriptMaxBytes: 10 * 1024 * 1024,
289
- },
290
- };
291
-
292
- // ── Resolution ────────────────────────────────────────────────────────────────
293
-
294
- /** Ordered list of default config file locations to probe */
295
- export function getDefaultConfigPaths(): string[] {
296
- return [getDefaultConfigPath(), getLegacyConfigPath()];
297
- }
298
-
299
- export function getResolvedConfigPath(explicitPath?: string): string {
300
- if (explicitPath) {
301
- return resolve(explicitPath);
302
- }
303
- if (process.env.SCRYLOG_CONFIG) {
304
- return resolve(process.env.SCRYLOG_CONFIG);
305
- }
306
- for (const path of getDefaultConfigPaths()) {
307
- if (existsSync(path)) {
308
- return path;
309
- }
310
- }
311
- return getDefaultConfigPath();
312
- }
313
-
314
- /**
315
- * Load and merge config from:
316
- * 1. --config flag value
317
- * 2. SCRYLOG_CONFIG env var
318
- * 3. ~/.config/scrylog/config.json
319
- * 4. ~/.scrylog.json
320
- * 5. Built-in defaults
321
- */
322
- export function loadConfig(explicitPath?: string): ScrylogConfig {
323
- const paths: string[] = [];
324
-
325
- if (explicitPath) {
326
- paths.push(resolve(explicitPath));
327
- } else if (process.env.SCRYLOG_CONFIG) {
328
- paths.push(resolve(process.env.SCRYLOG_CONFIG));
329
- } else {
330
- paths.push(...getDefaultConfigPaths());
331
- }
332
-
333
- for (const path of paths) {
334
- if (existsSync(path)) {
335
- return loadConfigFile(path);
336
- }
337
- }
338
-
339
- return { ...CONFIG_DEFAULTS };
340
- }
341
-
342
- function loadConfigFile(path: string): ScrylogConfig {
343
- const raw = readConfigOverride(path);
344
- if (!raw) {
345
- return { ...CONFIG_DEFAULTS };
346
- }
347
-
348
- return normalizeConfig(raw);
349
- }
350
-
351
- export function readConfigOverride(path: string): PlainObject | null {
352
- let raw: unknown;
353
-
354
- try {
355
- const content = readFileSync(path, "utf8");
356
- raw = JSON.parse(content);
357
- } catch (error) {
358
- process.stderr.write(
359
- `[scrylog] Warning: could not parse config file ${path}: ${error instanceof Error ? error.message : String(error)}\n`,
360
- );
361
- return null;
362
- }
363
-
364
- if (!raw || typeof raw !== "object") {
365
- return null;
366
- }
367
-
368
- return raw as PlainObject;
369
- }
370
-
371
- export function normalizeConfig(raw: PlainObject): ScrylogConfig {
372
- const merged = deepMerge(CONFIG_DEFAULTS as unknown as PlainObject, raw as Partial<PlainObject>) as unknown as ScrylogConfig;
373
- return normalizeOperationalConfig(applyLegacyCompatibility(merged, raw));
374
- }
375
-
376
- export function writeConfig(config: ScrylogConfig, explicitPath?: string): string {
377
- const path = getResolvedConfigPath(explicitPath);
378
- ensureConfigDir();
379
- ensureParentDir(path);
380
- const tempPath = `${path}.tmp`;
381
- writeFileSync(tempPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
382
- renameSync(tempPath, path);
383
- return path;
384
- }
385
-
386
- export function updateConfig(
387
- updater: (current: ScrylogConfig) => ScrylogConfig,
388
- explicitPath?: string,
389
- ): { path: string; config: ScrylogConfig } {
390
- const path = getResolvedConfigPath(explicitPath);
391
- const current = existsSync(path) ? loadConfig(path) : { ...CONFIG_DEFAULTS };
392
- const updated = updater(current);
393
- writeConfig(updated, path);
394
- return { path, config: updated };
395
- }
396
-
397
- // ── Deep merge ────────────────────────────────────────────────────────────────
398
-
399
- type PlainObject = Record<string, unknown>;
400
-
401
- function isPlainObject(value: unknown): value is PlainObject {
402
- return typeof value === "object" && value !== null && !Array.isArray(value);
403
- }
404
-
405
- export function deepMerge<T extends PlainObject>(base: T, override: Partial<T>): T {
406
- const result: PlainObject = { ...base };
407
-
408
- for (const key of Object.keys(override)) {
409
- const baseValue = result[key];
410
- const overrideValue = (override as PlainObject)[key];
411
-
412
- if (isPlainObject(baseValue) && isPlainObject(overrideValue)) {
413
- result[key] = deepMerge(baseValue, overrideValue);
414
- } else if (overrideValue !== undefined) {
415
- result[key] = overrideValue;
416
- }
417
- }
418
-
419
- return result as T;
420
- }
421
-
422
- function applyLegacyCompatibility(config: ScrylogConfig, raw: PlainObject): ScrylogConfig {
423
- const rawDaemon = isPlainObject(raw["daemon"]) ? raw["daemon"] : undefined;
424
- const rawApi = isPlainObject(raw["api"]) ? raw["api"] : undefined;
425
- const rawMcp = isPlainObject(raw["mcp"]) ? raw["mcp"] : undefined;
426
-
427
- const next: ScrylogConfig = {
428
- ...config,
429
- api: { ...config.api },
430
- daemon: { ...config.daemon },
431
- mcp: { ...config.mcp },
432
- };
433
-
434
- if (!rawApi && rawDaemon) {
435
- const legacyHost = typeof rawDaemon["host"] === "string" ? rawDaemon["host"] : undefined;
436
- const legacyPort = typeof rawDaemon["port"] === "number" ? rawDaemon["port"] : undefined;
437
- if (legacyHost) next.api.host = legacyHost;
438
- if (legacyPort !== undefined) next.api.port = legacyPort;
439
- if (legacyHost || legacyPort !== undefined) {
440
- next.api.tcpEnabled = legacyPort === 0 ? false : true;
441
- }
442
- }
443
-
444
- if (rawMcp && rawMcp["httpEnabled"] === undefined) {
445
- const legacyPort = typeof rawMcp["port"] === "number" ? rawMcp["port"] : undefined;
446
- const legacyHost = typeof rawMcp["host"] === "string" ? rawMcp["host"] : undefined;
447
- const legacyToken = typeof rawMcp["bearerToken"] === "string" ? rawMcp["bearerToken"] : undefined;
448
- if (legacyHost) next.mcp.host = legacyHost;
449
- if (legacyPort !== undefined) next.mcp.port = legacyPort;
450
- if (legacyToken) next.mcp.bearerToken = legacyToken;
451
- if (legacyHost || legacyPort !== undefined || legacyToken) {
452
- next.mcp.httpEnabled = true;
453
- }
454
- }
455
-
456
- return next;
457
- }
458
-
459
- function normalizeOperationalConfig(config: ScrylogConfig): ScrylogConfig {
460
- return {
461
- ...config,
462
- daemon: {
463
- ...config.daemon,
464
- pollIntervalMs: clampNumber(config.daemon.pollIntervalMs, 1_000, 300_000, CONFIG_DEFAULTS.daemon.pollIntervalMs),
465
- commandTimeoutMs: clampNumber(config.daemon.commandTimeoutMs, 1_000, 120_000, CONFIG_DEFAULTS.daemon.commandTimeoutMs),
466
- networkTimeoutMs: clampNumber(config.daemon.networkTimeoutMs, 1_000, 120_000, CONFIG_DEFAULTS.daemon.networkTimeoutMs),
467
- logFileMaxBytes: clampNumber(config.daemon.logFileMaxBytes, 0, 100 * 1024 * 1024, CONFIG_DEFAULTS.daemon.logFileMaxBytes),
468
- logFileMaxFiles: clampNumber(config.daemon.logFileMaxFiles, 1, 20, CONFIG_DEFAULTS.daemon.logFileMaxFiles),
469
- },
470
- api: {
471
- ...config.api,
472
- sseKeepaliveMs: clampNumber(config.api.sseKeepaliveMs, 0, 120_000, CONFIG_DEFAULTS.api.sseKeepaliveMs),
473
- },
474
- tmux: {
475
- ...config.tmux,
476
- previewLines: clampNumber(config.tmux.previewLines, 0, 500, CONFIG_DEFAULTS.tmux.previewLines),
477
- detailLines: clampNumber(config.tmux.detailLines, 0, 5_000, CONFIG_DEFAULTS.tmux.detailLines),
478
- },
479
- tui: {
480
- ...config.tui,
481
- refreshMs: clampNumber(config.tui.refreshMs, 500, 300_000, CONFIG_DEFAULTS.tui.refreshMs),
482
- standaloneMaxAgeHours: clampNumber(config.tui.standaloneMaxAgeHours, 0, 8_760, CONFIG_DEFAULTS.tui.standaloneMaxAgeHours),
483
- pushDebounceMs: clampNumber(config.tui.pushDebounceMs, 0, 2_000, CONFIG_DEFAULTS.tui.pushDebounceMs),
484
- providerIcons: PROVIDER_ICON_MODES.includes(config.tui.providerIcons)
485
- ? config.tui.providerIcons
486
- : CONFIG_DEFAULTS.tui.providerIcons,
487
- },
488
- usage: {
489
- ...config.usage,
490
- pollIntervalMs: clampNumber(config.usage.pollIntervalMs, 60_000, 3_600_000, CONFIG_DEFAULTS.usage.pollIntervalMs),
491
- auth: {
492
- allowKeychainPrompts: typeof config.usage.auth?.allowKeychainPrompts === "boolean"
493
- ? config.usage.auth.allowKeychainPrompts
494
- : CONFIG_DEFAULTS.usage.auth.allowKeychainPrompts,
495
- },
496
- },
497
- retention: {
498
- ...config.retention,
499
- maxSessions: clampNumber(config.retention.maxSessions, 0, 100_000, CONFIG_DEFAULTS.retention.maxSessions),
500
- standaloneMaxAgeHours: clampNumber(config.retention.standaloneMaxAgeHours, 0, 8_760, CONFIG_DEFAULTS.retention.standaloneMaxAgeHours),
501
- localCommandTtlHours: clampNumber(config.retention.localCommandTtlHours, 1, 8_760, CONFIG_DEFAULTS.retention.localCommandTtlHours),
502
- cacheMaxEntries: clampNumber(config.retention.cacheMaxEntries, 10, 100_000, CONFIG_DEFAULTS.retention.cacheMaxEntries),
503
- transcriptMaxBytes: clampNumber(config.retention.transcriptMaxBytes, 64 * 1024, 100 * 1024 * 1024, CONFIG_DEFAULTS.retention.transcriptMaxBytes),
504
- },
505
- };
506
- }
507
-
508
- function clampNumber(value: number, min: number, max: number, fallback: number): number {
509
- if (!Number.isFinite(value)) return fallback;
510
- return Math.min(max, Math.max(min, Math.trunc(value)));
511
- }
package/src/shared/ipc.ts DELETED
@@ -1,80 +0,0 @@
1
- import { join } from "node:path";
2
- import { IS_WINDOWS, getUserId } from "./platform";
3
-
4
- /**
5
- * @deprecated Import IS_WINDOWS and getUserId from `shared/platform.ts` instead.
6
- * Re-exported here for backward compatibility during the migration.
7
- */
8
- export { IS_WINDOWS, getUserId };
9
-
10
- /**
11
- * Return the Unix socket path for the daemon.
12
- * Includes the current user's uid so multiple users on the same host don't conflict.
13
- */
14
- export function getSocketPath(override?: string): string {
15
- if (override) {
16
- return override;
17
- }
18
-
19
- const uid = getUserId();
20
- return join("/tmp", `scrylog-${uid}.sock`);
21
- }
22
-
23
- /**
24
- * Build the base URL used for daemon HTTP connections.
25
- * - On macOS/Linux: HTTP over Unix socket via the `unix://` path convention
26
- * - On Windows (or when TCP is forced): regular TCP URL
27
- */
28
- export function getDaemonUrl(options: {
29
- host: string;
30
- port: number;
31
- socketPath?: string;
32
- forceTcp?: boolean;
33
- }): string {
34
- if (!IS_WINDOWS && !options.forceTcp) {
35
- return `http://localhost`;
36
- }
37
-
38
- return `http://${options.host}:${options.port}`;
39
- }
40
-
41
- /**
42
- * Create a `fetch` function that routes requests over the Unix socket.
43
- * Falls back to a plain `fetch` pointing at the TCP address on Windows.
44
- * Injects an Authorization header when bearerToken is provided.
45
- */
46
- export function createDaemonFetch(options: {
47
- host: string;
48
- port: number;
49
- socketPath?: string;
50
- forceTcp?: boolean;
51
- bearerToken?: string;
52
- }): (input: string | URL | Request, init?: RequestInit) => Promise<Response> {
53
- const headers: Record<string, string> = options.bearerToken
54
- ? { Authorization: `Bearer ${options.bearerToken}` }
55
- : {};
56
-
57
- if (IS_WINDOWS || options.forceTcp) {
58
- const base = `http://${options.host}:${options.port}`;
59
- return (input, init) => {
60
- const url = typeof input === "string" && input.startsWith("/") ? `${base}${input}` : input;
61
- return fetch(url, {
62
- ...init,
63
- headers: { ...headers, ...(init?.headers ?? {}) },
64
- });
65
- };
66
- }
67
-
68
- const socketPath = options.socketPath ?? getSocketPath();
69
-
70
- // Bun supports Unix socket via `unix` field in fetch options
71
- return (input, init) => {
72
- const url = typeof input === "string" && input.startsWith("/") ? `http://localhost${input}` : input;
73
- // @ts-ignore -- Bun-specific `unix` option not in standard types
74
- return fetch(url, {
75
- ...init,
76
- unix: socketPath,
77
- headers: { ...headers, ...(init?.headers ?? {}) },
78
- });
79
- };
80
- }