otacon 0.1.3 → 0.1.4

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 (201) hide show
  1. package/README.md +18 -5
  2. package/dist/cli/browser.js +1 -1
  3. package/dist/cli/browser.js.map +1 -1
  4. package/dist/cli/client.js +3 -3
  5. package/dist/cli/client.js.map +1 -1
  6. package/dist/cli/commands/answer.js +1 -1
  7. package/dist/cli/commands/answer.js.map +1 -1
  8. package/dist/cli/commands/ask.js +3 -3
  9. package/dist/cli/commands/ask.js.map +1 -1
  10. package/dist/cli/commands/clean.js +14 -16
  11. package/dist/cli/commands/clean.js.map +1 -1
  12. package/dist/cli/commands/config.js +1 -1
  13. package/dist/cli/commands/config.js.map +1 -1
  14. package/dist/cli/commands/doctor.js +4 -4
  15. package/dist/cli/commands/doctor.js.map +1 -1
  16. package/dist/cli/commands/expose.js +7 -7
  17. package/dist/cli/commands/expose.js.map +1 -1
  18. package/dist/cli/commands/implement-done.js +1 -1
  19. package/dist/cli/commands/implement-done.js.map +1 -1
  20. package/dist/cli/commands/install.js +16 -10
  21. package/dist/cli/commands/install.js.map +1 -1
  22. package/dist/cli/commands/open.js +31 -7
  23. package/dist/cli/commands/open.js.map +1 -1
  24. package/dist/cli/commands/progress.js +1 -1
  25. package/dist/cli/commands/progress.js.map +1 -1
  26. package/dist/cli/commands/resume.js +67 -0
  27. package/dist/cli/commands/resume.js.map +1 -0
  28. package/dist/cli/commands/start.js +18 -4
  29. package/dist/cli/commands/start.js.map +1 -1
  30. package/dist/cli/commands/status.js +15 -4
  31. package/dist/cli/commands/status.js.map +1 -1
  32. package/dist/cli/commands/submit.js +5 -5
  33. package/dist/cli/commands/submit.js.map +1 -1
  34. package/dist/cli/commands/update.js +24 -17
  35. package/dist/cli/commands/update.js.map +1 -1
  36. package/dist/cli/commands/wait.js +1 -1
  37. package/dist/cli/commands/wait.js.map +1 -1
  38. package/dist/cli/install/assets.js +123 -48
  39. package/dist/cli/install/assets.js.map +1 -1
  40. package/dist/cli/install/locations.js +1 -1
  41. package/dist/cli/install/locations.js.map +1 -1
  42. package/dist/cli/install/tailscale.js +2 -2
  43. package/dist/cli/install/tailscale.js.map +1 -1
  44. package/dist/cli/install/wrapper.js +202 -0
  45. package/dist/cli/install/wrapper.js.map +1 -0
  46. package/dist/cli/main.js +5 -2
  47. package/dist/cli/main.js.map +1 -1
  48. package/dist/cli/output.js +2 -2
  49. package/dist/cli/output.js.map +1 -1
  50. package/dist/cli/session.js +18 -7
  51. package/dist/cli/session.js.map +1 -1
  52. package/dist/cli/update.js +79 -38
  53. package/dist/cli/update.js.map +1 -1
  54. package/dist/daemon/activity.js +1 -1
  55. package/dist/daemon/activity.js.map +1 -1
  56. package/dist/daemon/anchor.js +1 -1
  57. package/dist/daemon/anchor.js.map +1 -1
  58. package/dist/daemon/app.js +370 -82
  59. package/dist/daemon/app.js.map +1 -1
  60. package/dist/daemon/approve.js +3 -3
  61. package/dist/daemon/approve.js.map +1 -1
  62. package/dist/daemon/capture/adapter.js +23 -0
  63. package/dist/daemon/capture/adapter.js.map +1 -0
  64. package/dist/daemon/capture/claude.js +274 -0
  65. package/dist/daemon/capture/claude.js.map +1 -0
  66. package/dist/daemon/capture/codex.js +413 -0
  67. package/dist/daemon/capture/codex.js.map +1 -0
  68. package/dist/daemon/capture/normalize.js +89 -0
  69. package/dist/daemon/capture/normalize.js.map +1 -0
  70. package/dist/daemon/capture/opencode.js +330 -0
  71. package/dist/daemon/capture/opencode.js.map +1 -0
  72. package/dist/daemon/capture/registry.js +33 -0
  73. package/dist/daemon/capture/registry.js.map +1 -0
  74. package/dist/daemon/capture/stream-store.js +121 -0
  75. package/dist/daemon/capture/stream-store.js.map +1 -0
  76. package/dist/daemon/capture/tailer.js +108 -0
  77. package/dist/daemon/capture/tailer.js.map +1 -0
  78. package/dist/daemon/desktop-notify.js +11 -5
  79. package/dist/daemon/desktop-notify.js.map +1 -1
  80. package/dist/daemon/diagrams.js +90 -0
  81. package/dist/daemon/diagrams.js.map +1 -0
  82. package/dist/daemon/diff.js +1 -2
  83. package/dist/daemon/diff.js.map +1 -1
  84. package/dist/daemon/linter/parse.js +45 -12
  85. package/dist/daemon/linter/parse.js.map +1 -1
  86. package/dist/daemon/linter/rules.js +20 -17
  87. package/dist/daemon/linter/rules.js.map +1 -1
  88. package/dist/daemon/main.js +1 -1
  89. package/dist/daemon/main.js.map +1 -1
  90. package/dist/daemon/notify.js +1 -1
  91. package/dist/daemon/notify.js.map +1 -1
  92. package/dist/daemon/presence.js +1 -1
  93. package/dist/daemon/presence.js.map +1 -1
  94. package/dist/daemon/queue.js +7 -7
  95. package/dist/daemon/queue.js.map +1 -1
  96. package/dist/daemon/store.js +66 -79
  97. package/dist/daemon/store.js.map +1 -1
  98. package/dist/daemon/threads.js +111 -25
  99. package/dist/daemon/threads.js.map +1 -1
  100. package/dist/daemon/transcript.js +1 -1
  101. package/dist/daemon/transcript.js.map +1 -1
  102. package/dist/daemon/ui.js +8 -6
  103. package/dist/daemon/ui.js.map +1 -1
  104. package/dist/daemon/viewers.js +37 -0
  105. package/dist/daemon/viewers.js.map +1 -0
  106. package/dist/shared/config.js +42 -8
  107. package/dist/shared/config.js.map +1 -1
  108. package/dist/shared/gwt.js +1 -1
  109. package/dist/shared/gwt.js.map +1 -1
  110. package/dist/shared/paths.js +63 -36
  111. package/dist/shared/paths.js.map +1 -1
  112. package/dist/shared/question-spec.js +1 -1
  113. package/dist/shared/question-spec.js.map +1 -1
  114. package/dist/shared/types.js +7 -3
  115. package/dist/shared/types.js.map +1 -1
  116. package/dist/shared/version.js +1 -1
  117. package/dist/skills/otacon/SKILL.md +250 -0
  118. package/dist/ui/assets/{arc-Cp3sPd_U.js → arc-BUR2DxNA.js} +1 -1
  119. package/dist/ui/assets/architecture-7EHR7CIX-TTokq2IO.js +1 -0
  120. package/dist/ui/assets/{architectureDiagram-3BPJPVTR-C4F3heWP.js → architectureDiagram-3BPJPVTR-unLnkDyM.js} +1 -1
  121. package/dist/ui/assets/{blockDiagram-GPEHLZMM-DmJoG8ky.js → blockDiagram-GPEHLZMM-DHx8lNeL.js} +1 -1
  122. package/dist/ui/assets/{c4Diagram-AAUBKEIU-BDEf52Jp.js → c4Diagram-AAUBKEIU-BU9T562l.js} +1 -1
  123. package/dist/ui/assets/channel-BA6ChrT3.js +1 -0
  124. package/dist/ui/assets/{chunk-2J33WTMH-Dh_UFriv.js → chunk-2J33WTMH-BEb0myVl.js} +1 -1
  125. package/dist/ui/assets/{chunk-3OPIFGDE-c66RlAN8.js → chunk-3OPIFGDE-DESBG_RB.js} +1 -1
  126. package/dist/ui/assets/{chunk-4BX2VUAB-CrGJaCCg.js → chunk-4BX2VUAB-dt3F_E_5.js} +1 -1
  127. package/dist/ui/assets/{chunk-55IACEB6-BvwqEeyq.js → chunk-55IACEB6-BcyuZM7U.js} +1 -1
  128. package/dist/ui/assets/{chunk-5ZQYHXKU-BfF2IfQe.js → chunk-5ZQYHXKU-DqdwSJlO.js} +1 -1
  129. package/dist/ui/assets/{chunk-727SXJPM-DOEUwc9I.js → chunk-727SXJPM-B04SqNWj.js} +1 -1
  130. package/dist/ui/assets/{chunk-AQP2D5EJ-CVf2xV2Z.js → chunk-AQP2D5EJ-DpjCPBWN.js} +1 -1
  131. package/dist/ui/assets/{chunk-BSJP7CBP-D_EbTWTC.js → chunk-BSJP7CBP-BNOU3k5G.js} +1 -1
  132. package/dist/ui/assets/{chunk-CSCIHK7Q-CkxTSMAM.js → chunk-CSCIHK7Q-iWOtNZm_.js} +1 -1
  133. package/dist/ui/assets/{chunk-FMBD7UC4-DVShhFc7.js → chunk-FMBD7UC4-BHH_etky.js} +1 -1
  134. package/dist/ui/assets/{chunk-KSCS5N6A-DnHEEYpq.js → chunk-KSCS5N6A-DbRuazP3.js} +1 -1
  135. package/dist/ui/assets/{chunk-L5ZTLDWV-B_gjyajS.js → chunk-L5ZTLDWV-B2ZZFONi.js} +1 -1
  136. package/dist/ui/assets/{chunk-LZXEDZCA-Bsf7basG.js → chunk-LZXEDZCA-DryVpwAh.js} +2 -2
  137. package/dist/ui/assets/{chunk-ND2GUHAM-DGepGb8_.js → chunk-ND2GUHAM-2Bb1izqg.js} +1 -1
  138. package/dist/ui/assets/{chunk-NZK2D7GU-BJIlh-gB.js → chunk-NZK2D7GU-2DhvLbqL.js} +1 -1
  139. package/dist/ui/assets/{chunk-O5CBEL6O-cdVbOOCs.js → chunk-O5CBEL6O-B5oigO7D.js} +1 -1
  140. package/dist/ui/assets/chunk-QZHKN3VN-BDHgdxoT.js +1 -0
  141. package/dist/ui/assets/chunk-WU5MYG2G-FDJTP_wT.js +1 -0
  142. package/dist/ui/assets/{chunk-XPW4576I-DPW39hNO.js → chunk-XPW4576I-Dmq-O7bc.js} +1 -1
  143. package/dist/ui/assets/classDiagram-4FO5ZUOK-B5kZsiIt.js +1 -0
  144. package/dist/ui/assets/classDiagram-v2-Q7XG4LA2-B5kZsiIt.js +1 -0
  145. package/dist/ui/assets/{cose-bilkent-S5V4N54A-DmMsC6om.js → cose-bilkent-S5V4N54A-oTsqU1DY.js} +1 -1
  146. package/dist/ui/assets/{dagre-BM42HDAG-BmVr7T7Z.js → dagre-BM42HDAG-CZykCU6B.js} +1 -1
  147. package/dist/ui/assets/{diagram-2AECGRRQ-e0VnXEyj.js → diagram-2AECGRRQ-BY7clIlO.js} +1 -1
  148. package/dist/ui/assets/{diagram-5GNKFQAL-SCJGYE6m.js → diagram-5GNKFQAL-BWdq4hV0.js} +1 -1
  149. package/dist/ui/assets/{diagram-KO2AKTUF-DDUZO1Mj.js → diagram-KO2AKTUF-CCGEaiZg.js} +1 -1
  150. package/dist/ui/assets/{diagram-LMA3HP47-5x1ypf6a.js → diagram-LMA3HP47-CVkepFYU.js} +1 -1
  151. package/dist/ui/assets/{diagram-OG6HWLK6-3nDhrQ20.js → diagram-OG6HWLK6-f-NjsfLG.js} +1 -1
  152. package/dist/ui/assets/{dist-NEinnePC.js → dist-ZqsueX9_.js} +1 -1
  153. package/dist/ui/assets/{erDiagram-TEJ5UH35-BNvwSDNZ.js → erDiagram-TEJ5UH35-D8Wn6QP3.js} +1 -1
  154. package/dist/ui/assets/eventmodeling-FCH6USID-BODDoY6e.js +1 -0
  155. package/dist/ui/assets/{flowDiagram-I6XJVG4X-Bt_wDUhb.js → flowDiagram-I6XJVG4X-BYnhla9k.js} +1 -1
  156. package/dist/ui/assets/{ganttDiagram-6RSMTGT7-DVcJ0rNX.js → ganttDiagram-6RSMTGT7-Dpu52ZRF.js} +1 -1
  157. package/dist/ui/assets/{gitGraph-WXDBUCRP-CO_SyAgP.js → gitGraph-WXDBUCRP-ou8xzQe1.js} +1 -1
  158. package/dist/ui/assets/{gitGraphDiagram-PVQCEYII-oDyO3lWI.js → gitGraphDiagram-PVQCEYII-BnTuFH7F.js} +1 -1
  159. package/dist/ui/assets/index-B2mL0c61.js +11 -0
  160. package/dist/ui/assets/index-sZ1TgAvb.css +1 -0
  161. package/dist/ui/assets/{info-J43DQDTF-Bn17NS7h.js → info-J43DQDTF-xgUHa7k0.js} +1 -1
  162. package/dist/ui/assets/{infoDiagram-5YYISTIA-HG1opLLT.js → infoDiagram-5YYISTIA-DJBudrwD.js} +1 -1
  163. package/dist/ui/assets/{ishikawaDiagram-YF4QCWOH-Di_yQwi8.js → ishikawaDiagram-YF4QCWOH-DpFHgERI.js} +1 -1
  164. package/dist/ui/assets/{journeyDiagram-JHISSGLW-DZtHvLeE.js → journeyDiagram-JHISSGLW-Cd32hdpY.js} +1 -1
  165. package/dist/ui/assets/{kanban-definition-UN3LZRKU-B_RCx3Km.js → kanban-definition-UN3LZRKU-Dm1V8lr0.js} +1 -1
  166. package/dist/ui/assets/{line-DenX-zXQ.js → line-CZu6_PMX.js} +1 -1
  167. package/dist/ui/assets/{linear-dly_ngoq.js → linear-2tkTX_U2.js} +1 -1
  168. package/dist/ui/assets/{mermaid-parser.core-CRmtm0s9.js → mermaid-parser.core-C4m04cRe.js} +2 -2
  169. package/dist/ui/assets/{mermaid.core-C_3KVfpx.js → mermaid.core-BPeg1ewg.js} +3 -3
  170. package/dist/ui/assets/{mindmap-definition-RKZ34NQL-wdzSyYO6.js → mindmap-definition-RKZ34NQL-BENqEkIK.js} +1 -1
  171. package/dist/ui/assets/{packet-YPE3B663-tUyFmR11.js → packet-YPE3B663-DNO0oBLH.js} +1 -1
  172. package/dist/ui/assets/{pie-LRSECV5Y-Cel48VVp.js → pie-LRSECV5Y-CWoz31oB.js} +1 -1
  173. package/dist/ui/assets/{pieDiagram-4H26LBE5-B55ypWtu.js → pieDiagram-4H26LBE5-CtmxhUGI.js} +1 -1
  174. package/dist/ui/assets/{plan-view-CMoo3_gE.js → plan-view-bZtdFbit.js} +4 -4
  175. package/dist/ui/assets/{quadrantDiagram-W4KKPZXB-9WcKQV0a.js → quadrantDiagram-W4KKPZXB-BO3IZNbx.js} +1 -1
  176. package/dist/ui/assets/{radar-GUYGQ44K-w5pk53Vr.js → radar-GUYGQ44K-AEfROz99.js} +1 -1
  177. package/dist/ui/assets/{requirementDiagram-4Y6WPE33-CzE82fXz.js → requirementDiagram-4Y6WPE33-D-wQ1szT.js} +1 -1
  178. package/dist/ui/assets/{sankeyDiagram-5OEKKPKP-DSGO39je.js → sankeyDiagram-5OEKKPKP-Bj4kD_AJ.js} +1 -1
  179. package/dist/ui/assets/{sequenceDiagram-3UESZ5HK-BKSDDr0S.js → sequenceDiagram-3UESZ5HK-M8QVVH74.js} +1 -1
  180. package/dist/ui/assets/{src-JXBGgRt-.js → src-DvptJAGq.js} +1 -1
  181. package/dist/ui/assets/{stateDiagram-AJRCARHV-DZHYA9aj.js → stateDiagram-AJRCARHV-BiPRs9rN.js} +1 -1
  182. package/dist/ui/assets/stateDiagram-v2-BHNVJYJU-BYjicZlC.js +1 -0
  183. package/dist/ui/assets/{timeline-definition-PNZ67QCA-WqJFw7aE.js → timeline-definition-PNZ67QCA-DS0AGRKw.js} +1 -1
  184. package/dist/ui/assets/{treeView-BLDUP644-DoIQYMiz.js → treeView-BLDUP644-DJakvUbF.js} +1 -1
  185. package/dist/ui/assets/{treemap-LRROVOQU-BA9si_Mo.js → treemap-LRROVOQU-QXfc2Vwr.js} +1 -1
  186. package/dist/ui/assets/{vennDiagram-CIIHVFJN-BvWRUfFr.js → vennDiagram-CIIHVFJN-CBdJA0hv.js} +1 -1
  187. package/dist/ui/assets/{wardley-L42UT6IY-Cf9PU44t.js → wardley-L42UT6IY-DI9SdW45.js} +1 -1
  188. package/dist/ui/assets/{wardleyDiagram-YWT4CUSO-CGuxl7AU.js → wardleyDiagram-YWT4CUSO-RMmsKLRe.js} +1 -1
  189. package/dist/ui/assets/{xychartDiagram-2RQKCTM6-CNteNXpe.js → xychartDiagram-2RQKCTM6-DFXccP8B.js} +1 -1
  190. package/dist/ui/index.html +2 -2
  191. package/package.json +7 -5
  192. package/dist/ui/assets/architecture-7EHR7CIX-BbwstElO.js +0 -1
  193. package/dist/ui/assets/channel-BMK3JFRf.js +0 -1
  194. package/dist/ui/assets/chunk-QZHKN3VN-DLNUTn7U.js +0 -1
  195. package/dist/ui/assets/chunk-WU5MYG2G-ipH3YPcA.js +0 -1
  196. package/dist/ui/assets/classDiagram-4FO5ZUOK-CvIMLlSD.js +0 -1
  197. package/dist/ui/assets/classDiagram-v2-Q7XG4LA2-CvIMLlSD.js +0 -1
  198. package/dist/ui/assets/eventmodeling-FCH6USID-BeFWDbla.js +0 -1
  199. package/dist/ui/assets/index-CJJIQ0dr.css +0 -1
  200. package/dist/ui/assets/index-Dh5CsH24.js +0 -11
  201. package/dist/ui/assets/stateDiagram-v2-BHNVJYJU-DnAfDlvZ.js +0 -1
