alaska-ai 0.1.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.
- package/README.md +56 -0
- package/dist/adapters/slack.d.ts +130 -0
- package/dist/adapters/slack.js +1484 -0
- package/dist/adapters/slack.js.map +1 -0
- package/dist/backends/claude.d.ts +78 -0
- package/dist/backends/claude.js +452 -0
- package/dist/backends/claude.js.map +1 -0
- package/dist/backends/codex.d.ts +53 -0
- package/dist/backends/codex.js +324 -0
- package/dist/backends/codex.js.map +1 -0
- package/dist/cli/init.d.ts +50 -0
- package/dist/cli/init.js +386 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/prompt.d.ts +31 -0
- package/dist/cli/prompt.js +145 -0
- package/dist/cli/prompt.js.map +1 -0
- package/dist/cli/start.d.ts +28 -0
- package/dist/cli/start.js +522 -0
- package/dist/cli/start.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +65 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/callbacks.d.ts +32 -0
- package/dist/mcp/callbacks.js +181 -0
- package/dist/mcp/callbacks.js.map +1 -0
- package/dist/mcp/config.d.ts +15 -0
- package/dist/mcp/config.js +22 -0
- package/dist/mcp/config.js.map +1 -0
- package/dist/mcp/entry.d.ts +13 -0
- package/dist/mcp/entry.js +119 -0
- package/dist/mcp/entry.js.map +1 -0
- package/dist/mcp/file-browser.d.ts +15 -0
- package/dist/mcp/file-browser.js +135 -0
- package/dist/mcp/file-browser.js.map +1 -0
- package/dist/mcp/ipc-server.d.ts +64 -0
- package/dist/mcp/ipc-server.js +380 -0
- package/dist/mcp/ipc-server.js.map +1 -0
- package/dist/mcp/preview-server.d.ts +33 -0
- package/dist/mcp/preview-server.js +254 -0
- package/dist/mcp/preview-server.js.map +1 -0
- package/dist/mcp/server.d.ts +51 -0
- package/dist/mcp/server.js +257 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tunnel.d.ts +17 -0
- package/dist/mcp/tunnel.js +154 -0
- package/dist/mcp/tunnel.js.map +1 -0
- package/dist/router.d.ts +113 -0
- package/dist/router.js +511 -0
- package/dist/router.js.map +1 -0
- package/dist/sandbox-policy.d.ts +6 -0
- package/dist/sandbox-policy.js +46 -0
- package/dist/sandbox-policy.js.map +1 -0
- package/dist/scheduler.d.ts +42 -0
- package/dist/scheduler.js +169 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/store.d.ts +95 -0
- package/dist/store.js +353 -0
- package/dist/store.js.map +1 -0
- package/dist/types/adapter.d.ts +50 -0
- package/dist/types/adapter.js +9 -0
- package/dist/types/adapter.js.map +1 -0
- package/dist/types/backend.d.ts +73 -0
- package/dist/types/backend.js +8 -0
- package/dist/types/backend.js.map +1 -0
- package/dist/types/events.d.ts +47 -0
- package/dist/types/events.js +9 -0
- package/dist/types/events.js.map +1 -0
- package/dist/utils.d.ts +59 -0
- package/dist/utils.js +272 -0
- package/dist/utils.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preview server manager — starts a local server and tunnels it.
|
|
3
|
+
*
|
|
4
|
+
* Consolidates the "start server + open tunnel" flow into one operation:
|
|
5
|
+
* 1. Find a free port
|
|
6
|
+
* 2. Start a server (built-in static or user command)
|
|
7
|
+
* 3. Wait for the server to respond
|
|
8
|
+
* 4. Open a tunnel to expose it publicly
|
|
9
|
+
*
|
|
10
|
+
* This prevents port collisions and silent failures that occur when
|
|
11
|
+
* the AI agent tries to background servers manually.
|
|
12
|
+
*/
|
|
13
|
+
import * as http from 'node:http';
|
|
14
|
+
import * as net from 'node:net';
|
|
15
|
+
import * as fs from 'node:fs';
|
|
16
|
+
import * as path from 'node:path';
|
|
17
|
+
import { spawn } from 'node:child_process';
|
|
18
|
+
import { openTunnel } from './tunnel.js';
|
|
19
|
+
/** Tracks all active preview servers for cleanup. */
|
|
20
|
+
const activePreviews = new Map();
|
|
21
|
+
/** Common MIME types for static file serving. */
|
|
22
|
+
const MIME_TYPES = {
|
|
23
|
+
'.html': 'text/html',
|
|
24
|
+
'.css': 'text/css',
|
|
25
|
+
'.js': 'application/javascript',
|
|
26
|
+
'.mjs': 'application/javascript',
|
|
27
|
+
'.json': 'application/json',
|
|
28
|
+
'.png': 'image/png',
|
|
29
|
+
'.jpg': 'image/jpeg',
|
|
30
|
+
'.jpeg': 'image/jpeg',
|
|
31
|
+
'.gif': 'image/gif',
|
|
32
|
+
'.svg': 'image/svg+xml',
|
|
33
|
+
'.ico': 'image/x-icon',
|
|
34
|
+
'.woff': 'font/woff',
|
|
35
|
+
'.woff2': 'font/woff2',
|
|
36
|
+
'.ttf': 'font/ttf',
|
|
37
|
+
'.pdf': 'application/pdf',
|
|
38
|
+
'.txt': 'text/plain',
|
|
39
|
+
'.xml': 'application/xml',
|
|
40
|
+
'.webp': 'image/webp',
|
|
41
|
+
'.mp4': 'video/mp4',
|
|
42
|
+
'.webm': 'video/webm',
|
|
43
|
+
'.mp3': 'audio/mpeg',
|
|
44
|
+
'.wav': 'audio/wav',
|
|
45
|
+
};
|
|
46
|
+
function getMimeType(filePath) {
|
|
47
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
48
|
+
return MIME_TYPES[ext] ?? 'application/octet-stream';
|
|
49
|
+
}
|
|
50
|
+
/** Find a free port by briefly binding to port 0. */
|
|
51
|
+
export function findFreePort() {
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
const srv = net.createServer();
|
|
54
|
+
srv.listen(0, '127.0.0.1', () => {
|
|
55
|
+
const addr = srv.address();
|
|
56
|
+
if (!addr || typeof addr === 'string') {
|
|
57
|
+
srv.close();
|
|
58
|
+
reject(new Error('Failed to get free port'));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const port = addr.port;
|
|
62
|
+
srv.close(() => resolve(port));
|
|
63
|
+
});
|
|
64
|
+
srv.on('error', reject);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Start a built-in static file server for the given directory.
|
|
69
|
+
* Serves files as-is with proper MIME types. For directories, serves index.html if present.
|
|
70
|
+
*/
|
|
71
|
+
function startStaticServer(directory, port) {
|
|
72
|
+
const rootDir = path.resolve(directory);
|
|
73
|
+
const server = http.createServer((req, res) => {
|
|
74
|
+
let urlPath = decodeURIComponent(req.url?.split('?')[0] ?? '/');
|
|
75
|
+
// Resolve to filesystem path
|
|
76
|
+
let filePath = path.resolve(rootDir, '.' + urlPath);
|
|
77
|
+
// Prevent path traversal
|
|
78
|
+
if (!filePath.startsWith(rootDir)) {
|
|
79
|
+
res.writeHead(403, { 'Content-Type': 'text/plain' });
|
|
80
|
+
res.end('Forbidden');
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const stat = fs.statSync(filePath);
|
|
85
|
+
if (stat.isDirectory()) {
|
|
86
|
+
// Try index.html
|
|
87
|
+
const indexPath = path.join(filePath, 'index.html');
|
|
88
|
+
if (fs.existsSync(indexPath)) {
|
|
89
|
+
filePath = indexPath;
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
// Simple directory listing
|
|
93
|
+
const entries = fs.readdirSync(filePath);
|
|
94
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
95
|
+
const links = entries.map(e => {
|
|
96
|
+
const href = urlPath.endsWith('/') ? `${urlPath}${e}` : `${urlPath}/${e}`;
|
|
97
|
+
return `<li><a href="${href}">${e}</a></li>`;
|
|
98
|
+
}).join('\n');
|
|
99
|
+
res.end(`<!DOCTYPE html><html><body><h2>Index of ${urlPath}</h2><ul>${links}</ul></body></html>`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (fs.statSync(filePath).isFile()) {
|
|
104
|
+
res.writeHead(200, { 'Content-Type': getMimeType(filePath) });
|
|
105
|
+
fs.createReadStream(filePath).pipe(res);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
109
|
+
res.end('Not found');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
114
|
+
res.end('Not found');
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
server.listen(port, '127.0.0.1', () => {
|
|
119
|
+
console.log(`[preview] static server listening on 127.0.0.1:${port} for ${rootDir}`);
|
|
120
|
+
resolve(server);
|
|
121
|
+
});
|
|
122
|
+
server.on('error', reject);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Spawn a user-provided command with PORT injected as an env var.
|
|
127
|
+
* Returns the child process. The caller should wait for the port to become available.
|
|
128
|
+
*/
|
|
129
|
+
function spawnServerCommand(command, cwd, port) {
|
|
130
|
+
console.log(`[preview] spawning command: ${command} (PORT=${port}, cwd=${cwd})`);
|
|
131
|
+
const child = spawn('sh', ['-c', command], {
|
|
132
|
+
cwd,
|
|
133
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
134
|
+
env: {
|
|
135
|
+
...process.env,
|
|
136
|
+
PORT: String(port),
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
// Log stderr for debugging
|
|
140
|
+
child.stderr?.on('data', (chunk) => {
|
|
141
|
+
const line = chunk.toString().trim();
|
|
142
|
+
if (line)
|
|
143
|
+
console.log(`[preview:stderr] ${line}`);
|
|
144
|
+
});
|
|
145
|
+
child.stdout?.on('data', (chunk) => {
|
|
146
|
+
const line = chunk.toString().trim();
|
|
147
|
+
if (line)
|
|
148
|
+
console.log(`[preview:stdout] ${line}`);
|
|
149
|
+
});
|
|
150
|
+
return child;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Wait for something to start listening on a port.
|
|
154
|
+
* Polls with TCP connect attempts.
|
|
155
|
+
*/
|
|
156
|
+
function waitForPort(port, timeoutMs = 30000) {
|
|
157
|
+
const start = Date.now();
|
|
158
|
+
return new Promise((resolve, reject) => {
|
|
159
|
+
function tryConnect() {
|
|
160
|
+
if (Date.now() - start > timeoutMs) {
|
|
161
|
+
reject(new Error(`Server did not start listening on port ${port} within ${timeoutMs / 1000}s`));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const sock = net.createConnection({ host: '127.0.0.1', port }, () => {
|
|
165
|
+
sock.destroy();
|
|
166
|
+
resolve();
|
|
167
|
+
});
|
|
168
|
+
sock.on('error', () => {
|
|
169
|
+
// Not ready yet — retry after 300ms
|
|
170
|
+
setTimeout(tryConnect, 300);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
tryConnect();
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Start a preview server and tunnel it.
|
|
178
|
+
*
|
|
179
|
+
* @param directory - The directory to serve (absolute path).
|
|
180
|
+
* @param command - Optional shell command to run instead of built-in static server.
|
|
181
|
+
* PORT env var is injected automatically.
|
|
182
|
+
* @param ttl - Tunnel time-to-live in seconds.
|
|
183
|
+
*/
|
|
184
|
+
export async function startPreviewServer(directory, command, ttl) {
|
|
185
|
+
const port = await findFreePort();
|
|
186
|
+
const id = `preview-${port}-${Date.now()}`;
|
|
187
|
+
let serverProcess = null;
|
|
188
|
+
let httpServer = null;
|
|
189
|
+
try {
|
|
190
|
+
if (command) {
|
|
191
|
+
// User-provided command (e.g., "npm run dev")
|
|
192
|
+
serverProcess = spawnServerCommand(command, directory, port);
|
|
193
|
+
// Watch for early exit (command failed to start)
|
|
194
|
+
const earlyExitPromise = new Promise((_, reject) => {
|
|
195
|
+
serverProcess.on('exit', (code) => {
|
|
196
|
+
reject(new Error(`Server command exited with code ${code} before it started listening`));
|
|
197
|
+
});
|
|
198
|
+
serverProcess.on('error', (err) => {
|
|
199
|
+
reject(new Error(`Server command failed: ${err.message}`));
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
// Race: either the port becomes available or the process exits early
|
|
203
|
+
await Promise.race([
|
|
204
|
+
waitForPort(port),
|
|
205
|
+
earlyExitPromise,
|
|
206
|
+
]);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
// Built-in static file server
|
|
210
|
+
httpServer = await startStaticServer(directory, port);
|
|
211
|
+
}
|
|
212
|
+
// Open tunnel
|
|
213
|
+
const tunnel = await openTunnel(port, ttl);
|
|
214
|
+
activePreviews.set(id, { serverProcess, httpServer, tunnel });
|
|
215
|
+
console.log(`[preview] preview server ready: ${tunnel.url} (port ${port}, TTL ${ttl}s)`);
|
|
216
|
+
return {
|
|
217
|
+
url: tunnel.url,
|
|
218
|
+
port,
|
|
219
|
+
close: () => closePreview(id),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
// Cleanup on failure
|
|
224
|
+
if (serverProcess) {
|
|
225
|
+
serverProcess.kill('SIGTERM');
|
|
226
|
+
}
|
|
227
|
+
if (httpServer) {
|
|
228
|
+
httpServer.close();
|
|
229
|
+
}
|
|
230
|
+
throw err;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/** Close a specific preview server. */
|
|
234
|
+
function closePreview(id) {
|
|
235
|
+
const preview = activePreviews.get(id);
|
|
236
|
+
if (!preview)
|
|
237
|
+
return;
|
|
238
|
+
preview.tunnel.close();
|
|
239
|
+
if (preview.serverProcess) {
|
|
240
|
+
preview.serverProcess.kill('SIGTERM');
|
|
241
|
+
}
|
|
242
|
+
if (preview.httpServer) {
|
|
243
|
+
preview.httpServer.close();
|
|
244
|
+
}
|
|
245
|
+
activePreviews.delete(id);
|
|
246
|
+
console.log(`[preview] closed preview ${id}`);
|
|
247
|
+
}
|
|
248
|
+
/** Close all active preview servers. Called during shutdown. */
|
|
249
|
+
export function closeAllPreviews() {
|
|
250
|
+
for (const [id] of activePreviews) {
|
|
251
|
+
closePreview(id);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=preview-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-server.js","sourceRoot":"","sources":["../../src/mcp/preview-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge MCP server for Alaska.
|
|
3
|
+
*
|
|
4
|
+
* Exposes tools (upload_file, serve_file_browser, preview_server, etc.) to coding
|
|
5
|
+
* backends via stdio transport. When the bridge spawns a backend session,
|
|
6
|
+
* it injects MCP config pointing to this server so the agent gains access
|
|
7
|
+
* to platform-specific actions without knowing about the concrete adapter.
|
|
8
|
+
*/
|
|
9
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
10
|
+
export type { McpSessionContext } from './config.js';
|
|
11
|
+
import type { McpSessionContext } from './config.js';
|
|
12
|
+
/**
|
|
13
|
+
* Callback interface for bridge actions that the MCP server triggers.
|
|
14
|
+
* The adapter implements these to perform platform-specific work.
|
|
15
|
+
*/
|
|
16
|
+
export interface BridgeCallbacks {
|
|
17
|
+
/** Upload a file as a chat attachment in the originating thread. */
|
|
18
|
+
uploadFile(filePath: string, channelId: string, threadId: string): Promise<void>;
|
|
19
|
+
/** Start a tunnel on the given port and return the public URL. */
|
|
20
|
+
openTunnel(port: number, ttl: number): Promise<string>;
|
|
21
|
+
/** Serve a file browser for a directory behind a tunnel and return the URL. */
|
|
22
|
+
serveFileBrowser(directory: string): Promise<string>;
|
|
23
|
+
/** Start a preview server (static or command) and tunnel it. Returns { url, port }. */
|
|
24
|
+
previewServer(directory: string, command: string | undefined, ttl: number): Promise<{
|
|
25
|
+
url: string;
|
|
26
|
+
port: number;
|
|
27
|
+
}>;
|
|
28
|
+
/** Post a text message in the originating thread. */
|
|
29
|
+
postMessage(channelId: string, threadId: string, text: string): Promise<void>;
|
|
30
|
+
/** Copy a staged uploaded file to a destination in the project directory. */
|
|
31
|
+
saveUploadedFile(uploadId: string, destination: string, projectDir: string): Promise<string>;
|
|
32
|
+
/** Register a scheduled session with the bridge. */
|
|
33
|
+
scheduleSession(channelId: string, threadId: string, prompt: string, originalRequest: string, cronExpression: string | undefined, scheduledAt: string | undefined, title: string | undefined): Promise<{
|
|
34
|
+
scheduleId: number;
|
|
35
|
+
}>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Validates that a file path is within the allowed project directory.
|
|
39
|
+
* Returns the resolved absolute path, or throws if out of bounds.
|
|
40
|
+
*/
|
|
41
|
+
export declare function validateProjectPath(filePath: string, projectDir: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Create and configure the Bridge MCP server with all tools registered.
|
|
44
|
+
*/
|
|
45
|
+
export declare function createMcpServer(context: McpSessionContext, callbacks: BridgeCallbacks): McpServer;
|
|
46
|
+
/**
|
|
47
|
+
* Start the MCP server with stdio transport.
|
|
48
|
+
* This is called when the bridge spawns a backend session and pipes
|
|
49
|
+
* the MCP server's stdin/stdout to the backend process.
|
|
50
|
+
*/
|
|
51
|
+
export declare function startMcpServer(context: McpSessionContext, callbacks: BridgeCallbacks): Promise<McpServer>;
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge MCP server for Alaska.
|
|
3
|
+
*
|
|
4
|
+
* Exposes tools (upload_file, serve_file_browser, preview_server, etc.) to coding
|
|
5
|
+
* backends via stdio transport. When the bridge spawns a backend session,
|
|
6
|
+
* it injects MCP config pointing to this server so the agent gains access
|
|
7
|
+
* to platform-specific actions without knowing about the concrete adapter.
|
|
8
|
+
*/
|
|
9
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
10
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import * as path from 'node:path';
|
|
13
|
+
import * as fs from 'node:fs';
|
|
14
|
+
/**
|
|
15
|
+
* Validates that a file path is within the allowed project directory.
|
|
16
|
+
* Returns the resolved absolute path, or throws if out of bounds.
|
|
17
|
+
*/
|
|
18
|
+
export function validateProjectPath(filePath, projectDir) {
|
|
19
|
+
const resolved = path.resolve(projectDir, filePath);
|
|
20
|
+
const normalizedProject = path.resolve(projectDir);
|
|
21
|
+
if (!resolved.startsWith(normalizedProject + path.sep) && resolved !== normalizedProject) {
|
|
22
|
+
throw new Error(`Path "${filePath}" is outside the project directory "${projectDir}". ` +
|
|
23
|
+
`Access is restricted to the project directory for safety.`);
|
|
24
|
+
}
|
|
25
|
+
return resolved;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Create and configure the Bridge MCP server with all tools registered.
|
|
29
|
+
*/
|
|
30
|
+
export function createMcpServer(context, callbacks) {
|
|
31
|
+
const server = new McpServer({
|
|
32
|
+
name: 'alaska',
|
|
33
|
+
version: '0.1.0',
|
|
34
|
+
});
|
|
35
|
+
// --- upload_file tool ---
|
|
36
|
+
server.registerTool('upload_file', {
|
|
37
|
+
description: 'Send a file from the project to the user as an attachment. ' +
|
|
38
|
+
'Use this when the user asks you to share, send, or show them a file from the project. ' +
|
|
39
|
+
'Provide the file path relative to the project directory. Use post_message to confirm the file was sent.',
|
|
40
|
+
inputSchema: {
|
|
41
|
+
file_path: z.string().describe('Path to the file (relative to project directory)'),
|
|
42
|
+
},
|
|
43
|
+
}, async ({ file_path }) => {
|
|
44
|
+
try {
|
|
45
|
+
const resolved = validateProjectPath(file_path, context.projectDir);
|
|
46
|
+
if (!fs.existsSync(resolved)) {
|
|
47
|
+
return {
|
|
48
|
+
content: [{ type: 'text', text: `Error: File not found: ${resolved}` }],
|
|
49
|
+
isError: true,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
await callbacks.uploadFile(resolved, context.channelId, context.threadId);
|
|
53
|
+
return {
|
|
54
|
+
content: [{ type: 'text', text: `File uploaded successfully: ${path.basename(resolved)}` }],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
59
|
+
return {
|
|
60
|
+
content: [{ type: 'text', text: `Error uploading file: ${message}` }],
|
|
61
|
+
isError: true,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
// --- serve_file_browser tool ---
|
|
66
|
+
server.registerTool('serve_file_browser', {
|
|
67
|
+
description: 'Give the user a browsable view of the project files and folders via a public URL. ' +
|
|
68
|
+
'Use this when the user asks to see, browse, or explore the project file structure. ' +
|
|
69
|
+
'Optionally provide a subdirectory to scope the view. Use post_message to share the URL with the user.',
|
|
70
|
+
inputSchema: {
|
|
71
|
+
directory: z.string().default('.').optional()
|
|
72
|
+
.describe('Directory to serve (relative to project directory, defaults to project root)'),
|
|
73
|
+
},
|
|
74
|
+
}, async ({ directory }) => {
|
|
75
|
+
try {
|
|
76
|
+
const dir = directory ?? '.';
|
|
77
|
+
const resolved = validateProjectPath(dir, context.projectDir);
|
|
78
|
+
if (!fs.existsSync(resolved) || !fs.statSync(resolved).isDirectory()) {
|
|
79
|
+
return {
|
|
80
|
+
content: [{ type: 'text', text: `Error: Directory not found: ${resolved}` }],
|
|
81
|
+
isError: true,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const url = await callbacks.serveFileBrowser(resolved);
|
|
85
|
+
return {
|
|
86
|
+
content: [{ type: 'text', text: `File browser available at: ${url}` }],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
91
|
+
return {
|
|
92
|
+
content: [{ type: 'text', text: `Error serving file browser: ${message}` }],
|
|
93
|
+
isError: true,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
// --- preview_server tool ---
|
|
98
|
+
server.registerTool('preview_server', {
|
|
99
|
+
description: 'Start a server and give the user a public URL to preview the project. ' +
|
|
100
|
+
'Handles port allocation, server startup, and public URL creation in one step. Works regardless of sandbox restrictions. ' +
|
|
101
|
+
'Use this when the user asks to preview, demo, or share their website or app, or asks for a live URL or dev server. ' +
|
|
102
|
+
'For static sites: provide the directory. For dev servers: provide a command (e.g. "npm run dev") — the PORT env var is injected automatically. ' +
|
|
103
|
+
'Use post_message to share the URL with the user. ' +
|
|
104
|
+
'Do NOT start servers manually via shell commands. Do NOT hardcode a port number.',
|
|
105
|
+
inputSchema: {
|
|
106
|
+
directory: z.string().default('.').optional()
|
|
107
|
+
.describe('Directory to serve (relative to project directory, defaults to project root)'),
|
|
108
|
+
command: z.string().optional()
|
|
109
|
+
.describe('Optional shell command to start a dev server (e.g. "npm run dev"). PORT env var is injected. If omitted, a built-in static file server is used.'),
|
|
110
|
+
ttl: z.number().int().min(60).max(86400).default(3600).optional()
|
|
111
|
+
.describe('Time-to-live in seconds (default 3600, max 86400)'),
|
|
112
|
+
},
|
|
113
|
+
}, async ({ directory, command, ttl }) => {
|
|
114
|
+
try {
|
|
115
|
+
const dir = directory ?? '.';
|
|
116
|
+
const resolved = validateProjectPath(dir, context.projectDir);
|
|
117
|
+
if (!fs.existsSync(resolved) || !fs.statSync(resolved).isDirectory()) {
|
|
118
|
+
return {
|
|
119
|
+
content: [{ type: 'text', text: `Error: Directory not found: ${resolved}` }],
|
|
120
|
+
isError: true,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const previewTtl = ttl ?? 3600;
|
|
124
|
+
const result = await callbacks.previewServer(resolved, command ?? undefined, previewTtl);
|
|
125
|
+
return {
|
|
126
|
+
content: [{ type: 'text', text: `Preview server started on port ${result.port}.\nPublic URL: ${result.url}\nTTL: ${previewTtl}s` }],
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
131
|
+
return {
|
|
132
|
+
content: [{ type: 'text', text: `Error starting preview server: ${message}` }],
|
|
133
|
+
isError: true,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
// --- save_uploaded_file tool ---
|
|
138
|
+
server.registerTool('save_uploaded_file', {
|
|
139
|
+
description: 'Save a file the user uploaded to a location in the project directory. ' +
|
|
140
|
+
'When the user sends a file, it is staged with an upload_id included in the message. ' +
|
|
141
|
+
'Use this when the user asks you to save, add, or place an uploaded file into the project. ' +
|
|
142
|
+
'Provide the upload_id from the message and a destination path relative to the project directory (e.g. "src/assets/logo.png").',
|
|
143
|
+
inputSchema: {
|
|
144
|
+
upload_id: z.string().describe('The upload ID from the image upload notification (e.g., upload_abc123def456)'),
|
|
145
|
+
destination: z.string().describe('Destination path relative to project directory (e.g., "public/logo.png" or "assets/images/hero.jpg")'),
|
|
146
|
+
},
|
|
147
|
+
}, async ({ upload_id, destination }) => {
|
|
148
|
+
try {
|
|
149
|
+
const savedPath = await callbacks.saveUploadedFile(upload_id, destination, context.projectDir);
|
|
150
|
+
return {
|
|
151
|
+
content: [{ type: 'text', text: `File saved successfully to: ${savedPath}` }],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
156
|
+
return {
|
|
157
|
+
content: [{ type: 'text', text: `Error saving uploaded file: ${message}` }],
|
|
158
|
+
isError: true,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
// --- post_message tool ---
|
|
163
|
+
server.registerTool('post_message', {
|
|
164
|
+
description: 'Post a message to the chat thread. ' +
|
|
165
|
+
'Always call this at least once per turn ONLY IF the message source is a supported messaging adapter. ' +
|
|
166
|
+
'Keep messages clear, concise, and focused on what matters to the user.',
|
|
167
|
+
inputSchema: {
|
|
168
|
+
text: z.string().describe('The message text to post to the user'),
|
|
169
|
+
},
|
|
170
|
+
}, async ({ text }) => {
|
|
171
|
+
try {
|
|
172
|
+
await callbacks.postMessage(context.channelId, context.threadId, text);
|
|
173
|
+
return {
|
|
174
|
+
content: [{ type: 'text', text: 'Message posted' }],
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
179
|
+
return {
|
|
180
|
+
content: [{ type: 'text', text: `Error posting message: ${message}` }],
|
|
181
|
+
isError: true,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
// --- schedule_session tool ---
|
|
186
|
+
server.registerTool('schedule_session', {
|
|
187
|
+
description: 'Schedule a future session with the bridge. ' +
|
|
188
|
+
'Use this when the user asks you to do something at a specific time or on a recurring schedule — ' +
|
|
189
|
+
'e.g. "remind me every morning at 9am", "check deploys on Friday at 5pm", "remind me in 5 minutes". ' +
|
|
190
|
+
`The current local time is: ${new Date().toLocaleString('sv-SE')} (${Intl.DateTimeFormat().resolvedOptions().timeZone}). ` +
|
|
191
|
+
'Use this to calculate scheduled_at for relative or calendar-based requests ' +
|
|
192
|
+
'(e.g. "in 5 minutes" = current time + 5 minutes, "tomorrow at 5pm", "next Friday at 3pm"). ' +
|
|
193
|
+
'All times are in the local timezone shown above. ' +
|
|
194
|
+
'The prompt field is what a fresh instance of yourself will receive in a new thread when the schedule fires. ' +
|
|
195
|
+
'Write it as clear instructions for that future AI to execute the user\'s request. ' +
|
|
196
|
+
'Do NOT include any scheduling, timing, or recurrence details in the prompt — the bridge handles all timing. ' +
|
|
197
|
+
'The prompt should only contain what to do, not when to do it. ' +
|
|
198
|
+
'For one-time: provide scheduled_at as an ISO 8601 datetime string. ' +
|
|
199
|
+
'For recurring: provide cron_expression (standard 5-field cron, e.g. "0 9 * * *" for daily at 9am, "0 17 * * 5" for Fridays at 5pm). ' +
|
|
200
|
+
'Exactly one of cron_expression or scheduled_at must be provided. ' +
|
|
201
|
+
'IMPORTANT: After calling this tool, do NOT post any confirmation message or use post_message. ' +
|
|
202
|
+
'The bridge will acknowledge the schedule to the user automatically (via emoji reaction). ' +
|
|
203
|
+
'Simply call this tool and end your turn.',
|
|
204
|
+
inputSchema: {
|
|
205
|
+
prompt: z.string().describe('Instructions for the AI that will run when this schedule fires. ' +
|
|
206
|
+
'This is a prompt for a fresh instance of yourself in a new thread — write clear instructions to fulfill the user\'s request. ' +
|
|
207
|
+
'Do NOT include scheduling or timing details (e.g. "every morning", "at 9am") — only include WHAT to do, not WHEN.'),
|
|
208
|
+
original_request: z.string().describe("The user's original request in their own words, including timing (shown when listing schedules)"),
|
|
209
|
+
title: z.string().optional().describe('Short label for this schedule (e.g. "News update", "Deploy check"). Used as the thread title for recurring schedules. If omitted, falls back to original_request.'),
|
|
210
|
+
cron_expression: z.string().optional().describe('5-field cron expression for recurring schedules (e.g. "0 9 * * *" for daily at 9am)'),
|
|
211
|
+
scheduled_at: z.string().optional().describe('ISO 8601 datetime in local time for one-time schedules, without Z suffix (e.g. "2026-02-25T09:00:00")'),
|
|
212
|
+
},
|
|
213
|
+
}, async ({ prompt, original_request, title, cron_expression, scheduled_at }) => {
|
|
214
|
+
try {
|
|
215
|
+
// Validate: exactly one of cron_expression or scheduled_at
|
|
216
|
+
if (cron_expression && scheduled_at) {
|
|
217
|
+
return {
|
|
218
|
+
content: [{ type: 'text', text: 'Error: Provide either cron_expression (recurring) or scheduled_at (one-time), not both.' }],
|
|
219
|
+
isError: true,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
if (!cron_expression && !scheduled_at) {
|
|
223
|
+
return {
|
|
224
|
+
content: [{ type: 'text', text: 'Error: Provide either cron_expression (recurring) or scheduled_at (one-time).' }],
|
|
225
|
+
isError: true,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
const result = await callbacks.scheduleSession(context.channelId, context.threadId, prompt, original_request, cron_expression ?? undefined, scheduled_at ?? undefined, title ?? undefined);
|
|
229
|
+
const typeLabel = cron_expression ? 'Recurring' : 'One-time';
|
|
230
|
+
return {
|
|
231
|
+
content: [{ type: 'text', text: `${typeLabel} schedule created (ID: ${result.scheduleId}). The bridge will fire this session automatically.` }],
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
236
|
+
return {
|
|
237
|
+
content: [{ type: 'text', text: `Error scheduling session: ${message}` }],
|
|
238
|
+
isError: true,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
console.error('[mcp] server created with tools: upload_file, serve_file_browser, preview_server, save_uploaded_file, post_message, schedule_session');
|
|
243
|
+
return server;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Start the MCP server with stdio transport.
|
|
247
|
+
* This is called when the bridge spawns a backend session and pipes
|
|
248
|
+
* the MCP server's stdin/stdout to the backend process.
|
|
249
|
+
*/
|
|
250
|
+
export async function startMcpServer(context, callbacks) {
|
|
251
|
+
const server = createMcpServer(context, callbacks);
|
|
252
|
+
const transport = new StdioServerTransport();
|
|
253
|
+
await server.connect(transport);
|
|
254
|
+
console.error('[mcp] server connected via stdio transport');
|
|
255
|
+
return server;
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tunnel manager — opens public tunnels via cloudflared or ngrok.
|
|
3
|
+
*
|
|
4
|
+
* Tunnels are only used for user-facing things (dev server previews,
|
|
5
|
+
* file browsers) — never for the bridge's own IPC server.
|
|
6
|
+
*/
|
|
7
|
+
export interface Tunnel {
|
|
8
|
+
url: string;
|
|
9
|
+
close(): void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Open a tunnel on the given port using cloudflared (preferred) or ngrok.
|
|
13
|
+
* Auto-closes after `ttl` seconds.
|
|
14
|
+
*/
|
|
15
|
+
export declare function openTunnel(port: number, ttl: number): Promise<Tunnel>;
|
|
16
|
+
/** Close all active tunnels. Called during shutdown. */
|
|
17
|
+
export declare function closeAllTunnels(): void;
|