remote-copilot-mcp 1.0.1

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Andriy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # remote-copilot-mcp
2
+
3
+ An MCP (Model Context Protocol) server for remote control of AI assistants via Telegram.
4
+
5
+ ## Overview
6
+
7
+ This server exposes two tools that allow an AI assistant (e.g. GitHub Copilot) to be operated remotely through a Telegram bot:
8
+
9
+ | Tool | Description |
10
+ |------|-------------|
11
+ | `remote_copilot_wait_for_instructions` | Blocks (long-polls Telegram) until a new message arrives or the timeout elapses. Returns the prompt in a format that instructs the agent to act and then call the tool again, keeping the feedback loop alive. |
12
+ | `report_progress` | Sends a progress update or result message back to the operator via Telegram. |
13
+
14
+ ## Prerequisites
15
+
16
+ - Node.js 18 or later (uses native `fetch`)
17
+ - A [Telegram bot token](https://core.telegram.org/bots#botfather) (`TELEGRAM_TOKEN`)
18
+ - The chat ID of the operator (`TELEGRAM_CHAT_ID`)
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ npm install
24
+ npm run build
25
+ ```
26
+
27
+ ## Configuration
28
+
29
+ Set the following environment variables:
30
+
31
+ | Variable | Required | Default | Description |
32
+ |----------|----------|---------|-------------|
33
+ | `TELEGRAM_TOKEN` | ✅ | — | Telegram Bot API token from @BotFather |
34
+ | `TELEGRAM_CHAT_ID` | ✅ | — | Chat / user ID that the bot will listen to |
35
+ | `WAIT_TIMEOUT_MINUTES` | ❌ | `30` | Minutes to wait for a message before timing out |
36
+
37
+ ## Usage
38
+
39
+ ### Run directly
40
+
41
+ ```bash
42
+ TELEGRAM_TOKEN=<token> TELEGRAM_CHAT_ID=<chat_id> npm start
43
+ ```
44
+
45
+ ### Configure in MCP client (e.g. VS Code Copilot)
46
+
47
+ Add to your MCP configuration:
48
+
49
+ ```json
50
+ {
51
+ "mcpServers": {
52
+ "remote-copilot": {
53
+ "command": "node",
54
+ "args": ["/path/to/remote-copilot-mcp/dist/index.js"],
55
+ "env": {
56
+ "TELEGRAM_TOKEN": "<your-bot-token>",
57
+ "TELEGRAM_CHAT_ID": "<your-chat-id>",
58
+ "WAIT_TIMEOUT_MINUTES": "30"
59
+ }
60
+ }
61
+ }
62
+ }
63
+ ```
64
+
65
+ ## How it works
66
+
67
+ 1. The AI assistant calls `remote_copilot_wait_for_instructions`.
68
+ 2. The server long-polls the Telegram Bot API (`getUpdates`) in 45-second windows until a message is received or the configured timeout elapses.
69
+ 3. When a message arrives the tool returns:
70
+ > *Follow the instructions: \<prompt\>. Create plan, use subagents. Use web search for framework/pattern related concerns. Use report_progress tool to proactively report progress to the user. After you're done (don't skip this step), call remote_copilot_wait_for_instructions again to keep the feedback loop alive*
71
+ 4. If the timeout elapses with no message the tool returns a notice instructing the assistant to call `remote_copilot_wait_for_instructions` again.
72
+ 5. At any point the assistant can call `report_progress` to send a status update back to the Telegram chat.
73
+
74
+ ## Development
75
+
76
+ ```bash
77
+ # Run in development mode (no build step needed)
78
+ TELEGRAM_TOKEN=<token> TELEGRAM_CHAT_ID=<chat_id> npm run dev
79
+
80
+ # Compile TypeScript
81
+ npm run build
82
+ ```
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Remote Copilot MCP Server
4
+ *
5
+ * Exposes two tools for AI assistants:
6
+ * - remote_copilot_wait_for_instructions Poll Telegram for new user messages.
7
+ * - report_progress Send a progress update to Telegram.
8
+ *
9
+ * Required environment variables:
10
+ * TELEGRAM_TOKEN – Telegram Bot API token.
11
+ * TELEGRAM_CHAT_ID – Target chat / user ID.
12
+ *
13
+ * Optional environment variables:
14
+ * WAIT_TIMEOUT_MINUTES – How long to wait for a message before timing out
15
+ * and instructing the agent to call the tool again
16
+ * (default: 30).
17
+ */
18
+ export {};
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;GAeG"}
package/dist/index.js ADDED
@@ -0,0 +1,229 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Remote Copilot MCP Server
4
+ *
5
+ * Exposes two tools for AI assistants:
6
+ * - remote_copilot_wait_for_instructions Poll Telegram for new user messages.
7
+ * - report_progress Send a progress update to Telegram.
8
+ *
9
+ * Required environment variables:
10
+ * TELEGRAM_TOKEN – Telegram Bot API token.
11
+ * TELEGRAM_CHAT_ID – Target chat / user ID.
12
+ *
13
+ * Optional environment variables:
14
+ * WAIT_TIMEOUT_MINUTES – How long to wait for a message before timing out
15
+ * and instructing the agent to call the tool again
16
+ * (default: 30).
17
+ */
18
+ import { createRequire } from "module";
19
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
20
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
21
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
22
+ import { TelegramClient } from "./telegram.js";
23
+ const _require = createRequire(import.meta.url);
24
+ const { version: PKG_VERSION } = _require("../package.json");
25
+ // ---------------------------------------------------------------------------
26
+ // Configuration
27
+ // ---------------------------------------------------------------------------
28
+ const TELEGRAM_TOKEN = process.env.TELEGRAM_TOKEN ?? "";
29
+ const TELEGRAM_CHAT_ID = process.env.TELEGRAM_CHAT_ID ?? "";
30
+ const rawWaitTimeoutMinutes = parseInt(process.env.WAIT_TIMEOUT_MINUTES ?? "", 10);
31
+ const WAIT_TIMEOUT_MINUTES = Math.max(1, Number.isFinite(rawWaitTimeoutMinutes) ? rawWaitTimeoutMinutes : 30);
32
+ if (!TELEGRAM_TOKEN || !TELEGRAM_CHAT_ID) {
33
+ process.stderr.write("Error: TELEGRAM_TOKEN and TELEGRAM_CHAT_ID environment variables are required.\n");
34
+ process.exit(1);
35
+ }
36
+ // ---------------------------------------------------------------------------
37
+ // Telegram client + update-offset tracking
38
+ // ---------------------------------------------------------------------------
39
+ const telegram = new TelegramClient(TELEGRAM_TOKEN);
40
+ /**
41
+ * The next update_id we want to receive.
42
+ * Persisted across tool calls within the same server process so we never
43
+ * re-deliver a message that was already handled.
44
+ *
45
+ * Initialised by draining the existing Telegram update backlog on startup
46
+ * so that old messages are not replayed as instructions.
47
+ */
48
+ let nextUpdateId = await (async () => {
49
+ try {
50
+ let offset = 0;
51
+ for (;;) {
52
+ const updates = await telegram.getUpdates(offset, 0);
53
+ if (updates.length === 0)
54
+ break;
55
+ offset = updates[updates.length - 1].update_id + 1;
56
+ }
57
+ return offset;
58
+ }
59
+ catch (err) {
60
+ process.stderr.write(`Warning: Failed to drain Telegram update backlog on startup: ${err instanceof Error ? err.message : String(err)}\n` +
61
+ "Old messages may be replayed. Continuing with offset 0.\n");
62
+ return 0;
63
+ }
64
+ })();
65
+ // ---------------------------------------------------------------------------
66
+ // MCP Server
67
+ // ---------------------------------------------------------------------------
68
+ const server = new Server({ name: "remote-copilot-mcp", version: PKG_VERSION }, { capabilities: { tools: {} } });
69
+ // ── Tool definitions ────────────────────────────────────────────────────────
70
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
71
+ tools: [
72
+ {
73
+ name: "remote_copilot_wait_for_instructions",
74
+ description: "Wait for a new instruction message from the operator via Telegram. " +
75
+ "The call blocks (long-polls) until a message arrives or the configured " +
76
+ "timeout elapses. If the timeout elapses with no message the tool output " +
77
+ "explicitly instructs the agent to call this tool again.",
78
+ inputSchema: {
79
+ type: "object",
80
+ properties: {},
81
+ required: [],
82
+ },
83
+ },
84
+ {
85
+ name: "report_progress",
86
+ description: "Send a progress update or result message to the operator via Telegram.",
87
+ inputSchema: {
88
+ type: "object",
89
+ properties: {
90
+ message: {
91
+ type: "string",
92
+ description: "The progress update or result to report.",
93
+ },
94
+ },
95
+ required: ["message"],
96
+ },
97
+ },
98
+ ],
99
+ }));
100
+ // ── Tool implementations ────────────────────────────────────────────────────
101
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
102
+ const { name, arguments: args } = request.params;
103
+ // ── remote_copilot_wait_for_instructions ──────────────────────────────────
104
+ if (name === "remote_copilot_wait_for_instructions") {
105
+ const timeoutMs = WAIT_TIMEOUT_MINUTES * 60 * 1000;
106
+ const deadline = Date.now() + timeoutMs;
107
+ // Telegram's maximum long-poll timeout is 50 s; we loop until the
108
+ // wall-clock deadline is reached.
109
+ const POLL_TIMEOUT_SECONDS = 45;
110
+ while (Date.now() < deadline) {
111
+ const remaining = deadline - Date.now();
112
+ if (remaining <= 0)
113
+ break;
114
+ const pollSeconds = Math.min(POLL_TIMEOUT_SECONDS, Math.ceil(remaining / 1000));
115
+ const controller = new AbortController();
116
+ // Give the fetch a bit of extra headroom over the Telegram timeout.
117
+ const fetchTimeoutId = setTimeout(() => controller.abort(), (pollSeconds + 10) * 1000);
118
+ let updates;
119
+ try {
120
+ updates = await telegram.getUpdates(nextUpdateId, pollSeconds, controller.signal);
121
+ }
122
+ catch (err) {
123
+ if (err instanceof Error && err.name === "AbortError") {
124
+ // Fetch was aborted due to per-request timeout; retry if overall
125
+ // deadline has not yet been reached.
126
+ continue;
127
+ }
128
+ throw new Error(`Failed to fetch updates from Telegram: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
129
+ }
130
+ finally {
131
+ clearTimeout(fetchTimeoutId);
132
+ }
133
+ if (updates.length > 0) {
134
+ // Advance the offset so we don't see these updates again.
135
+ nextUpdateId = updates[updates.length - 1].update_id + 1;
136
+ // Collect text messages from the target chat.
137
+ const messages = updates
138
+ .filter((u) => u.message?.text !== undefined &&
139
+ String(u.message.chat.id) === TELEGRAM_CHAT_ID)
140
+ .map((u) => u.message.text);
141
+ if (messages.length > 0) {
142
+ const prompt = messages.join("\n\n");
143
+ return {
144
+ content: [
145
+ {
146
+ type: "text",
147
+ text: `Follow the instructions: ${prompt}. ` +
148
+ `Create plan, use subagents. ` +
149
+ `Use web search for framework/pattern related concerns. ` +
150
+ `Use report_progress tool to proactively report progress to the user. ` +
151
+ `After you're done (don't skip this step), call remote_copilot_wait_for_instructions again to keep the feedback loop alive`,
152
+ },
153
+ ],
154
+ };
155
+ }
156
+ // Updates existed but none were relevant text messages from our chat;
157
+ // continue polling.
158
+ }
159
+ }
160
+ // Timeout elapsed with no actionable message.
161
+ return {
162
+ content: [
163
+ {
164
+ type: "text",
165
+ text: `No new instructions received within ${WAIT_TIMEOUT_MINUTES} minute(s). ` +
166
+ `Call remote_copilot_wait_for_instructions again to keep listening for instructions.`,
167
+ },
168
+ ],
169
+ };
170
+ }
171
+ // ── report_progress ───────────────────────────────────────────────────────
172
+ if (name === "report_progress") {
173
+ const message = typeof args?.message === "string"
174
+ ? args.message
175
+ : "";
176
+ if (!message) {
177
+ return {
178
+ content: [
179
+ {
180
+ type: "text",
181
+ text: "Error: 'message' argument is required for report_progress.",
182
+ },
183
+ ],
184
+ isError: true,
185
+ };
186
+ }
187
+ try {
188
+ await telegram.sendMessage(TELEGRAM_CHAT_ID, message);
189
+ }
190
+ catch (error) {
191
+ process.stderr.write(`Failed to send progress message via Telegram: ${error instanceof Error ? error.message : String(error)}\n`);
192
+ return {
193
+ content: [
194
+ {
195
+ type: "text",
196
+ text: "Error: Failed to send progress update to Telegram. " +
197
+ "Please check the Telegram configuration and try again.",
198
+ },
199
+ ],
200
+ isError: true,
201
+ };
202
+ }
203
+ return {
204
+ content: [
205
+ {
206
+ type: "text",
207
+ text: "Progress reported successfully.",
208
+ },
209
+ ],
210
+ };
211
+ }
212
+ // Unknown tool
213
+ return {
214
+ content: [
215
+ {
216
+ type: "text",
217
+ text: `Unknown tool: ${name}`,
218
+ },
219
+ ],
220
+ isError: true,
221
+ };
222
+ });
223
+ // ---------------------------------------------------------------------------
224
+ // Start the server
225
+ // ---------------------------------------------------------------------------
226
+ const transport = new StdioServerTransport();
227
+ await server.connect(transport);
228
+ process.stderr.write("Remote Copilot MCP server running on stdio.\n");
229
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Telegram Bot API client using native fetch.
3
+ * No third-party HTTP client required.
4
+ */
5
+ export interface TelegramMessage {
6
+ message_id: number;
7
+ chat: {
8
+ id: number;
9
+ };
10
+ text?: string;
11
+ date: number;
12
+ }
13
+ export interface TelegramUpdate {
14
+ update_id: number;
15
+ message?: TelegramMessage;
16
+ }
17
+ export interface GetUpdatesResult {
18
+ ok: boolean;
19
+ result: TelegramUpdate[];
20
+ description?: string;
21
+ }
22
+ export interface SendMessageResult {
23
+ ok: boolean;
24
+ result?: TelegramMessage;
25
+ description?: string;
26
+ }
27
+ export declare class TelegramClient {
28
+ private readonly token;
29
+ private readonly baseUrl;
30
+ constructor(token: string);
31
+ /**
32
+ * Long-poll for updates.
33
+ * @param offset Only return updates with update_id >= offset.
34
+ * @param timeout Long-poll server-side timeout in seconds (max 50 recommended).
35
+ */
36
+ getUpdates(offset: number, timeout: number, signal?: AbortSignal): Promise<TelegramUpdate[]>;
37
+ /**
38
+ * Send a text message to a chat.
39
+ */
40
+ sendMessage(chatId: string, text: string): Promise<void>;
41
+ }
42
+ //# sourceMappingURL=telegram.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegram.d.ts","sourceRoot":"","sources":["../src/telegram.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,eAAe,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,cAAc;IAGb,OAAO,CAAC,QAAQ,CAAC,KAAK;IAFlC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAEJ,KAAK,EAAE,MAAM;IAI1C;;;;OAIG;IACG,UAAU,CACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,EAAE,CAAC;IA8B5B;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAiC/D"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Telegram Bot API client using native fetch.
3
+ * No third-party HTTP client required.
4
+ */
5
+ export class TelegramClient {
6
+ token;
7
+ baseUrl;
8
+ constructor(token) {
9
+ this.token = token;
10
+ this.baseUrl = `https://api.telegram.org/bot${token}`;
11
+ }
12
+ /**
13
+ * Long-poll for updates.
14
+ * @param offset Only return updates with update_id >= offset.
15
+ * @param timeout Long-poll server-side timeout in seconds (max 50 recommended).
16
+ */
17
+ async getUpdates(offset, timeout, signal) {
18
+ const url = new URL(`${this.baseUrl}/getUpdates`);
19
+ url.searchParams.set("offset", String(offset));
20
+ url.searchParams.set("timeout", String(timeout));
21
+ url.searchParams.set("allowed_updates", JSON.stringify(["message"]));
22
+ const response = await fetch(url.toString(), signal ? { signal } : {});
23
+ let data;
24
+ try {
25
+ data = (await response.json());
26
+ }
27
+ catch (parseErr) {
28
+ process.stderr.write(`Failed to parse Telegram getUpdates response: ${parseErr instanceof Error ? parseErr.message : String(parseErr)}\n`);
29
+ data = undefined;
30
+ }
31
+ if (!response.ok) {
32
+ const description = data?.description ?? response.statusText;
33
+ throw new Error(`Telegram getUpdates failed: ${response.status} ${description}`);
34
+ }
35
+ if (!data || !data.ok) {
36
+ throw new Error(`Telegram API error in getUpdates${data?.description ? `: ${data.description}` : ""}`);
37
+ }
38
+ return data.result;
39
+ }
40
+ /**
41
+ * Send a text message to a chat.
42
+ */
43
+ async sendMessage(chatId, text) {
44
+ const url = `${this.baseUrl}/sendMessage`;
45
+ const response = await fetch(url, {
46
+ method: "POST",
47
+ headers: { "Content-Type": "application/json" },
48
+ body: JSON.stringify({ chat_id: chatId, text }),
49
+ });
50
+ let data;
51
+ let parseError;
52
+ try {
53
+ data = (await response.json());
54
+ }
55
+ catch (err) {
56
+ parseError = err;
57
+ data = undefined;
58
+ }
59
+ if (!response.ok) {
60
+ const description = data?.description ?? response.statusText;
61
+ throw new Error(`Telegram sendMessage failed: ${response.status} ${description}`);
62
+ }
63
+ if (data === undefined) {
64
+ throw new Error(`Telegram sendMessage failed: response body could not be parsed as JSON${parseError instanceof Error ? `: ${parseError.message}` : ""}`);
65
+ }
66
+ if (data.ok !== true) {
67
+ const description = data.description ?? "Unknown Telegram API error";
68
+ throw new Error(`Telegram API error in sendMessage: ${description}`);
69
+ }
70
+ }
71
+ }
72
+ //# sourceMappingURL=telegram.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegram.js","sourceRoot":"","sources":["../src/telegram.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "remote-copilot-mcp",
3
+ "version": "1.0.1",
4
+ "description": "MCP server for remote control of AI assistants via Telegram",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "remote-copilot-mcp": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "start": "node dist/index.js",
15
+ "dev": "tsx src/index.ts",
16
+ "prepublishOnly": "npm run build",
17
+ "test": "echo \"No tests defined\" && exit 0"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/andriyshevchenko/remote-copilot-mcp.git"
22
+ },
23
+ "keywords": [
24
+ "mcp",
25
+ "telegram",
26
+ "copilot",
27
+ "ai-assistant"
28
+ ],
29
+ "author": "",
30
+ "license": "MIT",
31
+ "type": "module",
32
+ "bugs": {
33
+ "url": "https://github.com/andriyshevchenko/remote-copilot-mcp/issues"
34
+ },
35
+ "homepage": "https://github.com/andriyshevchenko/remote-copilot-mcp#readme",
36
+ "dependencies": {
37
+ "@modelcontextprotocol/sdk": "^1.27.1"
38
+ },
39
+ "devDependencies": {
40
+ "@types/node": "^25.3.3",
41
+ "tsx": "^4.21.0",
42
+ "typescript": "^5.9.3"
43
+ }
44
+ }