@@ -0,0 +1,330 @@
1
+ // The OpenCode transcript adapter. Unlike Claude/Codex — each one JSONL file —
2
+ // OpenCode persists its sessions in a local SQLite database
3
+ // (`$XDG_DATA_HOME/opencode/opencode.db`, default `~/.local/share/opencode/`),
4
+ // with one row per session/message/part. We read that DB *read-only* with Node's
5
+ // built-in `node:sqlite` (zero new npm deps), which keeps the same file-based,
6
+ // no-extra-process model as the other adapters — no `opencode serve` to spawn.
7
+ //
8
+ // Schema we rely on (confirmed against a real install, storage migration 2):
9
+ // - `session(id, directory, time_created, time_updated, …)` — `directory` is
10
+ // the session's recorded cwd; that is the authoritative repo key.
11
+ // - `message(id, session_id, time_created, data)` — `data` JSON has a `role`.
12
+ // - `part(id, message_id, session_id, time_created, time_updated, data)` —
13
+ // `data` JSON's `type` is one of `text` / `reasoning` / `tool` / … We map
14
+ // `text` → text, `reasoning` → thinking, and `tool` → a `running` event plus,
15
+ // once its `state.status` settles, a SEPARATE `ok`/`error` outcome event.
16
+ //
17
+ // Because the source is a DB (rows, not a byte stream), the `Cursor`'s `offset`
18
+ // is unused — incrementality lives in the opaque carry: a high-water
19
+ // `time_created` watermark plus the set of part ids already emitted *at exactly*
20
+ // that watermark (so a same-millisecond insert is neither re-emitted nor
21
+ // dropped). The daemon round-trips the carry untouched, exactly as it does the
22
+ // byte offset for the JSONL adapters.
23
+ //
24
+ // Everything is fail-soft: a missing `node:sqlite` runtime (Node < 22), a locked
25
+ // or vanished DB, a torn `data` JSON, or any query error is swallowed — at worst
26
+ // the session runs on the `otacon progress` floor.
27
+ import { createRequire } from "node:module";
28
+ import { homedir } from "node:os";
29
+ import { isAbsolute, join, relative, sep } from "node:path";
30
+ const AGENT = "opencode";
31
+ // The daemon ships as an ESM bundle (`"type": "module"`), where the bare global
32
+ // `require` is undefined — so we mint a CommonJS-style `require` bound to this
33
+ // module via `createRequire`. That is the only way to load a built-in
34
+ // SYNCHRONOUSLY from ESM (`locate`/`parse` are sync per the adapter contract, so
35
+ // `await import()` is not an option). On Node < 22 or under bun, `node:sqlite`
36
+ // is absent and this `require` throws — caught below, degrading to the floor.
37
+ const nodeRequire = createRequire(import.meta.url);
38
+ /** Opens `path` read-only via `node:sqlite`, or null when unavailable/unreadable. */
39
+ function defaultOpen(path) {
40
+ let mod;
41
+ try {
42
+ // `node:sqlite` is built in on Node >= 22. require() so a missing module on
43
+ // an older runtime throws here and is swallowed (floor), not at import time.
44
+ mod = nodeRequire("node:sqlite");
45
+ }
46
+ catch {
47
+ return null;
48
+ }
49
+ try {
50
+ return new mod.DatabaseSync(path, { readonly: true });
51
+ }
52
+ catch {
53
+ return null; // DB missing, locked exclusively, or corrupt — degrade to floor
54
+ }
55
+ }
56
+ /** Test seam: the daemon uses the real `node:sqlite`; a test injects a fake. */
57
+ let openDb = defaultOpen;
58
+ /** @internal — swap the SQLite opener (tests only). */
59
+ export function __setOpenDb(fn) {
60
+ openDb = fn;
61
+ }
62
+ /** `$XDG_DATA_HOME/opencode` (if set) else `~/.local/share/opencode`. */
63
+ function dataRoot() {
64
+ const xdg = process.env.XDG_DATA_HOME;
65
+ if (typeof xdg === "string" && xdg !== "")
66
+ return join(xdg, "opencode");
67
+ // Prefer $HOME (the standard override; what a test or custom-home user
68
+ // expects) and fall back to os.homedir() — Bun's homedir() ignores $HOME.
69
+ const home = process.env.HOME && process.env.HOME !== "" ? process.env.HOME : homedir();
70
+ return join(home, ".local", "share", "opencode");
71
+ }
72
+ /** The OpenCode SQLite DB path: `<dataRoot>/opencode.db`. */
73
+ function dbPath() {
74
+ return join(dataRoot(), "opencode.db");
75
+ }
76
+ /** Run a query fail-soft, always closing the handle; [] on any error. */
77
+ function query(path, sql, params) {
78
+ const db = openDb(path);
79
+ if (!db)
80
+ return [];
81
+ try {
82
+ return db.prepare(sql).all(...params);
83
+ }
84
+ catch {
85
+ return []; // bad SQL/schema drift/locked read — degrade to floor
86
+ }
87
+ finally {
88
+ try {
89
+ db.close();
90
+ }
91
+ catch {
92
+ // best-effort close — never throw out of a fail-soft path
93
+ }
94
+ }
95
+ }
96
+ /**
97
+ * The freshest session whose recorded `directory` equals `repoRoot`. We let
98
+ * SQLite do the filter + newest-first ordering and take the first row. The
99
+ * returned handle's `path` is the DB file; the resolved session id rides on the
100
+ * carry (stashed by `parse`, keyed off this same query). Null when the DB is
101
+ * missing/unreadable or no session matches. Never throws.
102
+ */
103
+ function locate(repoRoot) {
104
+ const path = dbPath();
105
+ const rows = query(path, "SELECT id FROM session WHERE directory = ? ORDER BY time_updated DESC, time_created DESC LIMIT 1", [repoRoot]);
106
+ const id = rows[0] && typeof rows[0] === "object" ? rows[0].id : undefined;
107
+ if (typeof id !== "string" || id === "")
108
+ return null;
109
+ // Encode the session id into the handle path (`<db>#<sessionId>`) so `parse`
110
+ // knows which session to scan without re-running the directory match — the
111
+ // TranscriptHandle has no field for it, and the cwd→session mapping is fixed
112
+ // for a located handle's lifetime.
113
+ return { agent: AGENT, path: `${path}#${id}` };
114
+ }
115
+ /** Split a `<db>#<sessionId>` handle path back into its parts. */
116
+ function splitHandle(handlePath) {
117
+ const hash = handlePath.lastIndexOf("#");
118
+ if (hash === -1)
119
+ return { dbFile: handlePath, sessionId: "" };
120
+ return { dbFile: handlePath.slice(0, hash), sessionId: handlePath.slice(hash + 1) };
121
+ }
122
+ const TOOL_LABEL_MAX = 80;
123
+ /** First line of `text`, trimmed; the one-line label for a text/reasoning body. */
124
+ function firstLine(text) {
125
+ const nl = text.indexOf("\n");
126
+ return (nl === -1 ? text : text.slice(0, nl)).trim();
127
+ }
128
+ /** Make `p` repo-relative when it sits under `repoRoot`; else return it as-is. */
129
+ function relPath(p, repoRoot) {
130
+ if (typeof p !== "string" || p === "")
131
+ return String(p ?? "");
132
+ if (!isAbsolute(p))
133
+ return p;
134
+ const rel = relative(repoRoot, p);
135
+ // `relative` of a path outside the repo starts with ".." or is absolute on a
136
+ // different drive — keep the absolute path then (it is genuinely elsewhere).
137
+ if (rel === "" || rel.startsWith("..") || isAbsolute(rel))
138
+ return p;
139
+ return rel.split(sep).join("/");
140
+ }
141
+ /** Cap a one-line command/string to the label width with a trailing ellipsis. */
142
+ function clamp(text) {
143
+ const oneLine = firstLine(text);
144
+ return oneLine.length > TOOL_LABEL_MAX ? `${oneLine.slice(0, TOOL_LABEL_MAX - 1)}…` : oneLine;
145
+ }
146
+ /**
147
+ * Concise label for a tool part, e.g. "Bash: bun test" / "Read src/x.ts". Tool
148
+ * names are lowercase in OpenCode; we render the same verbs as the Claude
149
+ * adapter. Falls back to the tool's title (OpenCode computes a useful one) or
150
+ * the bare tool name.
151
+ */
152
+ function toolLabel(tool, input, title, repoRoot) {
153
+ const file = (k) => relPath(String(input[k] ?? ""), repoRoot);
154
+ switch (tool) {
155
+ case "bash": {
156
+ const cmd = String(input.command ?? "");
157
+ return cmd === "" ? "Bash" : `Bash: ${clamp(cmd)}`;
158
+ }
159
+ case "read":
160
+ return `Read ${file("filePath")}`;
161
+ case "edit":
162
+ return `Edit ${file("filePath")}`;
163
+ case "write":
164
+ return `Write ${file("filePath")}`;
165
+ case "grep":
166
+ return `Grep ${String(input.pattern ?? "")}`;
167
+ case "glob":
168
+ return `Glob ${String(input.pattern ?? "")}`;
169
+ case "webfetch":
170
+ return `Fetch ${String(input.url ?? "")}`;
171
+ case "websearch":
172
+ return `Search: ${String(input.query ?? "")}`;
173
+ case "task":
174
+ return `Task: ${String(input.description ?? input.prompt ?? "")}`;
175
+ default:
176
+ // A meaningful title (OpenCode often sets one, e.g. a glob pattern) beats
177
+ // the bare lowercase tool name; otherwise show the name.
178
+ return title !== "" ? `${tool}: ${clamp(title)}` : tool;
179
+ }
180
+ }
181
+ /** Coerce a tool `state.output`/`error` to a one-line-safe detail string. */
182
+ function detailText(v) {
183
+ if (typeof v === "string")
184
+ return v;
185
+ if (v === undefined || v === null)
186
+ return "";
187
+ try {
188
+ return JSON.stringify(v);
189
+ }
190
+ catch {
191
+ return "";
192
+ }
193
+ }
194
+ /**
195
+ * Map one part's parsed `data` to the events it produces — possibly TWO for a
196
+ * settled tool (the `running` event and its `ok`/`error` outcome, appended
197
+ * separately so the store never upserts). Unrecognized types yield nothing.
198
+ */
199
+ function partToEvents(data, repoRoot) {
200
+ switch (data.type) {
201
+ case "text": {
202
+ const text = typeof data.text === "string" ? data.text : "";
203
+ if (text.trim() === "")
204
+ return [];
205
+ return [{ kind: "text", label: clamp(text) || "text", detail: text }];
206
+ }
207
+ case "reasoning": {
208
+ const text = typeof data.text === "string" ? data.text : "";
209
+ if (text.trim() === "")
210
+ return [];
211
+ return [{ kind: "thinking", label: "thinking…", detail: text }];
212
+ }
213
+ case "tool": {
214
+ const tool = typeof data.tool === "string" ? data.tool : "tool";
215
+ const state = data.state && typeof data.state === "object" ? data.state : {};
216
+ const input = state.input && typeof state.input === "object" ? state.input : {};
217
+ const title = typeof state.title === "string" ? state.title : "";
218
+ const rawInput = (() => {
219
+ try {
220
+ return JSON.stringify(input);
221
+ }
222
+ catch {
223
+ return "";
224
+ }
225
+ })();
226
+ const events = [
227
+ {
228
+ kind: "tool",
229
+ tool,
230
+ label: toolLabel(tool, input, title, repoRoot),
231
+ detail: rawInput === "" || rawInput === "{}" ? undefined : rawInput,
232
+ status: "running",
233
+ },
234
+ ];
235
+ // A settled tool carries its outcome on the same part (OpenCode mutates the
236
+ // part in place as the tool runs). We still emit the outcome as its OWN
237
+ // event to keep the append-only "running then ok/error" shape; a part that
238
+ // is still `pending`/`running` only yields the running event for now and
239
+ // its outcome arrives when the part's `time_updated` bumps it back to us.
240
+ const status = state.status;
241
+ if (status === "completed") {
242
+ const detail = detailText(state.output);
243
+ events.push({ kind: "tool", status: "ok", label: "→ ok", detail: detail === "" ? undefined : detail });
244
+ }
245
+ else if (status === "error") {
246
+ const detail = detailText(state.error) || detailText(state.output);
247
+ events.push({ kind: "tool", status: "error", label: "→ error", detail: detail === "" ? undefined : detail });
248
+ }
249
+ return events;
250
+ }
251
+ default:
252
+ return []; // step-start/step-finish/patch/file/compaction/… are not activity
253
+ }
254
+ }
255
+ /**
256
+ * Scan the located session's parts newer than the cursor watermark, in
257
+ * chronological order, and emit their events. Incrementality lives in the carry:
258
+ * we ask for `time_created >= watermark` (so a part inserted in the SAME
259
+ * millisecond as the last frontier is not missed), then skip any id already
260
+ * emitted at exactly that watermark. After emitting, the watermark advances to
261
+ * the newest `time_created` we saw and the tie set is reset to just the ids at
262
+ * that new frontier — bounded, and enough to dedupe the next same-ms insert.
263
+ * Fail-soft throughout: a missing DB, a torn `data` JSON, or a query error
264
+ * yields no events and leaves the cursor put.
265
+ */
266
+ function parse(handle, cursor) {
267
+ const { dbFile, sessionId } = splitHandle(handle.path);
268
+ if (sessionId === "")
269
+ return { events: [], cursor };
270
+ const carry = cursor;
271
+ const watermark = typeof carry.watermark === "number" && carry.watermark >= 0 ? carry.watermark : 0;
272
+ const emitted = Array.isArray(carry.emittedAtWatermark) ? new Set(carry.emittedAtWatermark) : new Set();
273
+ // Resolve repoRoot once (from the session row) and stash it on the carry.
274
+ let repoRoot = typeof carry.repoRoot === "string" ? carry.repoRoot : undefined;
275
+ if (repoRoot === undefined) {
276
+ const sess = query(dbFile, "SELECT directory FROM session WHERE id = ? LIMIT 1", [sessionId]);
277
+ const dir = sess[0] && typeof sess[0] === "object" ? sess[0].directory : undefined;
278
+ repoRoot = typeof dir === "string" ? dir : "";
279
+ }
280
+ const rows = query(dbFile, "SELECT id, time_created, data FROM part WHERE session_id = ? AND time_created >= ? ORDER BY time_created ASC, id ASC", [sessionId, watermark]);
281
+ const events = [];
282
+ let maxTime = watermark;
283
+ const idsAtMax = emitted.size > 0 ? [...emitted] : []; // carry forward the current frontier's tie set
284
+ for (const row of rows) {
285
+ const id = typeof row.id === "string" ? row.id : undefined;
286
+ const tCreated = typeof row.time_created === "number" ? row.time_created : 0;
287
+ if (id === undefined)
288
+ continue;
289
+ if (emitted.has(id))
290
+ continue; // already emitted at the prior watermark — skip
291
+ let data;
292
+ try {
293
+ const raw = typeof row.data === "string" ? row.data : "";
294
+ const parsed = raw === "" ? {} : JSON.parse(raw);
295
+ data = parsed && typeof parsed === "object" ? parsed : {};
296
+ }
297
+ catch {
298
+ continue; // torn `data` JSON — skip this part, keep going
299
+ }
300
+ for (const event of partToEvents(data, repoRoot))
301
+ events.push(event);
302
+ // Advance the frontier. A part AT the current max joins its tie set; a newer
303
+ // one resets the tie set to just itself.
304
+ if (tCreated > maxTime) {
305
+ maxTime = tCreated;
306
+ idsAtMax.length = 0;
307
+ idsAtMax.push(id);
308
+ }
309
+ else if (tCreated === maxTime) {
310
+ idsAtMax.push(id);
311
+ }
312
+ }
313
+ const nextCursor = {
314
+ ...cursor,
315
+ offset: 0, // unused for a DB source; kept for the opaque-cursor contract
316
+ repoRoot,
317
+ watermark: maxTime,
318
+ emittedAtWatermark: idsAtMax,
319
+ };
320
+ return { events, cursor: nextCursor };
321
+ }
322
+ /** The OpenCode SQLite adapter (read-only, fail-soft). */
323
+ export const opencodeAdapter = {
324
+ agent: AGENT,
325
+ locate,
326
+ parse,
327
+ };
328
+ // Test-only exports — the pure mappers + path helpers are worth covering directly.
329
+ export const __test = { toolLabel, relPath, detailText, partToEvents, splitHandle, dbPath, dataRoot };
330
+ //# sourceMappingURL=opencode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode.js","sourceRoot":"","sources":["../../../src/daemon/capture/opencode.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ // The adapter registry: an ordered list of transcript adapters and the lookup
2
+ // the tailer uses to bind a session's repo to the agent that's actually working
3
+ // there. `findAdapter(repoRoot)` returns the first adapter whose `locate` finds
4
+ // a transcript, or null when none matches — and null is the WHOLE point: a repo
5
+ // whose agent has no adapter attaches no tailer and runs on the `otacon
6
+ // progress` floor (the graceful-degradation guarantee). Adding an agent (Phase
7
+ // 5/6: Codex, OpenCode, …) is one line here plus its adapter module.
8
+ import { claudeAdapter } from "./claude.js";
9
+ import { codexAdapter } from "./codex.js";
10
+ import { opencodeAdapter } from "./opencode.js";
11
+ /** Ordered adapters; the first whose `locate` matches wins. */
12
+ export const ADAPTERS = [claudeAdapter, codexAdapter, opencodeAdapter];
13
+ /**
14
+ * The first adapter with a located transcript for `repoRoot`, or null when no
15
+ * adapter matches (floor only). Fail-soft: a throwing `locate` is treated as no
16
+ * match, never propagated — one misbehaving adapter must not deny every other
17
+ * agent its stream.
18
+ */
19
+ export function findAdapter(repoRoot, adapters = ADAPTERS) {
20
+ for (const adapter of adapters) {
21
+ let handle = null;
22
+ try {
23
+ handle = adapter.locate(repoRoot);
24
+ }
25
+ catch {
26
+ handle = null;
27
+ }
28
+ if (handle)
29
+ return { adapter, handle };
30
+ }
31
+ return null;
32
+ }
33
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/daemon/capture/registry.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,gFAAgF;AAChF,gFAAgF;AAChF,gFAAgF;AAChF,wEAAwE;AACxE,+EAA+E;AAC/E,qEAAqE;AAGrE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,+DAA+D;AAC/D,MAAM,CAAC,MAAM,QAAQ,GAAiC,CAAC,aAAa,EAAE,YAAY,EAAE,eAAe,CAAC,CAAC;AAErG;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CACzB,QAAgB,EAChB,WAAyC,QAAQ;IAEjD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,MAAM,GAA4B,IAAI,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,GAAG,IAAI,CAAC;QAChB,CAAC;QACD,IAAI,MAAM;YAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IACzC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,121 @@
1
+ // Per-session normalized live-activity stream: .otacon/<id>/stream.jsonl holds
2
+ // the daemon's append-only, capped record of captured agent activity plus
3
+ // `otacon progress` highlights — the high-frequency telemetry the review UI
4
+ // watches while the agent works. JSONL (one StreamEvent per line) so a
5
+ // frequent capture source pays a cheap append, not a whole-file rewrite, on the
6
+ // common path; the file is rewritten only when it grows past the cap (keeping
7
+ // the newest N). Same storage posture as activity.ts and the queue/transcript
8
+ // readers: atomic rewrites, corrupt lines skipped (never quarantine the whole
9
+ // file — a JSONL stream's value is the lines that DID parse), never fatal.
10
+ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
11
+ import { dirname } from "node:path";
12
+ import { writeFileAtomic } from "../store.js";
13
+ const KINDS = ["tool", "text", "thinking", "highlight"];
14
+ /**
15
+ * One JSONL line → a StreamEvent, or undefined for a blank/corrupt/incomplete
16
+ * line (a torn final append, a hand-edit). Validates every field, not just the
17
+ * envelope (same argument as activity.ts): a bad event would otherwise flow a
18
+ * non-event into the stream snapshot and the SSE frame.
19
+ */
20
+ function parseLine(line) {
21
+ const trimmed = line.trim();
22
+ if (trimmed === "")
23
+ return undefined;
24
+ let raw;
25
+ try {
26
+ raw = JSON.parse(trimmed);
27
+ }
28
+ catch {
29
+ return undefined;
30
+ }
31
+ const e = raw;
32
+ const valid = typeof e === "object" &&
33
+ e !== null &&
34
+ typeof e.seq === "number" &&
35
+ Number.isInteger(e.seq) &&
36
+ typeof e.at === "string" &&
37
+ KINDS.includes(e.kind) &&
38
+ typeof e.label === "string" &&
39
+ (e.detail === undefined || typeof e.detail === "string") &&
40
+ (e.tool === undefined || typeof e.tool === "string") &&
41
+ (e.status === undefined ||
42
+ e.status === "running" ||
43
+ e.status === "ok" ||
44
+ e.status === "error");
45
+ return valid ? e : undefined;
46
+ }
47
+ /** Every parseable event, oldest first; missing file or all-corrupt = []. Never throws. */
48
+ function readAll(path) {
49
+ if (!existsSync(path))
50
+ return [];
51
+ let body;
52
+ try {
53
+ body = readFileSync(path, "utf8");
54
+ }
55
+ catch {
56
+ return [];
57
+ }
58
+ const events = [];
59
+ for (const line of body.split("\n")) {
60
+ const event = parseLine(line);
61
+ if (event)
62
+ events.push(event);
63
+ }
64
+ return events;
65
+ }
66
+ /**
67
+ * The stream's events, oldest first (newest last). `limit` returns only the
68
+ * newest N — what the per-session SSE snapshot serves. Corrupt lines are
69
+ * skipped; a missing file reads as empty.
70
+ */
71
+ export function readStream(path, limit) {
72
+ const events = readAll(path);
73
+ if (limit !== undefined && limit > 0 && events.length > limit) {
74
+ return events.slice(events.length - limit);
75
+ }
76
+ return events;
77
+ }
78
+ /**
79
+ * Durably append events as JSONL lines, then cap: when the file holds more than
80
+ * `cap`, it is rewritten atomically to the newest `cap` (older lines drop off
81
+ * the front). The append is the cheap common path; the rewrite is the rare
82
+ * trim. Returns the events as appended (unchanged). A `cap` of 0 or less keeps
83
+ * everything (no trim).
84
+ */
85
+ export function appendStreamEvents(path, events, cap) {
86
+ if (events.length === 0)
87
+ return events;
88
+ mkdirSync(dirname(path), { recursive: true });
89
+ const lines = events.map((e) => JSON.stringify(e)).join("\n") + "\n";
90
+ appendFileSync(path, lines);
91
+ if (cap > 0) {
92
+ const all = readAll(path);
93
+ if (all.length > cap) {
94
+ const kept = all.slice(all.length - cap);
95
+ writeFileAtomic(path, kept.map((e) => JSON.stringify(e)).join("\n") + "\n");
96
+ }
97
+ }
98
+ return events;
99
+ }
100
+ /**
101
+ * Monotonic per-session seq source for the daemon. Seeds lazily from the
102
+ * stream's max seq on first use (so a daemon restart never re-mints a live
103
+ * seq), then increments in memory. One instance per session id; the daemon owns
104
+ * the single writer, so no locking is needed (DECISIONS.md "One daemon process
105
+ * owns all state").
106
+ */
107
+ export class StreamSeq {
108
+ last;
109
+ /** Mint the next seq, seeding from the file's max on first call. */
110
+ next(path) {
111
+ if (this.last === undefined) {
112
+ let max = 0;
113
+ for (const event of readAll(path))
114
+ max = Math.max(max, event.seq);
115
+ this.last = max;
116
+ }
117
+ this.last += 1;
118
+ return this.last;
119
+ }
120
+ }
121
+ //# sourceMappingURL=stream-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-store.js","sourceRoot":"","sources":["../../../src/daemon/capture/stream-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,108 @@
1
+ // The per-session transcript tailer: the daemon-side loop that watches a coding
2
+ // agent's live transcript and feeds new activity into the Phase 1 stream
3
+ // pipeline. Bound to a session's lifetime — `start` when the session goes
4
+ // active, `stop` on terminal/removal — it (1) finds the agent's transcript via
5
+ // the registry, (2) polls it for new bytes, (3) `parse`s them into
6
+ // `RawStreamEvent`s, (4) `normalize`s each (the daemon stamps seq + `at`,
7
+ // redaction + truncation happen there), (5) appends them as ONE batch, and (6)
8
+ // publishes ONE coalesced `stream` frame per batch.
9
+ //
10
+ // Design choices (favoring reliability over cleverness):
11
+ // - A plain poll loop, not `fs.watch`. `fs.watch` is famously
12
+ // platform-flaky (missed events, double-fires, no support on some network
13
+ // FSes); a short interval poll is boring and always works. A burst between
14
+ // two polls is naturally coalesced into one parse → one append → one frame.
15
+ // - Re-locate while no transcript is found yet. A session can be created a
16
+ // beat before the agent's transcript file appears; we keep retrying
17
+ // `findAdapter` on each tick until one matches (bounded only by `stop`).
18
+ // - Fail-soft throughout: a parse/store/publish error on one tick is
19
+ // swallowed; the next tick tries again. The tailer never throws into the
20
+ // daemon, and at worst the session runs on the `otacon progress` floor.
21
+ import { INITIAL_CURSOR } from "./adapter.js";
22
+ import { normalize } from "./normalize.js";
23
+ import { findAdapter as defaultFindAdapter } from "./registry.js";
24
+ /** Default poll cadence — coalesces a burst of writes into one batch per tick. */
25
+ export const DEFAULT_POLL_MS = 150;
26
+ /**
27
+ * One session's tailer. Construct, `start()`, and `stop()`; both are idempotent.
28
+ * A test can skip the timer entirely and drive `tick()` by hand.
29
+ */
30
+ export class Tailer {
31
+ deps;
32
+ findAdapter;
33
+ pollMs;
34
+ setIntervalFn;
35
+ clearIntervalFn;
36
+ timer;
37
+ adapter;
38
+ handle;
39
+ cursor = { ...INITIAL_CURSOR };
40
+ running = false;
41
+ constructor(deps) {
42
+ this.deps = deps;
43
+ this.findAdapter = deps.findAdapter ?? defaultFindAdapter;
44
+ this.pollMs = deps.pollMs ?? DEFAULT_POLL_MS;
45
+ this.setIntervalFn = deps.setInterval ?? ((cb, ms) => setInterval(cb, ms));
46
+ this.clearIntervalFn = deps.clearInterval ?? ((h) => clearInterval(h));
47
+ }
48
+ /** Begin polling. Idempotent — a second `start` is a no-op. */
49
+ start() {
50
+ if (this.running)
51
+ return;
52
+ this.running = true;
53
+ // Try to bind immediately so a transcript that already exists streams on the
54
+ // first tick; if none is found yet, `tick` keeps re-locating.
55
+ this.locate();
56
+ const timer = this.setIntervalFn(() => this.tick(), this.pollMs);
57
+ // Don't let the poll timer keep the daemon process alive on its own.
58
+ timer.unref?.();
59
+ this.timer = timer;
60
+ }
61
+ /** Tear down the poller. Idempotent. */
62
+ stop() {
63
+ this.running = false;
64
+ if (this.timer !== undefined) {
65
+ this.clearIntervalFn(this.timer);
66
+ this.timer = undefined;
67
+ }
68
+ }
69
+ /**
70
+ * One poll: re-locate if still unbound, then parse any new bytes and flush a
71
+ * single batch. Public so a test can drive it without a real timer. Fully
72
+ * fail-soft — any error is swallowed and the next tick retries.
73
+ */
74
+ tick() {
75
+ try {
76
+ if (this.adapter === undefined || this.handle === undefined) {
77
+ this.locate();
78
+ if (this.adapter === undefined || this.handle === undefined)
79
+ return; // still no transcript
80
+ }
81
+ const { events: raw, cursor } = this.adapter.parse(this.handle, this.cursor);
82
+ this.cursor = cursor;
83
+ if (raw.length === 0)
84
+ return;
85
+ const cfg = this.deps.config();
86
+ const at = new Date().toISOString();
87
+ const normalized = raw.map((e) => normalize(e, cfg, this.deps.nextSeq(), at));
88
+ this.deps.append(normalized);
89
+ this.deps.publish(normalized);
90
+ }
91
+ catch {
92
+ // A parse/store/publish hiccup on one tick must not kill the loop — the
93
+ // session falls back to the floor for this tick and we retry next time.
94
+ }
95
+ }
96
+ /** Resolve the adapter+handle for this repo (no-op when one is already bound). */
97
+ locate() {
98
+ if (this.adapter !== undefined && this.handle !== undefined)
99
+ return;
100
+ const found = this.findAdapter(this.deps.repoRoot);
101
+ if (!found)
102
+ return; // floor: no adapter for this repo's agent (yet)
103
+ this.adapter = found.adapter;
104
+ this.handle = found.handle;
105
+ this.cursor = { ...INITIAL_CURSOR };
106
+ }
107
+ }
108
+ //# sourceMappingURL=tailer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tailer.js","sourceRoot":"","sources":["../../../src/daemon/capture/tailer.ts"],"names":[],"mappings":"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"}