@scrylog/cli 0.1.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.
- package/LICENSE +21 -0
- package/README.md +836 -0
- package/package.json +75 -0
- package/src/api/docs/sessions.keys.ts +6 -0
- package/src/api/docs/sessions.ts +81 -0
- package/src/api/docs/system.keys.ts +5 -0
- package/src/api/docs/system.ts +66 -0
- package/src/api/docs/usage.keys.ts +3 -0
- package/src/api/docs/usage.ts +24 -0
- package/src/api/docs/webhooks.keys.ts +5 -0
- package/src/api/docs/webhooks.ts +78 -0
- package/src/api/index.ts +18 -0
- package/src/api/lib/auth.ts +43 -0
- package/src/api/lib/errors.ts +28 -0
- package/src/api/lib/route.ts +18 -0
- package/src/api/models/claude.ts +4 -0
- package/src/api/models/codex.ts +4 -0
- package/src/api/models/opencode.ts +11 -0
- package/src/api/routes/sessions.ts +47 -0
- package/src/api/routes/system.ts +103 -0
- package/src/api/routes/usage.ts +15 -0
- package/src/api/routes/webhooks/claude.ts +161 -0
- package/src/api/routes/webhooks/codex.ts +175 -0
- package/src/api/routes/webhooks/opencode.ts +107 -0
- package/src/api/routes.ts +93 -0
- package/src/api/schemas/common.ts +21 -0
- package/src/api/schemas/sessions.ts +72 -0
- package/src/api/schemas/usage.ts +50 -0
- package/src/api/schemas/webhooks/claude.ts +32 -0
- package/src/api/schemas/webhooks/codex.ts +34 -0
- package/src/api/schemas/webhooks/opencode.ts +29 -0
- package/src/api/services/claude-session.ts +9 -0
- package/src/api/services/codex-session.ts +8 -0
- package/src/api/services/opencode-session.ts +73 -0
- package/src/classifier.ts +206 -0
- package/src/cli/commands.ts +498 -0
- package/src/cli/prompt.ts +44 -0
- package/src/daemon/cli.ts +27 -0
- package/src/daemon/collectors/claude/README.md +108 -0
- package/src/daemon/collectors/claude/collector.ts +413 -0
- package/src/daemon/collectors/claude/discovery.ts +148 -0
- package/src/daemon/collectors/claude/hooks.ts +181 -0
- package/src/daemon/collectors/claude/index.ts +8 -0
- package/src/daemon/collectors/claude/jsonl.ts +85 -0
- package/src/daemon/collectors/claude/transcript.ts +246 -0
- package/src/daemon/collectors/codex/README.md +147 -0
- package/src/daemon/collectors/codex/collector.ts +661 -0
- package/src/daemon/collectors/codex/discovery.ts +104 -0
- package/src/daemon/collectors/codex/hooks.ts +179 -0
- package/src/daemon/collectors/codex/index.ts +7 -0
- package/src/daemon/collectors/codex/jsonl.ts +108 -0
- package/src/daemon/collectors/codex/transcript.ts +299 -0
- package/src/daemon/collectors/git.ts +295 -0
- package/src/daemon/collectors/opencode/README.md +106 -0
- package/src/daemon/collectors/opencode/collector.ts +543 -0
- package/src/daemon/collectors/opencode/discovery.ts +257 -0
- package/src/daemon/collectors/opencode/index.ts +5 -0
- package/src/daemon/collectors/opencode/session-builder.ts +59 -0
- package/src/daemon/collectors/opencode-db.ts +282 -0
- package/src/daemon/collectors/process.ts +201 -0
- package/src/daemon/collectors/tmux/README.md +128 -0
- package/src/daemon/collectors/tmux/collector.ts +472 -0
- package/src/daemon/collectors/tmux/enrichment.ts +241 -0
- package/src/daemon/collectors/tmux/index.ts +4 -0
- package/src/daemon/collectors/tmux/parse.ts +128 -0
- package/src/daemon/collectors/usage.ts +193 -0
- package/src/daemon/index.ts +207 -0
- package/src/daemon/scheduler.ts +86 -0
- package/src/daemon/store.ts +535 -0
- package/src/detect.ts +143 -0
- package/src/hooks/claude.ts +596 -0
- package/src/hooks/codex.ts +517 -0
- package/src/legacy/classifier.ts +215 -0
- package/src/legacy/detect.ts +143 -0
- package/src/mcp/client-server.ts +21 -0
- package/src/mcp/http-handler.ts +126 -0
- package/src/mcp/index.ts +61 -0
- package/src/mcp/resources/config-current.ts +19 -0
- package/src/mcp/resources/index.ts +11 -0
- package/src/mcp/resources/sessions-active.ts +19 -0
- package/src/mcp/resources/sessions-all.ts +19 -0
- package/src/mcp/schemas/sessions.ts +3 -0
- package/src/mcp/server.ts +17 -0
- package/src/mcp/services/api-adapter.ts +48 -0
- package/src/mcp/services/data-source.ts +22 -0
- package/src/mcp/services/store-adapter.ts +40 -0
- package/src/mcp/tools/get-active-sessions.ts +16 -0
- package/src/mcp/tools/get-session-children.ts +24 -0
- package/src/mcp/tools/get-session-messages.ts +24 -0
- package/src/mcp/tools/get-session.ts +23 -0
- package/src/mcp/tools/index.ts +15 -0
- package/src/mcp/tools/list-sessions.ts +16 -0
- package/src/neo-blessed.d.ts +4 -0
- package/src/oc/index.ts +302 -0
- package/src/service/manager.ts +242 -0
- package/src/shared/config.ts +478 -0
- package/src/shared/ipc.ts +80 -0
- package/src/shared/logger.ts +248 -0
- package/src/shared/paths.ts +73 -0
- package/src/shared/platform.ts +110 -0
- package/src/shared/session-view.ts +241 -0
- package/src/shared/types.ts +299 -0
- package/src/shared/utils.ts +339 -0
- package/src/tui/app.ts +920 -0
- package/src/tui/client.ts +149 -0
- package/src/tui/index.ts +181 -0
- package/src/types.ts +113 -0
- package/src/usage/README.md +323 -0
- package/src/usage/api.ts +99 -0
- package/src/usage/client.ts +3 -0
- package/src/usage/clients/anthropic-oauth-usage.ts +178 -0
- package/src/usage/clients/anthropic-web-usage.ts +245 -0
- package/src/usage/clients/claude-cli-probe.ts +187 -0
- package/src/usage/clients/codex-oauth-usage.ts +113 -0
- package/src/usage/clients/opencode-go-usage.ts +441 -0
- package/src/usage/index.ts +19 -0
- package/src/usage/mappers.ts +273 -0
- package/src/usage/planner.ts +54 -0
- package/src/usage/providers/claude.ts +113 -0
- package/src/usage/providers/codex.ts +34 -0
- package/src/usage/providers/opencode-go.ts +37 -0
- package/src/usage/resolver.ts +4 -0
- package/src/usage/resolvers/browser-cookies.ts +198 -0
- package/src/usage/resolvers/claude-auth.ts +99 -0
- package/src/usage/resolvers/claude-cli.ts +24 -0
- package/src/usage/resolvers/claude-web.ts +16 -0
- package/src/usage/resolvers/codex-auth.ts +117 -0
- package/src/usage/resolvers/opencode-go-web.ts +14 -0
- package/src/usage/types.ts +190 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { existsSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { getResolvedConfigPath } from "../shared/config";
|
|
4
|
+
import {
|
|
5
|
+
ensureParentDir,
|
|
6
|
+
getLaunchAgentPath,
|
|
7
|
+
getServiceLabel,
|
|
8
|
+
getSystemdUnitName,
|
|
9
|
+
getSystemdUnitPath,
|
|
10
|
+
getUserUid,
|
|
11
|
+
} from "../shared/paths";
|
|
12
|
+
import { IS_LINUX, IS_MAC, IS_WSL } from "../shared/platform";
|
|
13
|
+
import { maybeRunCommand } from "../shared/utils";
|
|
14
|
+
|
|
15
|
+
export interface ServiceStatus {
|
|
16
|
+
platform: "macos" | "linux" | "wsl" | "unsupported" | "linux-no-systemd";
|
|
17
|
+
installed: boolean;
|
|
18
|
+
running: boolean;
|
|
19
|
+
detail: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ServiceManager {
|
|
23
|
+
readonly platform: ServiceStatus["platform"];
|
|
24
|
+
install(configPath?: string): void;
|
|
25
|
+
uninstall(): void;
|
|
26
|
+
start(): void;
|
|
27
|
+
stop(): void;
|
|
28
|
+
restart(): void;
|
|
29
|
+
status(): ServiceStatus;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getServiceManagerOrThrow(): ServiceManager {
|
|
33
|
+
if (IS_MAC) return new LaunchdServiceManager();
|
|
34
|
+
if (IS_LINUX) {
|
|
35
|
+
if (!hasSystemd()) {
|
|
36
|
+
const reason = IS_WSL
|
|
37
|
+
? "WSL detected without systemd. Enable systemd in /etc/wsl.conf to use service management."
|
|
38
|
+
: "No running systemd user session detected. Service management requires systemd --user.";
|
|
39
|
+
return new UnsupportedServiceManager(IS_WSL ? "wsl" : "linux-no-systemd", reason);
|
|
40
|
+
}
|
|
41
|
+
return new SystemdUserServiceManager();
|
|
42
|
+
}
|
|
43
|
+
return new UnsupportedServiceManager("unsupported", "Daemon service management is only supported on macOS and Linux. On Windows, run scrylogd manually or use Task Scheduler.");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function hasSystemd(): boolean {
|
|
47
|
+
const result = maybeRunCommand(["systemctl", "--user", "is-system-running"]);
|
|
48
|
+
return result.ok && result.output.trim() === "running";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function daemonScriptPath(): string {
|
|
52
|
+
return join(import.meta.dir, "../daemon/index.ts");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function daemonExec(configPath?: string): string[] {
|
|
56
|
+
const resolvedConfigPath = getResolvedConfigPath(configPath);
|
|
57
|
+
return [process.execPath, "run", daemonScriptPath(), "--config", resolvedConfigPath];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function renderLaunchAgentPlist(configPath?: string): string {
|
|
61
|
+
const [execPath, runArg, scriptPath, configFlag, resolvedConfigPath] = daemonExec(configPath);
|
|
62
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
63
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
64
|
+
<plist version="1.0">
|
|
65
|
+
<dict>
|
|
66
|
+
<key>Label</key>
|
|
67
|
+
<string>${getServiceLabel()}</string>
|
|
68
|
+
<key>ProgramArguments</key>
|
|
69
|
+
<array>
|
|
70
|
+
<string>${execPath}</string>
|
|
71
|
+
<string>${runArg}</string>
|
|
72
|
+
<string>${scriptPath}</string>
|
|
73
|
+
<string>${configFlag}</string>
|
|
74
|
+
<string>${resolvedConfigPath}</string>
|
|
75
|
+
</array>
|
|
76
|
+
<key>RunAtLoad</key>
|
|
77
|
+
<true/>
|
|
78
|
+
<key>KeepAlive</key>
|
|
79
|
+
<true/>
|
|
80
|
+
</dict>
|
|
81
|
+
</plist>
|
|
82
|
+
`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function renderSystemdUserUnit(configPath?: string): string {
|
|
86
|
+
const [execPath, runArg, scriptPath, configFlag, resolvedConfigPath] = daemonExec(configPath);
|
|
87
|
+
return `[Unit]
|
|
88
|
+
Description=scrylog daemon
|
|
89
|
+
After=default.target
|
|
90
|
+
|
|
91
|
+
[Service]
|
|
92
|
+
ExecStart=${shellQuote(execPath)} ${shellQuote(runArg)} ${shellQuote(scriptPath)} ${shellQuote(configFlag)} ${shellQuote(resolvedConfigPath)}
|
|
93
|
+
Restart=on-failure
|
|
94
|
+
RestartSec=3
|
|
95
|
+
|
|
96
|
+
[Install]
|
|
97
|
+
WantedBy=default.target
|
|
98
|
+
`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function run(command: string[], allowFailure = false): string {
|
|
102
|
+
const proc = Bun.spawnSync(command, { stdout: "pipe", stderr: "pipe" });
|
|
103
|
+
const stdout = Buffer.from(proc.stdout).toString("utf8").trim();
|
|
104
|
+
const stderr = Buffer.from(proc.stderr).toString("utf8").trim();
|
|
105
|
+
if (proc.exitCode !== 0 && !allowFailure) {
|
|
106
|
+
throw new Error(stderr || stdout || `Command failed: ${command.join(" ")}`);
|
|
107
|
+
}
|
|
108
|
+
return stdout || stderr;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
class LaunchdServiceManager implements ServiceManager {
|
|
112
|
+
readonly platform = "macos" as const;
|
|
113
|
+
|
|
114
|
+
install(configPath?: string): void {
|
|
115
|
+
const filePath = getLaunchAgentPath();
|
|
116
|
+
ensureParentDir(filePath);
|
|
117
|
+
writeFileSync(filePath, renderLaunchAgentPlist(configPath), "utf8");
|
|
118
|
+
this.stop();
|
|
119
|
+
run(["launchctl", "bootstrap", `gui/${getUserUid()}`, filePath], true);
|
|
120
|
+
this.start();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
uninstall(): void {
|
|
124
|
+
this.stop();
|
|
125
|
+
const filePath = getLaunchAgentPath();
|
|
126
|
+
if (existsSync(filePath)) {
|
|
127
|
+
rmSync(filePath, { force: true });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
start(): void {
|
|
132
|
+
const filePath = getLaunchAgentPath();
|
|
133
|
+
if (!existsSync(filePath)) {
|
|
134
|
+
throw new Error("LaunchAgent is not installed");
|
|
135
|
+
}
|
|
136
|
+
run(["launchctl", "bootstrap", `gui/${getUserUid()}`, filePath], true);
|
|
137
|
+
run(["launchctl", "kickstart", "-k", `gui/${getUserUid()}/${getServiceLabel()}`], true);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
stop(): void {
|
|
141
|
+
const filePath = getLaunchAgentPath();
|
|
142
|
+
run(["launchctl", "bootout", `gui/${getUserUid()}`, filePath], true);
|
|
143
|
+
run(["launchctl", "bootout", `gui/${getUserUid()}/${getServiceLabel()}`], true);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
restart(): void {
|
|
147
|
+
this.stop();
|
|
148
|
+
this.start();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
status(): ServiceStatus {
|
|
152
|
+
const installed = existsSync(getLaunchAgentPath());
|
|
153
|
+
const detail = run(["launchctl", "print", `gui/${getUserUid()}/${getServiceLabel()}`], true);
|
|
154
|
+
const running = /state = running/i.test(detail) || /pid = \d+/i.test(detail);
|
|
155
|
+
return { platform: this.platform, installed, running, detail: detail || "No launchd job loaded" };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
class SystemdUserServiceManager implements ServiceManager {
|
|
160
|
+
readonly platform = "linux" as const;
|
|
161
|
+
|
|
162
|
+
install(configPath?: string): void {
|
|
163
|
+
const filePath = getSystemdUnitPath();
|
|
164
|
+
ensureParentDir(filePath);
|
|
165
|
+
writeFileSync(filePath, renderSystemdUserUnit(configPath), "utf8");
|
|
166
|
+
run(["systemctl", "--user", "daemon-reload"]);
|
|
167
|
+
run(["systemctl", "--user", "enable", "--now", getSystemdUnitName()]);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
uninstall(): void {
|
|
171
|
+
run(["systemctl", "--user", "disable", "--now", getSystemdUnitName()], true);
|
|
172
|
+
const filePath = getSystemdUnitPath();
|
|
173
|
+
if (existsSync(filePath)) {
|
|
174
|
+
rmSync(filePath, { force: true });
|
|
175
|
+
}
|
|
176
|
+
run(["systemctl", "--user", "daemon-reload"], true);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
start(): void {
|
|
180
|
+
run(["systemctl", "--user", "start", getSystemdUnitName()]);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
stop(): void {
|
|
184
|
+
run(["systemctl", "--user", "stop", getSystemdUnitName()], true);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
restart(): void {
|
|
188
|
+
run(["systemctl", "--user", "restart", getSystemdUnitName()]);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
status(): ServiceStatus {
|
|
192
|
+
const installed = existsSync(getSystemdUnitPath()) && run(["systemctl", "--user", "is-enabled", getSystemdUnitName()], true).includes("enabled");
|
|
193
|
+
const active = run(["systemctl", "--user", "is-active", getSystemdUnitName()], true);
|
|
194
|
+
const detail = run(["systemctl", "--user", "status", getSystemdUnitName(), "--no-pager"], true);
|
|
195
|
+
return {
|
|
196
|
+
platform: this.platform,
|
|
197
|
+
installed,
|
|
198
|
+
// `is-active` returns "inactive" for a missing/stopped unit, which contains the
|
|
199
|
+
// substring "active" — only an exact match means the unit is actually running.
|
|
200
|
+
running: active.trim() === "active",
|
|
201
|
+
detail: detail || active || "Unit not loaded",
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
class UnsupportedServiceManager implements ServiceManager {
|
|
207
|
+
readonly platform: ServiceStatus["platform"];
|
|
208
|
+
private readonly detail: string;
|
|
209
|
+
|
|
210
|
+
constructor(platform: "wsl" | "unsupported" | "linux-no-systemd", detail: string) {
|
|
211
|
+
this.platform = platform;
|
|
212
|
+
this.detail = detail;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
install(): void {
|
|
216
|
+
throw new Error(this.detail);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
uninstall(): void {
|
|
220
|
+
// Nothing to uninstall on unsupported platforms.
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
start(): void {
|
|
224
|
+
throw new Error(this.detail);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
stop(): void {
|
|
228
|
+
// Nothing to stop.
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
restart(): void {
|
|
232
|
+
throw new Error(this.detail);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
status(): ServiceStatus {
|
|
236
|
+
return { platform: this.platform, installed: false, running: false, detail: this.detail };
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function shellQuote(value: string): string {
|
|
241
|
+
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
242
|
+
}
|