@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,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,2 +0,0 @@
1
- export { HerdrCollector } from "./collector";
2
- export { herdrPaneToSession, herdrSessionId, herdrState, herdrSummary, herdrTool } from "./normalization";
@@ -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