@rk0429/agentic-relay 21.3.0 → 22.0.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 (105) hide show
  1. package/dist/application/chat/chat-daemon-service.d.ts +81 -0
  2. package/dist/application/chat/chat-daemon-service.js +382 -0
  3. package/dist/application/chat/chat-daemon-service.js.map +1 -0
  4. package/dist/application/chat/chat-inbound-handler.d.ts +35 -0
  5. package/dist/application/chat/chat-inbound-handler.js +175 -0
  6. package/dist/application/chat/chat-inbound-handler.js.map +1 -0
  7. package/dist/application/chat/chat-logging.d.ts +8 -0
  8. package/dist/application/chat/chat-logging.js +16 -0
  9. package/dist/application/chat/chat-logging.js.map +1 -0
  10. package/dist/application/chat/chat-outbound-handler.d.ts +16 -0
  11. package/dist/application/chat/chat-outbound-handler.js +190 -0
  12. package/dist/application/chat/chat-outbound-handler.js.map +1 -0
  13. package/dist/application/chat/chat-question-service.d.ts +37 -0
  14. package/dist/application/chat/chat-question-service.js +135 -0
  15. package/dist/application/chat/chat-question-service.js.map +1 -0
  16. package/dist/application/chat/chat-setup-service.d.ts +27 -0
  17. package/dist/application/chat/chat-setup-service.js +183 -0
  18. package/dist/application/chat/chat-setup-service.js.map +1 -0
  19. package/dist/application/chat/message-queue-manager.d.ts +13 -0
  20. package/dist/application/chat/message-queue-manager.js +34 -0
  21. package/dist/application/chat/message-queue-manager.js.map +1 -0
  22. package/dist/application/routine-status-query-service.d.ts +17 -0
  23. package/dist/application/routine-status-query-service.js +111 -0
  24. package/dist/application/routine-status-query-service.js.map +1 -1
  25. package/dist/bin/relay.d.ts +4 -2
  26. package/dist/bin/relay.js +278 -11
  27. package/dist/bin/relay.js.map +1 -1
  28. package/dist/core/chat-types.d.ts +18 -0
  29. package/dist/core/chat-types.js +14 -0
  30. package/dist/core/chat-types.js.map +1 -0
  31. package/dist/core/types.d.ts +7 -0
  32. package/dist/domain/chat/message-cleaner.d.ts +11 -0
  33. package/dist/domain/chat/message-cleaner.js +45 -0
  34. package/dist/domain/chat/message-cleaner.js.map +1 -0
  35. package/dist/domain/chat/message-router.d.ts +8 -0
  36. package/dist/domain/chat/message-router.js +93 -0
  37. package/dist/domain/chat/message-router.js.map +1 -0
  38. package/dist/domain/chat/message-splitter.d.ts +3 -0
  39. package/dist/domain/chat/message-splitter.js +148 -0
  40. package/dist/domain/chat/message-splitter.js.map +1 -0
  41. package/dist/domain/chat/platform-connection.d.ts +31 -0
  42. package/dist/domain/chat/platform-connection.js +67 -0
  43. package/dist/domain/chat/platform-connection.js.map +1 -0
  44. package/dist/domain/chat/ports.d.ts +31 -0
  45. package/dist/domain/chat/ports.js +2 -0
  46. package/dist/domain/chat/ports.js.map +1 -0
  47. package/dist/domain/chat/session-bridge.d.ts +41 -0
  48. package/dist/domain/chat/session-bridge.js +93 -0
  49. package/dist/domain/chat/session-bridge.js.map +1 -0
  50. package/dist/domain/chat/types.d.ts +83 -0
  51. package/dist/domain/chat/types.js +2 -0
  52. package/dist/domain/chat/types.js.map +1 -0
  53. package/dist/domain/chat/user-question.d.ts +33 -0
  54. package/dist/domain/chat/user-question.js +73 -0
  55. package/dist/domain/chat/user-question.js.map +1 -0
  56. package/dist/domain/config.d.ts +3 -1
  57. package/dist/domain/config.js +9 -0
  58. package/dist/domain/config.js.map +1 -1
  59. package/dist/domain/routine-loader.d.ts +2 -0
  60. package/dist/domain/routine-loader.js +3 -0
  61. package/dist/domain/routine-loader.js.map +1 -1
  62. package/dist/infrastructure/chat/adapter-factory.d.ts +5 -0
  63. package/dist/infrastructure/chat/adapter-factory.js +15 -0
  64. package/dist/infrastructure/chat/adapter-factory.js.map +1 -0
  65. package/dist/infrastructure/chat/adapters/discord-adapter.d.ts +23 -0
  66. package/dist/infrastructure/chat/adapters/discord-adapter.js +199 -0
  67. package/dist/infrastructure/chat/adapters/discord-adapter.js.map +1 -0
  68. package/dist/infrastructure/chat/adapters/slack-adapter.d.ts +24 -0
  69. package/dist/infrastructure/chat/adapters/slack-adapter.js +155 -0
  70. package/dist/infrastructure/chat/adapters/slack-adapter.js.map +1 -0
  71. package/dist/infrastructure/chat/agent-gateway-impl.d.ts +21 -0
  72. package/dist/infrastructure/chat/agent-gateway-impl.js +101 -0
  73. package/dist/infrastructure/chat/agent-gateway-impl.js.map +1 -0
  74. package/dist/infrastructure/chat/chat-ipc-client.d.ts +33 -0
  75. package/dist/infrastructure/chat/chat-ipc-client.js +70 -0
  76. package/dist/infrastructure/chat/chat-ipc-client.js.map +1 -0
  77. package/dist/infrastructure/chat/chat-ipc-server.d.ts +19 -0
  78. package/dist/infrastructure/chat/chat-ipc-server.js +125 -0
  79. package/dist/infrastructure/chat/chat-ipc-server.js.map +1 -0
  80. package/dist/infrastructure/chat/chat-logger.d.ts +24 -0
  81. package/dist/infrastructure/chat/chat-logger.js +86 -0
  82. package/dist/infrastructure/chat/chat-logger.js.map +1 -0
  83. package/dist/infrastructure/chat/credentials-repository.d.ts +8 -0
  84. package/dist/infrastructure/chat/credentials-repository.js +84 -0
  85. package/dist/infrastructure/chat/credentials-repository.js.map +1 -0
  86. package/dist/infrastructure/chat/session-bridge-repository.d.ts +13 -0
  87. package/dist/infrastructure/chat/session-bridge-repository.js +120 -0
  88. package/dist/infrastructure/chat/session-bridge-repository.js.map +1 -0
  89. package/dist/infrastructure/chat/token-masking.d.ts +3 -0
  90. package/dist/infrastructure/chat/token-masking.js +22 -0
  91. package/dist/infrastructure/chat/token-masking.js.map +1 -0
  92. package/dist/infrastructure/store/relay-store.js +3 -1
  93. package/dist/infrastructure/store/relay-store.js.map +1 -1
  94. package/dist/interfaces/cli/chat-cli.d.ts +34 -0
  95. package/dist/interfaces/cli/chat-cli.js +68 -0
  96. package/dist/interfaces/cli/chat-cli.js.map +1 -0
  97. package/dist/interfaces/cli/relay-cli-args.d.ts +16 -1
  98. package/dist/interfaces/cli/relay-cli-args.js +135 -2
  99. package/dist/interfaces/cli/relay-cli-args.js.map +1 -1
  100. package/dist/interfaces/mcp/chat-tools.d.ts +33 -0
  101. package/dist/interfaces/mcp/chat-tools.js +129 -0
  102. package/dist/interfaces/mcp/chat-tools.js.map +1 -0
  103. package/dist/interfaces/mcp/relay-mcp-server.js +43 -6
  104. package/dist/interfaces/mcp/relay-mcp-server.js.map +1 -1
  105. package/package.json +3 -1
