@tienne/gestalt 0.110.0 → 0.112.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 (164) hide show
  1. package/CLAUDE.md +3 -3
  2. package/README.ko.md +1 -1
  3. package/README.md +1 -1
  4. package/dist/package.json +3 -1
  5. package/dist/plugin/role-agents/lens-context/AGENT.md +8 -7
  6. package/dist/plugin/role-agents/lens-reader/AGENT.md +48 -2
  7. package/dist/plugin/skills/lens/SKILL.md +22 -15
  8. package/dist/src/architecture/doc-fresh.d.ts.map +1 -1
  9. package/dist/src/architecture/doc-fresh.js +3 -1
  10. package/dist/src/architecture/doc-fresh.js.map +1 -1
  11. package/dist/src/cli/commands/lens.d.ts +8 -1
  12. package/dist/src/cli/commands/lens.d.ts.map +1 -1
  13. package/dist/src/cli/commands/lens.js +29 -2
  14. package/dist/src/cli/commands/lens.js.map +1 -1
  15. package/dist/src/cli/commands/peer.d.ts +19 -0
  16. package/dist/src/cli/commands/peer.d.ts.map +1 -1
  17. package/dist/src/cli/commands/peer.js +188 -11
  18. package/dist/src/cli/commands/peer.js.map +1 -1
  19. package/dist/src/cli/index.d.ts.map +1 -1
  20. package/dist/src/cli/index.js +43 -4
  21. package/dist/src/cli/index.js.map +1 -1
  22. package/dist/src/digest/lens-schema.d.ts +150 -3
  23. package/dist/src/digest/lens-schema.d.ts.map +1 -1
  24. package/dist/src/digest/lens-schema.js +14 -1
  25. package/dist/src/digest/lens-schema.js.map +1 -1
  26. package/dist/src/digest/lens-widget-schema.d.ts +2 -2
  27. package/dist/src/digest/lint-lens.d.ts +2 -1
  28. package/dist/src/digest/lint-lens.d.ts.map +1 -1
  29. package/dist/src/digest/lint-lens.js +68 -7
  30. package/dist/src/digest/lint-lens.js.map +1 -1
  31. package/dist/src/digest/ocr.d.ts +1 -1
  32. package/dist/src/digest/ocr.d.ts.map +1 -1
  33. package/dist/src/digest/ocr.js +10 -4
  34. package/dist/src/digest/ocr.js.map +1 -1
  35. package/dist/src/digest/render-html.d.ts +5 -0
  36. package/dist/src/digest/render-html.d.ts.map +1 -1
  37. package/dist/src/digest/render-html.js +31 -10
  38. package/dist/src/digest/render-html.js.map +1 -1
  39. package/dist/src/digest/render-lens.d.ts +1 -1
  40. package/dist/src/digest/render-lens.d.ts.map +1 -1
  41. package/dist/src/digest/render-lens.js +17 -2
  42. package/dist/src/digest/render-lens.js.map +1 -1
  43. package/dist/src/digest/schema.d.ts +62 -62
  44. package/dist/src/digest/server.d.ts.map +1 -1
  45. package/dist/src/digest/server.js +7 -4
  46. package/dist/src/digest/server.js.map +1 -1
  47. package/dist/src/digest/theme-diary.d.ts +10 -0
  48. package/dist/src/digest/theme-diary.d.ts.map +1 -0
  49. package/dist/src/digest/theme-diary.js +111 -0
  50. package/dist/src/digest/theme-diary.js.map +1 -0
  51. package/dist/src/digest/viewer-js.d.ts +6 -0
  52. package/dist/src/digest/viewer-js.d.ts.map +1 -1
  53. package/dist/src/digest/viewer-js.js +78 -4
  54. package/dist/src/digest/viewer-js.js.map +1 -1
  55. package/dist/src/lens/attach.d.ts +741 -0
  56. package/dist/src/lens/attach.d.ts.map +1 -1
  57. package/dist/src/lens/attach.js +41 -11
  58. package/dist/src/lens/attach.js.map +1 -1
  59. package/dist/src/lens/context-input.d.ts +17 -0
  60. package/dist/src/lens/context-input.d.ts.map +1 -0
  61. package/dist/src/lens/context-input.js +31 -0
  62. package/dist/src/lens/context-input.js.map +1 -0
  63. package/dist/src/lens/ingest.d.ts +19 -1
  64. package/dist/src/lens/ingest.d.ts.map +1 -1
  65. package/dist/src/lens/ingest.js +92 -14
  66. package/dist/src/lens/ingest.js.map +1 -1
  67. package/dist/src/lens/pdf-extract-py.d.ts +2 -0
  68. package/dist/src/lens/pdf-extract-py.d.ts.map +1 -1
  69. package/dist/src/lens/pdf-extract-py.js +37 -2
  70. package/dist/src/lens/pdf-extract-py.js.map +1 -1
  71. package/dist/src/mcp/schemas.d.ts +23 -0
  72. package/dist/src/mcp/schemas.d.ts.map +1 -1
  73. package/dist/src/mcp/schemas.js +20 -0
  74. package/dist/src/mcp/schemas.js.map +1 -1
  75. package/dist/src/mcp/server.d.ts.map +1 -1
  76. package/dist/src/mcp/server.js +6 -1
  77. package/dist/src/mcp/server.js.map +1 -1
  78. package/dist/src/mcp/tools/architecture-passthrough.d.ts.map +1 -1
  79. package/dist/src/mcp/tools/architecture-passthrough.js +1 -1
  80. package/dist/src/mcp/tools/architecture-passthrough.js.map +1 -1
  81. package/dist/src/mcp/tools/peer.d.ts +21 -0
  82. package/dist/src/mcp/tools/peer.d.ts.map +1 -0
  83. package/dist/src/mcp/tools/peer.js +71 -0
  84. package/dist/src/mcp/tools/peer.js.map +1 -0
  85. package/dist/src/peer/codex.d.ts +49 -0
  86. package/dist/src/peer/codex.d.ts.map +1 -0
  87. package/dist/src/peer/codex.js +240 -0
  88. package/dist/src/peer/codex.js.map +1 -0
  89. package/dist/src/peer/daemon.d.ts +52 -1
  90. package/dist/src/peer/daemon.d.ts.map +1 -1
  91. package/dist/src/peer/daemon.js +356 -16
  92. package/dist/src/peer/daemon.js.map +1 -1
  93. package/dist/src/peer/feed.d.ts +24 -2
  94. package/dist/src/peer/feed.d.ts.map +1 -1
  95. package/dist/src/peer/feed.js.map +1 -1
  96. package/dist/src/peer/inbox.d.ts +31 -0
  97. package/dist/src/peer/inbox.d.ts.map +1 -0
  98. package/dist/src/peer/inbox.js +80 -0
  99. package/dist/src/peer/inbox.js.map +1 -0
  100. package/dist/src/peer/locate.d.ts +3 -0
  101. package/dist/src/peer/locate.d.ts.map +1 -1
  102. package/dist/src/peer/locate.js +4 -2
  103. package/dist/src/peer/locate.js.map +1 -1
  104. package/dist/src/peer/message.d.ts +104 -0
  105. package/dist/src/peer/message.d.ts.map +1 -0
  106. package/dist/src/peer/message.js +171 -0
  107. package/dist/src/peer/message.js.map +1 -0
  108. package/dist/src/peer/mirror.d.ts +50 -1
  109. package/dist/src/peer/mirror.d.ts.map +1 -1
  110. package/dist/src/peer/mirror.js +138 -0
  111. package/dist/src/peer/mirror.js.map +1 -1
  112. package/dist/src/peer/network.d.ts +247 -0
  113. package/dist/src/peer/network.d.ts.map +1 -1
  114. package/dist/src/peer/network.js +1119 -43
  115. package/dist/src/peer/network.js.map +1 -1
  116. package/dist/src/peer/paths.d.ts +6 -0
  117. package/dist/src/peer/paths.d.ts.map +1 -1
  118. package/dist/src/peer/paths.js +8 -0
  119. package/dist/src/peer/paths.js.map +1 -1
  120. package/dist/src/peer/protocol.d.ts +96 -4
  121. package/dist/src/peer/protocol.d.ts.map +1 -1
  122. package/dist/src/peer/protocol.js.map +1 -1
  123. package/dist/src/peer/relay.d.ts +14 -0
  124. package/dist/src/peer/relay.d.ts.map +1 -1
  125. package/dist/src/peer/relay.js +102 -7
  126. package/dist/src/peer/relay.js.map +1 -1
  127. package/dist/src/peer/roster.d.ts +2 -0
  128. package/dist/src/peer/roster.d.ts.map +1 -1
  129. package/dist/src/peer/roster.js +5 -0
  130. package/dist/src/peer/roster.js.map +1 -1
  131. package/dist/src/peer/share.d.ts +48 -1
  132. package/dist/src/peer/share.d.ts.map +1 -1
  133. package/dist/src/peer/share.js +163 -2
  134. package/dist/src/peer/share.js.map +1 -1
  135. package/dist/src/peer/state.d.ts +12 -1
  136. package/dist/src/peer/state.d.ts.map +1 -1
  137. package/dist/src/peer/state.js +15 -0
  138. package/dist/src/peer/state.js.map +1 -1
  139. package/dist/src/peer/transform.d.ts +27 -0
  140. package/dist/src/peer/transform.d.ts.map +1 -1
  141. package/dist/src/peer/transform.js +37 -33
  142. package/dist/src/peer/transform.js.map +1 -1
  143. package/dist/src/peer/transport.d.ts +7 -3
  144. package/dist/src/peer/transport.d.ts.map +1 -1
  145. package/dist/src/peer/transport.js +12 -4
  146. package/dist/src/peer/transport.js.map +1 -1
  147. package/dist/src/peer/viewer-html.d.ts +1 -1
  148. package/dist/src/peer/viewer-html.d.ts.map +1 -1
  149. package/dist/src/peer/viewer-html.js +151 -1
  150. package/dist/src/peer/viewer-html.js.map +1 -1
  151. package/dist/src/peer/viewer.d.ts +30 -1
  152. package/dist/src/peer/viewer.d.ts.map +1 -1
  153. package/dist/src/peer/viewer.js +103 -5
  154. package/dist/src/peer/viewer.js.map +1 -1
  155. package/dist/src/peer/wire.d.ts +9 -2
  156. package/dist/src/peer/wire.d.ts.map +1 -1
  157. package/dist/src/peer/wire.js.map +1 -1
  158. package/package.json +3 -1
  159. package/plugin/.codex-plugin/plugin.json +1 -1
  160. package/plugin/.mcp.json +1 -1
  161. package/plugin/mcp.json +1 -1
  162. package/plugin/role-agents/lens-context/AGENT.md +8 -7
  163. package/plugin/role-agents/lens-reader/AGENT.md +48 -2
  164. package/plugin/skills/lens/SKILL.md +22 -15
