@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,248 +0,0 @@
1
- import { appendFileSync, existsSync, mkdirSync, renameSync, statSync, unlinkSync } from "node:fs";
2
- import { dirname, extname, join, basename } from "node:path";
3
- import { homedir } from "node:os";
4
-
5
- // ── Log levels ─────────────────────────────────────────────────────────────────
6
-
7
- export type LogLevel = "debug" | "info" | "warn" | "error";
8
- export type LogFormat = "auto" | "pretty" | "json";
9
- export type LoggerWriter = (line: string) => void;
10
-
11
- export interface LoggerOptions {
12
- level: LogLevel;
13
- format?: LogFormat;
14
- file?: {
15
- enabled: boolean;
16
- path: string;
17
- maxBytes: number;
18
- maxFiles: number;
19
- };
20
- writer?: LoggerWriter;
21
- }
22
-
23
- const LEVEL_RANK: Record<LogLevel, number> = {
24
- debug: 0,
25
- info: 1,
26
- warn: 2,
27
- error: 3,
28
- };
29
-
30
- const LEVEL_LABEL: Record<LogLevel, string> = {
31
- debug: "DBG",
32
- info: "INF",
33
- warn: "WRN",
34
- error: "ERR",
35
- };
36
-
37
- const LEVEL_COLOR: Record<LogLevel, string> = {
38
- debug: "\x1b[2m", // dim
39
- info: "\x1b[36m", // cyan
40
- warn: "\x1b[33m", // yellow
41
- error: "\x1b[31m", // red
42
- };
43
- const ANSI_RESET = "\x1b[0m";
44
-
45
- // ── Logger ─────────────────────────────────────────────────────────────────────
46
-
47
- export class Logger {
48
- private readonly minLevel: LogLevel;
49
- private readonly format: LogFormat;
50
- private readonly file?: NonNullable<LoggerOptions["file"]>;
51
- private readonly writer: LoggerWriter;
52
-
53
- constructor(optionsOrLevel: LoggerOptions | LogLevel) {
54
- const options = typeof optionsOrLevel === "string" ? { level: optionsOrLevel } : optionsOrLevel;
55
- this.minLevel = options.level;
56
- this.format = options.format ?? "auto";
57
- this.file = options.file?.enabled ? options.file : undefined;
58
- this.writer = options.writer ?? ((line) => process.stderr.write(line + "\n"));
59
- }
60
-
61
- debug(source: string, message: string, data?: Record<string, unknown>): void {
62
- this.write("debug", source, message, data);
63
- }
64
-
65
- info(source: string, message: string, data?: Record<string, unknown>): void {
66
- this.write("info", source, message, data);
67
- }
68
-
69
- warn(source: string, message: string, data?: Record<string, unknown>): void {
70
- this.write("warn", source, message, data);
71
- }
72
-
73
- error(source: string, message: string, data?: Record<string, unknown>): void {
74
- this.write("error", source, message, data);
75
- }
76
-
77
- private write(level: LogLevel, source: string, message: string, data?: Record<string, unknown>): void {
78
- if (LEVEL_RANK[level] < LEVEL_RANK[this.minLevel]) return;
79
-
80
- const now = new Date();
81
- const isoTs = now.toISOString();
82
- const hh = String(now.getHours()).padStart(2, "0");
83
- const mm = String(now.getMinutes()).padStart(2, "0");
84
- const ss = String(now.getSeconds()).padStart(2, "0");
85
- const ms = String(now.getMilliseconds()).padStart(3, "0");
86
- const ts = `${hh}:${mm}:${ss}.${ms}`;
87
- const label = LEVEL_LABEL[level];
88
-
89
- const { event, fields } = splitEventData(sanitizeData(data));
90
- const dataPart = formatPrettyData(fields);
91
-
92
- const isTTY = process.stderr.isTTY === true;
93
- const resolvedFormat = this.format === "auto" ? (isTTY ? "pretty" : "json") : this.format;
94
- let line: string;
95
-
96
- if (resolvedFormat === "json") {
97
- line = JSON.stringify({
98
- ts: isoTs,
99
- level,
100
- source,
101
- ...(event ? { event } : {}),
102
- msg: message,
103
- ...(Object.keys(fields).length > 0 ? { data: fields } : {}),
104
- });
105
- } else if (level === "debug") {
106
- // Whole line dimmed — visually recessive, won't distract from important logs
107
- line = `${LEVEL_COLOR.debug}[${ts}] [${label}] [${source}]${event ? ` ${event}` : ""} ${message}${dataPart}${ANSI_RESET}`;
108
- } else if (level === "info") {
109
- // Only the label colored — clean, easy to scan
110
- line = `[${ts}] ${LEVEL_COLOR.info}[${label}]${ANSI_RESET} [${source}]${event ? ` ${event}` : ""} ${message}${dataPart}`;
111
- } else {
112
- // warn / error — whole line colored so nothing is missed
113
- line = `${LEVEL_COLOR[level]}[${ts}] [${label}] [${source}]${event ? ` ${event}` : ""} ${message}${dataPart}${ANSI_RESET}`;
114
- }
115
-
116
- this.writer(line);
117
-
118
- if (this.file) {
119
- const fileFields = fields;
120
- const jsonLine = JSON.stringify({
121
- ts: isoTs,
122
- level,
123
- source,
124
- ...(event ? { event } : {}),
125
- msg: message,
126
- ...(Object.keys(fileFields).length > 0 ? { data: fileFields } : {}),
127
- });
128
- writeJsonLineToFile(this.file, jsonLine);
129
- }
130
- }
131
- }
132
-
133
- export function createLogger(optionsOrLevel: LoggerOptions | LogLevel): Logger {
134
- return new Logger(optionsOrLevel);
135
- }
136
-
137
- /** Parse a log level string, falling back to the default if invalid. */
138
- export function parseLogLevel(value: string | undefined, fallback: LogLevel = "info"): LogLevel {
139
- if (value === "debug" || value === "info" || value === "warn" || value === "error") {
140
- return value;
141
- }
142
- return fallback;
143
- }
144
-
145
- /** Parse a log format string, falling back to the default if invalid. */
146
- export function parseLogFormat(value: string | undefined, fallback: LogFormat = "auto"): LogFormat {
147
- if (value === "auto" || value === "pretty" || value === "json") {
148
- return value;
149
- }
150
- return fallback;
151
- }
152
-
153
- /** Singleton noop logger for use where no logger is injected. */
154
- export const NOOP_LOGGER = new Logger("error" as LogLevel);
155
- // Override write to do nothing — used in tests / non-daemon contexts
156
- (NOOP_LOGGER as unknown as { write: () => void }).write = () => {};
157
-
158
- function splitEventData(data: Record<string, unknown> | undefined): { event?: string; fields: Record<string, unknown> } {
159
- if (!data) return { fields: {} };
160
- const { event, ...rest } = data;
161
- return {
162
- ...(typeof event === "string" && event.length > 0 ? { event } : {}),
163
- fields: rest,
164
- };
165
- }
166
-
167
- function formatPrettyData(data: Record<string, unknown>): string {
168
- if (Object.keys(data).length === 0) return "";
169
- const pairs = Object.entries(data)
170
- .map(([k, v]) => `${k}=${typeof v === "string" ? v : JSON.stringify(v)}`)
171
- .join(" ");
172
- return ` ${pairs}`;
173
- }
174
-
175
- function sanitizeData(data: Record<string, unknown> | undefined): Record<string, unknown> | undefined {
176
- if (!data) return undefined;
177
- const sanitized: Record<string, unknown> = {};
178
- for (const [key, value] of Object.entries(data)) {
179
- sanitized[key] = sanitizeValue(key, value);
180
- }
181
- return sanitized;
182
- }
183
-
184
- function sanitizeValue(key: string, value: unknown): unknown {
185
- if (value === null || value === undefined) return value;
186
- if (typeof value === "string") return sanitizeString(key, value);
187
- if (Array.isArray(value)) return value.map((item) => sanitizeValue(key, item));
188
- if (typeof value === "object") {
189
- const out: Record<string, unknown> = {};
190
- for (const [childKey, childValue] of Object.entries(value)) {
191
- out[childKey] = sanitizeValue(childKey, childValue);
192
- }
193
- return out;
194
- }
195
- return value;
196
- }
197
-
198
- function sanitizeString(key: string, value: string): string {
199
- if (/authorization|token|secret|password|bearer/i.test(key)) return "[redacted]";
200
- let next = value.replace(new RegExp(escapeRegExp(homedir()), "g"), "~");
201
- next = next.replace(/(Authorization:\s*Bearer\s+)[^\s]+/gi, "$1[redacted]");
202
- next = next.replace(/(Bearer\s+)[A-Za-z0-9._~+\-/]+=*/g, "$1[redacted]");
203
- next = next.replace(/([?&](?:token|access_token|api_key|key|secret|password)=)[^&\s]+/gi, "$1[redacted]");
204
- next = next.replace(/(https?:\/\/[^\s?]+)\?[^\s]+/gi, "$1?[redacted]");
205
- return next;
206
- }
207
-
208
- function writeJsonLineToFile(file: NonNullable<LoggerOptions["file"]>, line: string): void {
209
- try {
210
- mkdirSync(dirname(file.path), { recursive: true });
211
- rotateIfNeeded(file);
212
- appendFileSync(file.path, `${line}\n`, "utf8");
213
- } catch {
214
- // Logging must never crash the daemon.
215
- }
216
- }
217
-
218
- function rotateIfNeeded(file: NonNullable<LoggerOptions["file"]>): void {
219
- if (file.maxBytes <= 0 || !existsSync(file.path)) return;
220
- const size = statSync(file.path).size;
221
- if (size < file.maxBytes) return;
222
-
223
- const maxFiles = Math.max(1, file.maxFiles);
224
- const rotatedCount = maxFiles - 1;
225
- if (rotatedCount <= 0) {
226
- unlinkSync(file.path);
227
- return;
228
- }
229
-
230
- const oldest = rotatedPath(file.path, rotatedCount);
231
- if (existsSync(oldest)) unlinkSync(oldest);
232
-
233
- for (let index = rotatedCount - 1; index >= 1; index--) {
234
- const from = rotatedPath(file.path, index);
235
- if (existsSync(from)) renameSync(from, rotatedPath(file.path, index + 1));
236
- }
237
- renameSync(file.path, rotatedPath(file.path, 1));
238
- }
239
-
240
- function rotatedPath(path: string, index: number): string {
241
- const ext = extname(path);
242
- const base = basename(path, ext);
243
- return join(dirname(path), `${base}.${index}${ext}`);
244
- }
245
-
246
- function escapeRegExp(value: string): string {
247
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
248
- }
@@ -1,73 +0,0 @@
1
- import { mkdirSync } from "node:fs";
2
- import { homedir, userInfo } from "node:os";
3
- import { dirname, join } from "node:path";
4
-
5
- export function getConfigDir(): string {
6
- return join(homedir(), ".config", "scrylog");
7
- }
8
-
9
- export function getDefaultConfigPath(): string {
10
- return join(getConfigDir(), "config.json");
11
- }
12
-
13
- export function getLegacyConfigPath(): string {
14
- return join(homedir(), ".scrylog.json");
15
- }
16
-
17
- export function getStateDir(): string {
18
- return join(homedir(), ".local", "state", "scrylog");
19
- }
20
-
21
- export function getDaemonLogPath(): string {
22
- return join(homedir(), ".scrylog", "logs", "scrylogd.log");
23
- }
24
-
25
- export function getServiceLabel(): string {
26
- return "com.scrylog.daemon";
27
- }
28
-
29
- export function getLaunchAgentsDir(): string {
30
- return join(homedir(), "Library", "LaunchAgents");
31
- }
32
-
33
- export function getLaunchAgentPath(): string {
34
- return join(getLaunchAgentsDir(), `${getServiceLabel()}.plist`);
35
- }
36
-
37
- export function getSystemdUserDir(): string {
38
- return join(homedir(), ".config", "systemd", "user");
39
- }
40
-
41
- export function getSystemdUnitName(): string {
42
- return "scrylogd.service";
43
- }
44
-
45
- export function getSystemdUnitPath(): string {
46
- return join(getSystemdUserDir(), getSystemdUnitName());
47
- }
48
-
49
- export function ensureParentDir(path: string): void {
50
- const dir = path.endsWith("/") ? path.slice(0, -1) : dirname(path);
51
- if (!dir || dir === ".") return;
52
- mkdirSync(dir, { recursive: true });
53
- }
54
-
55
- export function ensureConfigDir(): string {
56
- const dir = getConfigDir();
57
- mkdirSync(dir, { recursive: true });
58
- return dir;
59
- }
60
-
61
- export function ensureStateDir(): string {
62
- const dir = getStateDir();
63
- mkdirSync(dir, { recursive: true });
64
- return dir;
65
- }
66
-
67
- export function getUserUid(): number | string {
68
- try {
69
- return userInfo().uid;
70
- } catch {
71
- return process.env.USER ?? process.env.USERNAME ?? "default";
72
- }
73
- }
@@ -1,110 +0,0 @@
1
- import { homedir, tmpdir, userInfo } from "node:os";
2
- import { join } from "node:path";
3
- import { chmodSync, lstatSync, mkdirSync, readFileSync, rmSync } from "node:fs";
4
-
5
- /** True on Windows where Unix sockets are not available */
6
- export const IS_WINDOWS = process.platform === "win32";
7
-
8
- /** True on macOS */
9
- export const IS_MAC = process.platform === "darwin";
10
-
11
- /** True on Linux (including WSL) */
12
- export const IS_LINUX = process.platform === "linux";
13
-
14
- /** True when running inside Windows Subsystem for Linux */
15
- export const IS_WSL = ((): boolean => {
16
- if (!IS_LINUX) return false;
17
- try {
18
- const version = readFileSync("/proc/version", "utf8");
19
- return /microsoft|wsl/i.test(version);
20
- } catch {
21
- return false;
22
- }
23
- })();
24
-
25
- /**
26
- * Resolve the path to an executable using the platform-appropriate command.
27
- * Prefers Bun.which when available, falling back to `which` on Unix and `where` on Windows.
28
- */
29
- export function whichCommand(name: string): string | null {
30
- const viaBun = (Bun as typeof Bun & { which?: (name: string) => string | null }).which?.(name);
31
- if (viaBun) return viaBun;
32
-
33
- if (IS_WINDOWS) {
34
- return spawnAndRead("where", [name]);
35
- }
36
- return spawnAndRead("which", [name]);
37
- }
38
-
39
- function spawnAndRead(command: string, args: string[]): string | null {
40
- try {
41
- const proc = Bun.spawnSync([command, ...args], { stdout: "pipe", stderr: "ignore" });
42
- const text = new TextDecoder().decode(proc.stdout).trim();
43
- return text ? text.split(/\r?\n/)[0] ?? text : null;
44
- } catch {
45
- return null;
46
- }
47
- }
48
-
49
- /**
50
- * Return the current user's id, used to scope shared /tmp paths so multiple
51
- * users on the same host don't conflict or collide on the same path.
52
- */
53
- export function getUserId(): number | string {
54
- try {
55
- return userInfo().uid;
56
- } catch {
57
- // Fallback: use a hash of the username
58
- return process.env.USER ?? process.env.USERNAME ?? "default";
59
- }
60
- }
61
-
62
- /**
63
- * Return a stable hooks directory path for the given hook name.
64
- * On Unix we intentionally avoid os.tmpdir() because macOS per-session temp paths
65
- * would break rendezvous between independently-launched daemon and hook binaries.
66
- * The path is scoped by uid so multiple users on the same host don't conflict.
67
- * On Windows we use os.tmpdir() because %TEMP% is stable per user session and
68
- * already per-user.
69
- *
70
- * This function is pure (no I/O). Callers that need to actually create the
71
- * directory should use `ensureHooksDirSafe`.
72
- */
73
- export function getHooksDir(name: string): string {
74
- if (IS_WINDOWS) {
75
- return join(tmpdir(), `scrylog-${name}`);
76
- }
77
- return join("/tmp", `scrylog-${getUserId()}-${name}`);
78
- }
79
-
80
- function isOwnedDirectory(dir: string): boolean {
81
- const stat = lstatSync(dir);
82
- return stat.isDirectory() && stat.uid === process.getuid?.();
83
- }
84
-
85
- /**
86
- * Ensure `dir` exists and, on Unix, is a real directory (not a symlink)
87
- * owned by the current user, tightening its mode to 0700. A pre-existing
88
- * path that isn't a directory we own — e.g. a symlink or a directory
89
- * planted by another local user on a shared host — is untrusted: it is
90
- * removed and recreated rather than written into. A directory we already
91
- * own is tightened in place (not recreated) so existing session state isn't
92
- * discarded. Ownership/mode checks are skipped on Windows, where
93
- * `getHooksDir` already resolves to a per-user temp directory.
94
- */
95
- export function ensureHooksDirSafe(dir: string): void {
96
- mkdirSync(dir, { recursive: true, mode: 0o700 });
97
-
98
- if (IS_WINDOWS) return;
99
-
100
- if (!isOwnedDirectory(dir)) {
101
- rmSync(dir, { recursive: true, force: true });
102
- mkdirSync(dir, { recursive: true, mode: 0o700 });
103
-
104
- if (!isOwnedDirectory(dir)) {
105
- throw new Error(`Refusing to use untrusted hooks directory: ${dir}`);
106
- }
107
- }
108
-
109
- chmodSync(dir, 0o700);
110
- }
@@ -1,5 +0,0 @@
1
- const CLAUDE_PROBE_PATH = /(?:^|[\\/-])codexbar[\\/-]claudeprobe(?:$|[\\/-])/i;
2
-
3
- export function isKnownClaudeProbePath(path?: string): boolean {
4
- return typeof path === "string" && CLAUDE_PROBE_PATH.test(path);
5
- }