adde-acp 0.1.3

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 (136) hide show
  1. package/LICENSE +21 -0
  2. package/README.ko.md +88 -0
  3. package/README.md +88 -0
  4. package/dist/backend/acp/client.d.ts +149 -0
  5. package/dist/backend/acp/client.js +538 -0
  6. package/dist/backend/acp/client.js.map +1 -0
  7. package/dist/backend/acp/index.d.ts +8 -0
  8. package/dist/backend/acp/index.js +7 -0
  9. package/dist/backend/acp/index.js.map +1 -0
  10. package/dist/backend/acp/lifecycle.d.ts +15 -0
  11. package/dist/backend/acp/lifecycle.js +56 -0
  12. package/dist/backend/acp/lifecycle.js.map +1 -0
  13. package/dist/backend/acp/perm-diff.d.ts +37 -0
  14. package/dist/backend/acp/perm-diff.js +58 -0
  15. package/dist/backend/acp/perm-diff.js.map +1 -0
  16. package/dist/backend/acp/spawn.d.ts +20 -0
  17. package/dist/backend/acp/spawn.js +70 -0
  18. package/dist/backend/acp/spawn.js.map +1 -0
  19. package/dist/cli/adde.d.ts +2 -0
  20. package/dist/cli/adde.js +11 -0
  21. package/dist/cli/adde.js.map +1 -0
  22. package/dist/cli/alias.d.ts +45 -0
  23. package/dist/cli/alias.js +94 -0
  24. package/dist/cli/alias.js.map +1 -0
  25. package/dist/cli/completion.d.ts +4 -0
  26. package/dist/cli/completion.js +209 -0
  27. package/dist/cli/completion.js.map +1 -0
  28. package/dist/cli/init.d.ts +3 -0
  29. package/dist/cli/init.js +114 -0
  30. package/dist/cli/init.js.map +1 -0
  31. package/dist/cli/lane.d.ts +20 -0
  32. package/dist/cli/lane.js +350 -0
  33. package/dist/cli/lane.js.map +1 -0
  34. package/dist/cli/ops.d.ts +5 -0
  35. package/dist/cli/ops.js +230 -0
  36. package/dist/cli/ops.js.map +1 -0
  37. package/dist/cli/prompt.d.ts +15 -0
  38. package/dist/cli/prompt.js +41 -0
  39. package/dist/cli/prompt.js.map +1 -0
  40. package/dist/cli/run.d.ts +5 -0
  41. package/dist/cli/run.js +216 -0
  42. package/dist/cli/run.js.map +1 -0
  43. package/dist/cli/spec.d.ts +48 -0
  44. package/dist/cli/spec.js +98 -0
  45. package/dist/cli/spec.js.map +1 -0
  46. package/dist/core/diagnostics.d.ts +73 -0
  47. package/dist/core/diagnostics.js +333 -0
  48. package/dist/core/diagnostics.js.map +1 -0
  49. package/dist/core/index.d.ts +11 -0
  50. package/dist/core/index.js +9 -0
  51. package/dist/core/index.js.map +1 -0
  52. package/dist/core/injector.d.ts +27 -0
  53. package/dist/core/injector.js +297 -0
  54. package/dist/core/injector.js.map +1 -0
  55. package/dist/core/lane-config.d.ts +80 -0
  56. package/dist/core/lane-config.js +303 -0
  57. package/dist/core/lane-config.js.map +1 -0
  58. package/dist/core/launchd.d.ts +81 -0
  59. package/dist/core/launchd.js +216 -0
  60. package/dist/core/launchd.js.map +1 -0
  61. package/dist/core/messages.d.ts +31 -0
  62. package/dist/core/messages.js +71 -0
  63. package/dist/core/messages.js.map +1 -0
  64. package/dist/core/queue.d.ts +74 -0
  65. package/dist/core/queue.js +227 -0
  66. package/dist/core/queue.js.map +1 -0
  67. package/dist/core/runtime-state.d.ts +52 -0
  68. package/dist/core/runtime-state.js +90 -0
  69. package/dist/core/runtime-state.js.map +1 -0
  70. package/dist/core/session-ledger.d.ts +25 -0
  71. package/dist/core/session-ledger.js +89 -0
  72. package/dist/core/session-ledger.js.map +1 -0
  73. package/dist/core/supervisor.d.ts +41 -0
  74. package/dist/core/supervisor.js +315 -0
  75. package/dist/core/supervisor.js.map +1 -0
  76. package/dist/core/transcript.d.ts +22 -0
  77. package/dist/core/transcript.js +93 -0
  78. package/dist/core/transcript.js.map +1 -0
  79. package/dist/core/update-check.d.ts +25 -0
  80. package/dist/core/update-check.js +142 -0
  81. package/dist/core/update-check.js.map +1 -0
  82. package/dist/core/version.d.ts +7 -0
  83. package/dist/core/version.js +32 -0
  84. package/dist/core/version.js.map +1 -0
  85. package/dist/gate/gate.d.ts +41 -0
  86. package/dist/gate/gate.js +28 -0
  87. package/dist/gate/gate.js.map +1 -0
  88. package/dist/gate/index.d.ts +6 -0
  89. package/dist/gate/index.js +6 -0
  90. package/dist/gate/index.js.map +1 -0
  91. package/dist/shared/conf.d.ts +54 -0
  92. package/dist/shared/conf.js +85 -0
  93. package/dist/shared/conf.js.map +1 -0
  94. package/dist/shared/deny-match.d.ts +19 -0
  95. package/dist/shared/deny-match.js +122 -0
  96. package/dist/shared/deny-match.js.map +1 -0
  97. package/dist/shared/envelope.d.ts +37 -0
  98. package/dist/shared/envelope.js +91 -0
  99. package/dist/shared/envelope.js.map +1 -0
  100. package/dist/shared/errors.d.ts +8 -0
  101. package/dist/shared/errors.js +23 -0
  102. package/dist/shared/errors.js.map +1 -0
  103. package/dist/shared/fs-atomic.d.ts +17 -0
  104. package/dist/shared/fs-atomic.js +31 -0
  105. package/dist/shared/fs-atomic.js.map +1 -0
  106. package/dist/shared/i18n.d.ts +23 -0
  107. package/dist/shared/i18n.js +53 -0
  108. package/dist/shared/i18n.js.map +1 -0
  109. package/dist/shared/locales/en.d.ts +393 -0
  110. package/dist/shared/locales/en.js +447 -0
  111. package/dist/shared/locales/en.js.map +1 -0
  112. package/dist/shared/locales/ko.d.ts +389 -0
  113. package/dist/shared/locales/ko.js +443 -0
  114. package/dist/shared/locales/ko.js.map +1 -0
  115. package/dist/shared/mask.d.ts +6 -0
  116. package/dist/shared/mask.js +28 -0
  117. package/dist/shared/mask.js.map +1 -0
  118. package/dist/shared/notify.d.ts +15 -0
  119. package/dist/shared/notify.js +20 -0
  120. package/dist/shared/notify.js.map +1 -0
  121. package/dist/shared/paths.d.ts +42 -0
  122. package/dist/shared/paths.js +83 -0
  123. package/dist/shared/paths.js.map +1 -0
  124. package/dist/src-adapters/index.d.ts +8 -0
  125. package/dist/src-adapters/index.js +6 -0
  126. package/dist/src-adapters/index.js.map +1 -0
  127. package/dist/src-adapters/markdown.d.ts +80 -0
  128. package/dist/src-adapters/markdown.js +794 -0
  129. package/dist/src-adapters/markdown.js.map +1 -0
  130. package/dist/src-adapters/source.d.ts +33 -0
  131. package/dist/src-adapters/source.js +3 -0
  132. package/dist/src-adapters/source.js.map +1 -0
  133. package/dist/src-adapters/telegram.d.ts +48 -0
  134. package/dist/src-adapters/telegram.js +412 -0
  135. package/dist/src-adapters/telegram.js.map +1 -0
  136. package/package.json +62 -0