@@ -0,0 +1,71 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { request } from '../../peer/client.js';
3
+ import { errMessage } from '../../peer/errors.js';
4
+ import { claudeConfigDir, findSessionByPid } from '../../peer/locate.js';
5
+ import { peerRoot, socketPath } from '../../peer/paths.js';
6
+ /**
7
+ * 세션을 찾을 pid 후보. 부모가 먼저고 `npx`처럼 사이에 프로세스가 하나 끼면 그 부모까지 본다.
8
+ * Claude Code가 자식에게 주는 `CLAUDE_PID`가 있으면 그걸 맨 앞에 둔다.
9
+ */
10
+ export function sessionPids(env = process.env) {
11
+ const pids = [];
12
+ const fromEnv = Number(env['CLAUDE_PID']);
13
+ if (Number.isInteger(fromEnv) && fromEnv > 0)
14
+ pids.push(fromEnv);
15
+ pids.push(process.ppid);
16
+ const grand = parentOf(process.ppid);
17
+ if (grand !== undefined)
18
+ pids.push(grand);
19
+ return [...new Set(pids)];
20
+ }
21
+ function parentOf(pid) {
22
+ try {
23
+ const out = execFileSync('ps', ['-o', 'ppid=', '-p', String(pid)], {
24
+ encoding: 'utf8',
25
+ stdio: ['ignore', 'pipe', 'ignore'],
26
+ timeout: 2_000,
27
+ });
28
+ const n = Number(out.trim());
29
+ return Number.isInteger(n) && n > 1 ? n : undefined;
30
+ }
31
+ catch {
32
+ return undefined;
33
+ }
34
+ }
35
+ export async function handlePeer(input, deps = {}) {
36
+ const configDir = deps.configDir ?? claudeConfigDir();
37
+ const pids = deps.pids ?? sessionPids();
38
+ const session = pids.map((pid) => findSessionByPid(pid, configDir)).find((s) => s !== null);
39
+ if (!session) {
40
+ return JSON.stringify({
41
+ error: 'Claude Code 세션 안에서만 쓸 수 있어요. 이 MCP 서버의 부모 세션을 못 찾았어요',
42
+ configDir,
43
+ });
44
+ }
45
+ let sock;
46
+ try {
47
+ sock = deps.socket ?? socketPath(peerRoot());
48
+ }
49
+ catch (err) {
50
+ return JSON.stringify({ error: errMessage(err) });
51
+ }
52
+ try {
53
+ const res = await request(sock, {
54
+ op: 'ask',
55
+ sessionId: session.sessionId,
56
+ question: input.question,
57
+ ...(input.to !== undefined ? { to: input.to } : {}),
58
+ ...(input.timeoutSecs !== undefined ? { timeoutSecs: input.timeoutSecs } : {}),
59
+ });
60
+ if (!res.ok)
61
+ return JSON.stringify({ error: res.error });
62
+ return JSON.stringify({
63
+ ...res.result,
64
+ note: '질문을 보냈다. 답은 기다리지 말고 하던 일을 이어 간다. 답은 <peer-answer from=… question=… verified> 메시지로 이 세션에 들어온다. expiresAt이 지나면 더 안 온다.',
65
+ });
66
+ }
67
+ catch (err) {
68
+ return JSON.stringify({ error: errMessage(err) });
69
+ }
70
+ }
71
+ //# sourceMappingURL=peer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"peer.js","sourceRoot":"","sources":["../../../../src/mcp/tools/peer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import type { PeerEvent, TransformOptions } from './transform.js';
2
+ /**
3
+ * Codex 대화 기록(rollout) 읽기. codex-cli 0.147.0 exec 기록으로 확인한 형식이다. 허용 목록 가운데
4
+ * function_call, local_shell_call, function_call_output은 기록에 없어서 바이너리에 있는 이름을 따랐고
5
+ * 필드는 추정이다.
6
+ *
7
+ * 기록은 `$CODEX_HOME/sessions/<연>/<월>/<일>/rollout-<시각>-<thread id>.jsonl`에 남는다.
8
+ * `state_5.sqlite`의 threads 테이블은 이 경로를 가리키는 색인이고 본문은 jsonl에만 있다.
9
+ * 줄은 모두 `{timestamp, type, payload}`다. 문서화되지 않은 형식이라 허용 목록에 있는 것만 꺼낸다.
10
+ */
11
+ /** Codex 설정 디렉토리. 오르카처럼 `CODEX_HOME`으로 옮겨 둔 경우가 있다 */
12
+ export declare function codexHome(env?: NodeJS.ProcessEnv): string;
13
+ export type CodexLocateResult = {
14
+ ok: true;
15
+ path: string;
16
+ home: string;
17
+ } | {
18
+ ok: false;
19
+ reason: string;
20
+ home: string;
21
+ };
22
+ /**
23
+ * `--codex`에 준 값으로 rollout 파일을 찾는다. `.jsonl`로 끝나면 경로로 보고 아니면 thread id로
24
+ * `sessions/` 아래에서 파일 이름이 `-<id>.jsonl`로 끝나는 rollout을 찾는다. SQLite 색인은 안
25
+ * 읽는다. 스키마 번호가 바뀌어도 파일 이름 규칙은 그대로라 새 의존성 없이 찾을 수 있다.
26
+ */
27
+ export declare function locateCodexRollout(value: string, home?: string, cwd?: string): CodexLocateResult;
28
+ /**
29
+ * Codex rollout 한 줄씩 받아 허용 목록에 있는 것만 `PeerEvent`로 바꾼다. Claude Code 기록의
30
+ * TranscriptTransformer와 같은 자리에 끼우고 가리기도 같은 규칙을 탄다.
31
+ */
32
+ export declare class CodexTransformer {
33
+ private readonly options;
34
+ private unknown;
35
+ private failed;
36
+ private cliVersion;
37
+ constructor(options: TransformOptions);
38
+ get unknownLines(): number;
39
+ get failedLines(): number;
40
+ /** 마지막으로 본 codex-cli 버전. 모르는 줄이 생겼을 때 감사 로그에 남긴다 */
41
+ get version(): string | undefined;
42
+ push(line: string): PeerEvent[];
43
+ private parse;
44
+ private fromEvent;
45
+ private fromItem;
46
+ private call;
47
+ private redact;
48
+ }
49
+ //# sourceMappingURL=codex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../../src/peer/codex.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGlE;;;;;;;;GAQG;AAEH,sDAAsD;AACtD,wBAAgB,SAAS,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAItE;AAKD,MAAM,MAAM,iBAAiB,GACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,IAAI,SAAc,EAClB,GAAG,SAAgB,GAClB,iBAAiB,CA0BnB;AA4CD;;;GAGG;AACH,qBAAa,gBAAgB;IAKf,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,UAAU,CAAqB;gBAEV,OAAO,EAAE,gBAAgB;IAEtD,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,oDAAoD;IACpD,IAAI,OAAO,IAAI,MAAM,GAAG,SAAS,CAEhC;IAED,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE;IAS/B,OAAO,CAAC,KAAK;IA2Bb,OAAO,CAAC,SAAS;IAajB,OAAO,CAAC,QAAQ;IA0BhB,OAAO,CAAC,IAAI;IAKZ,OAAO,CAAC,MAAM;CAGf"}
@@ -0,0 +1,240 @@
1
+ import { readdirSync, statSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { basename, isAbsolute, join, resolve } from 'node:path';
4
+ import { redactText } from './redact.js';
5
+ import { isRecord, toolCallEvent, toolResultEvent } from './transform.js';
6
+ /**
7
+ * Codex 대화 기록(rollout) 읽기. codex-cli 0.147.0 exec 기록으로 확인한 형식이다. 허용 목록 가운데
8
+ * function_call, local_shell_call, function_call_output은 기록에 없어서 바이너리에 있는 이름을 따랐고
9
+ * 필드는 추정이다.
10
+ *
11
+ * 기록은 `$CODEX_HOME/sessions/<연>/<월>/<일>/rollout-<시각>-<thread id>.jsonl`에 남는다.
12
+ * `state_5.sqlite`의 threads 테이블은 이 경로를 가리키는 색인이고 본문은 jsonl에만 있다.
13
+ * 줄은 모두 `{timestamp, type, payload}`다. 문서화되지 않은 형식이라 허용 목록에 있는 것만 꺼낸다.
14
+ */
15
+ /** Codex 설정 디렉토리. 오르카처럼 `CODEX_HOME`으로 옮겨 둔 경우가 있다 */
16
+ export function codexHome(env = process.env) {
17
+ const raw = env['CODEX_HOME'];
18
+ if (raw && isAbsolute(raw))
19
+ return raw;
20
+ return join(homedir(), '.codex');
21
+ }
22
+ /** thread id. 파일 이름 조각으로 쓰이므로 `..`이나 `/`가 들어오면 안 된다 */
23
+ const THREAD_ID = /^[A-Za-z0-9][A-Za-z0-9-]{7,63}$/;
24
+ /**
25
+ * `--codex`에 준 값으로 rollout 파일을 찾는다. `.jsonl`로 끝나면 경로로 보고 아니면 thread id로
26
+ * `sessions/` 아래에서 파일 이름이 `-<id>.jsonl`로 끝나는 rollout을 찾는다. SQLite 색인은 안
27
+ * 읽는다. 스키마 번호가 바뀌어도 파일 이름 규칙은 그대로라 새 의존성 없이 찾을 수 있다.
28
+ */
29
+ export function locateCodexRollout(value, home = codexHome(), cwd = process.cwd()) {
30
+ if (value.endsWith('.jsonl')) {
31
+ const path = isAbsolute(value) ? value : resolve(cwd, value);
32
+ return isFile(path)
33
+ ? { ok: true, path, home }
34
+ : { ok: false, reason: `rollout 파일이 없어요: ${path}`, home };
35
+ }
36
+ if (!THREAD_ID.test(value)) {
37
+ return { ok: false, reason: 'thread id나 rollout 경로(.jsonl)를 주세요', home };
38
+ }
39
+ const root = join(home, 'sessions');
40
+ let names;
41
+ try {
42
+ names = readdirSync(root, { recursive: true, encoding: 'utf8' });
43
+ }
44
+ catch {
45
+ return { ok: false, reason: 'sessions 디렉토리를 못 읽었어요', home };
46
+ }
47
+ const suffix = `-${value}.jsonl`;
48
+ const hits = names
49
+ .filter((n) => basename(n).startsWith('rollout-') && n.endsWith(suffix))
50
+ .map((n) => join(root, n))
51
+ .filter(isFile);
52
+ if (hits.length === 0)
53
+ return { ok: false, reason: '그 id의 rollout을 못 찾았어요', home };
54
+ // 같은 id가 둘이면 이름이 뒤인(나중에 만든) 것을 쓴다. 파일 이름이 시각으로 시작한다
55
+ hits.sort();
56
+ return { ok: true, path: hits[hits.length - 1], home };
57
+ }
58
+ function isFile(path) {
59
+ try {
60
+ return statSync(path).isFile();
61
+ }
62
+ catch {
63
+ return false;
64
+ }
65
+ }
66
+ /**
67
+ * 내용을 안 보내는 줄 종류. compacted 말고는 0.147.0 기록에서 본 것이고 compacted는 바이너리에 있는
68
+ * 이름이다. 여기 있는 건 조용히 버리고 여기에도 허용
69
+ * 목록에도 없는 종류만 "모르는 줄"로 센다. 차단 목록이 아니다 — 지워도 새는 게 없다.
70
+ *
71
+ * - session_meta: 시스템 프롬프트 원문(base_instructions)이 들어 있다
72
+ * - world_state: AGENTS.md 원문(agents_md)과 환경 정보가 들어 있다
73
+ * - turn_context: 턴마다 cwd, 권한, 모델 설정
74
+ * - compacted: 압축 요약. 지난 대화 전체를 다시 적은 글이다
75
+ */
76
+ const KNOWN_DROPPED = new Set(['session_meta', 'world_state', 'turn_context', 'compacted']);
77
+ /**
78
+ * 안 보내는 event_msg 종류. 앞의 넷은 기록에서 봤고 추론 셋은 기록에는 안 남았지만 바이너리에
79
+ * 있는 이름이다. 추론 원문은 허용 목록 밖이라 이 목록에서 빠져도 안 나간다.
80
+ */
81
+ const KNOWN_DROPPED_EVENTS = new Set([
82
+ 'task_started',
83
+ 'task_complete',
84
+ 'token_count',
85
+ 'thread_settings_applied',
86
+ 'agent_reasoning',
87
+ 'agent_reasoning_raw_content',
88
+ 'agent_reasoning_section_break',
89
+ ]);
90
+ /**
91
+ * 안 보내는 response_item 종류. message는 role과 무관하게 버린다. role user 메시지에는 사람이 친
92
+ * 말과 함께 AGENTS.md 전문과 `<environment_context>`가 들어 있고 developer 메시지는 시스템 지시다.
93
+ * 프롬프트와 답은 event_msg의 user_message, agent_message에서 꺼낸다. reasoning은 추론 원문이고 기록에
94
+ * 없어서 바이너리에 있는 이름을 넣었다.
95
+ */
96
+ const KNOWN_DROPPED_ITEMS = new Set(['message', 'reasoning']);
97
+ /**
98
+ * Codex rollout 한 줄씩 받아 허용 목록에 있는 것만 `PeerEvent`로 바꾼다. Claude Code 기록의
99
+ * TranscriptTransformer와 같은 자리에 끼우고 가리기도 같은 규칙을 탄다.
100
+ */
101
+ export class CodexTransformer {
102
+ options;
103
+ unknown = 0;
104
+ failed = 0;
105
+ cliVersion;
106
+ constructor(options) {
107
+ this.options = options;
108
+ }
109
+ get unknownLines() {
110
+ return this.unknown;
111
+ }
112
+ get failedLines() {
113
+ return this.failed;
114
+ }
115
+ /** 마지막으로 본 codex-cli 버전. 모르는 줄이 생겼을 때 감사 로그에 남긴다 */
116
+ get version() {
117
+ return this.cliVersion;
118
+ }
119
+ push(line) {
120
+ try {
121
+ return this.parse(line);
122
+ }
123
+ catch {
124
+ this.failed++;
125
+ return [];
126
+ }
127
+ }
128
+ parse(line) {
129
+ const trimmed = line.trim();
130
+ if (trimmed === '')
131
+ return [];
132
+ let parsed;
133
+ try {
134
+ parsed = JSON.parse(trimmed);
135
+ }
136
+ catch {
137
+ this.unknown++;
138
+ return [];
139
+ }
140
+ if (!isRecord(parsed) || typeof parsed['type'] !== 'string' || !isRecord(parsed['payload'])) {
141
+ this.unknown++;
142
+ return [];
143
+ }
144
+ const type = parsed['type'];
145
+ const payload = parsed['payload'];
146
+ const at = typeof parsed['timestamp'] === 'string' ? parsed['timestamp'] : undefined;
147
+ if (type === 'session_meta' && typeof payload['cli_version'] === 'string') {
148
+ this.cliVersion = payload['cli_version'];
149
+ }
150
+ if (type === 'event_msg')
151
+ return this.fromEvent(payload, at);
152
+ if (type === 'response_item')
153
+ return this.fromItem(payload, at);
154
+ if (!KNOWN_DROPPED.has(type))
155
+ this.unknown++;
156
+ return [];
157
+ }
158
+ fromEvent(payload, at) {
159
+ const kind = payload['type'];
160
+ if (kind === 'user_message' || kind === 'agent_message') {
161
+ const raw = payload['message'];
162
+ if (typeof raw !== 'string')
163
+ return [];
164
+ const text = this.redact(raw).trim();
165
+ if (text === '')
166
+ return [];
167
+ return [{ kind: kind === 'user_message' ? 'prompt' : 'reply', text, at }];
168
+ }
169
+ if (typeof kind !== 'string' || !KNOWN_DROPPED_EVENTS.has(kind))
170
+ this.unknown++;
171
+ return [];
172
+ }
173
+ fromItem(payload, at) {
174
+ const kind = payload['type'];
175
+ const callId = typeof payload['call_id'] === 'string' ? payload['call_id'] : '';
176
+ switch (kind) {
177
+ // custom_tool_call과 그 결과는 기록에서 본 꼴이다. 나머지 분기의 필드는 추정이라 꼴이 다르면
178
+ // 도구 이름만 보이고 입력과 결과가 빈다
179
+ case 'function_call':
180
+ return [this.call(callId, payload['name'], parseArguments(payload['arguments']), at)];
181
+ case 'custom_tool_call':
182
+ return [this.call(callId, payload['name'], payload['input'] ?? '', at)];
183
+ case 'local_shell_call': {
184
+ const action = isRecord(payload['action']) ? payload['action'] : {};
185
+ return [this.call(callId, 'local_shell', { command: action['command'] }, at)];
186
+ }
187
+ case 'function_call_output':
188
+ case 'custom_tool_call_output': {
189
+ const output = payload['output'];
190
+ const failed = isRecord(output) && output['success'] === false;
191
+ return [toolResultEvent(callId, () => outputText(output), failed, at, this.options)];
192
+ }
193
+ default:
194
+ if (typeof kind !== 'string' || !KNOWN_DROPPED_ITEMS.has(kind))
195
+ this.unknown++;
196
+ return [];
197
+ }
198
+ }
199
+ call(id, rawName, input, at) {
200
+ const name = typeof rawName === 'string' ? this.redact(rawName) : '(이름 없음)';
201
+ return toolCallEvent(id, name, input, at, this.options);
202
+ }
203
+ redact(text) {
204
+ return redactText(text, this.options);
205
+ }
206
+ }
207
+ /** function_call의 arguments는 JSON 글자다. 객체로 읽히면 키 이름 가리기도 타게 객체로 넘긴다 */
208
+ function parseArguments(raw) {
209
+ if (typeof raw !== 'string')
210
+ return raw ?? {};
211
+ try {
212
+ return JSON.parse(raw);
213
+ }
214
+ catch {
215
+ return raw;
216
+ }
217
+ }
218
+ /**
219
+ * 도구 결과 글자. 글자이거나 `{content}` 객체이거나 `{type: 'input_text', text}` 블록 배열이다.
220
+ * 이미지 블록은 표시만 남긴다.
221
+ */
222
+ function outputText(output) {
223
+ if (typeof output === 'string')
224
+ return output;
225
+ if (isRecord(output))
226
+ return outputText(output['content']);
227
+ if (!Array.isArray(output))
228
+ return '';
229
+ const parts = [];
230
+ for (const block of output) {
231
+ if (!isRecord(block))
232
+ continue;
233
+ if (typeof block['text'] === 'string')
234
+ parts.push(block['text']);
235
+ else if (block['type'] === 'input_image')
236
+ parts.push('[이미지]');
237
+ }
238
+ return parts.join('');
239
+ }
240
+ //# sourceMappingURL=codex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.js","sourceRoot":"","sources":["../../../src/peer/codex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAE1E;;;;;;;;GAQG;AAEH,sDAAsD;AACtD,MAAM,UAAU,SAAS,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC5D,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC;IAC9B,IAAI,GAAG,IAAI,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IACvC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AACnC,CAAC;AAED,uDAAuD;AACvD,MAAM,SAAS,GAAG,iCAAiC,CAAC;AAMpD;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,KAAa,EACb,IAAI,GAAG,SAAS,EAAE,EAClB,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAEnB,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC7D,OAAO,MAAM,CAAC,IAAI,CAAC;YACjB,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE;YAC1B,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC;IAC9D,CAAC;IACD,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,oCAAoC,EAAE,IAAI,EAAE,CAAC;IAC3E,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACpC,IAAI,KAAe,CAAC;IACpB,IAAI,CAAC;QACH,KAAK,GAAG,WAAW,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,uBAAuB,EAAE,IAAI,EAAE,CAAC;IAC9D,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,KAAK,QAAQ,CAAC;IACjC,MAAM,IAAI,GAAG,KAAK;SACf,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;SACvE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;SACzB,MAAM,CAAC,MAAM,CAAC,CAAC;IAClB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,uBAAuB,EAAE,IAAI,EAAE,CAAC;IACnF,oDAAoD;IACpD,IAAI,CAAC,IAAI,EAAE,CAAC;IACZ,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAE,EAAE,IAAI,EAAE,CAAC;AAC1D,CAAC;AAED,SAAS,MAAM,CAAC,IAAY;IAC1B,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,CAAC,CAAC,CAAC;AAE5F;;;GAGG;AACH,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC;IACnC,cAAc;IACd,eAAe;IACf,aAAa;IACb,yBAAyB;IACzB,iBAAiB;IACjB,6BAA6B;IAC7B,+BAA+B;CAChC,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC;AAE9D;;;GAGG;AACH,MAAM,OAAO,gBAAgB;IAKE;IAJrB,OAAO,GAAG,CAAC,CAAC;IACZ,MAAM,GAAG,CAAC,CAAC;IACX,UAAU,CAAqB;IAEvC,YAA6B,OAAyB;QAAzB,YAAO,GAAP,OAAO,CAAkB;IAAG,CAAC;IAE1D,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,oDAAoD;IACpD,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,IAAI,CAAC,IAAY;QACf,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,IAAY;QACxB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,OAAO,KAAK,EAAE;YAAE,OAAO,EAAE,CAAC;QAC9B,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;YAC5F,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAC5B,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;QAClC,MAAM,EAAE,GAAG,OAAO,MAAM,CAAC,WAAW,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAErF,IAAI,IAAI,KAAK,cAAc,IAAI,OAAO,OAAO,CAAC,aAAa,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC1E,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;QAC3C,CAAC;QACD,IAAI,IAAI,KAAK,WAAW;YAAE,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAC7D,IAAI,IAAI,KAAK,eAAe;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAChE,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,IAAI,CAAC,OAAO,EAAE,CAAC;QAC7C,OAAO,EAAE,CAAC;IACZ,CAAC;IAEO,SAAS,CAAC,OAAgC,EAAE,EAAsB;QACxE,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QAC7B,IAAI,IAAI,KAAK,cAAc,IAAI,IAAI,KAAK,eAAe,EAAE,CAAC;YACxD,MAAM,GAAG,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;YAC/B,IAAI,OAAO,GAAG,KAAK,QAAQ;gBAAE,OAAO,EAAE,CAAC;YACvC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;YACrC,IAAI,IAAI,KAAK,EAAE;gBAAE,OAAO,EAAE,CAAC;YAC3B,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC5E,CAAC;QACD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,IAAI,CAAC,OAAO,EAAE,CAAC;QAChF,OAAO,EAAE,CAAC;IACZ,CAAC;IAEO,QAAQ,CAAC,OAAgC,EAAE,EAAsB;QACvE,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;QAC7B,MAAM,MAAM,GAAG,OAAO,OAAO,CAAC,SAAS,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAChF,QAAQ,IAAI,EAAE,CAAC;YACb,8DAA8D;YAC9D,wBAAwB;YACxB,KAAK,eAAe;gBAClB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,cAAc,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YACxF,KAAK,kBAAkB;gBACrB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;YAC1E,KAAK,kBAAkB,CAAC,CAAC,CAAC;gBACxB,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACpE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;YAChF,CAAC;YACD,KAAK,sBAAsB,CAAC;YAC5B,KAAK,yBAAyB,CAAC,CAAC,CAAC;gBAC/B,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;gBACjC,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,KAAK,CAAC;gBAC/D,OAAO,CAAC,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;YACvF,CAAC;YACD;gBACE,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC;oBAAE,IAAI,CAAC,OAAO,EAAE,CAAC;gBAC/E,OAAO,EAAE,CAAC;QACd,CAAC;IACH,CAAC;IAEO,IAAI,CAAC,EAAU,EAAE,OAAgB,EAAE,KAAc,EAAE,EAAsB;QAC/E,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5E,OAAO,aAAa,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1D,CAAC;IAEO,MAAM,CAAC,IAAY;QACzB,OAAO,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACxC,CAAC;CACF;AAED,uEAAuE;AACvE,SAAS,cAAc,CAAC,GAAY;IAClC,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,IAAI,EAAE,CAAC;IAC9C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC;IACb,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,UAAU,CAAC,MAAe;IACjC,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC;IAC9C,IAAI,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;IAC3D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAC;IACtC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QAC/B,IAAI,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,QAAQ;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;aAC5D,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,aAAa;YAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { PeerRequest, PeerResponse } from './protocol.js';
1
+ import type { PeerRequest, PeerResponse, PendingWatcher } from './protocol.js';
2
2
  import type { PeerNetworkOptions } from './network.js';
3
3
  export interface PeerDaemonOptions {
4
4
  root: string;
@@ -10,6 +10,10 @@ export interface PeerDaemonOptions {
10
10
  home?: string;
11
11
  /** 릴레이에 다시 붙는 간격 같은 것. 테스트가 줄인다 */
12
12
  network?: PeerNetworkOptions['transport'];
13
+ /** 첫 접속 승인을 기다리는 시한. 테스트가 줄인다 */
14
+ approvalTtlMs?: number;
15
+ /** 승인 대기를 알리는 시간. 이 동안은 긴 안내 문구를 붙이고 지나면 짧은 한 줄로 바꾼다. 테스트가 줄인다 */
16
+ approvalNoticeMs?: number;
13
17
  }
14
18
  /**
15
19
  * `gestalt peer daemon`. 공유를 들고 대화 기록을 따라 읽어 로컬 뷰어로 흘린다. 팀에
@@ -36,6 +40,15 @@ export declare class PeerDaemon {
36
40
  /** listen할 임시 이름. 권한을 좁힌 뒤 진짜 이름으로 링크한다 */
37
41
  private readonly tmpSock;
38
42
  private readonly pollMs;
43
+ /** 훅 상태 파일을 다시 쓰기로 예약했는지. 한 틱 안의 바뀜을 한 번에 쓴다 */
44
+ private watchingQueued;
45
+ /** 세션마다 마지막으로 쓴 훅 출력. 같으면 다시 안 쓴다 */
46
+ private watchingWritten;
47
+ /**
48
+ * 이 프로세스가 시작한 시각을 ps로 읽은 글자. 훅이 같은 pid의 시작 시각과 맞춰 봐서 데몬이 죽은 뒤
49
+ * 그 pid를 받은 다른 프로세스를 데몬으로 믿지 않게 한다. ps를 못 부르면 빈 글자다
50
+ */
51
+ private readonly startStamp;
39
52
  constructor(options: PeerDaemonOptions);
40
53
  get socketPath(): string;
41
54
  get viewerPort(): number | undefined;
@@ -51,13 +64,39 @@ export declare class PeerDaemon {
51
64
  */
52
65
  private closeShareSafely;
53
66
  handle(req: PeerRequest): PeerResponse | Promise<PeerResponse>;
67
+ /** kick, approve, deny. share가 없으면 열린 공유 전부에 건다 */
68
+ private control;
69
+ private config;
70
+ /**
71
+ * ges_peer ask_user. 그 세션 id로 연 Claude Code 공유를 찾아 지금 보는 팀원에게 질문을 보낸다.
72
+ * 메시지를 끈 공유는 고르지 않는다. 답이 메시지와 같은 길로 들어가는데 주인이 그 길을 막았다.
73
+ * 질문 글자는 이 머신을 떠나므로 대화 기록과 같은 가리기를 건다.
74
+ */
75
+ private ask;
54
76
  private withNetwork;
55
77
  private withNetworkAsync;
78
+ /**
79
+ * 팀원이 보낸 메시지나 서명을 확인한 답을 주인 세션 인박스에 쓴다. 들어간 게 대화 기록에 보이면
80
+ * 대화 흐름에 끼우고 onSeen을 부른다. 지켜보는 일은 쓰기 전에 걸어야 쓰자마자 생긴 줄을 놓치지 않는다.
81
+ */
82
+ private deliverMessage;
56
83
  private viewerUrl;
57
84
  private announceShares;
58
85
  private openShare;
59
86
  private closeShare;
60
87
  private info;
88
+ private scheduleWatching;
89
+ /**
90
+ * 보는 중 표시 훅이 읽을 세션별 상태 파일을 다시 쓴다. 원격으로 보는 사람이나 승인을 기다리는
91
+ * 사람이 있는 세션만 파일이 있다. 훅은 파일이 있으면 적힌 데몬이 살아 있는 한 그 내용을 그대로
92
+ * 내보낸다. 아무도 없는 세션은 파일을 지워서 끊긴 사람이 다음 프롬프트에 계속 보이지 않게 한다.
93
+ * `--transcript`로 연 공유는 세션 id가 없어서 표시하지 않는다.
94
+ *
95
+ * 마지막에 쓴 내용을 세션마다 기억해 같은 내용이면 건너뛰고 쓰기와 지우기는 파일마다 따로 실패를
96
+ * 처리한다. 디렉토리를 못 만들면 그 차례는 아무것도 안 쓴다.
97
+ */
98
+ private writeWatching;
99
+ private clearWatching;
61
100
  private status;
62
101
  /**
63
102
  * 남은 소켓 파일을 치운다. 누가 받고 있으면 데몬이 이미 떠 있는 거라 던진다.
@@ -80,4 +119,16 @@ export declare class PeerDaemon {
80
119
  private serve;
81
120
  private answer;
82
121
  }
122
+ /**
123
+ * 훅이 주인 세션 모델에 붙일 글자. 이름은 명단에서 온 값이지만 모델 컨텍스트로 들어가므로 한 줄
124
+ * 라벨로 정리하고 이름 꼴이 아니면 뺀다. 승인 명령은 모델이 직접 치지 않게 적는다. 주인이
125
+ * 받아 주는 자리를 모델이 대신 채우면 승인이 뜻이 없다.
126
+ */
127
+ export declare function watchingContext(watchers: string[], pending: PendingWatcher[]): string;
128
+ /**
129
+ * pid의 시작 시각을 ps로 읽어 공백을 뺀 글자로. 훅(`scripts/peer-watching-hook.sh`)이 같은 명령으로
130
+ * 읽어 맞춰 본다. 로캘과 시간대에 따라 글자가 달라지지 않게 LC_ALL=C, TZ=UTC로 부른다. 데몬과 세션이
131
+ * 다른 TZ로 떠도 같은 프로세스면 같은 글자가 나와야 한다.
132
+ */
133
+ export declare function processStartStamp(pid: number): string;
83
134
  //# sourceMappingURL=daemon.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../../src/peer/daemon.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAgB,WAAW,EAAE,YAAY,EAAa,MAAM,eAAe,CAAC;AAQxF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAGvD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sBAAsB;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,OAAO,CAAC,EAAE,kBAAkB,CAAC,WAAW,CAAC,CAAC;CAC3C;AAsBD;;;;;;GAMG;AACH,qBAAa,UAAU;IAkBT,OAAO,CAAC,QAAQ,CAAC,OAAO;IAjBpC,OAAO,CAAC,GAAG,CAAuB;IAClC,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,KAAK,CAA0B;IACvC,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,GAAG,CAA2B;IACtC,OAAO,CAAC,IAAI,CAA2B;IACvC,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,KAAK,CAAqB;IAClC,iDAAiD;IACjD,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,2CAA2C;IAC3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEH,OAAO,EAAE,iBAAiB;IAevD,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,IAAI,UAAU,IAAI,MAAM,GAAG,SAAS,CAEnC;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA6CtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAiC3B,oCAAoC;IACpC,IAAI,CAAC,GAAG,SAAa,GAAG,IAAI;IAyB5B;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAQxB,MAAM,CAAC,GAAG,EAAE,WAAW,GAAG,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;IAuD9D,OAAO,CAAC,WAAW;YASL,gBAAgB;IAS9B,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,SAAS;IA8DjB,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,IAAI;IAuBZ,OAAO,CAAC,MAAM;IAed;;;;;;;OAOG;YACW,gBAAgB;IAkB9B;;;;;;OAMG;IACH,OAAO,CAAC,SAAS;IA+BjB,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,KAAK;YAoCC,MAAM;CAoBrB"}
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../../src/peer/daemon.ts"],"names":[],"mappings":"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"}