@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.
- package/dist/assets/highlights-eq9cgrbb.scm +604 -0
- package/dist/assets/highlights-ghv9g403.scm +205 -0
- package/dist/assets/highlights-hk7bwhj4.scm +284 -0
- package/dist/assets/highlights-r812a2qc.scm +150 -0
- package/dist/assets/highlights-x6tmsnaa.scm +115 -0
- package/dist/assets/injections-73j83es3.scm +27 -0
- package/dist/assets/parser.worker-xyhf83rf.js +4563 -0
- package/dist/assets/tree-sitter-3jzf13jk.wasm +0 -0
- package/dist/assets/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
- package/dist/assets/tree-sitter-markdown-411r6y9b.wasm +0 -0
- package/dist/assets/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
- package/dist/assets/tree-sitter-typescript-zxjzwt75.wasm +0 -0
- package/dist/assets/tree-sitter-zig-e78zbjpm.wasm +0 -0
- package/dist/cli/index.js +499 -0
- package/dist/cli/index.js.map +61 -0
- package/dist/daemon/index.js +670 -0
- package/dist/daemon/index.js.map +1475 -0
- package/dist/hooks/claude.js +6 -0
- package/dist/hooks/claude.js.map +11 -0
- package/dist/hooks/codex.js +10 -0
- package/dist/hooks/codex.js.map +11 -0
- package/dist/mcp/index.js +100 -0
- package/dist/mcp/index.js.map +231 -0
- package/dist/oc/index.js +21 -0
- package/dist/oc/index.js.map +10 -0
- package/package.json +16 -13
- package/src/api/docs/sessions.keys.ts +0 -6
- package/src/api/docs/sessions.ts +0 -82
- package/src/api/docs/system.keys.ts +0 -5
- package/src/api/docs/system.ts +0 -69
- package/src/api/docs/usage.keys.ts +0 -3
- package/src/api/docs/usage.ts +0 -24
- package/src/api/docs/webhooks.keys.ts +0 -5
- package/src/api/docs/webhooks.ts +0 -78
- package/src/api/index.ts +0 -18
- package/src/api/lib/auth.ts +0 -43
- package/src/api/lib/errors.ts +0 -28
- package/src/api/lib/route.ts +0 -18
- package/src/api/models/claude.ts +0 -4
- package/src/api/models/codex.ts +0 -4
- package/src/api/models/opencode.ts +0 -11
- package/src/api/routes/sessions.ts +0 -51
- package/src/api/routes/system.ts +0 -106
- package/src/api/routes/usage.ts +0 -15
- package/src/api/routes/webhooks/claude.ts +0 -149
- package/src/api/routes/webhooks/codex.ts +0 -175
- package/src/api/routes/webhooks/opencode.ts +0 -112
- package/src/api/routes.ts +0 -93
- package/src/api/schemas/common.ts +0 -29
- package/src/api/schemas/sessions.ts +0 -86
- package/src/api/schemas/usage.ts +0 -51
- package/src/api/schemas/webhooks/claude.ts +0 -32
- package/src/api/schemas/webhooks/codex.ts +0 -34
- package/src/api/schemas/webhooks/opencode.ts +0 -29
- package/src/api/services/claude-session.ts +0 -9
- package/src/api/services/codex-session.ts +0 -8
- package/src/api/services/opencode-session.ts +0 -73
- package/src/classifier.ts +0 -206
- package/src/cli/commands.ts +0 -714
- package/src/cli/index.ts +0 -18
- package/src/cli/program.ts +0 -175
- package/src/cli/prompt.ts +0 -44
- package/src/daemon/cli.ts +0 -27
- package/src/daemon/collectors/claude/README.md +0 -108
- package/src/daemon/collectors/claude/collector.ts +0 -423
- package/src/daemon/collectors/claude/discovery.ts +0 -127
- package/src/daemon/collectors/claude/hooks.ts +0 -181
- package/src/daemon/collectors/claude/index.ts +0 -8
- package/src/daemon/collectors/claude/jsonl.ts +0 -85
- package/src/daemon/collectors/claude/transcript.ts +0 -246
- package/src/daemon/collectors/codex/README.md +0 -147
- package/src/daemon/collectors/codex/collector.ts +0 -661
- package/src/daemon/collectors/codex/discovery.ts +0 -104
- package/src/daemon/collectors/codex/hooks.ts +0 -179
- package/src/daemon/collectors/codex/index.ts +0 -7
- package/src/daemon/collectors/codex/jsonl.ts +0 -108
- package/src/daemon/collectors/codex/transcript.ts +0 -299
- package/src/daemon/collectors/git.ts +0 -353
- package/src/daemon/collectors/herdr/collector.ts +0 -128
- package/src/daemon/collectors/herdr/index.ts +0 -2
- package/src/daemon/collectors/herdr/normalization.ts +0 -111
- package/src/daemon/collectors/opencode/README.md +0 -106
- package/src/daemon/collectors/opencode/collector.ts +0 -545
- package/src/daemon/collectors/opencode/discovery.ts +0 -257
- package/src/daemon/collectors/opencode/index.ts +0 -5
- package/src/daemon/collectors/opencode/session-builder.ts +0 -59
- package/src/daemon/collectors/opencode-db.ts +0 -294
- package/src/daemon/collectors/process.ts +0 -201
- package/src/daemon/collectors/tmux/README.md +0 -128
- package/src/daemon/collectors/tmux/collector.ts +0 -492
- package/src/daemon/collectors/tmux/enrichment.ts +0 -242
- package/src/daemon/collectors/tmux/index.ts +0 -4
- package/src/daemon/collectors/tmux/parse.ts +0 -128
- package/src/daemon/collectors/usage.ts +0 -259
- package/src/daemon/index.ts +0 -209
- package/src/daemon/scheduler.ts +0 -86
- package/src/daemon/store.ts +0 -630
- package/src/detect.ts +0 -143
- package/src/hooks/claude.ts +0 -596
- package/src/hooks/codex.ts +0 -517
- package/src/legacy/classifier.ts +0 -215
- package/src/legacy/detect.ts +0 -143
- package/src/mcp/client-server.ts +0 -21
- package/src/mcp/http-handler.ts +0 -126
- package/src/mcp/index.ts +0 -61
- package/src/mcp/resources/config-current.ts +0 -19
- package/src/mcp/resources/index.ts +0 -11
- package/src/mcp/resources/sessions-active.ts +0 -19
- package/src/mcp/resources/sessions-all.ts +0 -19
- package/src/mcp/schemas/sessions.ts +0 -3
- package/src/mcp/server.ts +0 -17
- package/src/mcp/services/api-adapter.ts +0 -48
- package/src/mcp/services/data-source.ts +0 -22
- package/src/mcp/services/store-adapter.ts +0 -40
- package/src/mcp/tools/get-active-sessions.ts +0 -16
- package/src/mcp/tools/get-session-children.ts +0 -24
- package/src/mcp/tools/get-session-messages.ts +0 -24
- package/src/mcp/tools/get-session.ts +0 -23
- package/src/mcp/tools/index.ts +0 -15
- package/src/mcp/tools/list-sessions.ts +0 -16
- package/src/oc/index.ts +0 -302
- package/src/service/manager.ts +0 -284
- package/src/shared/config.ts +0 -511
- package/src/shared/ipc.ts +0 -80
- package/src/shared/logger.ts +0 -248
- package/src/shared/paths.ts +0 -73
- package/src/shared/platform.ts +0 -110
- package/src/shared/session-exclusions.ts +0 -5
- package/src/shared/session-view.ts +0 -302
- package/src/shared/terminal-capabilities.ts +0 -130
- package/src/shared/types.ts +0 -330
- package/src/shared/utils.ts +0 -339
- package/src/shared/version.ts +0 -44
- package/src/tui/App.tsx +0 -240
- package/src/tui/client.ts +0 -163
- package/src/tui/components/BindingsModal.tsx +0 -82
- package/src/tui/components/DashboardHeader.tsx +0 -256
- package/src/tui/components/KeyBar.tsx +0 -67
- package/src/tui/components/SessionDetailModal.tsx +0 -169
- package/src/tui/components/SessionsView.tsx +0 -184
- package/src/tui/components/TabBar.tsx +0 -40
- package/src/tui/components/UsageSummaryPane.tsx +0 -272
- package/src/tui/components/UsageView.tsx +0 -242
- package/src/tui/components/tool-icons.ts +0 -24
- package/src/tui/index.tsx +0 -106
- package/src/tui/keybindings.ts +0 -49
- package/src/tui/model.ts +0 -295
- package/src/tui/state.ts +0 -77
- package/src/tui/theme.ts +0 -110
- package/src/types.ts +0 -113
- package/src/usage/README.md +0 -323
- package/src/usage/api.ts +0 -99
- package/src/usage/client.ts +0 -3
- package/src/usage/clients/anthropic-oauth-usage.ts +0 -258
- package/src/usage/clients/anthropic-web-usage.ts +0 -329
- package/src/usage/clients/claude-cli-probe.ts +0 -513
- package/src/usage/clients/codex-app-server.ts +0 -354
- package/src/usage/clients/codex-oauth-usage.ts +0 -240
- package/src/usage/clients/opencode-go-api.ts +0 -100
- package/src/usage/clients/opencode-go-usage.ts +0 -577
- package/src/usage/errors.ts +0 -33
- package/src/usage/index.ts +0 -20
- package/src/usage/local/opencode-go-local.ts +0 -194
- package/src/usage/mappers.ts +0 -282
- package/src/usage/planner.ts +0 -118
- package/src/usage/providers/claude.ts +0 -233
- package/src/usage/providers/codex.ts +0 -115
- package/src/usage/providers/opencode-go.ts +0 -115
- package/src/usage/resolver.ts +0 -4
- package/src/usage/resolvers/browser-cookies.ts +0 -808
- package/src/usage/resolvers/claude-auth.ts +0 -208
- package/src/usage/resolvers/claude-cli.ts +0 -24
- package/src/usage/resolvers/claude-desktop-auth.ts +0 -170
- package/src/usage/resolvers/claude-web.ts +0 -82
- package/src/usage/resolvers/codex-auth.ts +0 -174
- package/src/usage/resolvers/keychain-coordinator.ts +0 -397
- package/src/usage/resolvers/opencode-go-web.ts +0 -55
- package/src/usage/types.ts +0 -225
- package/src/usage/web-session-cache.ts +0 -37
- /package/{src/tui → dist/cli}/assets/providers/SOURCES.md +0 -0
- /package/{src/tui → dist/cli}/assets/providers/claude-terminal.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/claude-terminal.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/claude.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/copilot.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/copilot.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/openai-terminal.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/openai-terminal.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/openai.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/opencode-terminal.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/opencode-terminal.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/opencode.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/shell.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/shell.svg +0 -0
- /package/{src/tui → dist/cli}/assets/providers/unknown.png +0 -0
- /package/{src/tui → dist/cli}/assets/providers/unknown.svg +0 -0
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import { listSessions } from "@joeldmtz/prtl-core";
|
|
2
|
-
import type { ListSessionsOptions, PrtlListResult } from "@joeldmtz/prtl-core";
|
|
3
|
-
import type { ScrylogConfig } from "../../../shared/config";
|
|
4
|
-
import type { SessionModel } from "../../../shared/types";
|
|
5
|
-
import type { Logger } from "../../../shared/logger";
|
|
6
|
-
import { NOOP_LOGGER } from "../../../shared/logger";
|
|
7
|
-
import type { Collector } from "../../scheduler";
|
|
8
|
-
import type { DaemonStore } from "../../store";
|
|
9
|
-
import type { GitCollector } from "../git";
|
|
10
|
-
import { herdrPaneToSession } from "./normalization";
|
|
11
|
-
|
|
12
|
-
type HerdrList = (options?: ListSessionsOptions) => Promise<PrtlListResult>;
|
|
13
|
-
|
|
14
|
-
export class HerdrCollector implements Collector {
|
|
15
|
-
name = "herdr";
|
|
16
|
-
private timer: ReturnType<typeof setInterval> | null = null;
|
|
17
|
-
private inFlight = false;
|
|
18
|
-
private generation = 0;
|
|
19
|
-
private logger: Logger = NOOP_LOGGER;
|
|
20
|
-
|
|
21
|
-
constructor(
|
|
22
|
-
private readonly gitCollector: GitCollector,
|
|
23
|
-
private readonly list: HerdrList = listSessions,
|
|
24
|
-
) {}
|
|
25
|
-
|
|
26
|
-
start(store: DaemonStore, config: ScrylogConfig, logger: Logger): void {
|
|
27
|
-
this.logger = logger;
|
|
28
|
-
const generation = ++this.generation;
|
|
29
|
-
const run = () => void this.collect(store, config.daemon.commandTimeoutMs, generation);
|
|
30
|
-
logger.info("herdr", "Collector started", { event: "collector.started", collector: this.name, intervalMs: config.daemon.pollIntervalMs });
|
|
31
|
-
run();
|
|
32
|
-
this.timer = setInterval(run, config.daemon.pollIntervalMs);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
stop(): void {
|
|
36
|
-
this.generation += 1;
|
|
37
|
-
if (this.timer) {
|
|
38
|
-
clearInterval(this.timer);
|
|
39
|
-
this.timer = null;
|
|
40
|
-
}
|
|
41
|
-
this.logger.info("herdr", "Collector stopped", { event: "collector.stopped", collector: this.name });
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async collect(store: DaemonStore, timeoutMs: number, generation = this.generation): Promise<void> {
|
|
45
|
-
if (this.inFlight) {
|
|
46
|
-
this.logger.debug("herdr", "Skipping overlapping poll", { event: "collector.poll_skipped", collector: this.name });
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
this.inFlight = true;
|
|
51
|
-
const startedAt = Date.now();
|
|
52
|
-
try {
|
|
53
|
-
const result = await this.list({ provider: "herdr", timeoutMs });
|
|
54
|
-
if (generation !== this.generation) return;
|
|
55
|
-
const provider = result.metadata.providers.herdr;
|
|
56
|
-
const liveHerdrIds = new Set<string>();
|
|
57
|
-
|
|
58
|
-
if (provider?.available) {
|
|
59
|
-
for (const pane of result.items.filter((item) => item.provider === "herdr")) {
|
|
60
|
-
const path = pane.foregroundCwd ?? pane.cwd;
|
|
61
|
-
const session = herdrPaneToSession(pane, path ? this.gitCollector.resolve(path) : undefined);
|
|
62
|
-
liveHerdrIds.add(session.id);
|
|
63
|
-
const existing = store.getSession(session.id);
|
|
64
|
-
if (existing && existing.detectionSource !== "herdr") {
|
|
65
|
-
if (existing.source !== "tmux") {
|
|
66
|
-
store.upsertSession({
|
|
67
|
-
...existing,
|
|
68
|
-
source: "herdr",
|
|
69
|
-
herdr: session.herdr,
|
|
70
|
-
project: session.project ?? existing.project,
|
|
71
|
-
currentPath: existing.currentPath ?? session.currentPath,
|
|
72
|
-
currentCommand: session.currentCommand,
|
|
73
|
-
paneTitle: session.paneTitle,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
} else {
|
|
77
|
-
// herdr-native sessions: prtl-core refreshes metadata.collectedAt on
|
|
78
|
-
// every collection, so a pane doing nothing would report a fresh
|
|
79
|
-
// lastActivity each poll and constantly reorder idle groups. Only
|
|
80
|
-
// real changes (state, agent status, summary) count as activity.
|
|
81
|
-
const previous = existing?.detectionSource === "herdr" ? existing : undefined;
|
|
82
|
-
const activityChanged = Boolean(
|
|
83
|
-
previous
|
|
84
|
-
&& (previous.state !== session.state
|
|
85
|
-
|| previous.herdr?.agentStatus !== session.herdr?.agentStatus
|
|
86
|
-
|| previous.summary !== session.summary),
|
|
87
|
-
);
|
|
88
|
-
const merged: SessionModel = previous && !activityChanged
|
|
89
|
-
? { ...session, lastActivity: previous.lastActivity }
|
|
90
|
-
: { ...session, lastActivity: Date.now() };
|
|
91
|
-
store.upsertSession(merged);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
for (const session of store.getSessions()) {
|
|
96
|
-
if (session.source !== "herdr" || liveHerdrIds.has(session.id)) continue;
|
|
97
|
-
if (session.detectionSource === "herdr") {
|
|
98
|
-
store.removeSession(session.id);
|
|
99
|
-
} else {
|
|
100
|
-
store.clearHerdrBinding(session.id);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const errors = provider?.errors?.map((error) => `${error.source}: ${error.message}`) ?? [];
|
|
106
|
-
const lastError = provider?.available ? undefined : (provider?.reason ?? errors.at(-1) ?? "herdr_unavailable");
|
|
107
|
-
store.setCollectorStatus(this.name, {
|
|
108
|
-
running: true,
|
|
109
|
-
lastRunAt: Date.now(),
|
|
110
|
-
lastDurationMs: Date.now() - startedAt,
|
|
111
|
-
lastError,
|
|
112
|
-
});
|
|
113
|
-
this.logger.debug("herdr", "Collection completed", {
|
|
114
|
-
event: "collector.poll_completed",
|
|
115
|
-
collector: this.name,
|
|
116
|
-
available: provider?.available ?? false,
|
|
117
|
-
panes: liveHerdrIds.size,
|
|
118
|
-
});
|
|
119
|
-
} catch (error) {
|
|
120
|
-
if (generation !== this.generation) return;
|
|
121
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
122
|
-
store.setCollectorStatus(this.name, { running: true, lastRunAt: Date.now(), lastDurationMs: Date.now() - startedAt, lastError: message });
|
|
123
|
-
this.logger.warn("herdr", "Collection failed", { event: "collector.poll_failed", collector: this.name, errorMessage: message });
|
|
124
|
-
} finally {
|
|
125
|
-
this.inFlight = false;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import type { PrtlPane } from "@joeldmtz/prtl-core";
|
|
2
|
-
import type { ProjectInfo, SessionModel, SessionState, SessionTool } from "../../../shared/types";
|
|
3
|
-
|
|
4
|
-
const SESSION_TOOLS = new Set<SessionTool>(["opencode", "claude", "codex"]);
|
|
5
|
-
|
|
6
|
-
function normalizeAgentName(value: string): string {
|
|
7
|
-
return value.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function herdrTool(pane: PrtlPane): SessionTool {
|
|
11
|
-
const candidates = [pane.agent?.session?.agent, pane.agent?.name, pane.agent?.session?.source?.split(":").at(-1)];
|
|
12
|
-
for (const candidate of candidates) {
|
|
13
|
-
if (!candidate) continue;
|
|
14
|
-
const normalized = normalizeAgentName(candidate);
|
|
15
|
-
if (normalized === "opencode") return "opencode";
|
|
16
|
-
if (normalized === "claude" || normalized === "claudecode") return "claude";
|
|
17
|
-
if (normalized === "codex" || normalized === "codexcli") return "codex";
|
|
18
|
-
if (normalized === "copilot" || normalized === "copilotcli" || normalized === "githubcopilot") return "copilotcli";
|
|
19
|
-
}
|
|
20
|
-
if (!pane.agent && pane.command) return "shell";
|
|
21
|
-
return "unknown";
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function herdrState(pane: PrtlPane, tool = herdrTool(pane)): SessionState {
|
|
25
|
-
if (!pane.agent) return tool === "shell" ? "shell" : "unknown";
|
|
26
|
-
switch (pane.agent.status?.toLowerCase()) {
|
|
27
|
-
case "working": return "working";
|
|
28
|
-
case "idle":
|
|
29
|
-
case "done": return "idle";
|
|
30
|
-
case "blocked": return "waiting-input";
|
|
31
|
-
default: return "unknown";
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function herdrSessionId(pane: PrtlPane, tool = herdrTool(pane)): string {
|
|
36
|
-
const agentSessionId = pane.agent?.sessionId ?? pane.agent?.session?.value;
|
|
37
|
-
return agentSessionId && SESSION_TOOLS.has(tool)
|
|
38
|
-
? `${tool}:${agentSessionId}`
|
|
39
|
-
: `herdr:${pane.id}`;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function herdrSummary(pane: PrtlPane): string {
|
|
43
|
-
const statusLabel = pane.agent?.status ? pane.stateLabels?.[pane.agent.status] : undefined;
|
|
44
|
-
return pane.customStatus
|
|
45
|
-
?? statusLabel
|
|
46
|
-
?? pane.title
|
|
47
|
-
?? pane.terminalTitleStripped
|
|
48
|
-
?? pane.displayAgent
|
|
49
|
-
?? pane.command
|
|
50
|
-
?? pane.agent?.name
|
|
51
|
-
?? pane.tab
|
|
52
|
-
?? pane.workspace
|
|
53
|
-
?? "Herdr pane";
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Explicit pane name only. prtl-core's `pane.name` falls back to the agent
|
|
58
|
-
* name when the pane has no label; in that case the pane is unnamed and the
|
|
59
|
-
* label logic must fall back to the tab name instead. The raw herdr record
|
|
60
|
-
* carries the original pane fields, which is the only way to distinguish an
|
|
61
|
-
* explicit name from prtl-core's agent-derived fallback.
|
|
62
|
-
*/
|
|
63
|
-
export function herdrPaneName(pane: PrtlPane): string | undefined {
|
|
64
|
-
const raw = (pane.metadata?.raw as { pane?: { title?: unknown; label?: unknown; name?: unknown } } | undefined)?.pane;
|
|
65
|
-
if (raw) {
|
|
66
|
-
const explicit = [raw.name, raw.title, raw.label].find(
|
|
67
|
-
(value) => typeof value === "string" && value.trim().length > 0,
|
|
68
|
-
);
|
|
69
|
-
return typeof explicit === "string" ? explicit.trim() : undefined;
|
|
70
|
-
}
|
|
71
|
-
// No raw record to discriminate: only trust an explicitly-typed PrtlPane
|
|
72
|
-
// name field (raw pane title/title/label from the provider), never render
|
|
73
|
-
// the agent-derived fallback as a pane name.
|
|
74
|
-
return pane.title?.trim() || undefined;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function herdrPaneToSession(pane: PrtlPane, project?: ProjectInfo): SessionModel {
|
|
78
|
-
const tool = herdrTool(pane);
|
|
79
|
-
const state = herdrState(pane, tool);
|
|
80
|
-
const agentSessionId = pane.agent?.sessionId ?? pane.agent?.session?.value;
|
|
81
|
-
const collectedAt = Date.parse(pane.metadata.collectedAt);
|
|
82
|
-
return {
|
|
83
|
-
id: herdrSessionId(pane, tool),
|
|
84
|
-
source: "herdr",
|
|
85
|
-
tool,
|
|
86
|
-
state,
|
|
87
|
-
project,
|
|
88
|
-
summary: herdrSummary(pane),
|
|
89
|
-
lastActivity: Number.isFinite(collectedAt) ? collectedAt : Date.now(),
|
|
90
|
-
hasAttention: state === "waiting-input",
|
|
91
|
-
activeTool: pane.agent?.name,
|
|
92
|
-
detectionSource: "herdr",
|
|
93
|
-
currentCommand: pane.command,
|
|
94
|
-
currentPath: pane.foregroundCwd ?? pane.cwd,
|
|
95
|
-
paneTitle: pane.title,
|
|
96
|
-
herdr: {
|
|
97
|
-
paneId: pane.id,
|
|
98
|
-
terminalId: pane.terminalId,
|
|
99
|
-
workspaceId: pane.workspaceId,
|
|
100
|
-
workspace: pane.workspace,
|
|
101
|
-
tabId: pane.tabId,
|
|
102
|
-
tab: pane.tab,
|
|
103
|
-
name: herdrPaneName(pane),
|
|
104
|
-
focused: pane.focused,
|
|
105
|
-
providerSource: pane.metadata.source,
|
|
106
|
-
agent: pane.agent?.name,
|
|
107
|
-
agentStatus: pane.agent?.status,
|
|
108
|
-
agentSessionId,
|
|
109
|
-
},
|
|
110
|
-
};
|
|
111
|
-
}
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
# OpenCode Collector
|
|
2
|
-
|
|
3
|
-
Monitors OpenCode sessions by connecting to running OpenCode instances via the
|
|
4
|
-
`@opencode-ai/sdk` HTTP client. Uses SSE live events as the primary transport
|
|
5
|
-
with HTTP polling as a fallback.
|
|
6
|
-
|
|
7
|
-
## Data Flow
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
config.opencode.instances[]
|
|
11
|
-
│
|
|
12
|
-
│
|
|
13
|
-
┌─────────────────────────▼──────────────────────────┐
|
|
14
|
-
│ discovery │
|
|
15
|
-
│ discoverInstances() │ parseOpencodeUrl() │
|
|
16
|
-
│ discoverListeningPort() │ findOpencodeInTree() │
|
|
17
|
-
└─────────────────────────┬──────────────────────────┘
|
|
18
|
-
│ DiscoveredInstance[]
|
|
19
|
-
│
|
|
20
|
-
▼
|
|
21
|
-
┌──────────────────────────────────────────────────────────────────┐
|
|
22
|
-
│ collector.ts │
|
|
23
|
-
│ │
|
|
24
|
-
│ ┌───────────────┐ ┌───────────────────────────┐ │
|
|
25
|
-
│ │ 60s refresh()│ │ SSE event.subscribe() │ │
|
|
26
|
-
│ │ discover + │ │ │ │
|
|
27
|
-
│ │ connectInstance() ┌─────►│ session.status │ │
|
|
28
|
-
│ └───────┬───────┘ │ │ session.updated/created │ │
|
|
29
|
-
│ │ │ │ permission.updated │ │
|
|
30
|
-
│ │ ┌───────┴───────┤ permission.replied │ │
|
|
31
|
-
│ │ │ poll fallback │ session.deleted │ │
|
|
32
|
-
│ │ │ (10s) │ session.error │ │
|
|
33
|
-
│ │ └───────┬───────┘ │ │
|
|
34
|
-
│ │ │ │ │
|
|
35
|
-
│ │ ▼ │ │
|
|
36
|
-
│ │ ┌─────────────────┐ │ │
|
|
37
|
-
│ └───►│ session-builder │ buildSessionModel() │ │
|
|
38
|
-
│ │ │ mapSessionStatus() │ │
|
|
39
|
-
│ └────────┬────────┘ │ │
|
|
40
|
-
│ │ │ │
|
|
41
|
-
│ ▼ │ │
|
|
42
|
-
│ store.upsertSession() │ │
|
|
43
|
-
│ store.syncChildSessionIds() │ │
|
|
44
|
-
│ store.registerSdkPid() │ │
|
|
45
|
-
└──────────────────────────────────────────────────────────────────┘
|
|
46
|
-
│
|
|
47
|
-
▼
|
|
48
|
-
DaemonStore ──► /api/events SSE ──► TUI
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
## Modules
|
|
52
|
-
|
|
53
|
-
| File | Responsibility | Key Exports |
|
|
54
|
-
|------|-------------|-------------|
|
|
55
|
-
| `discovery.ts` | Auto-discover OpenCode processes via `ps`, resolve ephemeral ports with `lsof`, parse command-line URLs | `discoverInstances()`, `parseOpencodeUrl()`, `hasExplicitPort()`, `discoverListeningPort()`, `findOpencodeInTree()`, `clearPidPortCache()`, `DiscoveredInstance` |
|
|
56
|
-
| `session-builder.ts` | Map SDK `Session` + `SessionStatus` → `SessionModel`; compute `state` and `hasAttention` from pending permissions/questions | `buildSessionModel()`, `mapSessionStatus()`, `InstanceState` |
|
|
57
|
-
| `collector.ts` | Maintain instance connections; subscribe to SSE; handle reconnects and poll fallback; dispatch events to `session-builder` | `OpenCodeCollector` (implements `Collector`) |
|
|
58
|
-
| `index.ts` | Public re-exports | `OpenCodeCollector`, discovery + session-builder functions |
|
|
59
|
-
|
|
60
|
-
## State Mapping (`session-builder.ts`)
|
|
61
|
-
|
|
62
|
-
| SDK `SessionStatus` | State | Attention |
|
|
63
|
-
|---------------------|-------|-----------|
|
|
64
|
-
| `busy` | `working` | `false` |
|
|
65
|
-
| `idle` | `idle` | `false` |
|
|
66
|
-
| `retry` | `working` | `false` |
|
|
67
|
-
| *(any + pending permission/question)* | `waiting-input` | `true` |
|
|
68
|
-
|
|
69
|
-
## Transports
|
|
70
|
-
|
|
71
|
-
| Transport | Trigger | Events Handled |
|
|
72
|
-
|-----------|---------|----------------|
|
|
73
|
-
| **SSE** | Primary. Established on `connectInstance()`. | `session.status`, `session.updated`, `session.created`, `session.deleted`, `session.idle`, `session.error`, `permission.updated`, `permission.replied` |
|
|
74
|
-
| **Poll** | Fallback. Triggered when SSE disconnects or `connectInstance()` fails. | Fetches full session list + status map every 10s. |
|
|
75
|
-
|
|
76
|
-
The collector seeds pending permissions on initial connect by calling the
|
|
77
|
-
(optional) `permission.list` SDK endpoint. This avoids stale "waiting-input"
|
|
78
|
-
states before the first SSE event arrives.
|
|
79
|
-
|
|
80
|
-
## Instance Lifecycle
|
|
81
|
-
|
|
82
|
-
```
|
|
83
|
-
discovered ──► connectInstance() ──► subscribeSSE()
|
|
84
|
-
│ │
|
|
85
|
-
│ ▼ (on disconnect/error)
|
|
86
|
-
│ startPollFallback() ──► pollInstance()
|
|
87
|
-
│ │
|
|
88
|
-
│ ▼ (on re-connect)
|
|
89
|
-
│ stop poll timer ──► subscribeSSE()
|
|
90
|
-
│ │
|
|
91
|
-
└──────────────────────────────────────────┘
|
|
92
|
-
teardownInstance() on instance gone
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
## Interactions
|
|
96
|
-
|
|
97
|
-
| Component | How |
|
|
98
|
-
|-----------|-----|
|
|
99
|
-
| `GitCollector` | Resolves `session.directory` → `project` in `buildSessionModel()` |
|
|
100
|
-
| `TmuxCollector` | Uses `parseOpencodeUrl()` to derive instance URLs from pane process trees; uses `store.registerSdkPid()` results for PID-ancestry matching |
|
|
101
|
-
| `DaemonStore` | Upserts sessions; registers SDK PIDs for tmux deduplication; rebuilds parent→child links |
|
|
102
|
-
| `Scheduler` | Calls `start()` → sets up `60s` discovery interval |
|
|
103
|
-
|
|
104
|
-
## Detection Source Produced
|
|
105
|
-
|
|
106
|
-
- `"opencode-sdk"` — session discovered and maintained via `@opencode-ai/sdk` HTTP/SSE
|