@deadragdoll/tellymcp 0.0.6 → 0.0.8

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.
@@ -19,6 +19,7 @@ const proxyFetch_1 = require("./proxyFetch");
19
19
  const client_1 = require("../tmux/client");
20
20
  const versionHandshake_1 = require("../../lib/version/versionHandshake");
21
21
  const LOCAL_INDEX_FILE_NAME = "LOCAL_INDEX.md";
22
+ const TMUX_NUDGE_FAILURE_NOTICE_COOLDOWN_MS = 5 * 60 * 1000;
22
23
  function trimTrailingSlashes(value) {
23
24
  return value.replace(/\/+$/u, "");
24
25
  }
@@ -306,6 +307,7 @@ class TelegramTransport {
306
307
  screenshotMessageMenu;
307
308
  waiters = new Map();
308
309
  tmuxNudgeDebounceTimers = new Map();
310
+ tmuxNudgeFailureNoticeAt = new Map();
309
311
  pendingRenames = new Map();
310
312
  pendingBroadcasts = new Map();
311
313
  pendingPartnerNotes = new Map();
@@ -2585,22 +2587,117 @@ class TelegramTransport {
2585
2587
  return;
2586
2588
  }
2587
2589
  await this.sendTypingForSession(sessionId);
2588
- await (0, client_1.sendTmuxLiteralLine)(this.config.tmux, session.tmuxTarget, input.message);
2590
+ let tmuxTarget = session.tmuxTarget;
2591
+ try {
2592
+ await (0, client_1.sendTmuxLiteralLine)(this.config.tmux, tmuxTarget, input.message);
2593
+ }
2594
+ catch (error) {
2595
+ if ((0, client_1.isTmuxTargetInvalidError)(error)) {
2596
+ const recoveredTarget = await this.tryRecoverTmuxTarget(sessionId, session);
2597
+ if (recoveredTarget) {
2598
+ tmuxTarget = recoveredTarget;
2599
+ await (0, client_1.sendTmuxLiteralLine)(this.config.tmux, recoveredTarget, input.message);
2600
+ }
2601
+ else {
2602
+ await this.notifyTmuxTargetInvalid(sessionId, session, error);
2603
+ throw error;
2604
+ }
2605
+ }
2606
+ else {
2607
+ throw error;
2608
+ }
2609
+ }
2589
2610
  const lastTmuxNudgeAt = new Date(nowMs).toISOString();
2590
2611
  await this.sessionStore.setSession({
2591
2612
  ...session,
2613
+ tmuxTarget,
2614
+ ...(tmuxTarget ? { tmuxPaneId: tmuxTarget } : {}),
2592
2615
  lastTmuxNudgeAt,
2593
2616
  });
2617
+ this.tmuxNudgeFailureNoticeAt.delete(sessionId);
2594
2618
  this.logger.info("tmux nudge sent", {
2595
2619
  sessionId,
2596
2620
  reason: input.reason,
2597
2621
  message: input.message,
2598
2622
  tmuxSessionName: session.tmuxSessionName,
2599
- tmuxTarget: session.tmuxTarget,
2623
+ tmuxTarget,
2600
2624
  inboxCount,
2601
2625
  lastTmuxNudgeAt,
2602
2626
  });
2603
2627
  }
