@tienne/gestalt 0.107.0 → 0.109.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/CLAUDE.md +2 -0
  2. package/README.ko.md +1 -0
  3. package/README.md +1 -0
  4. package/dist/package.json +1 -1
  5. package/dist/plugin/role-agents/lens-context/AGENT.md +94 -0
  6. package/dist/plugin/role-agents/lens-link/AGENT.md +83 -0
  7. package/dist/plugin/role-agents/lens-reader/AGENT.md +230 -0
  8. package/dist/plugin/skills/_shared/proactive-routing.md +1 -0
  9. package/dist/plugin/skills/lens/SKILL.md +375 -0
  10. package/dist/schemas/gestalt.schema.json +13 -0
  11. package/dist/src/cli/commands/digest.d.ts +2 -0
  12. package/dist/src/cli/commands/digest.d.ts.map +1 -1
  13. package/dist/src/cli/commands/digest.js +6 -0
  14. package/dist/src/cli/commands/digest.js.map +1 -1
  15. package/dist/src/cli/commands/lens.d.ts +27 -0
  16. package/dist/src/cli/commands/lens.d.ts.map +1 -0
  17. package/dist/src/cli/commands/lens.js +200 -0
  18. package/dist/src/cli/commands/lens.js.map +1 -0
  19. package/dist/src/cli/commands/peer.d.ts +19 -0
  20. package/dist/src/cli/commands/peer.d.ts.map +1 -0
  21. package/dist/src/cli/commands/peer.js +147 -0
  22. package/dist/src/cli/commands/peer.js.map +1 -0
  23. package/dist/src/cli/index.d.ts.map +1 -1
  24. package/dist/src/cli/index.js +72 -2
  25. package/dist/src/cli/index.js.map +1 -1
  26. package/dist/src/digest/format.d.ts.map +1 -1
  27. package/dist/src/digest/format.js +1 -0
  28. package/dist/src/digest/format.js.map +1 -1
  29. package/dist/src/digest/frames.d.ts.map +1 -1
  30. package/dist/src/digest/frames.js +1 -1
  31. package/dist/src/digest/frames.js.map +1 -1
  32. package/dist/src/digest/ingest.d.ts +8 -1
  33. package/dist/src/digest/ingest.d.ts.map +1 -1
  34. package/dist/src/digest/ingest.js +2 -1
  35. package/dist/src/digest/ingest.js.map +1 -1
  36. package/dist/src/digest/lens-schema.d.ts +542 -0
  37. package/dist/src/digest/lens-schema.d.ts.map +1 -0
  38. package/dist/src/digest/lens-schema.js +108 -0
  39. package/dist/src/digest/lens-schema.js.map +1 -0
  40. package/dist/src/digest/lens-source.d.ts +260 -0
  41. package/dist/src/digest/lens-source.d.ts.map +1 -0
  42. package/dist/src/digest/lens-source.js +92 -0
  43. package/dist/src/digest/lens-source.js.map +1 -0
  44. package/dist/src/digest/lens-widget-schema.d.ts +1225 -0
  45. package/dist/src/digest/lens-widget-schema.d.ts.map +1 -0
  46. package/dist/src/digest/lens-widget-schema.js +80 -0
  47. package/dist/src/digest/lens-widget-schema.js.map +1 -0
  48. package/dist/src/digest/lens-widgets.d.ts +25 -0
  49. package/dist/src/digest/lens-widgets.d.ts.map +1 -0
  50. package/dist/src/digest/lens-widgets.js +225 -0
  51. package/dist/src/digest/lens-widgets.js.map +1 -0
  52. package/dist/src/digest/lint-lens-context.d.ts +102 -0
  53. package/dist/src/digest/lint-lens-context.d.ts.map +1 -0
  54. package/dist/src/digest/lint-lens-context.js +192 -0
  55. package/dist/src/digest/lint-lens-context.js.map +1 -0
  56. package/dist/src/digest/lint-lens.d.ts +17 -0
  57. package/dist/src/digest/lint-lens.d.ts.map +1 -0
  58. package/dist/src/digest/lint-lens.js +237 -0
  59. package/dist/src/digest/lint-lens.js.map +1 -0
  60. package/dist/src/digest/lint.d.ts +14 -0
  61. package/dist/src/digest/lint.d.ts.map +1 -1
  62. package/dist/src/digest/lint.js +156 -2
  63. package/dist/src/digest/lint.js.map +1 -1
  64. package/dist/src/digest/merge.d.ts.map +1 -1
  65. package/dist/src/digest/merge.js +11 -2
  66. package/dist/src/digest/merge.js.map +1 -1
  67. package/dist/src/digest/prose.d.ts.map +1 -1
  68. package/dist/src/digest/prose.js +8 -0
  69. package/dist/src/digest/prose.js.map +1 -1
  70. package/dist/src/digest/render-html.d.ts +5 -0
  71. package/dist/src/digest/render-html.d.ts.map +1 -1
  72. package/dist/src/digest/render-html.js +93 -18
  73. package/dist/src/digest/render-html.js.map +1 -1
  74. package/dist/src/digest/render-lens.d.ts +61 -0
  75. package/dist/src/digest/render-lens.d.ts.map +1 -0
  76. package/dist/src/digest/render-lens.js +273 -0
  77. package/dist/src/digest/render-lens.js.map +1 -0
  78. package/dist/src/digest/render-md.d.ts +5 -0
  79. package/dist/src/digest/render-md.d.ts.map +1 -1
  80. package/dist/src/digest/render-md.js +116 -7
  81. package/dist/src/digest/render-md.js.map +1 -1
  82. package/dist/src/digest/render-widgets.d.ts +13 -0
  83. package/dist/src/digest/render-widgets.d.ts.map +1 -0
  84. package/dist/src/digest/render-widgets.js +197 -0
  85. package/dist/src/digest/render-widgets.js.map +1 -0
  86. package/dist/src/digest/render.d.ts +2 -0
  87. package/dist/src/digest/render.d.ts.map +1 -1
  88. package/dist/src/digest/render.js +17 -3
  89. package/dist/src/digest/render.js.map +1 -1
  90. package/dist/src/digest/schema.d.ts +14683 -2022
  91. package/dist/src/digest/schema.d.ts.map +1 -1
  92. package/dist/src/digest/schema.js +67 -6
  93. package/dist/src/digest/schema.js.map +1 -1
  94. package/dist/src/digest/server.d.ts +2 -1
  95. package/dist/src/digest/server.d.ts.map +1 -1
  96. package/dist/src/digest/server.js +26 -11
  97. package/dist/src/digest/server.js.map +1 -1
  98. package/dist/src/digest/viewer-js.d.ts.map +1 -1
  99. package/dist/src/digest/viewer-js.js +120 -4
  100. package/dist/src/digest/viewer-js.js.map +1 -1
  101. package/dist/src/lens/attach.d.ts +436 -0
  102. package/dist/src/lens/attach.d.ts.map +1 -0
  103. package/dist/src/lens/attach.js +46 -0
  104. package/dist/src/lens/attach.js.map +1 -0
  105. package/dist/src/lens/ingest.d.ts +75 -0
  106. package/dist/src/lens/ingest.d.ts.map +1 -0
  107. package/dist/src/lens/ingest.js +558 -0
  108. package/dist/src/lens/ingest.js.map +1 -0
  109. package/dist/src/lens/kind.d.ts +11 -0
  110. package/dist/src/lens/kind.d.ts.map +1 -0
  111. package/dist/src/lens/kind.js +59 -0
  112. package/dist/src/lens/kind.js.map +1 -0
  113. package/dist/src/lens/knowledge.d.ts +53 -0
  114. package/dist/src/lens/knowledge.d.ts.map +1 -0
  115. package/dist/src/lens/knowledge.js +202 -0
  116. package/dist/src/lens/knowledge.js.map +1 -0
  117. package/dist/src/lens/notes.d.ts +73 -0
  118. package/dist/src/lens/notes.d.ts.map +1 -0
  119. package/dist/src/lens/notes.js +276 -0
  120. package/dist/src/lens/notes.js.map +1 -0
  121. package/dist/src/lens/panels.d.ts +10 -0
  122. package/dist/src/lens/panels.d.ts.map +1 -0
  123. package/dist/src/lens/panels.js +25 -0
  124. package/dist/src/lens/panels.js.map +1 -0
  125. package/dist/src/lens/pdf-extract-py.d.ts +15 -0
  126. package/dist/src/lens/pdf-extract-py.d.ts.map +1 -0
  127. package/dist/src/lens/pdf-extract-py.js +414 -0
  128. package/dist/src/lens/pdf-extract-py.js.map +1 -0
  129. package/dist/src/lens/podcast.d.ts +36 -0
  130. package/dist/src/lens/podcast.d.ts.map +1 -0
  131. package/dist/src/lens/podcast.js +104 -0
  132. package/dist/src/lens/podcast.js.map +1 -0
  133. package/dist/src/lens/python.d.ts +33 -0
  134. package/dist/src/lens/python.d.ts.map +1 -0
  135. package/dist/src/lens/python.js +61 -0
  136. package/dist/src/lens/python.js.map +1 -0
  137. package/dist/src/lens/web-extract-py.d.ts +9 -0
  138. package/dist/src/lens/web-extract-py.d.ts.map +1 -0
  139. package/dist/src/lens/web-extract-py.js +114 -0
  140. package/dist/src/lens/web-extract-py.js.map +1 -0
  141. package/dist/src/mcp/schemas.d.ts +3 -0
  142. package/dist/src/mcp/schemas.d.ts.map +1 -1
  143. package/dist/src/mcp/schemas.js +4 -0
  144. package/dist/src/mcp/schemas.js.map +1 -1
  145. package/dist/src/mcp/server.js +1 -1
  146. package/dist/src/mcp/server.js.map +1 -1
  147. package/dist/src/mcp/tools/digest-passthrough.d.ts.map +1 -1
  148. package/dist/src/mcp/tools/digest-passthrough.js +5 -0
  149. package/dist/src/mcp/tools/digest-passthrough.js.map +1 -1
  150. package/dist/src/peer/client.d.ts +9 -0
  151. package/dist/src/peer/client.d.ts.map +1 -0
  152. package/dist/src/peer/client.js +51 -0
  153. package/dist/src/peer/client.js.map +1 -0
  154. package/dist/src/peer/daemon.d.ts +71 -0
  155. package/dist/src/peer/daemon.d.ts.map +1 -0
  156. package/dist/src/peer/daemon.js +407 -0
  157. package/dist/src/peer/daemon.js.map +1 -0
  158. package/dist/src/peer/errors.d.ts +2 -0
  159. package/dist/src/peer/errors.d.ts.map +1 -0
  160. package/dist/src/peer/errors.js +4 -0
  161. package/dist/src/peer/errors.js.map +1 -0
  162. package/dist/src/peer/index.d.ts +14 -0
  163. package/dist/src/peer/index.d.ts.map +1 -0
  164. package/dist/src/peer/index.js +9 -0
  165. package/dist/src/peer/index.js.map +1 -0
  166. package/dist/src/peer/locate.d.ts +44 -0
  167. package/dist/src/peer/locate.d.ts.map +1 -0
  168. package/dist/src/peer/locate.js +116 -0
  169. package/dist/src/peer/locate.js.map +1 -0
  170. package/dist/src/peer/paths.d.ts +23 -0
  171. package/dist/src/peer/paths.d.ts.map +1 -0
  172. package/dist/src/peer/paths.js +50 -0
  173. package/dist/src/peer/paths.js.map +1 -0
  174. package/dist/src/peer/protocol.d.ts +61 -0
  175. package/dist/src/peer/protocol.d.ts.map +1 -0
  176. package/dist/src/peer/protocol.js +7 -0
  177. package/dist/src/peer/protocol.js.map +1 -0
  178. package/dist/src/peer/redact.d.ts +30 -0
  179. package/dist/src/peer/redact.d.ts.map +1 -0
  180. package/dist/src/peer/redact.js +214 -0
  181. package/dist/src/peer/redact.js.map +1 -0
  182. package/dist/src/peer/share.d.ts +123 -0
  183. package/dist/src/peer/share.d.ts.map +1 -0
  184. package/dist/src/peer/share.js +204 -0
  185. package/dist/src/peer/share.js.map +1 -0
  186. package/dist/src/peer/state.d.ts +37 -0
  187. package/dist/src/peer/state.d.ts.map +1 -0
  188. package/dist/src/peer/state.js +99 -0
  189. package/dist/src/peer/state.js.map +1 -0
  190. package/dist/src/peer/tail.d.ts +55 -0
  191. package/dist/src/peer/tail.d.ts.map +1 -0
  192. package/dist/src/peer/tail.js +148 -0
  193. package/dist/src/peer/tail.js.map +1 -0
  194. package/dist/src/peer/transform.d.ts +79 -0
  195. package/dist/src/peer/transform.d.ts.map +1 -0
  196. package/dist/src/peer/transform.js +237 -0
  197. package/dist/src/peer/transform.js.map +1 -0
  198. package/dist/src/peer/viewer-html.d.ts +10 -0
  199. package/dist/src/peer/viewer-html.d.ts.map +1 -0
  200. package/dist/src/peer/viewer-html.js +157 -0
  201. package/dist/src/peer/viewer-html.js.map +1 -0
  202. package/dist/src/peer/viewer.d.ts +58 -0
  203. package/dist/src/peer/viewer.d.ts.map +1 -0
  204. package/dist/src/peer/viewer.js +249 -0
  205. package/dist/src/peer/viewer.js.map +1 -0
  206. package/dist/src/research/source-create.d.ts +6 -1
  207. package/dist/src/research/source-create.d.ts.map +1 -1
  208. package/dist/src/research/source-create.js +41 -10
  209. package/dist/src/research/source-create.js.map +1 -1
  210. package/package.json +1 -1
  211. package/plugin/.codex-plugin/plugin.json +1 -1
  212. package/plugin/.mcp.json +1 -1
  213. package/plugin/mcp.json +1 -1
  214. package/plugin/role-agents/lens-context/AGENT.md +94 -0
  215. package/plugin/role-agents/lens-link/AGENT.md +83 -0
  216. package/plugin/role-agents/lens-reader/AGENT.md +230 -0
  217. package/plugin/skills/_shared/proactive-routing.md +1 -0
  218. package/plugin/skills/lens/SKILL.md +375 -0
  219. package/schemas/gestalt.schema.json +13 -0