@@ -0,0 +1,101 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { BackendUnavailableError, ValidationError } from "../../core/errors.js";
3
+ export class AgentGatewayImpl {
4
+ spawnService;
5
+ taskService;
6
+ store;
7
+ backendRegistry;
8
+ actorId;
9
+ constructor(spawnService, taskService, store, backendRegistry, actorId = "chat-daemon") {
10
+ this.spawnService = spawnService;
11
+ this.taskService = taskService;
12
+ this.store = store;
13
+ this.backendRegistry = backendRegistry;
14
+ this.actorId = actorId;
15
+ }
16
+ async startSession(prompt, backend) {
17
+ const taskId = await this.createChatTask(prompt);
18
+ const resolvedBackend = backend?.backend ?? await this.resolveDefaultBackend();
19
+ const [result] = await this.spawnService.spawnAgents({
20
+ agents: [
21
+ {
22
+ task_id: taskId,
23
+ backend: resolvedBackend,
24
+ },
25
+ ],
26
+ summary_length: 4_000,
27
+ });
28
+ if (!result) {
29
+ throw new ValidationError("Chat session agent did not produce a result.");
30
+ }
31
+ return this.toAgentResult(result);
32
+ }
33
+ async continueSession(sessionId, prompt) {
34
+ const metadata = await this.store.readSessionMetadata(sessionId);
35
+ const taskId = await this.createChatTask(prompt);
36
+ const resolvedBackend = metadata?.backend ?? await this.resolveDefaultBackend();
37
+ const [result] = await this.spawnService.spawnAgents({
38
+ agents: [
39
+ {
40
+ task_id: taskId,
41
+ resume_from_relay_session_id: sessionId,
42
+ backend: resolvedBackend,
43
+ },
44
+ ],
45
+ summary_length: 4_000,
46
+ });
47
+ if (!result) {
48
+ throw new ValidationError("Chat session continuation did not produce a result.");
49
+ }
50
+ return this.toAgentResult(result);
51
+ }
52
+ async createChatTask(prompt) {
53
+ const task = await this.taskService.createTask({
54
+ title: buildChatTaskTitle(prompt),
55
+ description: prompt,
56
+ acceptance_criteria: "Respond to the chat message with a helpful answer.",
57
+ labels: ["chat"],
58
+ }, { actorId: this.actorId });
59
+ await this.taskService.updateTaskStatus({
60
+ task_id: task.task_id,
61
+ status: "ready",
62
+ }, { actorId: this.actorId });
63
+ return task.task_id;
64
+ }
65
+ // Chat messages have no taskType, so domain/routing.ts resolveBackend() cannot be used.
66
+ // Fall back to the first installed backend in BACKEND_TYPES order (claude > codex > gemini).
67
+ // If config.json gains a chat-specific routing key, this should be updated accordingly.
68
+ async resolveDefaultBackend() {
69
+ const availableBackends = await this.backendRegistry.detectInstalled();
70
+ const backend = availableBackends[0];
71
+ if (!backend) {
72
+ throw new BackendUnavailableError("agentic-relay: no supported backend CLI is installed.");
73
+ }
74
+ return backend;
75
+ }
76
+ async toAgentResult(result) {
77
+ if (result.process_status !== "success") {
78
+ throw new ValidationError(result.summary || "Agent execution failed.", {
79
+ recoveryHint: result.recovery_hint,
80
+ });
81
+ }
82
+ const [metadata, response] = await Promise.all([
83
+ this.store.readSessionMetadata(result.relay_session_id),
84
+ readFile(result.full_response_path, "utf8").catch(() => result.summary),
85
+ ]);
86
+ return {
87
+ relaySessionId: result.relay_session_id,
88
+ backendSessionId: metadata?.backendSessionId,
89
+ response,
90
+ backend: result.backend,
91
+ };
92
+ }
93
+ }
94
+ export function buildChatTaskTitle(prompt) {
95
+ const trimmed = prompt.trim();
96
+ if (!trimmed) {
97
+ return "Chat session";
98
+ }
99
+ return `Chat: ${trimmed.slice(0, 60)}`;
100
+ }
101
+ //# sourceMappingURL=agent-gateway-impl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-gateway-impl.js","sourceRoot":"","sources":["../../../src/infrastructure/chat/agent-gateway-impl.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { PlatformType } from "../../core/chat-types.js";
2
+ import type { SendResult } from "../../domain/chat/types.js";
3
+ import type { ChatIpcAskUserResult } from "./chat-ipc-server.js";
4
+ export declare class ChatIpcClient {
5
+ private readonly socketPath;
6
+ constructor(rootDir: string);
7
+ sendMessage(params: {
8
+ platform?: PlatformType;
9
+ channel?: string;
10
+ user_id?: string;
11
+ message: string;
12
+ thread_id?: string;
13
+ }): Promise<SendResult>;
14
+ replyThread(params: {
15
+ platform?: PlatformType;
16
+ channel: string;
17
+ thread_id: string;
18
+ message: string;
19
+ }): Promise<SendResult>;
20
+ askUser(params: {
21
+ platform?: PlatformType;
22
+ user_id: string;
23
+ question: string;
24
+ timeout_seconds?: number;
25
+ channel?: string;
26
+ }): Promise<ChatIpcAskUserResult>;
27
+ listChannels(params: {
28
+ platform?: PlatformType;
29
+ }): Promise<{
30
+ channels: Array<Record<string, unknown>>;
31
+ }>;
32
+ private request;
33
+ }
@@ -0,0 +1,70 @@
1
+ import { createConnection } from "node:net";
2
+ import path from "node:path";
3
+ import { ValidationError } from "../../core/errors.js";
4
+ export class ChatIpcClient {
5
+ socketPath;
6
+ constructor(rootDir) {
7
+ this.socketPath = path.join(rootDir, "chat.sock");
8
+ }
9
+ async sendMessage(params) {
10
+ return this.request("chat/sendMessage", params);
11
+ }
12
+ async replyThread(params) {
13
+ return this.request("chat/replyThread", params);
14
+ }
15
+ async askUser(params) {
16
+ return this.request("chat/askUser", params);
17
+ }
18
+ async listChannels(params) {
19
+ return this.request("chat/listChannels", params);
20
+ }
21
+ async request(method, params) {
22
+ const id = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
23
+ const socket = createConnection(this.socketPath);
24
+ socket.setEncoding("utf8");
25
+ return new Promise((resolve, reject) => {
26
+ let buffer = "";
27
+ socket.once("error", (error) => {
28
+ reject(new ValidationError("Chat daemon is not running.", {
29
+ recoveryHint: "Run `relay chat start` before using chat MCP tools.",
30
+ causeData: {
31
+ error: error.message,
32
+ },
33
+ }));
34
+ });
35
+ socket.on("data", (chunk) => {
36
+ buffer += chunk;
37
+ while (buffer.includes("\n")) {
38
+ const newlineIndex = buffer.indexOf("\n");
39
+ const line = buffer.slice(0, newlineIndex).trim();
40
+ buffer = buffer.slice(newlineIndex + 1);
41
+ if (!line) {
42
+ continue;
43
+ }
44
+ const response = JSON.parse(line);
45
+ if (response.id !== id) {
46
+ continue;
47
+ }
48
+ socket.end();
49
+ if (response.error) {
50
+ reject(new ValidationError(response.error.message, {
51
+ causeData: response.error.data,
52
+ }));
53
+ return;
54
+ }
55
+ resolve(response.result);
56
+ return;
57
+ }
58
+ });
59
+ socket.on("connect", () => {
60
+ socket.write(`${JSON.stringify({
61
+ jsonrpc: "2.0",
62
+ id,
63
+ method,
64
+ params,
65
+ })}\n`);
66
+ });
67
+ });
68
+ }
69
+ }
70
+ //# sourceMappingURL=chat-ipc-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-ipc-client.js","sourceRoot":"","sources":["../../../src/infrastructure/chat/chat-ipc-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { ChatOutboundHandler } from "../../application/chat/chat-outbound-handler.js";
2
+ import type { AskUserResult } from "../../application/chat/chat-question-service.js";
3
+ import { ChatQuestionService } from "../../application/chat/chat-question-service.js";
4
+ export declare class ChatIpcServer {
5
+ private readonly deps;
6
+ private readonly socketPath;
7
+ private server?;
8
+ constructor(deps: {
9
+ rootDir: string;
10
+ outboundHandler: ChatOutboundHandler;
11
+ questionService: ChatQuestionService;
12
+ });
13
+ listen(): Promise<void>;
14
+ close(): Promise<void>;
15
+ private handleSocket;
16
+ private handleLine;
17
+ private dispatch;
18
+ }
19
+ export type ChatIpcAskUserResult = AskUserResult;
@@ -0,0 +1,125 @@
1
+ import { createServer } from "node:net";
2
+ import { chmod, mkdir, rm } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { ValidationError } from "../../core/errors.js";
5
+ import { PlatformType } from "../../core/chat-types.js";
6
+ export class ChatIpcServer {
7
+ deps;
8
+ socketPath;
9
+ server;
10
+ constructor(deps) {
11
+ this.deps = deps;
12
+ this.socketPath = path.join(deps.rootDir, "chat.sock");
13
+ }
14
+ async listen() {
15
+ await mkdir(path.dirname(this.socketPath), { recursive: true });
16
+ await rm(this.socketPath, { force: true });
17
+ this.server = createServer((socket) => {
18
+ this.handleSocket(socket);
19
+ });
20
+ await new Promise((resolve, reject) => {
21
+ this.server.once("error", reject);
22
+ this.server.listen(this.socketPath, () => {
23
+ this.server.off("error", reject);
24
+ resolve();
25
+ });
26
+ });
27
+ await chmod(this.socketPath, 0o700);
28
+ }
29
+ async close() {
30
+ if (this.server) {
31
+ await new Promise((resolve) => this.server.close(() => resolve()));
32
+ this.server = undefined;
33
+ }
34
+ await rm(this.socketPath, { force: true });
35
+ }
36
+ handleSocket(socket) {
37
+ let buffer = "";
38
+ socket.setEncoding("utf8");
39
+ socket.on("data", (chunk) => {
40
+ buffer += chunk;
41
+ while (buffer.includes("\n")) {
42
+ const newlineIndex = buffer.indexOf("\n");
43
+ const line = buffer.slice(0, newlineIndex).trim();
44
+ buffer = buffer.slice(newlineIndex + 1);
45
+ if (!line) {
46
+ continue;
47
+ }
48
+ void this.handleLine(socket, line);
49
+ }
50
+ });
51
+ }
52
+ async handleLine(socket, line) {
53
+ let request;
54
+ try {
55
+ request = JSON.parse(line);
56
+ }
57
+ catch {
58
+ socket.write(`${JSON.stringify(buildErrorResponse(null, -32700, "Parse error"))}\n`);
59
+ return;
60
+ }
61
+ try {
62
+ const result = await this.dispatch(request.method, request.params ?? {});
63
+ socket.write(`${JSON.stringify({ jsonrpc: "2.0", id: request.id, result })}\n`);
64
+ }
65
+ catch (error) {
66
+ const message = error instanceof Error ? error.message : String(error);
67
+ socket.write(`${JSON.stringify(buildErrorResponse(request.id, -32000, message))}\n`);
68
+ }
69
+ }
70
+ async dispatch(method, params) {
71
+ switch (method) {
72
+ case "chat/sendMessage":
73
+ return this.deps.outboundHandler.sendResponse(asOptionalPlatform(params.platform), {
74
+ channelId: asOptionalString(params.channel),
75
+ userId: asOptionalString(params.user_id),
76
+ threadId: asOptionalString(params.thread_id),
77
+ }, asRequiredString(params.message, "message"));
78
+ case "chat/replyThread":
79
+ return this.deps.outboundHandler.sendResponse(asOptionalPlatform(params.platform), {
80
+ channelId: asRequiredString(params.channel, "channel"),
81
+ threadId: asRequiredString(params.thread_id, "thread_id"),
82
+ }, asRequiredString(params.message, "message"));
83
+ case "chat/askUser":
84
+ return this.deps.questionService.ask(asRequiredString(params.user_id, "user_id"), asRequiredString(params.question, "question"), asOptionalNumber(params.timeout_seconds) ?? 300, asOptionalPlatform(params.platform), asOptionalString(params.channel));
85
+ case "chat/listChannels":
86
+ return {
87
+ channels: await this.deps.outboundHandler.listChannels(asOptionalPlatform(params.platform)),
88
+ };
89
+ default:
90
+ throw new ValidationError(`Unsupported chat IPC method: ${method}`);
91
+ }
92
+ }
93
+ }
94
+ function buildErrorResponse(id, code, message) {
95
+ return {
96
+ jsonrpc: "2.0",
97
+ id,
98
+ error: {
99
+ code,
100
+ message,
101
+ },
102
+ };
103
+ }
104
+ function asRequiredString(value, field) {
105
+ if (typeof value !== "string" || value.length === 0) {
106
+ throw new ValidationError(`${field} must be a non-empty string.`);
107
+ }
108
+ return value;
109
+ }
110
+ function asOptionalString(value) {
111
+ return typeof value === "string" && value.length > 0 ? value : undefined;
112
+ }
113
+ function asOptionalNumber(value) {
114
+ return typeof value === "number" ? value : undefined;
115
+ }
116
+ function asOptionalPlatform(value) {
117
+ if (value === PlatformType.Slack) {
118
+ return PlatformType.Slack;
119
+ }
120
+ if (value === PlatformType.Discord) {
121
+ return PlatformType.Discord;
122
+ }
123
+ return undefined;
124
+ }
125
+ //# sourceMappingURL=chat-ipc-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-ipc-server.js","sourceRoot":"","sources":["../../../src/infrastructure/chat/chat-ipc-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { ChatConfig } from "../../core/types.js";
2
+ import type { ChatStructuredLogger } from "../../application/chat/chat-logging.js";
3
+ export interface ChatLogEntry {
4
+ ts: string;
5
+ level: "info" | "warn" | "error";
6
+ event: string;
7
+ [key: string]: unknown;
8
+ }
9
+ export declare class ChatLogger implements ChatStructuredLogger {
10
+ private readonly deps;
11
+ private readonly level;
12
+ constructor(deps: {
13
+ rootDir: string;
14
+ ensureLayout?: () => Promise<void>;
15
+ now?: () => Date;
16
+ logLevel?: ChatConfig["log_level"];
17
+ });
18
+ info(message: string, metadata?: Record<string, unknown>): Promise<void>;
19
+ warn(message: string, metadata?: Record<string, unknown>): Promise<void>;
20
+ error(message: string, metadata?: Record<string, unknown>): Promise<void>;
21
+ private write;
22
+ private ensureLogsDir;
23
+ private logPath;
24
+ }
@@ -0,0 +1,86 @@
1
+ import { appendFile, mkdir } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { maskCredentials, maskToken } from "./token-masking.js";
4
+ const PRIVATE_FILE_MODE = 0o600;
5
+ const LOG_LEVEL_ORDER = {
6
+ info: 0,
7
+ warn: 1,
8
+ error: 2,
9
+ };
10
+ export class ChatLogger {
11
+ deps;
12
+ level;
13
+ constructor(deps) {
14
+ this.deps = deps;
15
+ this.level = deps.logLevel ?? "info";
16
+ }
17
+ async info(message, metadata) {
18
+ await this.write("info", message, metadata);
19
+ }
20
+ async warn(message, metadata) {
21
+ await this.write("warn", message, metadata);
22
+ }
23
+ async error(message, metadata) {
24
+ await this.write("error", message, metadata);
25
+ }
26
+ async write(level, message, metadata) {
27
+ if (LOG_LEVEL_ORDER[level] < LOG_LEVEL_ORDER[this.level]) {
28
+ return;
29
+ }
30
+ const now = this.deps.now?.() ?? new Date();
31
+ const sanitizedMetadata = sanitizeMetadata(metadata);
32
+ const entry = {
33
+ ...(sanitizedMetadata ?? {}),
34
+ ts: now.toISOString(),
35
+ level,
36
+ event: message,
37
+ };
38
+ await this.ensureLogsDir();
39
+ await appendFile(this.logPath(now), `${JSON.stringify(entry)}\n`, {
40
+ encoding: "utf8",
41
+ mode: PRIVATE_FILE_MODE,
42
+ });
43
+ }
44
+ async ensureLogsDir() {
45
+ await this.deps.ensureLayout?.();
46
+ await mkdir(path.join(this.deps.rootDir, "logs"), { recursive: true });
47
+ }
48
+ logPath(date) {
49
+ return path.join(this.deps.rootDir, "logs", `chat-${date.toISOString().slice(0, 10)}.jsonl`);
50
+ }
51
+ }
52
+ function sanitizeMetadata(metadata) {
53
+ if (!metadata) {
54
+ return undefined;
55
+ }
56
+ return Object.fromEntries(Object.entries(metadata).map(([key, value]) => [key, sanitizeValue(key, value)]));
57
+ }
58
+ function sanitizeValue(key, value) {
59
+ if (value == null) {
60
+ return value;
61
+ }
62
+ if (typeof value === "string") {
63
+ return /token|secret/i.test(key) ? maskToken(value) : value;
64
+ }
65
+ if (Array.isArray(value)) {
66
+ return value.map((item) => sanitizeValue(key, item));
67
+ }
68
+ if (isChatCredentials(value)) {
69
+ return maskCredentials(value);
70
+ }
71
+ if (typeof value === "object") {
72
+ return Object.fromEntries(Object.entries(value).map(([nestedKey, nestedValue]) => [
73
+ nestedKey,
74
+ sanitizeValue(nestedKey, nestedValue),
75
+ ]));
76
+ }
77
+ return value;
78
+ }
79
+ function isChatCredentials(value) {
80
+ if (!value || typeof value !== "object") {
81
+ return false;
82
+ }
83
+ const candidate = value;
84
+ return "slack" in candidate || "discord" in candidate;
85
+ }
86
+ //# sourceMappingURL=chat-logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-logger.js","sourceRoot":"","sources":["../../../src/infrastructure/chat/chat-logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { CredentialsRepository } from "../../domain/chat/ports.js";
2
+ import type { ChatCredentials } from "../../domain/chat/types.js";
3
+ export declare class CredentialsFileRepository implements CredentialsRepository {
4
+ private readonly filePath;
5
+ constructor(rootDir: string);
6
+ load(): Promise<ChatCredentials | null>;
7
+ save(credentials: ChatCredentials): Promise<void>;
8
+ }
@@ -0,0 +1,84 @@
1
+ import { chmod, mkdir, readFile, stat, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { ValidationError } from "../../core/errors.js";
4
+ const PRIVATE_FILE_MODE = 0o600;
5
+ export class CredentialsFileRepository {
6
+ filePath;
7
+ constructor(rootDir) {
8
+ this.filePath = path.join(rootDir, "chat-credentials.json");
9
+ }
10
+ async load() {
11
+ try {
12
+ const [raw, info] = await Promise.all([
13
+ readFile(this.filePath, "utf8"),
14
+ stat(this.filePath),
15
+ ]);
16
+ if ((info.mode & 0o777) !== PRIVATE_FILE_MODE) {
17
+ throw new ValidationError("chat-credentials.json must have 0600 permissions.", {
18
+ recoveryHint: "Run `chmod 600 .relay/chat-credentials.json` and retry.",
19
+ });
20
+ }
21
+ return deserializeCredentials(JSON.parse(raw));
22
+ }
23
+ catch (error) {
24
+ if (typeof error === "object" &&
25
+ error !== null &&
26
+ "code" in error &&
27
+ error.code === "ENOENT") {
28
+ return null;
29
+ }
30
+ throw error;
31
+ }
32
+ }
33
+ async save(credentials) {
34
+ await mkdir(path.dirname(this.filePath), { recursive: true });
35
+ await writeFile(this.filePath, `${JSON.stringify(serializeCredentials(credentials), null, 2)}\n`, {
36
+ encoding: "utf8",
37
+ mode: PRIVATE_FILE_MODE,
38
+ });
39
+ await chmod(this.filePath, PRIVATE_FILE_MODE);
40
+ }
41
+ }
42
+ function serializeCredentials(credentials) {
43
+ return {
44
+ slack: credentials.slack && serializeSlack(credentials.slack),
45
+ discord: credentials.discord && serializeDiscord(credentials.discord),
46
+ };
47
+ }
48
+ function deserializeCredentials(value) {
49
+ return {
50
+ slack: value.slack && deserializeSlack(value.slack),
51
+ discord: value.discord && deserializeDiscord(value.discord),
52
+ };
53
+ }
54
+ function serializeSlack(credentials) {
55
+ return {
56
+ bot_token: credentials.botToken,
57
+ app_token: credentials.appToken,
58
+ team_id: credentials.teamId,
59
+ bot_user_id: credentials.botUserId,
60
+ };
61
+ }
62
+ function serializeDiscord(credentials) {
63
+ return {
64
+ bot_token: credentials.botToken,
65
+ application_id: credentials.applicationId,
66
+ bot_user_id: credentials.botUserId,
67
+ };
68
+ }
69
+ function deserializeSlack(credentials) {
70
+ return {
71
+ botToken: credentials.bot_token,
72
+ appToken: credentials.app_token,
73
+ teamId: credentials.team_id,
74
+ botUserId: credentials.bot_user_id,
75
+ };
76
+ }
77
+ function deserializeDiscord(credentials) {
78
+ return {
79
+ botToken: credentials.bot_token,
80
+ applicationId: credentials.application_id,
81
+ botUserId: credentials.bot_user_id,
82
+ };
83
+ }
84
+ //# sourceMappingURL=credentials-repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials-repository.js","sourceRoot":"","sources":["../../../src/infrastructure/chat/credentials-repository.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { SessionBridgeRepository } from "../../domain/chat/ports.js";
2
+ import { SessionBridge, ThreadKey } from "../../domain/chat/session-bridge.js";
3
+ export declare class SessionBridgeFileRepository implements SessionBridgeRepository {
4
+ private readonly directoryPath;
5
+ private loaded;
6
+ private readonly bridges;
7
+ constructor(rootDir: string);
8
+ findByThreadKey(key: ThreadKey): SessionBridge | null;
9
+ save(bridge: SessionBridge): void;
10
+ findAllActive(timeoutMinutes: number): SessionBridge[];
11
+ private ensureLoaded;
12
+ private persistGroup;
13
+ }