2628
+ async tryRecoverTmuxTarget(sessionId, session) {
2629
+ const recoveredTarget = await (0, client_1.resolveTmuxTargetFromHint)(this.config.tmux, {
2630
+ tmuxSessionName: session.tmuxSessionName,
2631
+ tmuxWindowName: session.tmuxWindowName,
2632
+ tmuxWindowIndex: session.tmuxWindowIndex,
2633
+ tmuxPaneId: session.tmuxPaneId,
2634
+ tmuxPaneIndex: session.tmuxPaneIndex,
2635
+ tmuxTarget: session.tmuxTarget,
2636
+ });
2637
+ if (!recoveredTarget || recoveredTarget === session.tmuxTarget) {
2638
+ return recoveredTarget;
2639
+ }
2640
+ await this.sessionStore.setSession({
2641
+ ...session,
2642
+ tmuxTarget: recoveredTarget,
2643
+ tmuxPaneId: recoveredTarget,
2644
+ updatedAt: new Date().toISOString(),
2645
+ });
2646
+ this.logger.warn("tmux target auto-recovered", {
2647
+ sessionId,
2648
+ previousTmuxTarget: session.tmuxTarget,
2649
+ recoveredTmuxTarget: recoveredTarget,
2650
+ tmuxSessionName: session.tmuxSessionName,
2651
+ tmuxWindowName: session.tmuxWindowName,
2652
+ tmuxWindowIndex: session.tmuxWindowIndex,
2653
+ tmuxPaneIndex: session.tmuxPaneIndex,
2654
+ });
2655
+ return recoveredTarget;
2656
+ }
2657
+ async notifyTmuxTargetInvalid(sessionId, session, error) {
2658
+ const binding = await this.bindingStore.getBinding(sessionId);
2659
+ if (!binding) {
2660
+ return;
2661
+ }
2662
+ const nowMs = Date.now();
2663
+ const lastNoticeAt = this.tmuxNudgeFailureNoticeAt.get(sessionId);
2664
+ if (lastNoticeAt &&
2665
+ nowMs - lastNoticeAt < TMUX_NUDGE_FAILURE_NOTICE_COOLDOWN_MS) {
2666
+ return;
2667
+ }
2668
+ this.tmuxNudgeFailureNoticeAt.set(sessionId, nowMs);
2669
+ const sessionLabel = session.label ?? sessionId;
2670
+ const tmuxTarget = session.tmuxTarget ?? "unknown";
2671
+ const errorMessage = error instanceof Error ? error.message : String(error);
2672
+ try {
2673
+ await this.sendNotification({
2674
+ sessionId,
2675
+ sessionLabel: "TellyMCP",
2676
+ recipient: {
2677
+ telegramChatId: binding.telegramChatId,
2678
+ telegramUserId: binding.telegramUserId,
2679
+ },
2680
+ message: [
2681
+ `⚠️ Автоматический tmux nudge для сессии ${sessionLabel} не сработал.`,
2682
+ `Сохранённый tmux target больше недействителен: ${tmuxTarget}`,
2683
+ `Ошибка: ${errorMessage}`,
2684
+ "Обычно это значит, что pane/window/session был пересоздан.",
2685
+ "Перепривяжи tmux target для этой сессии.",
2686
+ ].join("\n"),
2687
+ });
2688
+ }
2689
+ catch (notifyError) {
2690
+ this.logger.warn("Failed to deliver tmux target failure notification", {
2691
+ sessionId,
2692
+ tmuxTarget,
2693
+ telegramChatId: binding.telegramChatId,
2694
+ telegramUserId: binding.telegramUserId,
2695
+ notifyError: notifyError instanceof Error
2696
+ ? (notifyError.stack ?? notifyError.message)
2697
+ : String(notifyError),
2698
+ });
2699
+ }
2700
+ }
2604
2701
  async sendTypingForSession(sessionId) {
2605
2702
  const binding = await this.bindingStore.getBinding(sessionId);
2606
2703
  if (!binding) {
@@ -6454,7 +6551,7 @@ class TelegramTransport {
6454
6551
  telegramUserId: input.principal.telegramUserId,
6455
6552
  sourceTelegramMessageId: input.sourceTelegramMessageId,
6456
6553
  text: [
6457
- "Пользователь просит текущую сессию подготовить сообщение для другой сессии.",
6554
+ "Пользователь просит текущую сессию выполнить работу и отправить результат другой сессии.",
6458
6555
  `Маршрут отправки: ${sourceLabel} -> ${targetLabel}`,
6459
6556
  `Тип: ${input.kind}`,
6460
6557
  `Кратко: ${input.summary}`,
@@ -6466,6 +6563,9 @@ class TelegramTransport {
6466
6563
  "Содержимое для отправки:",
6467
6564
  input.message,
6468
6565
  "",
6566
+ "Не пересылай это как новую задачу в target-сессию.",
6567
+ "Сначала выполни работу в текущей сессии сам.",
6568
+ "Через send_partner_note или send_partner_file отправляй только результат, а не исходное поручение.",
6469
6569
  "Не используй linked partner для отправки. Передай target_session_id явно в send_partner_note.",
6470
6570
  "После подготовки обязательно используй send_partner_note.",
6471
6571
  "Задача не завершена, пока send_partner_note не отработал успешно.",
@@ -10,6 +10,8 @@ exports.listXchangeFiles = listXchangeFiles;
10
10
  exports.deleteXchangeFile = deleteXchangeFile;
11
11
  exports.readWorkspaceFile = readWorkspaceFile;
12
12
  exports.isTmuxUnavailableError = isTmuxUnavailableError;
13
+ exports.isTmuxTargetInvalidError = isTmuxTargetInvalidError;
14
+ exports.resolveTmuxTargetFromHint = resolveTmuxTargetFromHint;
13
15
  exports.getTmuxWindowHeight = getTmuxWindowHeight;
14
16
  exports.captureTmuxPaneRange = captureTmuxPaneRange;
15
17
  exports.captureVisibleTmuxPane = captureVisibleTmuxPane;
@@ -189,6 +191,79 @@ function isTmuxUnavailableError(error) {
189
191
  message.includes("ENOENT") ||
190
192
  message.includes("tmux is unavailable"));
191
193
  }
194
+ function isTmuxTargetInvalidError(error) {
195
+ const message = error instanceof Error ? (error.stack ?? error.message) : String(error);
196
+ return (message.includes("can't find pane") ||
197
+ message.includes("can't find window") ||
198
+ message.includes("can't find session"));
199
+ }
200
+ async function listTmuxPanes(config) {
201
+ const { stdout } = await execFileOutputAsync("tmux", buildTmuxArgs(config, [
202
+ "list-panes",
203
+ "-a",
204
+ "-F",
205
+ "#{session_name}\t#{window_name}\t#{window_index}\t#{pane_id}\t#{pane_index}",
206
+ ]));
207
+ return stdout
208
+ .split("\n")
209
+ .map((line) => line.trim())
210
+ .filter((line) => line.length > 0)
211
+ .map((line) => {
212
+ const [sessionName = "", windowName = "", windowIndexRaw = "", paneId = "", paneIndexRaw = ""] = line.split("\t");
213
+ return {
214
+ sessionName,
215
+ windowName,
216
+ windowIndex: Number.parseInt(windowIndexRaw, 10),
217
+ paneId,
218
+ paneIndex: Number.parseInt(paneIndexRaw, 10),
219
+ };
220
+ })
221
+ .filter((pane) => pane.sessionName &&
222
+ pane.paneId &&
223
+ Number.isFinite(pane.windowIndex) &&
224
+ Number.isFinite(pane.paneIndex));
225
+ }
226
+ async function resolveTmuxTargetFromHint(config, hint) {
227
+ const panes = await listTmuxPanes(config);
228
+ const byPaneId = hint.tmuxPaneId
229
+ ? panes.find((pane) => pane.paneId === hint.tmuxPaneId)
230
+ : null;
231
+ if (byPaneId) {
232
+ return byPaneId.paneId;
233
+ }
234
+ const exactMatch = panes.find((pane) => {
235
+ if (!hint.tmuxSessionName) {
236
+ return false;
237
+ }
238
+ if (pane.sessionName !== hint.tmuxSessionName) {
239
+ return false;
240
+ }
241
+ if (typeof hint.tmuxWindowIndex === "number" &&
242
+ pane.windowIndex !== hint.tmuxWindowIndex) {
243
+ return false;
244
+ }
245
+ if (typeof hint.tmuxPaneIndex === "number" &&
246
+ pane.paneIndex !== hint.tmuxPaneIndex) {
247
+ return false;
248
+ }
249
+ if (hint.tmuxWindowName &&
250
+ pane.windowName !== hint.tmuxWindowName) {
251
+ return false;
252
+ }
253
+ return true;
254
+ });
255
+ if (exactMatch) {
256
+ return exactMatch.paneId;
257
+ }
258
+ const fallbackBySessionAndPane = panes.find((pane) => {
259
+ if (!hint.tmuxSessionName || typeof hint.tmuxPaneIndex !== "number") {
260
+ return false;
261
+ }
262
+ return (pane.sessionName === hint.tmuxSessionName &&
263
+ pane.paneIndex === hint.tmuxPaneIndex);
264
+ });
265
+ return fallbackBySessionAndPane?.paneId ?? null;
266
+ }
192
267
  async function getTmuxWindowHeight(config, target) {
193
268
  const { stdout: heightRaw } = await execFileOutputAsync("tmux", buildTmuxArgs(config, ["display-message", "-p", "-t", target, "#{window_height}"]));
194
269
  const height = Number.parseInt(heightRaw.trim(), 10);
@@ -4,10 +4,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.createLogger = createLogger;
7
- const node_fs_1 = require("node:fs");
8
- const node_path_1 = require("node:path");
9
7
  const pino_1 = __importDefault(require("pino"));
10
- const DEFAULT_LOG_FILE_PATH = ".telegram-human-mcp/log.jsonl";
8
+ const pinoTargets_1 = require("../../../../../../../lib/pinoTargets");
11
9
  function write(logger, level, message, meta) {
12
10
  if (meta) {
13
11
  logger[level](meta, message);
@@ -16,32 +14,15 @@ function write(logger, level, message, meta) {
16
14
  logger[level](message);
17
15
  }
18
16
  function createLogger(config) {
19
- (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(DEFAULT_LOG_FILE_PATH), { recursive: true });
20
17
  const transport = pino_1.default.transport({
21
- targets: [
22
- {
23
- target: "pino/file",
24
- level: config.logging.level,
25
- options: {
26
- destination: DEFAULT_LOG_FILE_PATH,
27
- mkdir: true,
28
- },
29
- },
30
- {
31
- target: "pino-pretty",
32
- level: config.logging.level,
33
- options: {
34
- destination: 2,
35
- colorize: true,
36
- translateTime: "SYS:standard",
37
- ignore: "pid,hostname",
38
- singleLine: false,
39
- },
40
- },
41
- ],
18
+ targets: (0, pinoTargets_1.createPinoTargets)({
19
+ level: config.logging.level,
20
+ fileEnabled: config.logging.fileEnabled,
21
+ filePath: config.logging.filePath,
22
+ }),
42
23
  });
43
24
  const baseLogger = (0, pino_1.default)({
44
- name: "telegram-human-mcp",
25
+ name: "tellymcp",
45
26
  level: config.logging.level,
46
27
  timestamp: pino_1.default.stdTimeFunctions.isoTime,
47
28
  }, transport);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deadragdoll/tellymcp",
3
- "version": "0.0.6",
4
- "description": "TellyMCP - Telegram Human-in-the-Loop MCP Server",
3
+ "version": "0.0.8",
4
+ "description": "TellyMCP - Telegram control plane for MCP-connected coding agents",
5
5
  "main": "dist/services/features/telegram-mcp/runtime.service.js",
6
6
  "bin": {
7
7
  "tellymcp": "dist/cli.js"
@@ -75,7 +75,6 @@
75
75
  "amqplib": "0.10.9",
76
76
  "dotenv": "^17.4.2",
77
77
  "grammy": "1.43.0",
78
- "graphql": "^16.14.0",
79
78
  "https-proxy-agent": "^7.0.6",
80
79
  "ioredis": "5.10.1",
81
80
  "knex": "^3.2.10",
@@ -88,7 +87,6 @@
88
87
  "pino": "^10.3.1",
89
88
  "pino-pretty": "^13.1.3",
90
89
  "playwright": "^1.60.0",
91
- "redis": "5.12.1",
92
90
  "socks-proxy-agent": "^8.0.5",
93
91
  "ws": "8.20.1",
94
92
  "zod": "4.4.3"
@@ -1,172 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createTracerMiddleware = void 0;
4
- const traceContext_1 = require("../traceContext");
5
- const trace_1 = require("../trace");
6
- const shouldSkipTrace = (actionName, ctx) => Boolean(ctx?.meta?.$traceInternal ||
7
- !actionName ||
8
- actionName === "context" ||
9
- actionName === "graphql.publish" ||
10
- (actionName === "rest" && ctx?.params?.req?.url === "/api/graphql"));
11
- const createTracerMiddleware = () => {
12
- let broker = null;
13
- const resolveTracer = (action) => {
14
- const rawName = String(action?.rawName || action?.name || "");
15
- if (action?.tracer)
16
- return { tracer: action.tracer, source: "action.tracer" };
17
- if (action?.schema?.tracer)
18
- return { tracer: action.schema.tracer, source: "action.schema.tracer" };
19
- if (action?.service?.schema?.actions?.[rawName]?.tracer) {
20
- return {
21
- tracer: action.service.schema.actions[rawName].tracer,
22
- source: "service.schema.actions[rawName].tracer",
23
- };
24
- }
25
- return {};
26
- };
27
- const callTrace = async (ctx, actionName, params, silent = false) => {
28
- if (!broker) {
29
- return null;
30
- }
31
- try {
32
- return await broker.call(actionName, params, {
33
- requestID: ctx.requestID,
34
- meta: {
35
- user: ctx.meta?.user,
36
- $traceInternal: true,
37
- },
38
- });
39
- }
40
- catch (error) {
41
- if (!silent) {
42
- broker.logger.warn("Tracer call failed", {
43
- actionName,
44
- sourceAction: ctx.action?.name || null,
45
- error: error instanceof Error ? error.message : String(error),
46
- });
47
- }
48
- return null;
49
- }
50
- };
51
- return {
52
- created(localBroker) {
53
- broker = localBroker;
54
- },
55
- localAction(next, action) {
56
- const actionName = String(action?.rawName || action?.name || "");
57
- const serviceName = String(action?.service?.name || "");
58
- return async function traceAction(ctx) {
59
- if (serviceName === "trace" || shouldSkipTrace(actionName, ctx)) {
60
- return next(ctx);
61
- }
62
- const resolvedTracer = resolveTracer(ctx?.action || action);
63
- const tracer = resolvedTracer.tracer;
64
- let traceMeta = null;
65
- const startSessionName = tracer?.startSession;
66
- const tracerTag = tracer?.tag;
67
- const shouldStartRootSession = Boolean(startSessionName);
68
- if (!shouldStartRootSession) {
69
- traceMeta = await (0, traceContext_1.loadTraceContext)();
70
- }
71
- let startedSession = false;
72
- if (shouldStartRootSession) {
73
- await (0, traceContext_1.deleteTraceContext)().catch(() => null);
74
- const session = (await callTrace(ctx, "trace.startSession", {
75
- name: startSessionName,
76
- tag: tracerTag || null,
77
- source: actionName,
78
- meta: (0, trace_1.sanitizeTraceValue)({
79
- action: actionName,
80
- params: ctx.params,
81
- }),
82
- }, false));
83
- if (session?.session_id) {
84
- traceMeta = {
85
- sessionId: String(session.session_id),
86
- name: startSessionName ?? null,
87
- tag: tracerTag || null,
88
- rootAction: actionName,
89
- startedBy: actionName,
90
- };
91
- await (0, traceContext_1.saveTraceContext)(traceMeta);
92
- startedSession = true;
93
- }
94
- }
95
- const sessionId = String(traceMeta?.sessionId || "").trim();
96
- const hasSession = Boolean(sessionId);
97
- const startedAt = Date.now();
98
- if (hasSession) {
99
- await (0, traceContext_1.touchTraceContext)().catch(() => null);
100
- await callTrace(ctx, "trace.log", {
101
- session_id: sessionId,
102
- level: (0, trace_1.normalizeTraceLevel)(tracer?.level || "debug"),
103
- action: actionName,
104
- state: "started",
105
- marker: tracer?.marker || null,
106
- step: tracer?.step || "action",
107
- message: actionName,
108
- data: (0, trace_1.buildTraceStartData)(ctx, actionName, tracer, startedSession),
109
- }, false);
110
- }
111
- try {
112
- const result = await next(ctx);
113
- const durationMs = Date.now() - startedAt;
114
- if (hasSession) {
115
- await callTrace(ctx, "trace.log", {
116
- session_id: sessionId,
117
- level: (0, trace_1.normalizeTraceLevel)(tracer?.level || "debug"),
118
- action: actionName,
119
- state: "succeeded",
120
- marker: tracer?.marker || null,
121
- step: tracer?.step || "action",
122
- message: actionName,
123
- data: (0, trace_1.buildTraceSuccessData)(ctx, actionName, result, durationMs, tracer),
124
- }, false);
125
- if (tracer?.stopSession) {
126
- await callTrace(ctx, "trace.endSession", {
127
- session_id: sessionId,
128
- status: "succeeded",
129
- summary: `${actionName} completed`,
130
- meta: (0, trace_1.sanitizeTraceValue)({
131
- action: actionName,
132
- durationMs,
133
- }),
134
- }, false);
135
- await (0, traceContext_1.deleteTraceContext)();
136
- }
137
- }
138
- return result;
139
- }
140
- catch (error) {
141
- const durationMs = Date.now() - startedAt;
142
- if (hasSession) {
143
- await callTrace(ctx, "trace.log", {
144
- session_id: sessionId,
145
- level: "error",
146
- action: actionName,
147
- state: "failed",
148
- marker: tracer?.marker || null,
149
- step: tracer?.step || "action",
150
- message: actionName,
151
- data: (0, trace_1.buildTraceErrorData)(ctx, actionName, error, durationMs, tracer),
152
- }, false);
153
- if (tracer?.stopSession || startedSession) {
154
- await callTrace(ctx, "trace.endSession", {
155
- session_id: sessionId,
156
- status: "failed",
157
- summary: error instanceof Error ? error.message : `Action failed: ${actionName}`,
158
- meta: (0, trace_1.sanitizeTraceValue)({
159
- action: actionName,
160
- durationMs,
161
- }),
162
- }, false);
163
- await (0, traceContext_1.deleteTraceContext)();
164
- }
165
- }
166
- throw error;
167
- }
168
- };
169
- },
170
- };
171
- };
172
- exports.createTracerMiddleware = createTracerMiddleware;
package/dist/lib/trace.js DELETED
@@ -1,147 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildTraceErrorData = exports.buildTraceSuccessData = exports.buildTraceStartData = exports.buildTraceMetaSummary = exports.sanitizeTraceValue = exports.normalizeTraceLevel = void 0;
4
- const TRACE_MAX_DEPTH = 4;
5
- const TRACE_MAX_ARRAY = 25;
6
- const TRACE_MAX_KEYS = 40;
7
- const TRACE_MAX_STRING = 1000;
8
- const truncateString = (value) => value.length > TRACE_MAX_STRING ? `${value.slice(0, TRACE_MAX_STRING)}...` : value;
9
- const normalizeTraceLevel = (value) => {
10
- const level = String(value || "info").toLowerCase();
11
- if (["fatal", "error", "warn", "info", "debug", "trace"].includes(level)) {
12
- return level;
13
- }
14
- return "info";
15
- };
16
- exports.normalizeTraceLevel = normalizeTraceLevel;
17
- const sanitizeError = (error, depth, seen) => {
18
- if (!(error instanceof Error)) {
19
- return (0, exports.sanitizeTraceValue)(error, depth, seen);
20
- }
21
- return {
22
- name: error.name,
23
- message: truncateString(error.message || ""),
24
- stack: truncateString(error.stack || ""),
25
- };
26
- };
27
- const sanitizeTraceValue = (value, depth = 0, seen = new WeakSet()) => {
28
- if (value == null || typeof value === "boolean" || typeof value === "number") {
29
- return value;
30
- }
31
- if (typeof value === "string") {
32
- return truncateString(value);
33
- }
34
- if (typeof value === "bigint") {
35
- return value.toString();
36
- }
37
- if (typeof value === "symbol") {
38
- return value.toString();
39
- }
40
- if (typeof value === "function") {
41
- return `[Function ${value.name || "anonymous"}]`;
42
- }
43
- if (value instanceof Date) {
44
- return value.toISOString();
45
- }
46
- if (value instanceof Error) {
47
- return sanitizeError(value, depth, seen);
48
- }
49
- if (Buffer.isBuffer(value)) {
50
- return `[Buffer ${value.length}]`;
51
- }
52
- if (value instanceof Set) {
53
- return {
54
- type: "Set",
55
- size: value.size,
56
- values: Array.from(value.values())
57
- .slice(0, TRACE_MAX_ARRAY)
58
- .map(item => (0, exports.sanitizeTraceValue)(item, depth + 1, seen)),
59
- };
60
- }
61
- if (value instanceof Map) {
62
- return {
63
- type: "Map",
64
- size: value.size,
65
- entries: Array.from(value.entries())
66
- .slice(0, TRACE_MAX_ARRAY)
67
- .map(([key, item]) => [
68
- (0, exports.sanitizeTraceValue)(key, depth + 1, seen),
69
- (0, exports.sanitizeTraceValue)(item, depth + 1, seen),
70
- ]),
71
- };
72
- }
73
- if (Array.isArray(value)) {
74
- if (depth >= TRACE_MAX_DEPTH) {
75
- return `[Array(${value.length})]`;
76
- }
77
- return value
78
- .slice(0, TRACE_MAX_ARRAY)
79
- .map(item => (0, exports.sanitizeTraceValue)(item, depth + 1, seen));
80
- }
81
- if (typeof value === "object") {
82
- const objectValue = value;
83
- if (seen.has(objectValue)) {
84
- return "[Circular]";
85
- }
86
- seen.add(objectValue);
87
- if (depth >= TRACE_MAX_DEPTH) {
88
- return `[${objectValue?.constructor?.name || "Object"}]`;
89
- }
90
- const keys = Object.keys(objectValue).slice(0, TRACE_MAX_KEYS);
91
- const result = {};
92
- for (const key of keys) {
93
- result[key] = (0, exports.sanitizeTraceValue)(objectValue[key], depth + 1, seen);
94
- }
95
- if (Object.keys(objectValue).length > TRACE_MAX_KEYS) {
96
- result.__truncatedKeys = Object.keys(objectValue).length - TRACE_MAX_KEYS;
97
- }
98
- return result;
99
- }
100
- return String(value);
101
- };
102
- exports.sanitizeTraceValue = sanitizeTraceValue;
103
- const pickFields = (source, fields) => {
104
- if (fields === false) {
105
- return undefined;
106
- }
107
- if (fields === true || fields == null) {
108
- return (0, exports.sanitizeTraceValue)(source);
109
- }
110
- if (!source || typeof source !== "object") {
111
- return (0, exports.sanitizeTraceValue)(source);
112
- }
113
- const result = {};
114
- for (const key of fields) {
115
- result[key] = (0, exports.sanitizeTraceValue)(source[key]);
116
- }
117
- return result;
118
- };
119
- const buildTraceMetaSummary = (ctx) => (0, exports.sanitizeTraceValue)({
120
- requestID: ctx.requestID || null,
121
- userSub: ctx.meta?.user?.sub || null,
122
- });
123
- exports.buildTraceMetaSummary = buildTraceMetaSummary;
124
- const buildTraceStartData = (ctx, actionName, tracer, startedSession = false) => (0, exports.sanitizeTraceValue)({
125
- action: actionName,
126
- startedSession,
127
- params: pickFields(ctx.params, tracer?.captureParams),
128
- meta: (0, exports.buildTraceMetaSummary)(ctx),
129
- });
130
- exports.buildTraceStartData = buildTraceStartData;
131
- const buildTraceSuccessData = (ctx, actionName, result, durationMs, tracer) => (0, exports.sanitizeTraceValue)({
132
- action: actionName,
133
- durationMs,
134
- result: tracer?.captureResult === undefined
135
- ? undefined
136
- : pickFields(result, tracer.captureResult),
137
- });
138
- exports.buildTraceSuccessData = buildTraceSuccessData;
139
- const buildTraceErrorData = (ctx, actionName, error, durationMs, tracer) => (0, exports.sanitizeTraceValue)({
140
- action: actionName,
141
- durationMs,
142
- error: tracer?.captureError === undefined
143
- ? sanitizeError(error, 0, new WeakSet())
144
- : pickFields(error, tracer.captureError),
145
- meta: (0, exports.buildTraceMetaSummary)(ctx),
146
- });
147
- exports.buildTraceErrorData = buildTraceErrorData;