@sarkar-ai/deskmate 0.2.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.
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TelegramClient = void 0;
4
+ const grammy_1 = require("grammy");
5
+ const logger_1 = require("../core/logger");
6
+ const log = (0, logger_1.createLogger)("TelegramClient");
7
+ class TelegramClient {
8
+ clientType = "telegram";
9
+ bot;
10
+ handler = null;
11
+ constructor(token) {
12
+ this.bot = new grammy_1.Bot(token);
13
+ }
14
+ async start(handler) {
15
+ this.handler = handler;
16
+ // Map commands to IncomingMessage
17
+ this.bot.command("start", (ctx) => this.dispatch(ctx, "start"));
18
+ this.bot.command("screenshot", (ctx) => this.dispatch(ctx, "screenshot"));
19
+ this.bot.command("status", (ctx) => this.dispatch(ctx, "status"));
20
+ this.bot.command("reset", (ctx) => this.dispatch(ctx, "reset"));
21
+ // Text messages
22
+ this.bot.on("message:text", async (ctx) => {
23
+ if (!this.handler)
24
+ return;
25
+ await this.handler.handleMessage({
26
+ platformMessageId: String(ctx.message.message_id),
27
+ channelId: String(ctx.chat.id),
28
+ userId: String(ctx.from?.id ?? ""),
29
+ clientType: this.clientType,
30
+ text: ctx.message.text,
31
+ });
32
+ });
33
+ // Approval callbacks
34
+ this.bot.callbackQuery(/^approve:(.+)$/, async (ctx) => {
35
+ if (!this.handler)
36
+ return;
37
+ const actionId = ctx.match[1];
38
+ await this.handler.handleApproval({ actionId, approved: true }, String(ctx.chat?.id ?? ""));
39
+ await ctx.answerCallbackQuery({ text: "Approved!" });
40
+ });
41
+ this.bot.callbackQuery(/^reject:(.+)$/, async (ctx) => {
42
+ if (!this.handler)
43
+ return;
44
+ const actionId = ctx.match[1];
45
+ await this.handler.handleApproval({ actionId, approved: false }, String(ctx.chat?.id ?? ""));
46
+ await ctx.answerCallbackQuery({ text: "Rejected" });
47
+ });
48
+ // Start polling
49
+ this.bot.start({
50
+ drop_pending_updates: true,
51
+ onStart: (info) => log.info("Telegram bot started", { username: info.username }),
52
+ });
53
+ }
54
+ async dispatch(ctx, command) {
55
+ if (!this.handler)
56
+ return;
57
+ await this.handler.handleMessage({
58
+ platformMessageId: String(ctx.message?.message_id ?? ""),
59
+ channelId: String(ctx.chat.id),
60
+ userId: String(ctx.from?.id ?? ""),
61
+ clientType: this.clientType,
62
+ text: ctx.message?.text ?? "",
63
+ command,
64
+ });
65
+ }
66
+ async sendMessage(message) {
67
+ const chatId = Number(message.channelId);
68
+ // Image message
69
+ if (message.image) {
70
+ const source = typeof message.image === "string"
71
+ ? new grammy_1.InputFile(message.image)
72
+ : new grammy_1.InputFile(message.image);
73
+ const sent = await this.bot.api.sendPhoto(chatId, source, {
74
+ caption: message.imageCaption,
75
+ });
76
+ return String(sent.message_id);
77
+ }
78
+ const text = message.text ?? "";
79
+ const truncated = text.slice(0, 4000);
80
+ // Edit existing message
81
+ if (message.editMessageId) {
82
+ const msgId = Number(message.editMessageId);
83
+ try {
84
+ if (message.parseMode === "markdown") {
85
+ await this.bot.api.editMessageText(chatId, msgId, truncated, {
86
+ parse_mode: "Markdown",
87
+ });
88
+ }
89
+ else {
90
+ await this.bot.api.editMessageText(chatId, msgId, truncated);
91
+ }
92
+ }
93
+ catch {
94
+ // If markdown parse fails, retry as plain
95
+ await this.bot.api.editMessageText(chatId, msgId, truncated);
96
+ }
97
+ return message.editMessageId;
98
+ }
99
+ // New message
100
+ const opts = {};
101
+ if (message.parseMode === "markdown") {
102
+ opts.parse_mode = "Markdown";
103
+ }
104
+ const sent = await this.bot.api.sendMessage(chatId, truncated, opts);
105
+ return String(sent.message_id);
106
+ }
107
+ async sendApprovalPrompt(prompt) {
108
+ const chatId = Number(prompt.channelId);
109
+ const keyboard = new grammy_1.InlineKeyboard()
110
+ .text("Approve", `approve:${prompt.actionId}`)
111
+ .text("Reject", `reject:${prompt.actionId}`);
112
+ let emoji = "";
113
+ switch (prompt.type) {
114
+ case "command":
115
+ emoji = "Command";
116
+ break;
117
+ case "write_file":
118
+ emoji = "Write File";
119
+ break;
120
+ case "folder_access":
121
+ emoji = "Folder Access";
122
+ break;
123
+ case "read_file":
124
+ emoji = "Read File";
125
+ break;
126
+ default:
127
+ emoji = "Action";
128
+ }
129
+ await this.bot.api.sendMessage(chatId, `*Approval Required — ${emoji}*\n\n` +
130
+ `Type: \`${prompt.type}\`\n` +
131
+ `${prompt.description}\n\n` +
132
+ `${prompt.details}\n\n` +
133
+ `Expires in ${prompt.expiresInSeconds}s`, { parse_mode: "Markdown", reply_markup: keyboard });
134
+ }
135
+ async updateApprovalStatus(channelId, _actionId, approved) {
136
+ // The callback query handler already answered; we don't need to
137
+ // edit anything extra here since Grammy's callbackQuery handler
138
+ // will have already been invoked. If we wanted to edit the original
139
+ // approval message we'd need to track message IDs per actionId.
140
+ // For now, the inline callback answer suffices.
141
+ log.debug("Approval status updated", { channelId, approved });
142
+ }
143
+ async stop() {
144
+ this.bot.stop();
145
+ log.info("Telegram bot stopped");
146
+ }
147
+ }
148
+ exports.TelegramClient = TelegramClient;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ /**
3
+ * Agent Provider Factory
4
+ *
5
+ * Creates agent provider instances based on configuration.
6
+ * Ships with claude-code as the default provider.
7
+ * Users can register custom providers via registerProvider().
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.createAgentProvider = createAgentProvider;
11
+ exports.getDefaultProviderType = getDefaultProviderType;
12
+ exports.getAvailableProviders = getAvailableProviders;
13
+ exports.registerProvider = registerProvider;
14
+ exports.isProviderAvailable = isProviderAvailable;
15
+ const claude_code_1 = require("./providers/claude-code");
16
+ const logger_1 = require("../logger");
17
+ const log = (0, logger_1.createLogger)("AgentFactory");
18
+ // Registry of available providers — extensible via registerProvider()
19
+ const providerRegistry = new Map([
20
+ ["claude-code", claude_code_1.ClaudeCodeProvider],
21
+ ]);
22
+ /**
23
+ * Create an agent provider based on configuration
24
+ */
25
+ function createAgentProvider(config) {
26
+ const providerType = config?.type || getDefaultProviderType();
27
+ log.info("Creating agent provider", { type: providerType });
28
+ const ProviderClass = providerRegistry.get(providerType);
29
+ if (!ProviderClass) {
30
+ const available = Array.from(providerRegistry.keys()).join(", ");
31
+ throw new Error(`Unknown agent provider: ${providerType}. Available: ${available}`);
32
+ }
33
+ return new ProviderClass();
34
+ }
35
+ /**
36
+ * Get the default provider type from environment or fallback
37
+ */
38
+ function getDefaultProviderType() {
39
+ const envProvider = process.env.AGENT_PROVIDER;
40
+ if (envProvider && providerRegistry.has(envProvider)) {
41
+ return envProvider;
42
+ }
43
+ return "claude-code";
44
+ }
45
+ /**
46
+ * Get list of available provider types
47
+ */
48
+ function getAvailableProviders() {
49
+ return Array.from(providerRegistry.keys());
50
+ }
51
+ /**
52
+ * Register a custom agent provider.
53
+ *
54
+ * Example:
55
+ * registerProvider("my-agent", MyAgentProvider);
56
+ * // then set AGENT_PROVIDER=my-agent in .env
57
+ */
58
+ function registerProvider(type, providerClass) {
59
+ log.info("Registering custom provider", { type });
60
+ providerRegistry.set(type, providerClass);
61
+ }
62
+ /**
63
+ * Check if a provider type is available
64
+ */
65
+ function isProviderAvailable(type) {
66
+ return providerRegistry.has(type);
67
+ }
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ /**
3
+ * Agent Module
4
+ *
5
+ * Provides an abstraction layer for AI agent providers.
6
+ * Ships with claude-code as the default (and recommended) provider.
7
+ *
8
+ * Usage:
9
+ * import { createAgentProvider } from "./core/agent";
10
+ * const agent = createAgentProvider();
11
+ * const response = await agent.query("Hello");
12
+ *
13
+ * To add a custom provider:
14
+ * 1. Implement the AgentProvider interface
15
+ * 2. Call registerProvider("my-agent", MyProvider)
16
+ * 3. Set AGENT_PROVIDER=my-agent in .env
17
+ *
18
+ * Environment:
19
+ * AGENT_PROVIDER - Set to override default provider (default: "claude-code")
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.ClaudeCodeProvider = exports.isProviderAvailable = exports.registerProvider = exports.getAvailableProviders = exports.getDefaultProviderType = exports.createAgentProvider = void 0;
23
+ // Factory
24
+ var factory_1 = require("./factory");
25
+ Object.defineProperty(exports, "createAgentProvider", { enumerable: true, get: function () { return factory_1.createAgentProvider; } });
26
+ Object.defineProperty(exports, "getDefaultProviderType", { enumerable: true, get: function () { return factory_1.getDefaultProviderType; } });
27
+ Object.defineProperty(exports, "getAvailableProviders", { enumerable: true, get: function () { return factory_1.getAvailableProviders; } });
28
+ Object.defineProperty(exports, "registerProvider", { enumerable: true, get: function () { return factory_1.registerProvider; } });
29
+ Object.defineProperty(exports, "isProviderAvailable", { enumerable: true, get: function () { return factory_1.isProviderAvailable; } });
30
+ // Built-in provider
31
+ var claude_code_1 = require("./providers/claude-code");
32
+ Object.defineProperty(exports, "ClaudeCodeProvider", { enumerable: true, get: function () { return claude_code_1.ClaudeCodeProvider; } });
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ /**
3
+ * Claude Code Agent Provider
4
+ *
5
+ * Uses the Claude Agent SDK (@anthropic-ai/claude-agent-sdk) to process requests.
6
+ * This provider leverages Claude Code's built-in tools (Bash, Read, Write, etc.)
7
+ */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.ClaudeCodeProvider = void 0;
43
+ const claude_agent_sdk_1 = require("@anthropic-ai/claude-agent-sdk");
44
+ const logger_1 = require("../../logger");
45
+ const log = (0, logger_1.createLogger)("ClaudeCodeProvider");
46
+ class ClaudeCodeProvider {
47
+ name = "claude-code";
48
+ version = "1.0.0";
49
+ defaultTools = ["Bash", "Read", "Write", "Edit", "Glob", "Grep"];
50
+ async query(prompt, options) {
51
+ let result = "";
52
+ let sessionId;
53
+ for await (const event of this.queryStream(prompt, options)) {
54
+ if (event.type === "done" && event.response) {
55
+ return event.response;
56
+ }
57
+ if (event.type === "text" && event.text) {
58
+ result = event.text;
59
+ }
60
+ }
61
+ return { text: result, sessionId };
62
+ }
63
+ async *queryStream(prompt, options) {
64
+ const queryOptions = {
65
+ systemPrompt: options?.systemPrompt,
66
+ cwd: options?.workingDir || process.env.WORKING_DIR || process.env.HOME || "/",
67
+ allowedTools: options?.allowedTools || this.defaultTools,
68
+ permissionMode: "bypassPermissions",
69
+ maxTurns: options?.maxTurns || 10,
70
+ };
71
+ // Resume session if provided
72
+ if (options?.sessionId) {
73
+ queryOptions.resume = options.sessionId;
74
+ log.debug("Resuming session", { sessionId: options.sessionId });
75
+ }
76
+ let result = "";
77
+ let sessionId;
78
+ let usedTools = false;
79
+ try {
80
+ yield { type: "thinking" };
81
+ for await (const message of (0, claude_agent_sdk_1.query)({ prompt, options: queryOptions })) {
82
+ log.debug("Agent message", { type: message.type, subtype: message.subtype });
83
+ // Capture session ID from init message
84
+ if (message.type === "system" && message.subtype === "init") {
85
+ sessionId = message.session_id;
86
+ log.debug("Got session ID", { sessionId });
87
+ }
88
+ // Handle result messages
89
+ if ("result" in message && message.result) {
90
+ result = message.result;
91
+ }
92
+ // Handle assistant text messages
93
+ if (message.type === "assistant" && "content" in message) {
94
+ const content = message.content;
95
+ if (Array.isArray(content)) {
96
+ for (const block of content) {
97
+ if (block.type === "text" && block.text) {
98
+ result = block.text;
99
+ yield { type: "text", text: block.text };
100
+ }
101
+ if (block.type === "tool_use") {
102
+ usedTools = true;
103
+ yield {
104
+ type: "tool_use",
105
+ toolName: block.name,
106
+ toolInput: block.input,
107
+ };
108
+ }
109
+ }
110
+ }
111
+ }
112
+ // Handle tool results
113
+ if (message.type === "user" && "content" in message) {
114
+ const content = message.content;
115
+ if (Array.isArray(content)) {
116
+ for (const block of content) {
117
+ if (block.type === "tool_result") {
118
+ yield {
119
+ type: "tool_result",
120
+ toolResult: typeof block.content === "string"
121
+ ? block.content
122
+ : JSON.stringify(block.content),
123
+ };
124
+ }
125
+ }
126
+ }
127
+ }
128
+ }
129
+ yield {
130
+ type: "done",
131
+ response: {
132
+ text: result || "Task completed (no output)",
133
+ sessionId,
134
+ usedTools,
135
+ },
136
+ };
137
+ }
138
+ catch (error) {
139
+ log.error("Query failed", { error: error.message });
140
+ yield { type: "error", error: error.message };
141
+ }
142
+ }
143
+ async isAvailable() {
144
+ try {
145
+ // Check if claude CLI is available
146
+ const { exec } = await Promise.resolve().then(() => __importStar(require("child_process")));
147
+ const { promisify } = await Promise.resolve().then(() => __importStar(require("util")));
148
+ const execAsync = promisify(exec);
149
+ await execAsync("which claude");
150
+ return true;
151
+ }
152
+ catch {
153
+ log.warn("Claude Code CLI not found");
154
+ return false;
155
+ }
156
+ }
157
+ }
158
+ exports.ClaudeCodeProvider = ClaudeCodeProvider;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ /**
3
+ * Agent Provider Abstraction
4
+ *
5
+ * This module defines the interface for AI agent providers.
6
+ * Implement this interface to add support for different AI backends
7
+ * (Claude Code, OpenAI, local LLMs, etc.)
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.approvalManager = exports.ApprovalManager = void 0;
4
+ const events_1 = require("events");
5
+ const logger_1 = require("./logger");
6
+ const platform_1 = require("./platform");
7
+ const log = (0, logger_1.createLogger)("Approval");
8
+ // Protected folders that require approval (platform-aware)
9
+ const PROTECTED_FOLDERS = (0, platform_1.getProtectedFolderPatterns)();
10
+ class ApprovalManager extends events_1.EventEmitter {
11
+ pendingActions = new Map();
12
+ notifiers = [];
13
+ autoApprovePatterns = [];
14
+ defaultTimeoutMs = 5 * 60 * 1000; // 5 minutes
15
+ approvedFolders = new Set(); // Folders approved in this session
16
+ requireApprovalForAll;
17
+ constructor() {
18
+ super();
19
+ this.requireApprovalForAll = process.env.REQUIRE_APPROVAL_FOR_ALL === "true";
20
+ // Load pre-approved folders from env
21
+ const allowedFolders = process.env.ALLOWED_FOLDERS?.split(":").filter(Boolean) || [];
22
+ allowedFolders.forEach(folder => this.approvedFolders.add(folder));
23
+ }
24
+ // Check if a path requires folder access approval
25
+ isProtectedPath(filePath) {
26
+ // If folder is already approved in this session, no approval needed
27
+ for (const approved of this.approvedFolders) {
28
+ if (filePath.startsWith(approved)) {
29
+ return false;
30
+ }
31
+ }
32
+ // Check against protected folder patterns
33
+ return PROTECTED_FOLDERS.some(pattern => pattern.test(filePath));
34
+ }
35
+ // Mark a folder as approved for this session
36
+ approveFolder(folderPath) {
37
+ this.approvedFolders.add(folderPath);
38
+ log.info("Folder approved for session", { folderPath });
39
+ }
40
+ // Request approval for folder access
41
+ async requestFolderAccess(filePath) {
42
+ if (!this.isProtectedPath(filePath)) {
43
+ return true; // Not protected, no approval needed
44
+ }
45
+ // Extract the base protected folder from the path
46
+ const baseFolder = (0, platform_1.extractBaseFolder)(filePath) || filePath;
47
+ const approved = await this.requestApproval("folder_access", `Access to ${baseFolder}`, { path: filePath, baseFolder }, { autoApprove: false, timeoutMs: 2 * 60 * 1000 } // 2 minute timeout
48
+ );
49
+ if (approved) {
50
+ this.approveFolder(baseFolder);
51
+ }
52
+ return approved;
53
+ }
54
+ addNotifier(notifier) {
55
+ this.notifiers.push(notifier);
56
+ }
57
+ addAutoApprovePattern(pattern) {
58
+ this.autoApprovePatterns.push(pattern);
59
+ }
60
+ shouldAutoApprove(action) {
61
+ // If require approval for all is set, never auto-approve
62
+ if (this.requireApprovalForAll) {
63
+ return false;
64
+ }
65
+ // Folder access always requires explicit approval
66
+ if (action.type === "folder_access") {
67
+ return false;
68
+ }
69
+ if (action.type === "command") {
70
+ const cmd = action.details.command;
71
+ // Safe read-only commands
72
+ const safeCommands = [
73
+ /^ls\b/,
74
+ /^pwd$/,
75
+ /^whoami$/,
76
+ /^date$/,
77
+ /^cat\s/,
78
+ /^head\s/,
79
+ /^tail\s/,
80
+ /^wc\s/,
81
+ /^du\s/,
82
+ /^df\s/,
83
+ /^echo\s/,
84
+ /^which\s/,
85
+ /^type\s/,
86
+ /^file\s/,
87
+ /^stat\s/,
88
+ /^uname/,
89
+ /^hostname$/,
90
+ /^uptime$/,
91
+ /^ps\b/,
92
+ /^top\s+-l\s+1/,
93
+ /^docker\s+ps/,
94
+ /^docker\s+images/,
95
+ /^git\s+status/,
96
+ /^git\s+log/,
97
+ /^git\s+branch/,
98
+ /^git\s+diff/,
99
+ /^npm\s+list/,
100
+ /^node\s+-v/,
101
+ /^python\s+--version/,
102
+ ];
103
+ for (const pattern of [...safeCommands, ...this.autoApprovePatterns]) {
104
+ if (pattern.test(cmd)) {
105
+ return true;
106
+ }
107
+ }
108
+ }
109
+ return false;
110
+ }
111
+ async requestApproval(type, description, details, options) {
112
+ const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
113
+ const timeoutMs = options?.timeoutMs || this.defaultTimeoutMs;
114
+ const action = {
115
+ id,
116
+ type,
117
+ description,
118
+ details,
119
+ createdAt: new Date(),
120
+ expiresAt: new Date(Date.now() + timeoutMs),
121
+ resolve: () => { }, // Will be set below
122
+ };
123
+ // Check for auto-approve
124
+ if (options?.autoApprove !== false && this.shouldAutoApprove(action)) {
125
+ log.info("Action auto-approved", { id, type, description });
126
+ log.debug("Auto-approved action details", { details });
127
+ this.emit("auto-approved", action);
128
+ return true;
129
+ }
130
+ log.info("Approval requested", { id, type, description, expiresAt: action.expiresAt });
131
+ // Create a promise that will be resolved when approved/rejected
132
+ const approvalPromise = new Promise((resolve) => {
133
+ action.resolve = resolve;
134
+ });
135
+ this.pendingActions.set(id, action);
136
+ // Set up timeout
137
+ const timeout = setTimeout(() => {
138
+ if (this.pendingActions.has(id)) {
139
+ log.warn("Approval request expired", { id, type, description });
140
+ this.pendingActions.delete(id);
141
+ action.resolve(false);
142
+ this.emit("expired", action);
143
+ }
144
+ }, timeoutMs);
145
+ // Notify all registered notifiers
146
+ for (const notifier of this.notifiers) {
147
+ try {
148
+ await notifier(action);
149
+ }
150
+ catch (error) {
151
+ console.error("Notifier error:", error);
152
+ }
153
+ }
154
+ this.emit("pending", action);
155
+ // Wait for approval
156
+ const result = await approvalPromise;
157
+ clearTimeout(timeout);
158
+ this.pendingActions.delete(id);
159
+ return result;
160
+ }
161
+ approve(actionId) {
162
+ const action = this.pendingActions.get(actionId);
163
+ if (action) {
164
+ log.info("Action approved", { id: actionId, type: action.type, description: action.description });
165
+ action.resolve(true);
166
+ this.emit("approved", action);
167
+ return true;
168
+ }
169
+ log.warn("Attempted to approve unknown action", { actionId });
170
+ return false;
171
+ }
172
+ reject(actionId) {
173
+ const action = this.pendingActions.get(actionId);
174
+ if (action) {
175
+ log.info("Action rejected", { id: actionId, type: action.type, description: action.description });
176
+ action.resolve(false);
177
+ this.emit("rejected", action);
178
+ return true;
179
+ }
180
+ log.warn("Attempted to reject unknown action", { actionId });
181
+ return false;
182
+ }
183
+ getPendingActions() {
184
+ return Array.from(this.pendingActions.values());
185
+ }
186
+ getPendingAction(id) {
187
+ return this.pendingActions.get(id);
188
+ }
189
+ }
190
+ exports.ApprovalManager = ApprovalManager;
191
+ // Singleton instance
192
+ exports.approvalManager = new ApprovalManager();