@tienne/gestalt 0.108.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 (69) hide show
  1. package/CLAUDE.md +1 -0
  2. package/dist/package.json +1 -1
  3. package/dist/src/cli/commands/peer.d.ts +19 -0
  4. package/dist/src/cli/commands/peer.d.ts.map +1 -0
  5. package/dist/src/cli/commands/peer.js +147 -0
  6. package/dist/src/cli/commands/peer.js.map +1 -0
  7. package/dist/src/cli/index.d.ts.map +1 -1
  8. package/dist/src/cli/index.js +35 -2
  9. package/dist/src/cli/index.js.map +1 -1
  10. package/dist/src/peer/client.d.ts +9 -0
  11. package/dist/src/peer/client.d.ts.map +1 -0
  12. package/dist/src/peer/client.js +51 -0
  13. package/dist/src/peer/client.js.map +1 -0
  14. package/dist/src/peer/daemon.d.ts +71 -0
  15. package/dist/src/peer/daemon.d.ts.map +1 -0
  16. package/dist/src/peer/daemon.js +407 -0
  17. package/dist/src/peer/daemon.js.map +1 -0
  18. package/dist/src/peer/errors.d.ts +2 -0
  19. package/dist/src/peer/errors.d.ts.map +1 -0
  20. package/dist/src/peer/errors.js +4 -0
  21. package/dist/src/peer/errors.js.map +1 -0
  22. package/dist/src/peer/index.d.ts +14 -0
  23. package/dist/src/peer/index.d.ts.map +1 -0
  24. package/dist/src/peer/index.js +9 -0
  25. package/dist/src/peer/index.js.map +1 -0
  26. package/dist/src/peer/locate.d.ts +44 -0
  27. package/dist/src/peer/locate.d.ts.map +1 -0
  28. package/dist/src/peer/locate.js +116 -0
  29. package/dist/src/peer/locate.js.map +1 -0
  30. package/dist/src/peer/paths.d.ts +23 -0
  31. package/dist/src/peer/paths.d.ts.map +1 -0
  32. package/dist/src/peer/paths.js +50 -0
  33. package/dist/src/peer/paths.js.map +1 -0
  34. package/dist/src/peer/protocol.d.ts +61 -0
  35. package/dist/src/peer/protocol.d.ts.map +1 -0
  36. package/dist/src/peer/protocol.js +7 -0
  37. package/dist/src/peer/protocol.js.map +1 -0
  38. package/dist/src/peer/redact.d.ts +30 -0
  39. package/dist/src/peer/redact.d.ts.map +1 -0
  40. package/dist/src/peer/redact.js +214 -0
  41. package/dist/src/peer/redact.js.map +1 -0
  42. package/dist/src/peer/share.d.ts +123 -0
  43. package/dist/src/peer/share.d.ts.map +1 -0
  44. package/dist/src/peer/share.js +204 -0
  45. package/dist/src/peer/share.js.map +1 -0
  46. package/dist/src/peer/state.d.ts +37 -0
  47. package/dist/src/peer/state.d.ts.map +1 -0
  48. package/dist/src/peer/state.js +99 -0
  49. package/dist/src/peer/state.js.map +1 -0
  50. package/dist/src/peer/tail.d.ts +55 -0
  51. package/dist/src/peer/tail.d.ts.map +1 -0
  52. package/dist/src/peer/tail.js +148 -0
  53. package/dist/src/peer/tail.js.map +1 -0
  54. package/dist/src/peer/transform.d.ts +79 -0
  55. package/dist/src/peer/transform.d.ts.map +1 -0
  56. package/dist/src/peer/transform.js +237 -0
  57. package/dist/src/peer/transform.js.map +1 -0
  58. package/dist/src/peer/viewer-html.d.ts +10 -0
  59. package/dist/src/peer/viewer-html.d.ts.map +1 -0
  60. package/dist/src/peer/viewer-html.js +157 -0
  61. package/dist/src/peer/viewer-html.js.map +1 -0
  62. package/dist/src/peer/viewer.d.ts +58 -0
  63. package/dist/src/peer/viewer.d.ts.map +1 -0
  64. package/dist/src/peer/viewer.js +249 -0
  65. package/dist/src/peer/viewer.js.map +1 -0
  66. package/package.json +1 -1
  67. package/plugin/.codex-plugin/plugin.json +1 -1
  68. package/plugin/.mcp.json +1 -1
  69. package/plugin/mcp.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../../src/peer/transform.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE7D,6CAA6C;AAC7C,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5E;IACE,IAAI,EAAE,aAAa,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,GACD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAClC;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AA6BvC,iEAAiE;AACjE,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACrD,KAAK,EAAE,UAAU,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,qBAAa,qBAAqB;IAMpB,OAAO,CAAC,QAAQ,CAAC,OAAO;IALpC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,aAAa,CAAqB;gBAEb,OAAO,EAAE,gBAAgB;IAEtD,oCAAoC;IACpC,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED;;;OAGG;IACH,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,sDAAsD;IACtD,IAAI,OAAO,IAAI,MAAM,GAAG,SAAS,CAEhC;IAED;;;;;OAKG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE;IAS/B,OAAO,CAAC,KAAK;IAmDb,OAAO,CAAC,QAAQ;IAgBhB,OAAO,CAAC,aAAa;IAqBrB,OAAO,CAAC,MAAM;IAKd,OAAO,CAAC,QAAQ;IAYhB,OAAO,CAAC,UAAU;IA+BlB,OAAO,CAAC,MAAM;CAGf"}