@@ -0,0 +1,123 @@
1
+ import type { TailOptions } from './tail.js';
2
+ import type { RedactOptions, ToolsLevel } from './redact.js';
3
+ /**
4
+ * 뷰어 하나가 받는 것. seq는 SSE id로 써서 다시 붙었을 때 이어 받게 한다. 이벤트는
5
+ * 쌓을 때 한 번 직렬화한 글자를 그대로 넘긴다. 뷰어마다 다시 직렬화하지 않는다.
6
+ */
7
+ export type ShareFrame = {
8
+ type: 'event';
9
+ seq: number;
10
+ json: string;
11
+ }
12
+ /** 붙은 뷰어가 받을 앞부분 중 메모리 상한에 밀려 버려진 이벤트 수 */
13
+ | {
14
+ type: 'trimmed';
15
+ dropped: number;
16
+ } | {
17
+ type: 'stats';
18
+ unknownLines: number;
19
+ } | {
20
+ type: 'state';
21
+ status: ShareStatus;
22
+ }
23
+ /**
24
+ * 공유 종료. 앞부분을 따라잡는 중에 종료되면 아직 못 보낸 이벤트는 사라지므로 그 개수를
25
+ * missed에 싣는다. 붙어 있던 뷰어는 다 받은 뒤라 안 싣는다
26
+ */
27
+ | {
28
+ type: 'closed';
29
+ reason: string;
30
+ missed?: number;
31
+ };
32
+ export type ShareStatus = 'live' | 'missing' | 'truncated';
33
+ export type ShareListener = (frame: ShareFrame) => void;
34
+ export interface ShareStreamOptions extends RedactOptions {
35
+ id: string;
36
+ transcriptPath: string;
37
+ tools: ToolsLevel;
38
+ /** 들고 있는 이벤트의 직렬화 바이트 상한. 테스트가 작게 준다 */
39
+ maxBacklogBytes?: number;
40
+ tail?: Omit<TailOptions, 'startOffset'>;
41
+ }
42
+ /**
43
+ * 새 뷰어에게 다시 보낼 앞부분의 상한. 넘으면 오래된 것부터 버린다. 공유는 최대 7일
44
+ * 열려 있고 도구 출력이 크면 events가 끝없이 는다.
45
+ */
46
+ export declare const DEFAULT_MAX_BACKLOG_BYTES: number;
47
+ export interface BacklogSlice {
48
+ /** `afterSeq` 뒤로 상한에 밀려 버려진 이벤트 수 */
49
+ dropped: number;
50
+ items: {
51
+ seq: number;
52
+ json: string;
53
+ }[];
54
+ }
55
+ /**
56
+ * 공유 하나. 대화 기록을 따라 읽어 바꾼 이벤트를 메모리에 쌓고 붙은 뷰어에 흘린다.
57
+ *
58
+ * 바꾼 결과만 메모리에 들고 원문 줄은 들지 않는다. 디스크에는 아무것도 안 쓴다.
59
+ * 처음 붙는 뷰어가 앞부분을 받아야 해서 바꾼 이벤트를 직렬화한 글자로 상한까지 남긴다.
60
+ *
61
+ * seq는 공유가 연 뒤로 나온 이벤트의 번호다. 앞부분을 버려도 번호는 안 당긴다.
62
+ * 배열 위치로 매기면 버린 만큼 번호가 밀려 Last-Event-ID로 이어 받을 때 겹치거나 빠진다.
63
+ */
64
+ export declare class ShareStream {
65
+ readonly id: string;
66
+ readonly transcriptPath: string;
67
+ readonly tools: ToolsLevel;
68
+ private tail;
69
+ private transformer;
70
+ /**
71
+ * `events[head]`부터가 살아 있다. 앞에서 하나씩 shift하면 큰 배열에서 매번 다 옮긴다.
72
+ * head 앞 칸은 버릴 때 비워 둔다. 압축은 head가 1024를 넘어야 돌아서 칸에 참조를
73
+ * 남기면 버린 이벤트를 GC가 못 가져간다.
74
+ */
75
+ private events;
76
+ private head;
77
+ /** `events[0]` 앞에서 잘라 낸 이벤트 수. `events[i]`의 seq는 `base + i + 1`이다 */
78
+ private base;
79
+ private backlogBytes;
80
+ private readonly maxBacklogBytes;
81
+ private listeners;
82
+ private status;
83
+ private title;
84
+ private lastUnknown;
85
+ private closedReason;
86
+ constructor(options: ShareStreamOptions);
87
+ get currentStatus(): ShareStatus;
88
+ get unknownLines(): number;
89
+ get failedLines(): number;
90
+ get oversizedLines(): number;
91
+ get claudeVersion(): string | undefined;
92
+ get sessionTitle(): string | undefined;
93
+ get eventCount(): number;
94
+ /** 상한에 밀려 버린 이벤트 수 */
95
+ get droppedEvents(): number;
96
+ get viewerCount(): number;
97
+ get isClosed(): boolean;
98
+ /**
99
+ * 새로 쓰인 줄을 읽어 흘린다. 데몬이 주기적으로 부른다.
100
+ * 모르는 줄 수가 늘었으면 true — 데몬이 감사 로그에 버전을 남길 때 쓴다.
101
+ */
102
+ poll(): {
103
+ unknownGrew: boolean;
104
+ };
105
+ /**
106
+ * 쌓인 것 중 `afterSeq` 뒤를 `maxBytes`쯤까지 꺼낸다. 이벤트 하나가 상한보다 커도 하나는
107
+ * 꺼낸다. 뷰어가 소켓이 비는 만큼씩 앞부분을 보내려고 쓴다. 한 번에 다 넘기면 안 읽는
108
+ * 뷰어마다 앞부분 전체가 소켓 버퍼에 쌓인다.
109
+ */
110
+ backlogAfter(afterSeq: number, maxBytes: number): BacklogSlice;
111
+ /**
112
+ * 뷰어를 붙인다. 지금 상태와 `afterSeq` 뒤 앞부분을 한 번에 보내고 이후는 생기는 대로.
113
+ * 떼어내는 함수를 돌려준다. 앞부분이 큰 뷰어는 backlogAfter로 먼저 따라잡은 뒤 이벤트
114
+ * 루프로 돌아가기 전에 바로 붙어야 사이에 poll이 끼어 이벤트가 빠지지 않는다.
115
+ */
116
+ subscribe(listener: ShareListener, afterSeq?: number): () => void;
117
+ close(reason: string): void;
118
+ /** 가장 최근 이벤트 하나는 상한을 넘어도 남긴다. 다 버리면 새 뷰어가 지금 상태를 못 본다 */
119
+ private trim;
120
+ private setStatus;
121
+ private emit;
122
+ }
123
+ //# sourceMappingURL=share.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"share.d.ts","sourceRoot":"","sources":["../../../src/peer/share.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,204 @@
1
+ import { TranscriptTail } from './tail.js';
2
+ import { TranscriptTransformer } from './transform.js';
3
+ import { errMessage } from './errors.js';
4
+ /**
5
+ * 새 뷰어에게 다시 보낼 앞부분의 상한. 넘으면 오래된 것부터 버린다. 공유는 최대 7일
6
+ * 열려 있고 도구 출력이 크면 events가 끝없이 는다.
7
+ */
8
+ export const DEFAULT_MAX_BACKLOG_BYTES = 32 * 1024 * 1024;
9
+ /**
10
+ * 공유 하나. 대화 기록을 따라 읽어 바꾼 이벤트를 메모리에 쌓고 붙은 뷰어에 흘린다.
11
+ *
12
+ * 바꾼 결과만 메모리에 들고 원문 줄은 들지 않는다. 디스크에는 아무것도 안 쓴다.
13
+ * 처음 붙는 뷰어가 앞부분을 받아야 해서 바꾼 이벤트를 직렬화한 글자로 상한까지 남긴다.
14
+ *
15
+ * seq는 공유가 연 뒤로 나온 이벤트의 번호다. 앞부분을 버려도 번호는 안 당긴다.
16
+ * 배열 위치로 매기면 버린 만큼 번호가 밀려 Last-Event-ID로 이어 받을 때 겹치거나 빠진다.
17
+ */
18
+ export class ShareStream {
19
+ id;
20
+ transcriptPath;
21
+ tools;
22
+ tail;
23
+ transformer;
24
+ /**
25
+ * `events[head]`부터가 살아 있다. 앞에서 하나씩 shift하면 큰 배열에서 매번 다 옮긴다.
26
+ * head 앞 칸은 버릴 때 비워 둔다. 압축은 head가 1024를 넘어야 돌아서 칸에 참조를
27
+ * 남기면 버린 이벤트를 GC가 못 가져간다.
28
+ */
29
+ events = [];
30
+ head = 0;
31
+ /** `events[0]` 앞에서 잘라 낸 이벤트 수. `events[i]`의 seq는 `base + i + 1`이다 */
32
+ base = 0;
33
+ backlogBytes = 0;
34
+ maxBacklogBytes;
35
+ listeners = new Set();
36
+ status = 'live';
37
+ title;
38
+ lastUnknown = 0;
39
+ closedReason;
40
+ constructor(options) {
41
+ this.id = options.id;
42
+ this.transcriptPath = options.transcriptPath;
43
+ this.tools = options.tools;
44
+ this.maxBacklogBytes = options.maxBacklogBytes ?? DEFAULT_MAX_BACKLOG_BYTES;
45
+ this.tail = new TranscriptTail(options.transcriptPath, options.tail);
46
+ this.transformer = new TranscriptTransformer({ tools: options.tools, home: options.home });
47
+ }
48
+ get currentStatus() {
49
+ return this.status;
50
+ }
51
+ get unknownLines() {
52
+ return this.transformer.unknownLines;
53
+ }
54
+ get failedLines() {
55
+ return this.transformer.failedLines;
56
+ }
57
+ get oversizedLines() {
58
+ return this.tail.oversizedLines;
59
+ }
60
+ get claudeVersion() {
61
+ return this.transformer.version;
62
+ }
63
+ get sessionTitle() {
64
+ return this.title;
65
+ }
66
+ get eventCount() {
67
+ return this.base + this.events.length;
68
+ }
69
+ /** 상한에 밀려 버린 이벤트 수 */
70
+ get droppedEvents() {
71
+ return this.base + this.head;
72
+ }
73
+ get viewerCount() {
74
+ return this.listeners.size;
75
+ }
76
+ get isClosed() {
77
+ return this.closedReason !== undefined;
78
+ }
79
+ /**
80
+ * 새로 쓰인 줄을 읽어 흘린다. 데몬이 주기적으로 부른다.
81
+ * 모르는 줄 수가 늘었으면 true — 데몬이 감사 로그에 버전을 남길 때 쓴다.
82
+ */
83
+ poll() {
84
+ if (this.closedReason !== undefined || this.status === 'truncated') {
85
+ return { unknownGrew: false };
86
+ }
87
+ const { lines, status } = this.tail.read();
88
+ this.setStatus(toShareStatus(status));
89
+ for (const line of lines) {
90
+ for (const event of this.transformer.push(line)) {
91
+ if (event.kind === 'title')
92
+ this.title = event.text;
93
+ const json = JSON.stringify(event);
94
+ const bytes = Buffer.byteLength(json);
95
+ this.events.push({ json, bytes });
96
+ this.backlogBytes += bytes;
97
+ this.emit({ type: 'event', seq: this.eventCount, json });
98
+ }
99
+ }
100
+ this.trim();
101
+ const unknown = this.transformer.unknownLines;
102
+ const unknownGrew = unknown > this.lastUnknown;
103
+ if (unknownGrew) {
104
+ this.lastUnknown = unknown;
105
+ this.emit({ type: 'stats', unknownLines: unknown });
106
+ }
107
+ return { unknownGrew };
108
+ }
109
+ /**
110
+ * 쌓인 것 중 `afterSeq` 뒤를 `maxBytes`쯤까지 꺼낸다. 이벤트 하나가 상한보다 커도 하나는
111
+ * 꺼낸다. 뷰어가 소켓이 비는 만큼씩 앞부분을 보내려고 쓴다. 한 번에 다 넘기면 안 읽는
112
+ * 뷰어마다 앞부분 전체가 소켓 버퍼에 쌓인다.
113
+ */
114
+ backlogAfter(afterSeq, maxBytes) {
115
+ if (this.closedReason !== undefined)
116
+ return { dropped: 0, items: [] };
117
+ const droppedTotal = this.droppedEvents;
118
+ const after = Math.max(0, afterSeq);
119
+ const items = [];
120
+ let bytes = 0;
121
+ for (let i = Math.max(after, droppedTotal) - this.base; i < this.events.length; i++) {
122
+ const held = this.events[i];
123
+ if (items.length > 0 && bytes + held.bytes > maxBytes)
124
+ break;
125
+ items.push({ seq: this.base + i + 1, json: held.json });
126
+ bytes += held.bytes;
127
+ }
128
+ return { dropped: after < droppedTotal ? droppedTotal - after : 0, items };
129
+ }
130
+ /**
131
+ * 뷰어를 붙인다. 지금 상태와 `afterSeq` 뒤 앞부분을 한 번에 보내고 이후는 생기는 대로.
132
+ * 떼어내는 함수를 돌려준다. 앞부분이 큰 뷰어는 backlogAfter로 먼저 따라잡은 뒤 이벤트
133
+ * 루프로 돌아가기 전에 바로 붙어야 사이에 poll이 끼어 이벤트가 빠지지 않는다.
134
+ */
135
+ subscribe(listener, afterSeq = 0) {
136
+ listener({ type: 'state', status: this.status });
137
+ listener({ type: 'stats', unknownLines: this.transformer.unknownLines });
138
+ const { dropped, items } = this.backlogAfter(afterSeq, Infinity);
139
+ if (dropped > 0)
140
+ listener({ type: 'trimmed', dropped });
141
+ for (const item of items)
142
+ listener({ type: 'event', ...item });
143
+ if (this.closedReason !== undefined) {
144
+ // close가 events를 비워도 base를 늘려 두므로 eventCount는 그때까지 나온 전체 개수다
145
+ listener({
146
+ type: 'closed',
147
+ reason: this.closedReason,
148
+ missed: Math.max(0, this.eventCount - afterSeq),
149
+ });
150
+ return () => { };
151
+ }
152
+ this.listeners.add(listener);
153
+ return () => {
154
+ this.listeners.delete(listener);
155
+ };
156
+ }
157
+ close(reason) {
158
+ if (this.closedReason !== undefined)
159
+ return;
160
+ this.closedReason = reason;
161
+ this.emit({ type: 'closed', reason });
162
+ this.listeners.clear();
163
+ // 종료된 공유의 내용을 메모리에 붙잡아 둘 이유가 없다
164
+ this.base += this.events.length;
165
+ this.events = [];
166
+ this.head = 0;
167
+ this.backlogBytes = 0;
168
+ }
169
+ /** 가장 최근 이벤트 하나는 상한을 넘어도 남긴다. 다 버리면 새 뷰어가 지금 상태를 못 본다 */
170
+ trim() {
171
+ while (this.backlogBytes > this.maxBacklogBytes && this.events.length - this.head > 1) {
172
+ this.backlogBytes -= this.events[this.head].bytes;
173
+ this.events[this.head] = undefined;
174
+ this.head++;
175
+ }
176
+ if (this.head > 1024 && this.head * 2 > this.events.length) {
177
+ this.base += this.head;
178
+ this.events = this.events.slice(this.head);
179
+ this.head = 0;
180
+ }
181
+ }
182
+ setStatus(next) {
183
+ if (next === this.status)
184
+ return;
185
+ this.status = next;
186
+ this.emit({ type: 'state', status: next });
187
+ }
188
+ emit(frame) {
189
+ for (const listener of [...this.listeners]) {
190
+ try {
191
+ listener(frame);
192
+ }
193
+ catch (err) {
194
+ // 뷰어 하나가 끊겨 쓰기에 실패해도 다른 뷰어와 데몬은 계속 돈다
195
+ this.listeners.delete(listener);
196
+ process.stderr.write(`[peer] viewer listener failed: ${errMessage(err)}\n`);
197
+ }
198
+ }
199
+ }
200
+ }
201
+ function toShareStatus(status) {
202
+ return status === 'ok' ? 'live' : status;
203
+ }
204
+ //# sourceMappingURL=share.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"share.js","sourceRoot":"","sources":["../../../src/peer/share.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type { ToolsLevel } from './redact.js';
2
+ export type ShareCloseReason = 'unshare' | 'expired' | 'daemon_stop' | 'daemon_restart' | 'error';
3
+ export interface ShareRow {
4
+ id: string;
5
+ sessionId: string | null;
6
+ transcriptPath: string;
7
+ tools: ToolsLevel;
8
+ createdAt: number;
9
+ expiresAt: number;
10
+ closedAt: number | null;
11
+ closeReason: string | null;
12
+ }
13
+ export type AuditAction = 'share_open' | 'share_close' | 'viewer_connect' | 'viewer_disconnect' | 'unknown_lines';
14
+ export interface AuditRow {
15
+ at: number;
16
+ shareId: string | null;
17
+ action: AuditAction;
18
+ detail: string | null;
19
+ }
20
+ /**
21
+ * 공유 목록과 감사 로그. 대화 내용은 한 글자도 여기 안 들어온다 — 줄 수나 버전 같은
22
+ * 숫자와 이름만 남긴다. 공유 토큰도 저장하지 않는다. 데몬이 다시 뜨면 이전 공유는
23
+ * 종료된 걸로 치고 새로 연다.
24
+ */
25
+ export declare class PeerState {
26
+ private db;
27
+ constructor(path: string);
28
+ insertShare(row: Omit<ShareRow, 'closedAt' | 'closeReason'>): void;
29
+ closeShare(id: string, reason: ShareCloseReason, at?: number): void;
30
+ /** 데몬이 죽어서 종료 기록 없이 남은 공유를 종료한다. 종료한 id 목록을 돌려준다 */
31
+ closeDangling(reason: ShareCloseReason, at?: number): string[];
32
+ getShare(id: string): ShareRow | undefined;
33
+ audit(action: AuditAction, shareId: string | null, detail?: string, at?: number): void;
34
+ auditLog(shareId?: string): AuditRow[];
35
+ close(): void;
36
+ }
37
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../../src/peer/state.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AA0B9C,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,gBAAgB,GAAG,OAAO,CAAC;AAElG,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,MAAM,WAAW,GACnB,YAAY,GACZ,aAAa,GACb,gBAAgB,GAChB,mBAAmB,GACnB,eAAe,CAAC;AAEpB,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED;;;;GAIG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,EAAE,CAAiB;gBAEf,IAAI,EAAE,MAAM;IA8BxB,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,UAAU,GAAG,aAAa,CAAC,GAAG,IAAI;IASlE,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,EAAE,SAAa,GAAG,IAAI;IAQvE,oDAAoD;IACpD,aAAa,CAAC,MAAM,EAAE,gBAAgB,EAAE,EAAE,SAAa,GAAG,MAAM,EAAE;IAQlE,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAK1C,KAAK,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,SAAa,GAAG,IAAI;IAM1F,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,QAAQ,EAAE;IAStC,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,99 @@
1
+ import { chmodSync, mkdirSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import { dirname } from 'node:path';
4
+ const require = createRequire(import.meta.url);
5
+ function loadSqlite() {
6
+ const mod = require('better-sqlite3');
7
+ if (typeof mod === 'function')
8
+ return mod;
9
+ if (mod.default)
10
+ return mod.default;
11
+ throw new Error('better-sqlite3 did not export a Database constructor');
12
+ }
13
+ /**
14
+ * 공유 목록과 감사 로그. 대화 내용은 한 글자도 여기 안 들어온다 — 줄 수나 버전 같은
15
+ * 숫자와 이름만 남긴다. 공유 토큰도 저장하지 않는다. 데몬이 다시 뜨면 이전 공유는
16
+ * 종료된 걸로 치고 새로 연다.
17
+ */
18
+ export class PeerState {
19
+ db;
20
+ constructor(path) {
21
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
22
+ const Database = loadSqlite();
23
+ this.db = new Database(path);
24
+ try {
25
+ chmodSync(path, 0o600);
26
+ }
27
+ catch {
28
+ // :memory: 같은 경로는 파일이 없다
29
+ }
30
+ this.db.exec(`
31
+ CREATE TABLE IF NOT EXISTS shares (
32
+ id TEXT PRIMARY KEY,
33
+ session_id TEXT,
34
+ transcript_path TEXT NOT NULL,
35
+ tools TEXT NOT NULL,
36
+ created_at INTEGER NOT NULL,
37
+ expires_at INTEGER NOT NULL,
38
+ closed_at INTEGER,
39
+ close_reason TEXT
40
+ );
41
+ CREATE TABLE IF NOT EXISTS audit (
42
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
43
+ at INTEGER NOT NULL,
44
+ share_id TEXT,
45
+ action TEXT NOT NULL,
46
+ detail TEXT
47
+ );
48
+ `);
49
+ }
50
+ insertShare(row) {
51
+ this.db
52
+ .prepare(`INSERT INTO shares (id, session_id, transcript_path, tools, created_at, expires_at)
53
+ VALUES (?, ?, ?, ?, ?, ?)`)
54
+ .run(row.id, row.sessionId, row.transcriptPath, row.tools, row.createdAt, row.expiresAt);
55
+ }
56
+ closeShare(id, reason, at = Date.now()) {
57
+ this.db
58
+ .prepare(`UPDATE shares SET closed_at = ?, close_reason = ? WHERE id = ? AND closed_at IS NULL`)
59
+ .run(at, reason, id);
60
+ }
61
+ /** 데몬이 죽어서 종료 기록 없이 남은 공유를 종료한다. 종료한 id 목록을 돌려준다 */
62
+ closeDangling(reason, at = Date.now()) {
63
+ const rows = this.db.prepare(`SELECT id FROM shares WHERE closed_at IS NULL`).all();
64
+ for (const r of rows)
65
+ this.closeShare(r.id, reason, at);
66
+ return rows.map((r) => r.id);
67
+ }
68
+ getShare(id) {
69
+ const r = this.db.prepare(`SELECT * FROM shares WHERE id = ?`).get(id);
70
+ return r ? toShare(r) : undefined;
71
+ }
72
+ audit(action, shareId, detail, at = Date.now()) {
73
+ this.db
74
+ .prepare(`INSERT INTO audit (at, share_id, action, detail) VALUES (?, ?, ?, ?)`)
75
+ .run(at, shareId, action, detail ?? null);
76
+ }
77
+ auditLog(shareId) {
78
+ const rows = (shareId
79
+ ? this.db.prepare(`SELECT * FROM audit WHERE share_id = ? ORDER BY id`).all(shareId)
80
+ : this.db.prepare(`SELECT * FROM audit ORDER BY id`).all());
81
+ return rows.map((r) => ({ at: r.at, shareId: r.share_id, action: r.action, detail: r.detail }));
82
+ }
83
+ close() {
84
+ this.db.close();
85
+ }
86
+ }
87
+ function toShare(r) {
88
+ return {
89
+ id: r.id,
90
+ sessionId: r.session_id,
91
+ transcriptPath: r.transcript_path,
92
+ tools: r.tools,
93
+ createdAt: r.created_at,
94
+ expiresAt: r.expires_at,
95
+ closedAt: r.closed_at,
96
+ closeReason: r.close_reason,
97
+ };
98
+ }
99
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/peer/state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ export type TailStatus = 'ok' | 'missing' | 'truncated';
2
+ export interface TailRead {
3
+ lines: string[];
4
+ status: TailStatus;
5
+ }
6
+ /**
7
+ * read 한 번에 읽는 상한. 데몬은 IPC와 뷰어를 같은 스레드에서 돌려서 큰 기록을 한 번에
8
+ * 읽으면 그동안 명령이 멈춘다. 나머지는 다음 read가 이어 읽는다.
9
+ */
10
+ export declare const DEFAULT_READ_BUDGET_BYTES: number;
11
+ /**
12
+ * 한 줄 상한. 이미지가 든 도구 결과 줄도 들어가게 넉넉히 잡는다. 넘는 줄은 개행까지
13
+ * 버린다. 개행이 끝내 안 오는 파일에서 메모리가 끝없이 늘지 않게 하는 게 목적이다.
14
+ */
15
+ export declare const DEFAULT_MAX_LINE_BYTES: number;
16
+ export interface TailOptions {
17
+ startOffset?: number;
18
+ readBudgetBytes?: number;
19
+ maxLineBytes?: number;
20
+ }
21
+ /**
22
+ * jsonl 파일을 바이트 위치로 이어 읽는다.
23
+ *
24
+ * 줄은 개행이 붙어야 끝난 걸로 본다. Claude Code가 한 줄을 쓰는 도중에 읽으면
25
+ * 반쪽 JSON이 나오는데 그걸 모르는 줄로 세면 숫자가 부풀고 다음 읽기에서 뒷반쪽이
26
+ * 또 깨진다. 개행 없는 꼬리는 다음 읽기까지 들고 있는다.
27
+ *
28
+ * 파일이 줄거나 다른 파일로 바뀌면 멈춘다. 처음부터 다시 읽으면 이미 보낸 줄이
29
+ * 두 번 나간다. 무엇이 바뀌었는지 모르는 채로 계속 흘리는 것보다 멈추고 알리는 게 낫다.
30
+ */
31
+ export declare class TranscriptTail {
32
+ private readonly path;
33
+ private offset;
34
+ /** 개행을 아직 못 만난 꼬리. 청크마다 합치면 긴 줄에서 누적분을 매번 다시 복사한다 */
35
+ private pending;
36
+ private pendingBytes;
37
+ /** 상한을 넘은 줄의 나머지를 개행까지 버리는 중 */
38
+ private discarding;
39
+ private oversized;
40
+ private inode;
41
+ private stopped;
42
+ private readonly budget;
43
+ private readonly maxLine;
44
+ constructor(path: string, options?: TailOptions);
45
+ /** 다음 읽기가 시작할 바이트 위치. 덜 쓰인 꼬리는 포함하지 않는다 */
46
+ get position(): number;
47
+ /** 상한을 넘어 버린 줄 수. 형식을 몰라 건너뛴 줄과 섞지 않는다 */
48
+ get oversizedLines(): number;
49
+ read(): TailRead;
50
+ /** `buf`는 다음 청크가 덮어쓴다. 들고 있을 꼬리는 복사해 둔다 */
51
+ private feed;
52
+ private dropPending;
53
+ private halt;
54
+ }
55
+ //# sourceMappingURL=tail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tail.d.ts","sourceRoot":"","sources":["../../../src/peer/tail.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,SAAS,GAAG,WAAW,CAAC;AAExD,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,UAAU,CAAC;CACpB;AAKD;;;GAGG;AACH,eAAO,MAAM,yBAAyB,QAAkB,CAAC;AAEzD;;;GAGG;AACH,eAAO,MAAM,sBAAsB,QAAmB,CAAC;AAEvD,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;GASG;AACH,qBAAa,cAAc;IAcvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAbvB,OAAO,CAAC,MAAM,CAAS;IACvB,qDAAqD;IACrD,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,YAAY,CAAK;IACzB,gCAAgC;IAChC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAGd,IAAI,EAAE,MAAM,EAC7B,OAAO,GAAE,WAAgB;IAO3B,2CAA2C;IAC3C,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,0CAA0C;IAC1C,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED,IAAI,IAAI,QAAQ;IA8ChB,2CAA2C;IAC3C,OAAO,CAAC,IAAI;IAkCZ,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,IAAI;CAMb"}