@@ -0,0 +1,89 @@
1
+ /**
2
+ * 세션 장부(state/<lane>/sessions.json) — /resume 목록·마지막 대화 시각의 SSOT.
3
+ * ACP session/list 는 어댑터 구현이 미확인이고 엔진 무관성이 필요해 ADDE 가 자체 관리한다.
4
+ * 갱신 주체는 직렬(injector·기동 시 supervisor)이라 파일 락 없이 atomic rewrite 로 충분.
5
+ */
6
+ import { readFile } from "node:fs/promises";
7
+ import { atomicWrite } from "../shared/fs-atomic.js";
8
+ /** 장부 보존 상한 — 초과 시 마지막 대화가 오래된 항목부터 회전 제거. */
9
+ const MAX_ENTRIES = 20;
10
+ /** 장부 읽기 — 부재·파손은 빈 장부(보조 데이터, fail-open). */
11
+ export async function readLedger(paths) {
12
+ try {
13
+ const raw = JSON.parse(await readFile(paths.sessionsFile, "utf8"));
14
+ if (!Array.isArray(raw))
15
+ return [];
16
+ return raw.filter((e) => typeof e === "object" &&
17
+ e !== null &&
18
+ typeof e.id === "string" &&
19
+ typeof e.createdAt === "string" &&
20
+ typeof e.lastActivityAt === "string");
21
+ }
22
+ catch {
23
+ return [];
24
+ }
25
+ }
26
+ async function writeLedger(paths, entries) {
27
+ const sorted = [...entries]
28
+ .sort((a, b) => b.lastActivityAt.localeCompare(a.lastActivityAt))
29
+ .slice(0, MAX_ENTRIES);
30
+ await atomicWrite(paths.sessionsFile, JSON.stringify(sorted, null, 2) + "\n");
31
+ }
32
+ /** 세션 목록의 시각 표기 — 로컬 `MM-DD HH:mm`(목록 가독 우선, 정밀 시각은 장부 파일에 보존). */
33
+ export function formatWhen(iso) {
34
+ const d = new Date(iso);
35
+ if (Number.isNaN(d.getTime()))
36
+ return iso;
37
+ const p = (n) => String(n).padStart(2, "0");
38
+ return `${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`;
39
+ }
40
+ /** 세션 id 허용 문자셋 — envelope 검증(CHANNEL_MSG_ID_RE)과 동일 계약(주입 방어). */
41
+ const SESSION_ID_RE = /^[A-Za-z0-9_:-]+$/;
42
+ /**
43
+ * resume 인자 → ControlRequest 해석(채널 공통). 무인자=목록(sessions), 숫자=장부 최신순
44
+ * 번호, 그 외=세션 id 직접 지정(문자셋 위반·번호 범위 밖은 sessionId 미지정 — 수신측이
45
+ * "재개할 세션 없음" 통지, fail-closed).
46
+ */
47
+ export function resolveResumeControl(arg, entries) {
48
+ if (!arg)
49
+ return { kind: "sessions" };
50
+ if (/^\d+$/.test(arg)) {
51
+ const entry = entries[parseInt(arg, 10) - 1];
52
+ return entry ? { kind: "resume", sessionId: entry.id } : { kind: "resume" };
53
+ }
54
+ return SESSION_ID_RE.test(arg) ? { kind: "resume", sessionId: arg } : { kind: "resume" };
55
+ }
56
+ /** 세션 생성/복귀 시 upsert — 기존 항목이면 lastActivityAt 만 갱신. */
57
+ export async function recordSession(paths, id) {
58
+ const now = new Date().toISOString();
59
+ const entries = await readLedger(paths);
60
+ const existing = entries.find((e) => e.id === id);
61
+ if (existing) {
62
+ existing.lastActivityAt = now;
63
+ }
64
+ else {
65
+ entries.push({ id, createdAt: now, lastActivityAt: now });
66
+ }
67
+ await writeLedger(paths, entries);
68
+ }
69
+ /** 턴 종료 시 마지막 대화 시각 갱신(+미기재 label 이면 발췌 기록). 항목 부재 시 생성. */
70
+ export async function touchSession(paths, id, labelIfEmpty) {
71
+ const now = new Date().toISOString();
72
+ const entries = await readLedger(paths);
73
+ const existing = entries.find((e) => e.id === id);
74
+ if (existing) {
75
+ existing.lastActivityAt = now;
76
+ if (labelIfEmpty && !existing.label)
77
+ existing.label = labelIfEmpty;
78
+ }
79
+ else {
80
+ entries.push({
81
+ id,
82
+ createdAt: now,
83
+ lastActivityAt: now,
84
+ ...(labelIfEmpty ? { label: labelIfEmpty } : {}),
85
+ });
86
+ }
87
+ await writeLedger(paths, entries);
88
+ }
89
+ //# sourceMappingURL=session-ledger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-ledger.js","sourceRoot":"","sources":["../../src/core/session-ledger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import type { AcpBackend } from "../backend/acp/client.js";
2
+ /** 런타임 ACP 어댑터 바이너리 경로. package.json 의 bin 항목을 SoT 로 해석. */
3
+ export declare function resolveAdapterBin(): string;
4
+ /** 레인 기동 상태 결과. */
5
+ export interface LaneStatus {
6
+ lane: string;
7
+ status: "running" | "error" | "stopped";
8
+ /** status==="error" 일 때 실패 사유(사용자 안내·doctor 유도용). */
9
+ error?: string;
10
+ }
11
+ /** supervisorUp 반환값. */
12
+ export interface SupervisorUpResult {
13
+ lanes: LaneStatus[];
14
+ message: string;
15
+ }
16
+ /** supervisorDown 반환값. */
17
+ export interface SupervisorDownResult {
18
+ lanes: LaneStatus[];
19
+ message: string;
20
+ }
21
+ /**
22
+ * ACP 백엔드 팩토리 — 테스트에서 주입 가능하도록 의존을 분리.
23
+ * (lane: string, adapterBin: string) → AcpBackend.
24
+ * 기본값: new AcpBackendImpl(adapterBin).
25
+ */
26
+ export type AcpFactory = (lane: string, adapterBin: string) => AcpBackend;
27
+ export interface SupervisorUpOptions {
28
+ base?: string;
29
+ acpFactory?: AcpFactory;
30
+ }
31
+ export interface SupervisorDownOptions {
32
+ base?: string;
33
+ }
34
+ /**
35
+ * `adde up <proj>` — lanes.d 의 conf 파일 스캔 → 레인별 기동.
36
+ */
37
+ export declare function supervisorUp(proj: string, opts?: SupervisorUpOptions): Promise<SupervisorUpResult>;
38
+ /**
39
+ * `adde down <proj>` — 프로젝트의 모든 레인 종료.
40
+ */
41
+ export declare function supervisorDown(proj: string, _opts?: SupervisorDownOptions): Promise<SupervisorDownResult>;
@@ -0,0 +1,315 @@
1
+ /**
2
+ * 레인 라이프사이클 수퍼바이저.
3
+ * lanes.d conf 스캔 → 레인별 기동·헬스.
4
+ * adde up → source/injector/backend/gate 인스턴스화 + 기동.
5
+ * adde down → 레인 프로세스 종료.
6
+ */
7
+ import { readdir, readFile, mkdir } from "node:fs/promises";
8
+ import { errMsg } from "../shared/errors.js";
9
+ import { join, resolve } from "node:path";
10
+ import { createRequire } from "node:module";
11
+ import { fileURLToPath } from "node:url";
12
+ import { parseLaneConf } from "../shared/conf.js";
13
+ import { lanePaths, defaultBase, expandTilde } from "../shared/paths.js";
14
+ import { secureLaneDirs } from "../shared/fs-atomic.js";
15
+ import { parseCsv, resolveFileMode } from "./lane-config.js";
16
+ import { AcpBackendImpl } from "../backend/acp/client.js";
17
+ import { createInjector } from "./injector.js";
18
+ import { recordSession } from "./session-ledger.js";
19
+ import { createTelegramSource, createMarkdownSource } from "../src-adapters/index.js";
20
+ import { selfAuthorizedChatId } from "../src-adapters/telegram.js";
21
+ import { gateRequestDecision } from "../gate/gate.js";
22
+ import { formatWarnNote, formatException } from "../shared/notify.js";
23
+ import { maskSecrets } from "../shared/mask.js";
24
+ import { t, tFor } from "../shared/i18n.js";
25
+ import { writeRuntime, removeRuntime, touchRuntime, readRuntime, isPidAlive, HEARTBEAT_INTERVAL_MS, } from "./runtime-state.js";
26
+ /** 런타임 ACP 어댑터 바이너리 경로. package.json 의 bin 항목을 SoT 로 해석. */
27
+ export function resolveAdapterBin() {
28
+ const require = createRequire(import.meta.url);
29
+ try {
30
+ const pkgPath = require.resolve("@zed-industries/claude-code-acp/package.json");
31
+ const dir = pkgPath.slice(0, pkgPath.lastIndexOf("/package.json"));
32
+ const pkg = require(pkgPath);
33
+ const binRel = typeof pkg.bin === "string" ? pkg.bin : pkg.bin?.["claude-code-acp"];
34
+ if (binRel)
35
+ return resolve(dir, binRel);
36
+ }
37
+ catch {
38
+ // 폴백(.bin shim)으로 진행
39
+ }
40
+ const thisDir = fileURLToPath(new URL(".", import.meta.url));
41
+ return resolve(thisDir, "../../../node_modules/.bin/claude-code-acp");
42
+ }
43
+ const activeLanes = new Map();
44
+ /** proj 별 하트비트 타이머 — up 이 등록, down 이 정리. */
45
+ const heartbeats = new Map();
46
+ /**
47
+ * proj 의 하트비트 타이머를 (재)설정한다. 기존 타이머는 먼저 정리(re-up 대비).
48
+ * 각 틱에 running 레인의 runtime.json mtime 만 touch(메타데이터 쓰기). `.unref()` 로
49
+ * 타이머 단독으로 프로세스를 살려두지 않는다(상주는 호출부 never-resolve promise 담당).
50
+ */
51
+ function armHeartbeat(proj, handles) {
52
+ const existing = heartbeats.get(proj);
53
+ if (existing)
54
+ clearInterval(existing);
55
+ if (handles.length === 0) {
56
+ heartbeats.delete(proj);
57
+ return;
58
+ }
59
+ const timer = setInterval(() => {
60
+ for (const h of handles) {
61
+ // 하트비트는 보조 신호 — touch 실패는 warn 후 흡수(레인 동작에 영향 없음).
62
+ void touchRuntime(h.paths).catch((err) => console.warn(t("log.supervisor.heartbeatFail", { lane: h.lane, error: errMsg(err) })));
63
+ }
64
+ }, HEARTBEAT_INTERVAL_MS);
65
+ timer.unref();
66
+ heartbeats.set(proj, timer);
67
+ }
68
+ /** proj 하트비트 타이머 정리(down·셧다운). */
69
+ function disarmHeartbeat(proj) {
70
+ const timer = heartbeats.get(proj);
71
+ if (timer)
72
+ clearInterval(timer);
73
+ heartbeats.delete(proj);
74
+ }
75
+ /**
76
+ * `adde up <proj>` — lanes.d 의 conf 파일 스캔 → 레인별 기동.
77
+ */
78
+ export async function supervisorUp(proj, opts) {
79
+ const baseDir = opts?.base ?? defaultBase();
80
+ const projDir = join(baseDir, proj);
81
+ const lanesDir = join(projDir, "lanes.d");
82
+ await mkdir(lanesDir, { recursive: true });
83
+ let confFiles;
84
+ try {
85
+ confFiles = (await readdir(lanesDir)).filter((f) => f.endsWith(".conf"));
86
+ }
87
+ catch {
88
+ confFiles = [];
89
+ }
90
+ if (confFiles.length === 0) {
91
+ console.log(t("log.supervisor.noConf", { proj }));
92
+ return { lanes: [], message: t("supervisor.noLanesMsg", { proj }) };
93
+ }
94
+ const adapterBin = resolveAdapterBin();
95
+ const handles = [];
96
+ const results = [];
97
+ for (const confFile of confFiles) {
98
+ const lane = confFile.replace(/\.conf$/, "");
99
+ const confPath = join(lanesDir, confFile);
100
+ const confText = await readFile(confPath, "utf8");
101
+ const conf = parseLaneConf(confText);
102
+ // 사용자 입력 경로의 ~ 확장 (Node 는 자동 확장 안 함).
103
+ if (conf.cwd)
104
+ conf.cwd = expandTilde(conf.cwd);
105
+ if (conf.root)
106
+ conf.root = expandTilde(conf.root);
107
+ const paths = lanePaths(baseDir, proj, lane);
108
+ // 상태·출력·큐 디렉터리 권한 잠금(private=0700 / shared=no-op). chmod 실패는 보조
109
+ // 하드닝 신호 — warn 후 흡수(기동 자체는 진행, 권한 미적용은 로그로 가시화).
110
+ await secureLaneDirs([paths.stateDir, paths.outDir, paths.queueDir, paths.processingDir, paths.lanesDir], resolveFileMode(conf.file_mode)).catch((err) => console.warn(t("log.supervisor.securePermsFail", { lane, error: errMsg(err) })));
111
+ // 중복 기동 가드 — backend 생성 전 runtime.json + pid 생존 확인.
112
+ const existingRuntime = await readRuntime(paths);
113
+ if (existingRuntime !== null) {
114
+ if (isPidAlive(existingRuntime.pid)) {
115
+ // 이미 running — 경고+스킵.
116
+ process.stderr.write(t("supervisor.alreadyRunning", { lane, pid: existingRuntime.pid, proj }) + "\n");
117
+ results.push({ lane, status: "running" });
118
+ continue;
119
+ }
120
+ else {
121
+ // dead 레인 — 크래시 잔존 runtime.json 정리 후 정상 기동.
122
+ // 자식 pid 는 runtime.json 에 미기록(스키마 한계) — removeRuntime 으로 파일만 정리.
123
+ await removeRuntime(paths).catch((err) => console.warn(t("log.supervisor.deadCleanupFail", { lane, error: errMsg(err) })));
124
+ }
125
+ }
126
+ const channel = conf.source === "markdown" ? "markdown" : "telegram";
127
+ // 권한 경고(perm-diff 등)를 채널로도 표면화 — source 는 아래에서 생성되므로 지연 참조.
128
+ // 알림은 보조 신호: 전송 실패는 warn 후 흡수(레인 동작에 영향 없음).
129
+ const channelWarn = (msg) => {
130
+ void source
131
+ .notify(msg)
132
+ .catch((err) => console.warn(t("log.supervisor.channelWarnFail", { lane, error: errMsg(err) })));
133
+ };
134
+ let backend;
135
+ if (opts?.acpFactory) {
136
+ backend = opts.acpFactory(lane, adapterBin);
137
+ }
138
+ else {
139
+ const impl = new AcpBackendImpl(adapterBin);
140
+ impl.configureLane(lane, {
141
+ paths,
142
+ addePolicy: {
143
+ perm_tier: conf.perm_tier,
144
+ allowlist: conf.allowlist,
145
+ denylist: conf.denylist,
146
+ hard_deny: conf.hard_deny,
147
+ },
148
+ cwd: conf.cwd,
149
+ channel,
150
+ channelWarn,
151
+ lang: conf.lang,
152
+ });
153
+ backend = impl;
154
+ }
155
+ const pendingDecisions = new Map();
156
+ const engine = conf.engine || "claude";
157
+ let source;
158
+ // injector 를 source 보다 먼저 생성 — render 는 source 를 지연 참조(closure, turn 종료 시 호출).
159
+ // in-process 배선: source.onInbound → injector.notify, injector.render → source.renderOut.
160
+ // 주입 실패도 채널로 표면화(onFail → source.notify) — 채널 언어(레인 로케일)로 렌더.
161
+ const laneT = tFor(conf.lang);
162
+ const injector = createInjector(paths, lane, backend, (id) => source.renderOut(id), (id, detail) =>
163
+ // 채널 egress 는 마스킹 일관 적용 — 엔진 예외 메시지에 시크릿이 섞일 수 있다
164
+ // (.failed/콘솔 등 로컬 경로는 기존대로 원문 유지).
165
+ source.notify(formatException({
166
+ situation: laneT("injector.failNote.situation", { id, detail: maskSecrets(detail) }),
167
+ action: laneT("injector.failNote.action"),
168
+ }, laneT)), laneT);
169
+ const onInbound = () => injector.notify();
170
+ if (conf.source === "markdown") {
171
+ source = createMarkdownSource({ lane, proj, engine, paths, conf, onInbound });
172
+ }
173
+ else {
174
+ const chatId = conf.chat_id && !Number.isNaN(Number(conf.chat_id)) ? Number(conf.chat_id) : undefined;
175
+ // 인바운드/콜백 허용 발신자 = allow_from ∪ (개인 chat 인 chatId). 비면 어댑터가 fail-closed.
176
+ // 음수 chatId(그룹)는 인증 앵커 아님 — 멤버는 allow_from 으로만 인증(어댑터 병합 규칙과 동일).
177
+ const authorizedIds = [];
178
+ const selfAuth = selfAuthorizedChatId(chatId);
179
+ if (selfAuth !== undefined)
180
+ authorizedIds.push(selfAuth);
181
+ for (const raw of conf.allow_from ? parseCsv(conf.allow_from) : []) {
182
+ const n = Number(raw);
183
+ if (!Number.isNaN(n))
184
+ authorizedIds.push(n);
185
+ }
186
+ source = createTelegramSource({
187
+ lane,
188
+ proj,
189
+ engine,
190
+ paths,
191
+ chatId,
192
+ authorizedIds,
193
+ onInbound,
194
+ lang: conf.lang,
195
+ });
196
+ }
197
+ source.onDecision((reqId, decision) => {
198
+ const resolve = pendingDecisions.get(reqId);
199
+ if (resolve) {
200
+ pendingDecisions.delete(reqId);
201
+ resolve(decision);
202
+ }
203
+ });
204
+ try {
205
+ // launch 가 레인 state 를 생성한다 — 구독·권한 핸들러 등록은 launch 이후라야 한다.
206
+ const { sessionId } = await backend.launch(lane);
207
+ // 세션 장부 기록(보조 — /resume 목록·마지막 대화 시각). 실패는 로그 후 흡수.
208
+ await recordSession(paths, sessionId).catch((err) => console.warn(t("log.supervisor.ledgerFail", { lane, error: errMsg(err) })));
209
+ // 엔진 세션 이벤트 → injector(응답 누적). injector 가 turn 종료에 writeOut + renderOut(B).
210
+ backend.subscribe(lane, (e) => injector.onSessionEvent(e));
211
+ backend.onPermissionRequest(lane, async (req) => {
212
+ const waitForDecision = () => new Promise((resolveFn) => {
213
+ pendingDecisions.set(req.id, resolveFn);
214
+ });
215
+ const sendPermPrompt = async () => {
216
+ await source.requestPermission(req);
217
+ };
218
+ try {
219
+ return await gateRequestDecision(req, { sendPermPrompt, waitForDecision });
220
+ }
221
+ finally {
222
+ // 모든 종결 경로(timeout·전송오류·정상결정)에서 대기자 정리 — timeout 시 영구 잔존 누수 제거.
223
+ // 늦게 도착한 콜백은 빈 맵에서 no-op(무해).
224
+ pendingDecisions.delete(req.id);
225
+ }
226
+ });
227
+ // 인젝터 기동은 비차단(첫 inject 가 turn 종료까지 블록될 수 있어 await 하지 않음).
228
+ // fire-and-forget 이므로 rejection 은 unhandled 가 되지 않도록 로깅한다.
229
+ void injector.start().catch((err) => {
230
+ console.error(t("log.supervisor.injectorStartFail", {
231
+ lane,
232
+ error: errMsg(err),
233
+ }));
234
+ });
235
+ source.start();
236
+ // autopass 레인 기동 배너 — 자동 허용 모드임을 채널에 명시(no-silent).
237
+ if (conf.perm_tier === "autopass") {
238
+ const tl = tFor(conf.lang);
239
+ const denyDesc = conf.denylist.length > 0
240
+ ? tl("supervisor.autopassDenySome", { tools: conf.denylist.join(", ") })
241
+ : tl("supervisor.autopassDenyEmpty");
242
+ const banner = formatWarnNote({
243
+ situation: tl("supervisor.autopassBanner.situation", { denyDesc }),
244
+ action: tl("supervisor.autopassBanner.action", { lane, proj }),
245
+ }, tl);
246
+ console.warn(`[supervisor] lane=${lane} ${banner}`);
247
+ channelWarn(banner);
248
+ }
249
+ // 라이브니스 상태 파일 — status 가 교차 프로세스로 읽는다. 기동 성공 후 기록.
250
+ // 기록 실패는 보조(가시성 저하일 뿐 기동 자체는 성공) → warn 후 흡수.
251
+ await writeRuntime(paths, {
252
+ v: 1,
253
+ pid: process.pid,
254
+ lane,
255
+ sessionId,
256
+ startedAt: new Date().toISOString(),
257
+ source: conf.source || channel,
258
+ backend: conf.backend || "acp",
259
+ engine,
260
+ }).catch((err) => console.warn(t("log.supervisor.runtimeWriteFail", { lane, error: errMsg(err) })));
261
+ console.log(`[supervisor] lane=${lane} running`);
262
+ handles.push({
263
+ lane,
264
+ paths,
265
+ // 정지 순서: 소스 먼저(신규 인바운드·turn 차단) → 백엔드 child 정리 → 상태 파일 제거.
266
+ async stop() {
267
+ await source.stop();
268
+ await backend.close(lane);
269
+ await removeRuntime(paths).catch((err) => console.warn(t("log.supervisor.runtimeRemoveFail", { lane, error: errMsg(err) })));
270
+ },
271
+ });
272
+ results.push({ lane, status: "running" });
273
+ }
274
+ catch (err) {
275
+ const reason = errMsg(err);
276
+ console.error(t("log.supervisor.laneStartFail", { lane, reason }));
277
+ results.push({ lane, status: "error", error: reason });
278
+ }
279
+ }
280
+ const existing = activeLanes.get(proj) ?? [];
281
+ const merged = [...existing, ...handles];
282
+ activeLanes.set(proj, merged);
283
+ // 하트비트는 기동된 레인이 있을 때만(merged 전체 대상 — re-up 시 기존+신규 함께 touch).
284
+ armHeartbeat(proj, merged);
285
+ const runningCount = results.filter((r) => r.status === "running").length;
286
+ const newlyStarted = handles.length;
287
+ const skipped = runningCount - newlyStarted;
288
+ const messageParts = [t("supervisor.upStarted", { proj, count: newlyStarted })];
289
+ if (skipped > 0)
290
+ messageParts.push(t("supervisor.upSkipped", { count: skipped }));
291
+ return {
292
+ lanes: results,
293
+ message: messageParts.join(", "),
294
+ };
295
+ }
296
+ /**
297
+ * `adde down <proj>` — 프로젝트의 모든 레인 종료.
298
+ */
299
+ export async function supervisorDown(proj, _opts) {
300
+ const handles = activeLanes.get(proj) ?? [];
301
+ const results = [];
302
+ // 하트비트 먼저 정지 — 종료 중 runtime.json touch/제거 레이스 방지.
303
+ disarmHeartbeat(proj);
304
+ for (const handle of handles) {
305
+ await handle.stop();
306
+ console.log(`[supervisor] lane=${handle.lane} stopped`);
307
+ results.push({ lane: handle.lane, status: "stopped" });
308
+ }
309
+ activeLanes.delete(proj);
310
+ return {
311
+ lanes: results,
312
+ message: t("supervisor.downStopped", { proj, count: results.length }),
313
+ };
314
+ }
315
+ //# sourceMappingURL=supervisor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supervisor.js","sourceRoot":"","sources":["../../src/core/supervisor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { LanePaths } from "../shared/paths.js";
2
+ /**
3
+ * session 이벤트 — ACP SDK sessionUpdate 형식 또는 단순 type/content 형식 모두 수용.
4
+ * ACP SDK: { sessionUpdate: string; ... }
5
+ * Telegram adapter / 간단한 이벤트: { type: string; ... }
6
+ */
7
+ export type SessionEvent = {
8
+ [key: string]: unknown;
9
+ } & ({
10
+ sessionUpdate: string;
11
+ } | {
12
+ type: string;
13
+ });
14
+ /**
15
+ * session/update 이벤트를 사람이 읽을 텍스트 줄로 렌더.
16
+ */
17
+ export declare function renderEvent(event: SessionEvent): string;
18
+ /**
19
+ * transcript.log 에 이벤트를 append.
20
+ * 보조 실패(파일 append 오류)는 warn↑ 로그 후 흡수.
21
+ */
22
+ export declare function appendTranscript(paths: LanePaths, event: SessionEvent): Promise<void>;
@@ -0,0 +1,93 @@
1
+ /**
2
+ * transcript 렌더·append·마스킹.
3
+ * session/update 이벤트를 사람이 읽을 텍스트로 렌더 → mask → append.
4
+ * append 실패는 보조(warn↑ 로그 후 흡수) — error-handling.md 보조 분류.
5
+ */
6
+ import { t } from "../shared/i18n.js";
7
+ import { errMsg } from "../shared/errors.js";
8
+ import { appendFile, mkdir } from "node:fs/promises";
9
+ import { dirname } from "node:path";
10
+ import { maskSecrets } from "../shared/mask.js";
11
+ /** sessionUpdate 키 추출 — 두 형식 모두 지원. */
12
+ function getEventKind(event) {
13
+ if ("sessionUpdate" in event && typeof event["sessionUpdate"] === "string") {
14
+ return event["sessionUpdate"];
15
+ }
16
+ if ("type" in event && typeof event["type"] === "string") {
17
+ return event["type"];
18
+ }
19
+ return "unknown";
20
+ }
21
+ /**
22
+ * session/update 이벤트를 사람이 읽을 텍스트 줄로 렌더.
23
+ */
24
+ export function renderEvent(event) {
25
+ const ts = new Date().toISOString();
26
+ const kind = getEventKind(event);
27
+ switch (kind) {
28
+ case "agent_message_chunk": {
29
+ // ACP SDK 형식: content 가 { type, text } 객체
30
+ // 단순 형식: content 가 string
31
+ const rawContent = event["content"];
32
+ let text;
33
+ if (typeof rawContent === "string") {
34
+ text = rawContent;
35
+ }
36
+ else if (rawContent && typeof rawContent === "object") {
37
+ const c = rawContent;
38
+ text = c.type === "text" ? (c.text ?? "") : "";
39
+ }
40
+ else {
41
+ text = "";
42
+ }
43
+ return `[${ts}] agent: ${text}`;
44
+ }
45
+ case "available_commands_update":
46
+ return t("transcript.commandsUpdated", { ts });
47
+ case "current_mode_update": {
48
+ const mode = event["mode"];
49
+ return `[${ts}] mode_update: ${JSON.stringify(mode)}`;
50
+ }
51
+ case "usage_update": {
52
+ const usage = event["usage"];
53
+ return `[${ts}] usage: ${JSON.stringify(usage)}`;
54
+ }
55
+ case "tool_call": {
56
+ const toolCall = event["toolCall"];
57
+ return `[${ts}] tool_call: ${JSON.stringify(toolCall)}`;
58
+ }
59
+ case "session_info_update": {
60
+ const info = event["sessionInfo"];
61
+ return `[${ts}] session_info: ${JSON.stringify(info)}`;
62
+ }
63
+ default:
64
+ return `[${ts}] ${kind}: ${JSON.stringify(event)}`;
65
+ }
66
+ }
67
+ /**
68
+ * transcript.log 에 이벤트를 append.
69
+ * 보조 실패(파일 append 오류)는 warn↑ 로그 후 흡수.
70
+ */
71
+ export async function appendTranscript(paths, event) {
72
+ const rendered = renderEvent(event);
73
+ const masked = maskSecrets(rendered);
74
+ const line = `${masked}\n`;
75
+ try {
76
+ await mkdir(dirname(paths.transcriptLog), { recursive: true });
77
+ await appendFile(paths.transcriptLog, line, "utf8");
78
+ }
79
+ catch (err) {
80
+ // 감사 이벤트(차단 경고·자동허용)는 소실 시 감사 추적이 불완전해진다 → error 로 승격(E2).
81
+ // 일반 이벤트는 보조 분류로 warn 후 흡수.
82
+ const kind = getEventKind(event);
83
+ const isAudit = kind === "adde_warn" || kind === "adde_auto_allow";
84
+ const detail = errMsg(err);
85
+ if (isAudit) {
86
+ console.error(t("log.transcript.auditAppendFail", { kind, detail }));
87
+ }
88
+ else {
89
+ console.warn(t("log.transcript.appendFail", { detail }));
90
+ }
91
+ }
92
+ }
93
+ //# sourceMappingURL=transcript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcript.js","sourceRoot":"","sources":["../../src/core/transcript.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,mBAAmB,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAYhD,uCAAuC;AACvC,SAAS,YAAY,CAAC,KAAmB;IACvC,IAAI,eAAe,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,eAAe,CAAC,KAAK,QAAQ,EAAE,CAAC;QAC3E,OAAO,KAAK,CAAC,eAAe,CAAC,CAAC;IAChC,CAAC;IACD,IAAI,MAAM,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,QAAQ,EAAE,CAAC;QACzD,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC;IACvB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,KAAmB;IAC7C,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACpC,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IAEjC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,qBAAqB,CAAC,CAAC,CAAC;YAC3B,0CAA0C;YAC1C,0BAA0B;YAC1B,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC;YACpC,IAAI,IAAY,CAAC;YACjB,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;gBACnC,IAAI,GAAG,UAAU,CAAC;YACpB,CAAC;iBAAM,IAAI,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;gBACxD,MAAM,CAAC,GAAG,UAA8C,CAAC;gBACzD,IAAI,GAAG,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACjD,CAAC;iBAAM,CAAC;gBACN,IAAI,GAAG,EAAE,CAAC;YACZ,CAAC;YACD,OAAO,IAAI,EAAE,YAAY,IAAI,EAAE,CAAC;QAClC,CAAC;QACD,KAAK,2BAA2B;YAC9B,OAAO,CAAC,CAAC,4BAA4B,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QACjD,KAAK,qBAAqB,CAAC,CAAC,CAAC;YAC3B,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;YAC3B,OAAO,IAAI,EAAE,kBAAkB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QACxD,CAAC;QACD,KAAK,cAAc,CAAC,CAAC,CAAC;YACpB,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;YAC7B,OAAO,IAAI,EAAE,YAAY,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QACnD,CAAC;QACD,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC;YACnC,OAAO,IAAI,EAAE,gBAAgB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC1D,CAAC;QACD,KAAK,qBAAqB,CAAC,CAAC,CAAC;YAC3B,MAAM,IAAI,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC;YAClC,OAAO,IAAI,EAAE,mBAAmB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QACzD,CAAC;QACD;YACE,OAAO,IAAI,EAAE,KAAK,IAAI,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;IACvD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAAgB,EAAE,KAAmB;IAC1E,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,MAAM,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,GAAG,MAAM,IAAI,CAAC;IAE3B,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,MAAM,UAAU,CAAC,KAAK,CAAC,aAAa,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,2DAA2D;QAC3D,4BAA4B;QAC5B,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;QACjC,MAAM,OAAO,GAAG,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,iBAAiB,CAAC;QACnE,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAC3B,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,gCAAgC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QACvE,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,2BAA2B,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,25 @@
1
+ export interface UpdateCheckOptions {
2
+ base?: string;
3
+ currentVersion?: string;
4
+ /** 네트워크 조회 허용(대화형에서만 true 권장). false 면 캐시만 사용해 지연 0. */
5
+ allowNetwork?: boolean;
6
+ now?: number;
7
+ ttlMs?: number;
8
+ /** 테스트 주입용 fetch. 미지정 시 global fetch. */
9
+ fetchImpl?: typeof fetch;
10
+ /** 환경 opt-out(테스트 주입). 미지정 시 process.env.ADDE_NO_UPDATE_CHECK. */
11
+ optOut?: string | undefined;
12
+ }
13
+ /** a<b → -1, a>b → 1, 같음/비교불가 → 0. 프리릴리스는 동일 core 의 정식 릴리스보다 낮다. */
14
+ export declare function compareSemver(a: string, b: string): number;
15
+ export interface UpdateNotice {
16
+ current: string;
17
+ latest: string;
18
+ }
19
+ /**
20
+ * 업데이트 여부 판정 — 새 버전이 있으면 {current, latest}, 없으면 null.
21
+ * 캐시가 신선하면 캐시만 사용, 오래됐고 allowNetwork 면 레지스트리 조회 후 캐시 갱신.
22
+ */
23
+ export declare function checkForUpdate(opts?: UpdateCheckOptions): Promise<UpdateNotice | null>;
24
+ /** 업데이트 안내 문구. 표면 계층이 stdout 에 덧붙인다. */
25
+ export declare function formatUpdateNotice(notice: UpdateNotice): string;