@@ -0,0 +1,237 @@
1
+ import { redactText, redactValue, truncateBytes, TOOL_RESULT_SUMMARY_BYTES } from './redact.js';
2
+ /** `tool_use` 입력 요약 길이. 결과보다 짧게 둔다 — 입력은 접힌 줄 제목처럼 읽힌다 */
3
+ const TOOL_INPUT_SUMMARY_BYTES = 1024;
4
+ /**
5
+ * 내용을 안 보내는 줄 종류. 2.1.311 기록에서 본 것들이다. 여기 있는 건 조용히
6
+ * 버리고 여기에도 허용 목록에도 없는 종류만 "모르는 줄"로 센다.
7
+ *
8
+ * 이 목록은 차단 목록이 아니다. 지워도 새는 게 없고 "모르는 줄" 숫자만 는다.
9
+ */
10
+ const KNOWN_DROPPED = new Set([
11
+ 'api-request',
12
+ 'api-request-blob',
13
+ 'api-request-shape',
14
+ 'attachment',
15
+ 'system',
16
+ 'last-prompt',
17
+ 'queue-operation',
18
+ 'tools-used',
19
+ 'cost-state',
20
+ 'mode',
21
+ 'atis-latch',
22
+ 'summary',
23
+ 'file-history-snapshot',
24
+ ]);
25
+ const SYSTEM_REMINDER = /<system-reminder>[\s\S]*?(?:<\/system-reminder>|$)/g;
26
+ /** user 텍스트에서 `<system-reminder>` 블록을 지운다. 닫는 태그가 없으면 끝까지 지운다 */
27
+ export function stripSystemReminders(text) {
28
+ return text.replace(SYSTEM_REMINDER, '').trim();
29
+ }
30
+ /**
31
+ * 대화 기록 jsonl 한 줄씩 받아 허용 목록에 있는 것만 `PeerEvent`로 바꾼다.
32
+ *
33
+ * 허용 목록이다. 줄 종류든 content 블록 종류든 여기서 명시적으로 꺼내지 않은 건
34
+ * 아무것도 안 나간다. 형식이 바뀌어 새 필드나 새 종류가 생겨도 그대로 새지 않는다.
35
+ *
36
+ * 상태가 있다 — 서브에이전트 줄이 이어지면 표시를 한 번만 낸다.
37
+ */
38
+ export class TranscriptTransformer {
39
+ options;
40
+ inSidechain = false;
41
+ unknown = 0;
42
+ failed = 0;
43
+ claudeVersion;
44
+ constructor(options) {
45
+ this.options = options;
46
+ }
47
+ /** 모르는 줄 종류와 JSON으로 안 읽힌 줄을 합친 수 */
48
+ get unknownLines() {
49
+ return this.unknown;
50
+ }
51
+ /**
52
+ * 바꾸다 예외가 난 줄 수. 모르는 줄과 따로 센다. 모르는 줄은 형식이 바뀐 신호라 감사
53
+ * 로그에 버전과 함께 남기는데 여기에 섞이면 그 신호가 흐려진다.
54
+ */
55
+ get failedLines() {
56
+ return this.failed;
57
+ }
58
+ /** 마지막으로 본 Claude Code 버전. 모르는 줄이 생겼을 때 감사 로그에 남긴다 */
59
+ get version() {
60
+ return this.claudeVersion;
61
+ }
62
+ /**
63
+ * 한 줄을 바꾼다. 줄 하나에서 난 예외는 여기서 접고 그 줄만 버린다. 2만 단계로
64
+ * 중첩된 도구 입력이 가리기 재귀에서 스택을 넘기는 걸 봤다. 던지게 두면 이 공유의
65
+ * 나머지 줄을 더 못 읽는다. 읽던 중이면 tick이 그 공유를 error로 종료하고 공유를 여는
66
+ * 첫 poll이면 share 요청이 실패한다.
67
+ */
68
+ push(line) {
69
+ try {
70
+ return this.parse(line);
71
+ }
72
+ catch {
73
+ this.failed++;
74
+ return [];
75
+ }
76
+ }
77
+ parse(line) {
78
+ const trimmed = line.trim();
79
+ if (trimmed === '')
80
+ return [];
81
+ let parsed;
82
+ try {
83
+ parsed = JSON.parse(trimmed);
84
+ }
85
+ catch {
86
+ this.unknown++;
87
+ return [];
88
+ }
89
+ if (!isRecord(parsed) || typeof parsed['type'] !== 'string') {
90
+ this.unknown++;
91
+ return [];
92
+ }
93
+ if (typeof parsed['version'] === 'string')
94
+ this.claudeVersion = parsed['version'];
95
+ const type = parsed['type'];
96
+ const at = typeof parsed['timestamp'] === 'string' ? parsed['timestamp'] : undefined;
97
+ if (type === 'ai-title') {
98
+ const title = parsed['aiTitle'];
99
+ return typeof title === 'string' && title.trim() !== ''
100
+ ? [{ kind: 'title', text: this.redact(title) }]
101
+ : [];
102
+ }
103
+ if (type !== 'user' && type !== 'assistant') {
104
+ if (!KNOWN_DROPPED.has(type))
105
+ this.unknown++;
106
+ return [];
107
+ }
108
+ if (parsed['isSidechain'] === true) {
109
+ if (this.inSidechain)
110
+ return [];
111
+ this.inSidechain = true;
112
+ return [{ kind: 'sidechain', at }];
113
+ }
114
+ this.inSidechain = false;
115
+ // 하네스가 끼워 넣은 줄이다. 주인이 친 말이 아니고 CLAUDE.md나 명령 출력이 들어 있다
116
+ if (parsed['isMeta'] === true)
117
+ return [];
118
+ // 압축 요약은 지난 대화 전체를 다시 적은 글이라 그 안에 무엇이 섞였는지 모른다
119
+ if (parsed['isCompactSummary'] === true)
120
+ return [{ kind: 'compacted', at }];
121
+ const message = parsed['message'];
122
+ if (!isRecord(message))
123
+ return [];
124
+ const content = message['content'];
125
+ return type === 'user' ? this.fromUser(content, at) : this.fromAssistant(content, at);
126
+ }
127
+ fromUser(content, at) {
128
+ if (typeof content === 'string')
129
+ return this.prompt(content, at);
130
+ if (!Array.isArray(content))
131
+ return [];
132
+ const events = [];
133
+ for (const block of content) {
134
+ if (!isRecord(block))
135
+ continue;
136
+ if (block['type'] === 'text' && typeof block['text'] === 'string') {
137
+ events.push(...this.prompt(block['text'], at));
138
+ }
139
+ else if (block['type'] === 'tool_result') {
140
+ events.push(this.toolResult(block, at));
141
+ }
142
+ }
143
+ return events;
144
+ }
145
+ fromAssistant(content, at) {
146
+ if (typeof content === 'string') {
147
+ const text = this.redact(content).trim();
148
+ return text === '' ? [] : [{ kind: 'reply', text, at }];
149
+ }
150
+ if (!Array.isArray(content))
151
+ return [];
152
+ const events = [];
153
+ for (const block of content) {
154
+ if (!isRecord(block))
155
+ continue;
156
+ if (block['type'] === 'text' && typeof block['text'] === 'string') {
157
+ const text = this.redact(block['text']).trim();
158
+ if (text !== '')
159
+ events.push({ kind: 'reply', text, at });
160
+ }
161
+ else if (block['type'] === 'tool_use') {
162
+ events.push(this.toolCall(block, at));
163
+ }
164
+ // thinking, redacted_thinking, server_tool_use 등은 꺼내지 않는다
165
+ }
166
+ return events;
167
+ }
168
+ prompt(raw, at) {
169
+ const text = this.redact(stripSystemReminders(raw));
170
+ return text === '' ? [] : [{ kind: 'prompt', text, at }];
171
+ }
172
+ toolCall(block, at) {
173
+ const id = typeof block['id'] === 'string' ? block['id'] : '';
174
+ const name = typeof block['name'] === 'string' ? this.redact(block['name']) : '(이름 없음)';
175
+ if (this.options.tools === 'none')
176
+ return { kind: 'tool_call', id, name, at };
177
+ const redacted = redactValue(block['input'] ?? {}, this.options);
178
+ const json = JSON.stringify(redacted) ?? '';
179
+ const input = this.options.tools === 'full' ? json : truncateBytes(json, TOOL_INPUT_SUMMARY_BYTES).text;
180
+ return { kind: 'tool_call', id, name, input, at };
181
+ }
182
+ toolResult(block, at) {
183
+ const toolUseId = typeof block['tool_use_id'] === 'string' ? block['tool_use_id'] : '';
184
+ const isError = block['is_error'] === true;
185
+ if (this.options.tools === 'none') {
186
+ return { kind: 'tool_result', toolUseId, truncated: false, hidden: true, isError, at };
187
+ }
188
+ const full = this.redact(resultText(block['content']));
189
+ if (this.options.tools === 'full') {
190
+ return {
191
+ kind: 'tool_result',
192
+ toolUseId,
193
+ text: full,
194
+ truncated: false,
195
+ hidden: false,
196
+ isError,
197
+ at,
198
+ };
199
+ }
200
+ const cut = truncateBytes(full, TOOL_RESULT_SUMMARY_BYTES);
201
+ return {
202
+ kind: 'tool_result',
203
+ toolUseId,
204
+ text: cut.text,
205
+ truncated: cut.truncated,
206
+ hidden: false,
207
+ isError,
208
+ at,
209
+ };
210
+ }
211
+ redact(text) {
212
+ return redactText(text, this.options);
213
+ }
214
+ }
215
+ /** tool_result의 content는 문자열이거나 블록 배열이다. 텍스트 블록만 꺼낸다 */
216
+ function resultText(content) {
217
+ if (typeof content === 'string')
218
+ return stripSystemReminders(content);
219
+ if (!Array.isArray(content))
220
+ return '';
221
+ const parts = [];
222
+ for (const block of content) {
223
+ if (!isRecord(block))
224
+ continue;
225
+ if (block['type'] === 'text' && typeof block['text'] === 'string') {
226
+ parts.push(stripSystemReminders(block['text']));
227
+ }
228
+ else if (block['type'] === 'image') {
229
+ parts.push('[이미지]');
230
+ }
231
+ }
232
+ return parts.join('\n');
233
+ }
234
+ function isRecord(value) {
235
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
236
+ }
237
+ //# sourceMappingURL=transform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transform.js","sourceRoot":"","sources":["../../../src/peer/transform.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export declare const VIEWER_WARNING = "\uBE44\uBC00\uAC12 \uD328\uD134\uC740 \uAC00\uB9AC\uC9C0\uB9CC \uC804\uBD80 \uC7A1\uC9C0\uB294 \uBABB\uD574\uC694.";
2
+ /**
3
+ * 뷰어 페이지. 바깥 자원을 하나도 안 끌어온다 — CSP가 `default-src 'none'`이라
4
+ * 인라인 스크립트와 스타일도 nonce가 맞는 것만 돈다.
5
+ *
6
+ * 대화 내용은 전부 textContent로 넣는다. 마크다운을 HTML로 그리면 남의 도구 출력에
7
+ * 든 태그가 이 페이지에서 실행될 길이 생긴다. localStorage나 IndexedDB도 안 쓴다.
8
+ */
9
+ export declare function renderViewerHtml(nonce: string, maxViewers: number): string;
10
+ //# sourceMappingURL=viewer-html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewer-html.d.ts","sourceRoot":"","sources":["../../../src/peer/viewer-html.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,uHAA6B,CAAC;AAEzD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAmJ1E"}
@@ -0,0 +1,157 @@
1
+ export const VIEWER_WARNING = '비밀값 패턴은 가리지만 전부 잡지는 못해요.';
2
+ /**
3
+ * 뷰어 페이지. 바깥 자원을 하나도 안 끌어온다 — CSP가 `default-src 'none'`이라
4
+ * 인라인 스크립트와 스타일도 nonce가 맞는 것만 돈다.
5
+ *
6
+ * 대화 내용은 전부 textContent로 넣는다. 마크다운을 HTML로 그리면 남의 도구 출력에
7
+ * 든 태그가 이 페이지에서 실행될 길이 생긴다. localStorage나 IndexedDB도 안 쓴다.
8
+ */
9
+ export function renderViewerHtml(nonce, maxViewers) {
10
+ return `<!doctype html>
11
+ <html lang="ko">
12
+ <head>
13
+ <meta charset="utf-8">
14
+ <meta name="viewport" content="width=device-width, initial-scale=1">
15
+ <meta name="referrer" content="no-referrer">
16
+ <title>gestalt peer</title>
17
+ <style nonce="${nonce}">
18
+ :root { color-scheme: light dark; --muted: #888; --line: #8884; }
19
+ body { font: 14px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; margin: 0; }
20
+ header { position: sticky; top: 0; padding: 10px 16px; border-bottom: 1px solid var(--line);
21
+ background: Canvas; z-index: 1; }
22
+ header h1 { font-size: 15px; margin: 0 0 4px; }
23
+ .warn { color: #b45309; font-size: 13px; }
24
+ .meta { color: var(--muted); font-size: 12px; display: flex; gap: 12px; flex-wrap: wrap; }
25
+ main { padding: 12px 16px 48px; max-width: 980px; }
26
+ .ev { margin: 10px 0; }
27
+ .who { font-size: 12px; color: var(--muted); }
28
+ .text { white-space: pre-wrap; word-break: break-word; }
29
+ .prompt .text { border-left: 3px solid #2563eb; padding-left: 8px; }
30
+ details { border: 1px solid var(--line); border-radius: 6px; padding: 4px 8px; }
31
+ details pre { white-space: pre-wrap; word-break: break-word; font-size: 12px; margin: 6px 0 2px; }
32
+ summary { cursor: pointer; font-size: 13px; }
33
+ .error summary { color: #dc2626; }
34
+ .note { color: var(--muted); font-style: italic; font-size: 13px; }
35
+ </style>
36
+ </head>
37
+ <body>
38
+ <header>
39
+ <h1 id="title">gestalt peer</h1>
40
+ <div class="warn">${VIEWER_WARNING}</div>
41
+ <div class="meta">
42
+ <span id="status">연결 중</span>
43
+ <span id="unknown"></span>
44
+ </div>
45
+ </header>
46
+ <main id="log"></main>
47
+ <script nonce="${nonce}">
48
+ (() => {
49
+ const log = document.getElementById('log');
50
+ const statusEl = document.getElementById('status');
51
+ const unknownEl = document.getElementById('unknown');
52
+ const titleEl = document.getElementById('title');
53
+ const params = new URLSearchParams(location.search);
54
+ const token = params.get('t') || '';
55
+ const base = location.pathname.replace(/\\/$/, '');
56
+ const results = new Map();
57
+
58
+ const el = (tag, cls, text) => {
59
+ const n = document.createElement(tag);
60
+ if (cls) n.className = cls;
61
+ if (text !== undefined) n.textContent = text;
62
+ return n;
63
+ };
64
+ const time = (at) => (at ? new Date(at).toLocaleTimeString() : '');
65
+ const atBottom = () => window.innerHeight + window.scrollY >= document.body.scrollHeight - 40;
66
+
67
+ const STATUS = {
68
+ live: '실시간',
69
+ missing: '대화 기록 파일이 안 보여요',
70
+ truncated: '대화 기록이 줄어들어서 멈췄어요',
71
+ };
72
+
73
+ function render(ev) {
74
+ const box = el('div', 'ev ' + ev.kind);
75
+ if (ev.kind === 'prompt' || ev.kind === 'reply') {
76
+ box.append(el('div', 'who', (ev.kind === 'prompt' ? '주인' : '모델') + ' ' + time(ev.at)));
77
+ box.append(el('div', 'text', ev.text));
78
+ } else if (ev.kind === 'tool_call') {
79
+ const d = el('details');
80
+ d.append(el('summary', '', '도구 ' + ev.name + ' ' + time(ev.at)));
81
+ if (ev.input !== undefined) d.append(el('pre', '', ev.input));
82
+ const slot = el('div');
83
+ d.append(slot);
84
+ if (ev.id) results.set(ev.id, slot);
85
+ box.append(d);
86
+ } else if (ev.kind === 'tool_result') {
87
+ const d = el('details', ev.isError ? 'error' : '');
88
+ const label = ev.hidden ? '결과 (주인이 숨김)' : ev.isError ? '결과 (에러)' : '결과';
89
+ d.append(el('summary', '', label + (ev.truncated ? ' · 앞부분만' : '')));
90
+ if (!ev.hidden) d.append(el('pre', '', ev.text || ''));
91
+ const slot = ev.toolUseId ? results.get(ev.toolUseId) : undefined;
92
+ if (slot) { slot.append(d); return; }
93
+ box.append(d);
94
+ } else if (ev.kind === 'title') {
95
+ titleEl.textContent = ev.text;
96
+ document.title = ev.text + ' · gestalt peer';
97
+ return;
98
+ } else if (ev.kind === 'sidechain') {
99
+ box.append(el('div', 'note', '서브에이전트 실행 중'));
100
+ } else if (ev.kind === 'compacted') {
101
+ box.append(el('div', 'note', '이전 대화가 압축됐어요'));
102
+ } else {
103
+ return;
104
+ }
105
+ log.append(box);
106
+ }
107
+
108
+ const eventsUrl = base + '/events?t=' + encodeURIComponent(token);
109
+ const es = new EventSource(eventsUrl);
110
+ es.addEventListener('line', (m) => {
111
+ const stick = atBottom();
112
+ render(JSON.parse(m.data));
113
+ if (stick) window.scrollTo(0, document.body.scrollHeight);
114
+ });
115
+ es.addEventListener('trimmed', (m) => {
116
+ const n = JSON.parse(m.data).dropped;
117
+ log.append(el('div', 'ev note', '메모리 상한에 걸려 앞부분 ' + n + '개를 생략했어요'));
118
+ });
119
+ es.addEventListener('stats', (m) => {
120
+ const n = JSON.parse(m.data).unknownLines;
121
+ unknownEl.textContent = n > 0 ? '모르는 줄 ' + n + '개 건너뜀' : '';
122
+ });
123
+ es.addEventListener('state', (m) => {
124
+ statusEl.textContent = STATUS[JSON.parse(m.data).status] || '';
125
+ });
126
+ es.addEventListener('closed', (m) => {
127
+ statusEl.textContent = '공유가 종료됐어요';
128
+ const missed = JSON.parse(m.data).missed;
129
+ if (missed > 0) {
130
+ log.append(el('div', 'ev note', '공유가 종료돼 뒤쪽 ' + missed + '개는 못 받았어요'));
131
+ }
132
+ es.close();
133
+ });
134
+ // EventSource는 응답 코드를 안 알려 준다. 끊긴 이유가 뷰어 상한(429)인지 한 번 물어본다
135
+ es.onerror = () => {
136
+ if (es.readyState !== EventSource.CLOSED) {
137
+ statusEl.textContent = '다시 연결 중';
138
+ return;
139
+ }
140
+ statusEl.textContent = '연결이 끊겼어요';
141
+ const ctl = new AbortController();
142
+ fetch(eventsUrl, { signal: ctl.signal, cache: 'no-store' })
143
+ .then((r) => {
144
+ ctl.abort();
145
+ if (r.status === 429) {
146
+ statusEl.textContent = '보는 사람 수 상한(${maxViewers}명)에 걸려 못 붙었어요';
147
+ }
148
+ })
149
+ .catch(() => {});
150
+ };
151
+ })();
152
+ </script>
153
+ </body>
154
+ </html>
155
+ `;
156
+ }
157
+ //# sourceMappingURL=viewer-html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewer-html.js","sourceRoot":"","sources":["../../../src/peer/viewer-html.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG,0BAA0B,CAAC;AAEzD;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa,EAAE,UAAkB;IAChE,OAAO;;;;;;;gBAOO,KAAK;;;;;;;;;;;;;;;;;;;;;;;sBAuBC,cAAc;;;;;;;iBAOnB,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+CAmGyB,UAAU;;;;;;;;;CASxD,CAAC;AACF,CAAC"}
@@ -0,0 +1,58 @@
1
+ import type { ShareStream } from './share.js';
2
+ export interface ViewerShare {
3
+ stream: ShareStream;
4
+ token: string;
5
+ }
6
+ export interface ViewerHooks {
7
+ lookup(id: string): ViewerShare | undefined;
8
+ onConnect?(shareId: string): void;
9
+ onDisconnect?(shareId: string): void;
10
+ }
11
+ /**
12
+ * 공유 하나에 동시에 붙는 뷰어 상한. 넘으면 429. 뷰어 하나가 붙잡는 메모리는 소켓 버퍼로
13
+ * 묶여 있지만 연결마다 소켓과 타이머가 생기니 토큰을 아는 쪽이 끝없이 열지 못하게 한다.
14
+ * 사람이 보는 숫자로는 넉넉하다.
15
+ */
16
+ export declare const MAX_VIEWERS_PER_SHARE = 16;
17
+ /**
18
+ * 실시간 프레임을 쓰기 전에 소켓 버퍼에 이만큼 넘게 밀려 있으면 그 뷰어를 끊는다. 안 읽는
19
+ * 뷰어 하나가 데몬 메모리를 끝없이 붙잡지 못하게 한다. 이보다 큰 이벤트 하나는 들어가지만
20
+ * 소켓으로 다 빠지기 전에 다음 프레임이 오면 그때 끊는다. 앞부분은 소켓이 비는 만큼만 꺼내
21
+ * 보내서 여기에 안 쌓인다. 브라우저는 다시 붙어 Last-Event-ID부터 받는다.
22
+ */
23
+ export declare const MAX_LIVE_BACKLOG_BYTES: number;
24
+ /** 앞부분을 공유에서 한 번에 꺼내는 양. 소켓이 차면 그 자리에서 멈추므로 대략이면 된다 */
25
+ export declare const REPLAY_CHUNK_BYTES: number;
26
+ export interface ViewerServerOptions {
27
+ /** 테스트가 작게 준다 */
28
+ maxLiveBacklogBytes?: number;
29
+ }
30
+ /**
31
+ * 로컬 뷰어 HTTP 서버. `127.0.0.1`에만 붙는다.
32
+ *
33
+ * 같은 머신의 다른 프로세스나 브라우저에 열린 남의 웹페이지도 이 포트에 닿을 수 있다.
34
+ * 그래서 공유마다 토큰을 주소에 붙이고 없거나 틀리면 401이다. 모르는 공유 id도 401로
35
+ * 돌려준다 — 404와 401을 나누면 토큰 없이 공유 id가 살아 있는지 떠볼 수 있다.
36
+ *
37
+ * Routes:
38
+ * GET /s/:id?t=TOKEN → 뷰어 HTML
39
+ * GET /s/:id/events?t=TOKEN → SSE (line, trimmed, stats, state, closed). 뷰어 상한을 넘으면 429.
40
+ * closed의 missed는 따라잡는 중에 공유가 종료돼 못 보낸 이벤트 수
41
+ * * → 404
42
+ */
43
+ export declare class ViewerServer {
44
+ private readonly hooks;
45
+ private server;
46
+ private open;
47
+ private viewers;
48
+ private readonly maxLiveBacklogBytes;
49
+ constructor(hooks: ViewerHooks, options?: ViewerServerOptions);
50
+ start(port?: number): Promise<number>;
51
+ get port(): number | undefined;
52
+ stop(): Promise<void>;
53
+ private handle;
54
+ private hostAllowed;
55
+ private page;
56
+ private stream;
57
+ }
58
+ //# sourceMappingURL=viewer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewer.d.ts","sourceRoot":"","sources":["../../../src/peer/viewer.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAc,WAAW,EAAE,MAAM,YAAY,CAAC;AAI1D,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;IAC5C,SAAS,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,YAAY,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAQD;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,QAAkB,CAAC;AAEtD,uDAAuD;AACvD,eAAO,MAAM,kBAAkB,QAAa,CAAC;AAE7C,MAAM,WAAW,mBAAmB;IAClC,iBAAiB;IACjB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,YAAY;IAOrB,OAAO,CAAC,QAAQ,CAAC,KAAK;IANxB,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,IAAI,CAA6B;IACzC,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;gBAG1B,KAAK,EAAE,WAAW,EACnC,OAAO,GAAE,mBAAwB;IAKnC,KAAK,CAAC,IAAI,SAAI,GAAG,OAAO,CAAC,MAAM,CAAC;IAwBhC,IAAI,IAAI,IAAI,MAAM,GAAG,SAAS,CAG7B;IAED,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBrB,OAAO,CAAC,MAAM;IAwBd,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,IAAI;IAkBZ,OAAO,CAAC,MAAM;CA4Ef"}