session-collab-mcp 2.2.0 → 2.3.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/dist/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  getMcpTools,
9
9
  handleMcpRequest,
10
10
  loadMigrationsFromDir
11
- } from "./chunk-SOUW3JSS.js";
11
+ } from "./chunk-UDJMG3TR.js";
12
12
 
13
13
  // src/cli.ts
14
14
  import { createInterface } from "readline";
package/dist/http/cli.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  getMcpTools,
9
9
  handleMcpRequest,
10
10
  loadMigrationsFromDir
11
- } from "../chunk-SOUW3JSS.js";
11
+ } from "../chunk-UDJMG3TR.js";
12
12
 
13
13
  // src/http/cli.ts
14
14
  import { join, dirname } from "path";
@@ -272,6 +272,12 @@ function createHttpServer(db, options = {}) {
272
272
  sendJson(res, response.ok ? 200 : 400, response, traceId);
273
273
  return;
274
274
  }
275
+ if (method === "POST" && url.pathname === "/v1/sessions/update") {
276
+ const body = await readJsonBody(req);
277
+ const response = await handleRestTool(db, "collab_session_update", body ?? {}, traceId);
278
+ sendJson(res, response.ok ? 200 : 400, response, traceId);
279
+ return;
280
+ }
275
281
  if (method === "GET" && url.pathname === "/v1/sessions") {
276
282
  const response = await handleRestTool(db, "collab_session_list", parseQueryParams(url), traceId);
277
283
  sendJson(res, response.ok ? 200 : 400, response, traceId);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/http/cli.ts","../../src/http/server.ts"],"sourcesContent":["#!/usr/bin/env node\n// HTTP server for Session Collaboration MCP\n\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\nimport { createLocalDatabase, getDefaultDbPath } from '../db/sqlite-adapter.js';\nimport { startHttpServer } from './server.js';\nimport { loadMigrationsFromDir } from '../db/migrations.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nfunction loadMigrations(): string[] {\n const migrationsDir = join(__dirname, '..', '..', 'migrations');\n return loadMigrationsFromDir(migrationsDir);\n}\n\nfunction parseArgs(): { dbPath?: string; host: string; port: number; allowedHosts: string[] } {\n const args = process.argv.slice(2);\n let dbPath: string | undefined;\n let host = '127.0.0.1';\n let port = 8765;\n const allowedHosts: string[] = [];\n\n for (let i = 0; i < args.length; i++) {\n const value = args[i + 1];\n if (args[i] === '--db' && value) {\n dbPath = value;\n i++;\n } else if (args[i] === '--host' && value) {\n host = value;\n i++;\n } else if (args[i] === '--port' && value) {\n port = Number(value);\n i++;\n } else if (args[i] === '--allowed-host' && value) {\n allowedHosts.push(value);\n i++;\n }\n }\n\n return { dbPath, host, port, allowedHosts };\n}\n\nasync function main(): Promise<void> {\n const { dbPath, host, port, allowedHosts } = parseArgs();\n const db = createLocalDatabase(dbPath);\n const apiToken = process.env.SESSION_COLLAB_HTTP_TOKEN;\n const envAllowedHosts = (process.env.SESSION_COLLAB_ALLOWED_HOSTS ?? '')\n .split(',')\n .map((hostEntry) => hostEntry.trim())\n .filter(Boolean);\n\n try {\n const migrations = loadMigrations();\n db.initSchema(migrations);\n } catch (error) {\n if (!(error instanceof Error && error.message.includes('already exists'))) {\n console.error('Warning: Migration error:', error);\n }\n }\n\n await startHttpServer(db, {\n host,\n port,\n apiToken,\n allowedHosts: [...envAllowedHosts, ...allowedHosts],\n });\n console.error(`Session Collab HTTP Server running at http://${host}:${port}`);\n console.error(`Database: ${dbPath ?? getDefaultDbPath()}`);\n console.error(`MCP endpoint: POST /mcp`);\n console.error(`Convenience REST API: /v1/*`);\n}\n\nmain().catch((error) => {\n console.error('Fatal error:', error);\n process.exit(1);\n});\n","import http from 'http';\nimport { URL } from 'url';\nimport type { DatabaseAdapter } from '../db/sqlite-adapter.js';\nimport type { JsonRpcRequest, JsonRpcResponse, McpToolResult } from '../mcp/protocol.js';\nimport { JsonRpcRequestSchema } from '../mcp/protocol.js';\nimport { McpServer, getMcpTools, handleMcpRequest } from '../mcp/server.js';\nimport { generateId } from '../utils/crypto.js';\n\ntype JsonPrimitive = null | boolean | number | string;\ntype JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue };\n\nexport type HttpSuccessResponse = {\n ok: true;\n data?: JsonValue;\n};\n\nexport type HttpErrorResponse = {\n ok: false;\n error: string;\n code: string;\n message: string;\n trace_id: string;\n};\n\nexport type HttpResponse = HttpSuccessResponse | HttpErrorResponse;\n\nexport type HttpServerOptions = {\n host?: string;\n allowedHosts?: string[];\n apiToken?: string;\n};\n\nclass HttpRequestError extends Error {\n constructor(\n readonly status: number,\n readonly code: string,\n message: string\n ) {\n super(message);\n this.name = 'HttpRequestError';\n }\n}\n\nfunction normalizeHost(host: string): string {\n return host.trim().toLowerCase();\n}\n\nfunction normalizeHostHeader(rawHost: string | undefined): string | null {\n if (!rawHost) return null;\n const first = rawHost.split(',')[0]?.trim();\n if (!first) return null;\n\n if (first.startsWith('[')) {\n const closingIndex = first.indexOf(']');\n if (closingIndex !== -1) {\n return normalizeHost(first.slice(1, closingIndex));\n }\n }\n\n const withoutPort = first.includes(':') ? first.split(':')[0] : first;\n return normalizeHost(withoutPort);\n}\n\nfunction isLocalBindHost(host: string): boolean {\n const normalized = normalizeHost(host);\n return normalized === '127.0.0.1' || normalized === 'localhost' || normalized === '::1';\n}\n\nfunction buildAllowedHosts(host: string, configuredHosts: string[] = []): Set<string> {\n const allowedHosts = new Set(configuredHosts.map(normalizeHost).filter(Boolean));\n\n if (isLocalBindHost(host)) {\n allowedHosts.add('localhost');\n allowedHosts.add('127.0.0.1');\n allowedHosts.add('::1');\n } else if (host !== '0.0.0.0' && host !== '::') {\n allowedHosts.add(normalizeHost(host));\n }\n\n return allowedHosts;\n}\n\nfunction getTraceId(req: http.IncomingMessage): string {\n const requestId = req.headers['x-request-id'];\n if (typeof requestId === 'string' && requestId.trim()) {\n return requestId.trim();\n }\n return generateId();\n}\n\nfunction sendJson(\n res: http.ServerResponse,\n status: number,\n body: unknown,\n traceId: string,\n contentType: string = 'application/json'\n): void {\n const payload = JSON.stringify(body);\n res.writeHead(status, {\n 'Content-Type': contentType,\n 'Content-Length': Buffer.byteLength(payload),\n 'X-Request-ID': traceId,\n });\n res.end(payload);\n}\n\nfunction sendHttpError(\n res: http.ServerResponse,\n status: number,\n code: string,\n message: string,\n traceId: string\n): void {\n sendJson(res, status, { ok: false, error: code, code, message, trace_id: traceId }, traceId);\n}\n\nfunction sendJsonRpcResponse(\n res: http.ServerResponse,\n status: number,\n response: JsonRpcResponse,\n traceId: string\n): void {\n sendJson(res, status, response as unknown as JsonValue, traceId);\n}\n\nfunction sendJsonRpcError(\n res: http.ServerResponse,\n status: number,\n id: string | number | null,\n code: number,\n message: string,\n traceId: string\n): void {\n sendJson(\n res,\n status,\n {\n jsonrpc: '2.0',\n id,\n error: {\n code,\n message,\n data: { trace_id: traceId },\n },\n } as unknown as JsonValue,\n traceId\n );\n}\n\nasync function readJsonBody(req: http.IncomingMessage): Promise<JsonValue | undefined> {\n return await new Promise((resolve, reject) => {\n let data = '';\n let settled = false;\n\n const rejectOnce = (error: Error) => {\n if (settled) return;\n settled = true;\n reject(error);\n };\n\n const resolveOnce = (value: JsonValue | undefined) => {\n if (settled) return;\n settled = true;\n resolve(value);\n };\n\n req.on('data', (chunk) => {\n if (settled) return;\n data += chunk;\n if (data.length > 1_000_000) {\n rejectOnce(new HttpRequestError(413, 'PAYLOAD_TOO_LARGE', 'Payload too large'));\n req.pause();\n }\n });\n\n req.on('end', () => {\n if (settled) return;\n if (!data) {\n resolveOnce(undefined);\n return;\n }\n\n try {\n resolveOnce(JSON.parse(data) as JsonValue);\n } catch {\n rejectOnce(new HttpRequestError(400, 'INVALID_JSON', 'Request body must be valid JSON'));\n }\n });\n\n req.on('error', (error) => rejectOnce(error instanceof Error ? error : new Error(String(error))));\n });\n}\n\nexport function normalizeToolResult(result: McpToolResult, traceId: string): HttpResponse {\n const text = result.content?.[0]?.text ?? '';\n\n try {\n const data = JSON.parse(text) as JsonValue;\n if (result.isError) {\n const errorCode =\n data && typeof data === 'object' && 'error' in data\n ? String((data as { error?: string }).error ?? 'UNKNOWN')\n : 'UNKNOWN';\n const message =\n data && typeof data === 'object' && 'message' in data\n ? String((data as { message?: string }).message ?? 'Error')\n : 'Error';\n return {\n ok: false,\n error: errorCode,\n code: errorCode,\n message,\n trace_id: traceId,\n };\n }\n\n return { ok: true, data };\n } catch {\n if (result.isError) {\n return {\n ok: false,\n error: 'UNKNOWN',\n code: 'UNKNOWN',\n message: text || 'Error',\n trace_id: traceId,\n };\n }\n\n return { ok: true, data: text };\n }\n}\n\nexport function coerceQueryValue(value: string): string | number | boolean {\n if (value === 'true') return true;\n if (value === 'false') return false;\n const num = Number(value);\n if (!Number.isNaN(num) && value.trim() !== '') {\n return num;\n }\n return value;\n}\n\nexport function parseQueryParams(url: URL): Record<string, JsonValue> {\n const params: Record<string, JsonValue> = {};\n for (const [key, value] of url.searchParams.entries()) {\n params[key] = coerceQueryValue(value);\n }\n return params;\n}\n\nfunction enforceHttpSecurity(\n req: http.IncomingMessage,\n options: Required<Pick<HttpServerOptions, 'host'>> & Pick<HttpServerOptions, 'allowedHosts' | 'apiToken'>\n): void {\n const allowedHosts = buildAllowedHosts(options.host, options.allowedHosts);\n const hostHeader = normalizeHostHeader(req.headers.host);\n\n if (allowedHosts.size > 0) {\n if (!hostHeader || !allowedHosts.has(hostHeader)) {\n throw new HttpRequestError(403, 'INVALID_HOST', 'Host header is not allowed');\n }\n\n const originHeader = req.headers.origin;\n if (originHeader) {\n let origin: URL;\n try {\n origin = new URL(originHeader);\n } catch {\n throw new HttpRequestError(403, 'INVALID_ORIGIN', 'Origin header is invalid');\n }\n\n if (!allowedHosts.has(normalizeHost(origin.hostname))) {\n throw new HttpRequestError(403, 'INVALID_ORIGIN', 'Origin header is not allowed');\n }\n }\n }\n\n if (options.apiToken) {\n const authHeader = req.headers.authorization;\n const expected = `Bearer ${options.apiToken}`;\n if (authHeader !== expected) {\n throw new HttpRequestError(401, 'UNAUTHORIZED', 'Valid bearer token is required');\n }\n }\n}\n\nasync function handleRestTool(\n db: DatabaseAdapter,\n name: string,\n args: Record<string, unknown>,\n traceId: string\n): Promise<HttpResponse> {\n const result = await handleMcpRequest(db, name, args);\n return normalizeToolResult(result, traceId);\n}\n\nexport function createHttpServer(db: DatabaseAdapter, options: HttpServerOptions = {}): http.Server {\n const host = options.host ?? '127.0.0.1';\n const mcpServer = new McpServer(db);\n\n return http.createServer(async (req, res) => {\n const traceId = getTraceId(req);\n\n try {\n const method = req.method ?? 'GET';\n const url = new URL(req.url ?? '/', 'http://localhost');\n\n if (method === 'GET' && url.pathname === '/health') {\n sendJson(res, 200, { ok: true, data: { status: 'ok' } }, traceId);\n return;\n }\n\n enforceHttpSecurity(req, {\n host,\n allowedHosts: options.allowedHosts,\n apiToken: options.apiToken,\n });\n\n if (method === 'GET' && url.pathname === '/v1/tools') {\n sendJson(res, 200, { ok: true, data: { tools: getMcpTools() } }, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/tools/call') {\n const body = (await readJsonBody(req)) as { name?: string; args?: Record<string, unknown> } | undefined;\n if (!body?.name) {\n sendHttpError(res, 400, 'INVALID_INPUT', 'name is required', traceId);\n return;\n }\n const response = await handleRestTool(db, body.name, body.args ?? {}, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/mcp') {\n const body = await readJsonBody(req);\n const validation = JsonRpcRequestSchema.safeParse(body);\n if (!validation.success) {\n sendJsonRpcError(res, 400, null, -32600, 'Invalid Request', traceId);\n return;\n }\n\n const response = await mcpServer.handleRequest(validation.data as JsonRpcRequest);\n sendJsonRpcResponse(res, 200, response, traceId);\n return;\n }\n\n if (method === 'GET' && url.pathname === '/mcp') {\n sendHttpError(\n res,\n 501,\n 'STREAM_NOT_SUPPORTED',\n 'This server exposes MCP JSON-RPC over HTTP POST at /mcp. SSE streaming is not implemented.',\n traceId\n );\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/sessions/start') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_session_start', body ?? {}, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/sessions/end') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_session_end', body ?? {}, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'GET' && url.pathname === '/v1/sessions') {\n const response = await handleRestTool(db, 'collab_session_list', parseQueryParams(url), traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/config') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_config', body ?? {}, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'GET' && url.pathname === '/v1/status') {\n const response = await handleRestTool(db, 'collab_status', parseQueryParams(url), traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/claims') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_claim', { action: 'create', ...(body ?? {}) }, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/claims/check') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_claim', { action: 'check', ...(body ?? {}) }, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/claims/release') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_claim', { action: 'release', ...(body ?? {}) }, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'GET' && url.pathname === '/v1/claims') {\n const response = await handleRestTool(\n db,\n 'collab_claim',\n { action: 'list', ...parseQueryParams(url) },\n traceId\n );\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/memory/save') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_memory_save', body ?? {}, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/memory/recall') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_memory_recall', body ?? {}, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/memory/clear') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_memory_clear', body ?? {}, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/protect/register') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_protect', { action: 'register', ...(body ?? {}) }, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/protect/check') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_protect', { action: 'check', ...(body ?? {}) }, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'GET' && url.pathname === '/v1/protect/list') {\n const response = await handleRestTool(\n db,\n 'collab_protect',\n { action: 'list', ...parseQueryParams(url) },\n traceId\n );\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n sendHttpError(res, 404, 'NOT_FOUND', 'Not found', traceId);\n } catch (error) {\n if (error instanceof HttpRequestError) {\n sendHttpError(res, error.status, error.code, error.message, traceId);\n return;\n }\n\n const message = error instanceof Error ? error.message : 'Unexpected error';\n sendHttpError(res, 500, 'INTERNAL_ERROR', message, traceId);\n }\n });\n}\n\nexport async function startHttpServer(\n db: DatabaseAdapter,\n options: { host: string; port: number; allowedHosts?: string[]; apiToken?: string }\n): Promise<http.Server> {\n const normalizedHost = normalizeHost(options.host);\n\n if (!isLocalBindHost(normalizedHost)) {\n if (!options.apiToken) {\n throw new Error('Non-local HTTP binds require SESSION_COLLAB_HTTP_TOKEN');\n }\n\n const allowedHosts = buildAllowedHosts(normalizedHost, options.allowedHosts);\n if (allowedHosts.size === 0) {\n throw new Error('Non-local HTTP binds require at least one allowed host');\n }\n }\n\n const server = createHttpServer(db, options);\n await new Promise<void>((resolve, reject) => {\n server.listen(options.port, options.host, () => resolve());\n server.on('error', reject);\n });\n return server;\n}\n"],"mappings":"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","names":["__dirname"]}
1
+ {"version":3,"sources":["../../src/http/cli.ts","../../src/http/server.ts"],"sourcesContent":["#!/usr/bin/env node\n// HTTP server for Session Collaboration MCP\n\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\nimport { createLocalDatabase, getDefaultDbPath } from '../db/sqlite-adapter.js';\nimport { startHttpServer } from './server.js';\nimport { loadMigrationsFromDir } from '../db/migrations.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\nfunction loadMigrations(): string[] {\n const migrationsDir = join(__dirname, '..', '..', 'migrations');\n return loadMigrationsFromDir(migrationsDir);\n}\n\nfunction parseArgs(): { dbPath?: string; host: string; port: number; allowedHosts: string[] } {\n const args = process.argv.slice(2);\n let dbPath: string | undefined;\n let host = '127.0.0.1';\n let port = 8765;\n const allowedHosts: string[] = [];\n\n for (let i = 0; i < args.length; i++) {\n const value = args[i + 1];\n if (args[i] === '--db' && value) {\n dbPath = value;\n i++;\n } else if (args[i] === '--host' && value) {\n host = value;\n i++;\n } else if (args[i] === '--port' && value) {\n port = Number(value);\n i++;\n } else if (args[i] === '--allowed-host' && value) {\n allowedHosts.push(value);\n i++;\n }\n }\n\n return { dbPath, host, port, allowedHosts };\n}\n\nasync function main(): Promise<void> {\n const { dbPath, host, port, allowedHosts } = parseArgs();\n const db = createLocalDatabase(dbPath);\n const apiToken = process.env.SESSION_COLLAB_HTTP_TOKEN;\n const envAllowedHosts = (process.env.SESSION_COLLAB_ALLOWED_HOSTS ?? '')\n .split(',')\n .map((hostEntry) => hostEntry.trim())\n .filter(Boolean);\n\n try {\n const migrations = loadMigrations();\n db.initSchema(migrations);\n } catch (error) {\n if (!(error instanceof Error && error.message.includes('already exists'))) {\n console.error('Warning: Migration error:', error);\n }\n }\n\n await startHttpServer(db, {\n host,\n port,\n apiToken,\n allowedHosts: [...envAllowedHosts, ...allowedHosts],\n });\n console.error(`Session Collab HTTP Server running at http://${host}:${port}`);\n console.error(`Database: ${dbPath ?? getDefaultDbPath()}`);\n console.error(`MCP endpoint: POST /mcp`);\n console.error(`Convenience REST API: /v1/*`);\n}\n\nmain().catch((error) => {\n console.error('Fatal error:', error);\n process.exit(1);\n});\n","import http from 'http';\nimport { URL } from 'url';\nimport type { DatabaseAdapter } from '../db/sqlite-adapter.js';\nimport type { JsonRpcRequest, JsonRpcResponse, McpToolResult } from '../mcp/protocol.js';\nimport { JsonRpcRequestSchema } from '../mcp/protocol.js';\nimport { McpServer, getMcpTools, handleMcpRequest } from '../mcp/server.js';\nimport { generateId } from '../utils/crypto.js';\n\ntype JsonPrimitive = null | boolean | number | string;\ntype JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue };\n\nexport type HttpSuccessResponse = {\n ok: true;\n data?: JsonValue;\n};\n\nexport type HttpErrorResponse = {\n ok: false;\n error: string;\n code: string;\n message: string;\n trace_id: string;\n};\n\nexport type HttpResponse = HttpSuccessResponse | HttpErrorResponse;\n\nexport type HttpServerOptions = {\n host?: string;\n allowedHosts?: string[];\n apiToken?: string;\n};\n\nclass HttpRequestError extends Error {\n constructor(\n readonly status: number,\n readonly code: string,\n message: string\n ) {\n super(message);\n this.name = 'HttpRequestError';\n }\n}\n\nfunction normalizeHost(host: string): string {\n return host.trim().toLowerCase();\n}\n\nfunction normalizeHostHeader(rawHost: string | undefined): string | null {\n if (!rawHost) return null;\n const first = rawHost.split(',')[0]?.trim();\n if (!first) return null;\n\n if (first.startsWith('[')) {\n const closingIndex = first.indexOf(']');\n if (closingIndex !== -1) {\n return normalizeHost(first.slice(1, closingIndex));\n }\n }\n\n const withoutPort = first.includes(':') ? first.split(':')[0] : first;\n return normalizeHost(withoutPort);\n}\n\nfunction isLocalBindHost(host: string): boolean {\n const normalized = normalizeHost(host);\n return normalized === '127.0.0.1' || normalized === 'localhost' || normalized === '::1';\n}\n\nfunction buildAllowedHosts(host: string, configuredHosts: string[] = []): Set<string> {\n const allowedHosts = new Set(configuredHosts.map(normalizeHost).filter(Boolean));\n\n if (isLocalBindHost(host)) {\n allowedHosts.add('localhost');\n allowedHosts.add('127.0.0.1');\n allowedHosts.add('::1');\n } else if (host !== '0.0.0.0' && host !== '::') {\n allowedHosts.add(normalizeHost(host));\n }\n\n return allowedHosts;\n}\n\nfunction getTraceId(req: http.IncomingMessage): string {\n const requestId = req.headers['x-request-id'];\n if (typeof requestId === 'string' && requestId.trim()) {\n return requestId.trim();\n }\n return generateId();\n}\n\nfunction sendJson(\n res: http.ServerResponse,\n status: number,\n body: unknown,\n traceId: string,\n contentType: string = 'application/json'\n): void {\n const payload = JSON.stringify(body);\n res.writeHead(status, {\n 'Content-Type': contentType,\n 'Content-Length': Buffer.byteLength(payload),\n 'X-Request-ID': traceId,\n });\n res.end(payload);\n}\n\nfunction sendHttpError(\n res: http.ServerResponse,\n status: number,\n code: string,\n message: string,\n traceId: string\n): void {\n sendJson(res, status, { ok: false, error: code, code, message, trace_id: traceId }, traceId);\n}\n\nfunction sendJsonRpcResponse(\n res: http.ServerResponse,\n status: number,\n response: JsonRpcResponse,\n traceId: string\n): void {\n sendJson(res, status, response as unknown as JsonValue, traceId);\n}\n\nfunction sendJsonRpcError(\n res: http.ServerResponse,\n status: number,\n id: string | number | null,\n code: number,\n message: string,\n traceId: string\n): void {\n sendJson(\n res,\n status,\n {\n jsonrpc: '2.0',\n id,\n error: {\n code,\n message,\n data: { trace_id: traceId },\n },\n } as unknown as JsonValue,\n traceId\n );\n}\n\nasync function readJsonBody(req: http.IncomingMessage): Promise<JsonValue | undefined> {\n return await new Promise((resolve, reject) => {\n let data = '';\n let settled = false;\n\n const rejectOnce = (error: Error) => {\n if (settled) return;\n settled = true;\n reject(error);\n };\n\n const resolveOnce = (value: JsonValue | undefined) => {\n if (settled) return;\n settled = true;\n resolve(value);\n };\n\n req.on('data', (chunk) => {\n if (settled) return;\n data += chunk;\n if (data.length > 1_000_000) {\n rejectOnce(new HttpRequestError(413, 'PAYLOAD_TOO_LARGE', 'Payload too large'));\n req.pause();\n }\n });\n\n req.on('end', () => {\n if (settled) return;\n if (!data) {\n resolveOnce(undefined);\n return;\n }\n\n try {\n resolveOnce(JSON.parse(data) as JsonValue);\n } catch {\n rejectOnce(new HttpRequestError(400, 'INVALID_JSON', 'Request body must be valid JSON'));\n }\n });\n\n req.on('error', (error) => rejectOnce(error instanceof Error ? error : new Error(String(error))));\n });\n}\n\nexport function normalizeToolResult(result: McpToolResult, traceId: string): HttpResponse {\n const text = result.content?.[0]?.text ?? '';\n\n try {\n const data = JSON.parse(text) as JsonValue;\n if (result.isError) {\n const errorCode =\n data && typeof data === 'object' && 'error' in data\n ? String((data as { error?: string }).error ?? 'UNKNOWN')\n : 'UNKNOWN';\n const message =\n data && typeof data === 'object' && 'message' in data\n ? String((data as { message?: string }).message ?? 'Error')\n : 'Error';\n return {\n ok: false,\n error: errorCode,\n code: errorCode,\n message,\n trace_id: traceId,\n };\n }\n\n return { ok: true, data };\n } catch {\n if (result.isError) {\n return {\n ok: false,\n error: 'UNKNOWN',\n code: 'UNKNOWN',\n message: text || 'Error',\n trace_id: traceId,\n };\n }\n\n return { ok: true, data: text };\n }\n}\n\nexport function coerceQueryValue(value: string): string | number | boolean {\n if (value === 'true') return true;\n if (value === 'false') return false;\n const num = Number(value);\n if (!Number.isNaN(num) && value.trim() !== '') {\n return num;\n }\n return value;\n}\n\nexport function parseQueryParams(url: URL): Record<string, JsonValue> {\n const params: Record<string, JsonValue> = {};\n for (const [key, value] of url.searchParams.entries()) {\n params[key] = coerceQueryValue(value);\n }\n return params;\n}\n\nfunction enforceHttpSecurity(\n req: http.IncomingMessage,\n options: Required<Pick<HttpServerOptions, 'host'>> & Pick<HttpServerOptions, 'allowedHosts' | 'apiToken'>\n): void {\n const allowedHosts = buildAllowedHosts(options.host, options.allowedHosts);\n const hostHeader = normalizeHostHeader(req.headers.host);\n\n if (allowedHosts.size > 0) {\n if (!hostHeader || !allowedHosts.has(hostHeader)) {\n throw new HttpRequestError(403, 'INVALID_HOST', 'Host header is not allowed');\n }\n\n const originHeader = req.headers.origin;\n if (originHeader) {\n let origin: URL;\n try {\n origin = new URL(originHeader);\n } catch {\n throw new HttpRequestError(403, 'INVALID_ORIGIN', 'Origin header is invalid');\n }\n\n if (!allowedHosts.has(normalizeHost(origin.hostname))) {\n throw new HttpRequestError(403, 'INVALID_ORIGIN', 'Origin header is not allowed');\n }\n }\n }\n\n if (options.apiToken) {\n const authHeader = req.headers.authorization;\n const expected = `Bearer ${options.apiToken}`;\n if (authHeader !== expected) {\n throw new HttpRequestError(401, 'UNAUTHORIZED', 'Valid bearer token is required');\n }\n }\n}\n\nasync function handleRestTool(\n db: DatabaseAdapter,\n name: string,\n args: Record<string, unknown>,\n traceId: string\n): Promise<HttpResponse> {\n const result = await handleMcpRequest(db, name, args);\n return normalizeToolResult(result, traceId);\n}\n\nexport function createHttpServer(db: DatabaseAdapter, options: HttpServerOptions = {}): http.Server {\n const host = options.host ?? '127.0.0.1';\n const mcpServer = new McpServer(db);\n\n return http.createServer(async (req, res) => {\n const traceId = getTraceId(req);\n\n try {\n const method = req.method ?? 'GET';\n const url = new URL(req.url ?? '/', 'http://localhost');\n\n if (method === 'GET' && url.pathname === '/health') {\n sendJson(res, 200, { ok: true, data: { status: 'ok' } }, traceId);\n return;\n }\n\n enforceHttpSecurity(req, {\n host,\n allowedHosts: options.allowedHosts,\n apiToken: options.apiToken,\n });\n\n if (method === 'GET' && url.pathname === '/v1/tools') {\n sendJson(res, 200, { ok: true, data: { tools: getMcpTools() } }, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/tools/call') {\n const body = (await readJsonBody(req)) as { name?: string; args?: Record<string, unknown> } | undefined;\n if (!body?.name) {\n sendHttpError(res, 400, 'INVALID_INPUT', 'name is required', traceId);\n return;\n }\n const response = await handleRestTool(db, body.name, body.args ?? {}, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/mcp') {\n const body = await readJsonBody(req);\n const validation = JsonRpcRequestSchema.safeParse(body);\n if (!validation.success) {\n sendJsonRpcError(res, 400, null, -32600, 'Invalid Request', traceId);\n return;\n }\n\n const response = await mcpServer.handleRequest(validation.data as JsonRpcRequest);\n sendJsonRpcResponse(res, 200, response, traceId);\n return;\n }\n\n if (method === 'GET' && url.pathname === '/mcp') {\n sendHttpError(\n res,\n 501,\n 'STREAM_NOT_SUPPORTED',\n 'This server exposes MCP JSON-RPC over HTTP POST at /mcp. SSE streaming is not implemented.',\n traceId\n );\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/sessions/start') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_session_start', body ?? {}, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/sessions/end') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_session_end', body ?? {}, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/sessions/update') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_session_update', body ?? {}, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'GET' && url.pathname === '/v1/sessions') {\n const response = await handleRestTool(db, 'collab_session_list', parseQueryParams(url), traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/config') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_config', body ?? {}, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'GET' && url.pathname === '/v1/status') {\n const response = await handleRestTool(db, 'collab_status', parseQueryParams(url), traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/claims') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_claim', { action: 'create', ...(body ?? {}) }, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/claims/check') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_claim', { action: 'check', ...(body ?? {}) }, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/claims/release') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_claim', { action: 'release', ...(body ?? {}) }, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'GET' && url.pathname === '/v1/claims') {\n const response = await handleRestTool(\n db,\n 'collab_claim',\n { action: 'list', ...parseQueryParams(url) },\n traceId\n );\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/memory/save') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_memory_save', body ?? {}, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/memory/recall') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_memory_recall', body ?? {}, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/memory/clear') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_memory_clear', body ?? {}, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/protect/register') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_protect', { action: 'register', ...(body ?? {}) }, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'POST' && url.pathname === '/v1/protect/check') {\n const body = (await readJsonBody(req)) as Record<string, unknown> | undefined;\n const response = await handleRestTool(db, 'collab_protect', { action: 'check', ...(body ?? {}) }, traceId);\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n if (method === 'GET' && url.pathname === '/v1/protect/list') {\n const response = await handleRestTool(\n db,\n 'collab_protect',\n { action: 'list', ...parseQueryParams(url) },\n traceId\n );\n sendJson(res, response.ok ? 200 : 400, response as unknown as JsonValue, traceId);\n return;\n }\n\n sendHttpError(res, 404, 'NOT_FOUND', 'Not found', traceId);\n } catch (error) {\n if (error instanceof HttpRequestError) {\n sendHttpError(res, error.status, error.code, error.message, traceId);\n return;\n }\n\n const message = error instanceof Error ? error.message : 'Unexpected error';\n sendHttpError(res, 500, 'INTERNAL_ERROR', message, traceId);\n }\n });\n}\n\nexport async function startHttpServer(\n db: DatabaseAdapter,\n options: { host: string; port: number; allowedHosts?: string[]; apiToken?: string }\n): Promise<http.Server> {\n const normalizedHost = normalizeHost(options.host);\n\n if (!isLocalBindHost(normalizedHost)) {\n if (!options.apiToken) {\n throw new Error('Non-local HTTP binds require SESSION_COLLAB_HTTP_TOKEN');\n }\n\n const allowedHosts = buildAllowedHosts(normalizedHost, options.allowedHosts);\n if (allowedHosts.size === 0) {\n throw new Error('Non-local HTTP binds require at least one allowed host');\n }\n }\n\n const server = createHttpServer(db, options);\n await new Promise<void>((resolve, reject) => {\n server.listen(options.port, options.host, () => resolve());\n server.on('error', reject);\n });\n return server;\n}\n"],"mappings":";;;;;;;;;;;;;AAGA,SAAS,MAAM,eAAe;AAC9B,SAAS,qBAAqB;;;ACJ9B,OAAO,UAAU;AACjB,SAAS,WAAW;AA+BpB,IAAM,mBAAN,cAA+B,MAAM;AAAA,EACnC,YACW,QACA,MACT,SACA;AACA,UAAM,OAAO;AAJJ;AACA;AAIT,SAAK,OAAO;AAAA,EACd;AACF;AAEA,SAAS,cAAc,MAAsB;AAC3C,SAAO,KAAK,KAAK,EAAE,YAAY;AACjC;AAEA,SAAS,oBAAoB,SAA4C;AACvE,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,QAAQ,QAAQ,MAAM,GAAG,EAAE,CAAC,GAAG,KAAK;AAC1C,MAAI,CAAC,MAAO,QAAO;AAEnB,MAAI,MAAM,WAAW,GAAG,GAAG;AACzB,UAAM,eAAe,MAAM,QAAQ,GAAG;AACtC,QAAI,iBAAiB,IAAI;AACvB,aAAO,cAAc,MAAM,MAAM,GAAG,YAAY,CAAC;AAAA,IACnD;AAAA,EACF;AAEA,QAAM,cAAc,MAAM,SAAS,GAAG,IAAI,MAAM,MAAM,GAAG,EAAE,CAAC,IAAI;AAChE,SAAO,cAAc,WAAW;AAClC;AAEA,SAAS,gBAAgB,MAAuB;AAC9C,QAAM,aAAa,cAAc,IAAI;AACrC,SAAO,eAAe,eAAe,eAAe,eAAe,eAAe;AACpF;AAEA,SAAS,kBAAkB,MAAc,kBAA4B,CAAC,GAAgB;AACpF,QAAM,eAAe,IAAI,IAAI,gBAAgB,IAAI,aAAa,EAAE,OAAO,OAAO,CAAC;AAE/E,MAAI,gBAAgB,IAAI,GAAG;AACzB,iBAAa,IAAI,WAAW;AAC5B,iBAAa,IAAI,WAAW;AAC5B,iBAAa,IAAI,KAAK;AAAA,EACxB,WAAW,SAAS,aAAa,SAAS,MAAM;AAC9C,iBAAa,IAAI,cAAc,IAAI,CAAC;AAAA,EACtC;AAEA,SAAO;AACT;AAEA,SAAS,WAAW,KAAmC;AACrD,QAAM,YAAY,IAAI,QAAQ,cAAc;AAC5C,MAAI,OAAO,cAAc,YAAY,UAAU,KAAK,GAAG;AACrD,WAAO,UAAU,KAAK;AAAA,EACxB;AACA,SAAO,WAAW;AACpB;AAEA,SAAS,SACP,KACA,QACA,MACA,SACA,cAAsB,oBAChB;AACN,QAAM,UAAU,KAAK,UAAU,IAAI;AACnC,MAAI,UAAU,QAAQ;AAAA,IACpB,gBAAgB;AAAA,IAChB,kBAAkB,OAAO,WAAW,OAAO;AAAA,IAC3C,gBAAgB;AAAA,EAClB,CAAC;AACD,MAAI,IAAI,OAAO;AACjB;AAEA,SAAS,cACP,KACA,QACA,MACA,SACA,SACM;AACN,WAAS,KAAK,QAAQ,EAAE,IAAI,OAAO,OAAO,MAAM,MAAM,SAAS,UAAU,QAAQ,GAAG,OAAO;AAC7F;AAEA,SAAS,oBACP,KACA,QACA,UACA,SACM;AACN,WAAS,KAAK,QAAQ,UAAkC,OAAO;AACjE;AAEA,SAAS,iBACP,KACA,QACA,IACA,MACA,SACA,SACM;AACN;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,MACE,SAAS;AAAA,MACT;AAAA,MACA,OAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA,MAAM,EAAE,UAAU,QAAQ;AAAA,MAC5B;AAAA,IACF;AAAA,IACA;AAAA,EACF;AACF;AAEA,eAAe,aAAa,KAA2D;AACrF,SAAO,MAAM,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC5C,QAAI,OAAO;AACX,QAAI,UAAU;AAEd,UAAM,aAAa,CAAC,UAAiB;AACnC,UAAI,QAAS;AACb,gBAAU;AACV,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,cAAc,CAAC,UAAiC;AACpD,UAAI,QAAS;AACb,gBAAU;AACV,cAAQ,KAAK;AAAA,IACf;AAEA,QAAI,GAAG,QAAQ,CAAC,UAAU;AACxB,UAAI,QAAS;AACb,cAAQ;AACR,UAAI,KAAK,SAAS,KAAW;AAC3B,mBAAW,IAAI,iBAAiB,KAAK,qBAAqB,mBAAmB,CAAC;AAC9E,YAAI,MAAM;AAAA,MACZ;AAAA,IACF,CAAC;AAED,QAAI,GAAG,OAAO,MAAM;AAClB,UAAI,QAAS;AACb,UAAI,CAAC,MAAM;AACT,oBAAY,MAAS;AACrB;AAAA,MACF;AAEA,UAAI;AACF,oBAAY,KAAK,MAAM,IAAI,CAAc;AAAA,MAC3C,QAAQ;AACN,mBAAW,IAAI,iBAAiB,KAAK,gBAAgB,iCAAiC,CAAC;AAAA,MACzF;AAAA,IACF,CAAC;AAED,QAAI,GAAG,SAAS,CAAC,UAAU,WAAW,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC,CAAC;AAAA,EAClG,CAAC;AACH;AAEO,SAAS,oBAAoB,QAAuB,SAA+B;AACxF,QAAM,OAAO,OAAO,UAAU,CAAC,GAAG,QAAQ;AAE1C,MAAI;AACF,UAAM,OAAO,KAAK,MAAM,IAAI;AAC5B,QAAI,OAAO,SAAS;AAClB,YAAM,YACJ,QAAQ,OAAO,SAAS,YAAY,WAAW,OAC3C,OAAQ,KAA4B,SAAS,SAAS,IACtD;AACN,YAAM,UACJ,QAAQ,OAAO,SAAS,YAAY,aAAa,OAC7C,OAAQ,KAA8B,WAAW,OAAO,IACxD;AACN,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,MAAM;AAAA,QACN;AAAA,QACA,UAAU;AAAA,MACZ;AAAA,IACF;AAEA,WAAO,EAAE,IAAI,MAAM,KAAK;AAAA,EAC1B,QAAQ;AACN,QAAI,OAAO,SAAS;AAClB,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,OAAO;AAAA,QACP,MAAM;AAAA,QACN,SAAS,QAAQ;AAAA,QACjB,UAAU;AAAA,MACZ;AAAA,IACF;AAEA,WAAO,EAAE,IAAI,MAAM,MAAM,KAAK;AAAA,EAChC;AACF;AAEO,SAAS,iBAAiB,OAA0C;AACzE,MAAI,UAAU,OAAQ,QAAO;AAC7B,MAAI,UAAU,QAAS,QAAO;AAC9B,QAAM,MAAM,OAAO,KAAK;AACxB,MAAI,CAAC,OAAO,MAAM,GAAG,KAAK,MAAM,KAAK,MAAM,IAAI;AAC7C,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEO,SAAS,iBAAiB,KAAqC;AACpE,QAAM,SAAoC,CAAC;AAC3C,aAAW,CAAC,KAAK,KAAK,KAAK,IAAI,aAAa,QAAQ,GAAG;AACrD,WAAO,GAAG,IAAI,iBAAiB,KAAK;AAAA,EACtC;AACA,SAAO;AACT;AAEA,SAAS,oBACP,KACA,SACM;AACN,QAAM,eAAe,kBAAkB,QAAQ,MAAM,QAAQ,YAAY;AACzE,QAAM,aAAa,oBAAoB,IAAI,QAAQ,IAAI;AAEvD,MAAI,aAAa,OAAO,GAAG;AACzB,QAAI,CAAC,cAAc,CAAC,aAAa,IAAI,UAAU,GAAG;AAChD,YAAM,IAAI,iBAAiB,KAAK,gBAAgB,4BAA4B;AAAA,IAC9E;AAEA,UAAM,eAAe,IAAI,QAAQ;AACjC,QAAI,cAAc;AAChB,UAAI;AACJ,UAAI;AACF,iBAAS,IAAI,IAAI,YAAY;AAAA,MAC/B,QAAQ;AACN,cAAM,IAAI,iBAAiB,KAAK,kBAAkB,0BAA0B;AAAA,MAC9E;AAEA,UAAI,CAAC,aAAa,IAAI,cAAc,OAAO,QAAQ,CAAC,GAAG;AACrD,cAAM,IAAI,iBAAiB,KAAK,kBAAkB,8BAA8B;AAAA,MAClF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,QAAQ,UAAU;AACpB,UAAM,aAAa,IAAI,QAAQ;AAC/B,UAAM,WAAW,UAAU,QAAQ,QAAQ;AAC3C,QAAI,eAAe,UAAU;AAC3B,YAAM,IAAI,iBAAiB,KAAK,gBAAgB,gCAAgC;AAAA,IAClF;AAAA,EACF;AACF;AAEA,eAAe,eACb,IACA,MACA,MACA,SACuB;AACvB,QAAM,SAAS,MAAM,iBAAiB,IAAI,MAAM,IAAI;AACpD,SAAO,oBAAoB,QAAQ,OAAO;AAC5C;AAEO,SAAS,iBAAiB,IAAqB,UAA6B,CAAC,GAAgB;AAClG,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,YAAY,IAAI,UAAU,EAAE;AAElC,SAAO,KAAK,aAAa,OAAO,KAAK,QAAQ;AAC3C,UAAM,UAAU,WAAW,GAAG;AAE9B,QAAI;AACF,YAAM,SAAS,IAAI,UAAU;AAC7B,YAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,kBAAkB;AAEtD,UAAI,WAAW,SAAS,IAAI,aAAa,WAAW;AAClD,iBAAS,KAAK,KAAK,EAAE,IAAI,MAAM,MAAM,EAAE,QAAQ,KAAK,EAAE,GAAG,OAAO;AAChE;AAAA,MACF;AAEA,0BAAoB,KAAK;AAAA,QACvB;AAAA,QACA,cAAc,QAAQ;AAAA,QACtB,UAAU,QAAQ;AAAA,MACpB,CAAC;AAED,UAAI,WAAW,SAAS,IAAI,aAAa,aAAa;AACpD,iBAAS,KAAK,KAAK,EAAE,IAAI,MAAM,MAAM,EAAE,OAAO,YAAY,EAAE,EAAE,GAAG,OAAO;AACxE;AAAA,MACF;AAEA,UAAI,WAAW,UAAU,IAAI,aAAa,kBAAkB;AAC1D,cAAM,OAAQ,MAAM,aAAa,GAAG;AACpC,YAAI,CAAC,MAAM,MAAM;AACf,wBAAc,KAAK,KAAK,iBAAiB,oBAAoB,OAAO;AACpE;AAAA,QACF;AACA,cAAM,WAAW,MAAM,eAAe,IAAI,KAAK,MAAM,KAAK,QAAQ,CAAC,GAAG,OAAO;AAC7E,iBAAS,KAAK,SAAS,KAAK,MAAM,KAAK,UAAkC,OAAO;AAChF;AAAA,MACF;AAEA,UAAI,WAAW,UAAU,IAAI,aAAa,QAAQ;AAChD,cAAM,OAAO,MAAM,aAAa,GAAG;AACnC,cAAM,aAAa,qBAAqB,UAAU,IAAI;AACtD,YAAI,CAAC,WAAW,SAAS;AACvB,2BAAiB,KAAK,KAAK,MAAM,QAAQ,mBAAmB,OAAO;AACnE;AAAA,QACF;AAEA,cAAM,WAAW,MAAM,UAAU,cAAc,WAAW,IAAsB;AAChF,4BAAoB,KAAK,KAAK,UAAU,OAAO;AAC/C;AAAA,MACF;AAEA,UAAI,WAAW,SAAS,IAAI,aAAa,QAAQ;AAC/C;AAAA,UACE;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA;AAAA,MACF;AAEA,UAAI,WAAW,UAAU,IAAI,aAAa,sBAAsB;AAC9D,cAAM,OAAQ,MAAM,aAAa,GAAG;AACpC,cAAM,WAAW,MAAM,eAAe,IAAI,wBAAwB,QAAQ,CAAC,GAAG,OAAO;AACrF,iBAAS,KAAK,SAAS,KAAK,MAAM,KAAK,UAAkC,OAAO;AAChF;AAAA,MACF;AAEA,UAAI,WAAW,UAAU,IAAI,aAAa,oBAAoB;AAC5D,cAAM,OAAQ,MAAM,aAAa,GAAG;AACpC,cAAM,WAAW,MAAM,eAAe,IAAI,sBAAsB,QAAQ,CAAC,GAAG,OAAO;AACnF,iBAAS,KAAK,SAAS,KAAK,MAAM,KAAK,UAAkC,OAAO;AAChF;AAAA,MACF;AAEA,UAAI,WAAW,UAAU,IAAI,aAAa,uBAAuB;AAC/D,cAAM,OAAQ,MAAM,aAAa,GAAG;AACpC,cAAM,WAAW,MAAM,eAAe,IAAI,yBAAyB,QAAQ,CAAC,GAAG,OAAO;AACtF,iBAAS,KAAK,SAAS,KAAK,MAAM,KAAK,UAAkC,OAAO;AAChF;AAAA,MACF;AAEA,UAAI,WAAW,SAAS,IAAI,aAAa,gBAAgB;AACvD,cAAM,WAAW,MAAM,eAAe,IAAI,uBAAuB,iBAAiB,GAAG,GAAG,OAAO;AAC/F,iBAAS,KAAK,SAAS,KAAK,MAAM,KAAK,UAAkC,OAAO;AAChF;AAAA,MACF;AAEA,UAAI,WAAW,UAAU,IAAI,aAAa,cAAc;AACtD,cAAM,OAAQ,MAAM,aAAa,GAAG;AACpC,cAAM,WAAW,MAAM,eAAe,IAAI,iBAAiB,QAAQ,CAAC,GAAG,OAAO;AAC9E,iBAAS,KAAK,SAAS,KAAK,MAAM,KAAK,UAAkC,OAAO;AAChF;AAAA,MACF;AAEA,UAAI,WAAW,SAAS,IAAI,aAAa,cAAc;AACrD,cAAM,WAAW,MAAM,eAAe,IAAI,iBAAiB,iBAAiB,GAAG,GAAG,OAAO;AACzF,iBAAS,KAAK,SAAS,KAAK,MAAM,KAAK,UAAkC,OAAO;AAChF;AAAA,MACF;AAEA,UAAI,WAAW,UAAU,IAAI,aAAa,cAAc;AACtD,cAAM,OAAQ,MAAM,aAAa,GAAG;AACpC,cAAM,WAAW,MAAM,eAAe,IAAI,gBAAgB,EAAE,QAAQ,UAAU,GAAI,QAAQ,CAAC,EAAG,GAAG,OAAO;AACxG,iBAAS,KAAK,SAAS,KAAK,MAAM,KAAK,UAAkC,OAAO;AAChF;AAAA,MACF;AAEA,UAAI,WAAW,UAAU,IAAI,aAAa,oBAAoB;AAC5D,cAAM,OAAQ,MAAM,aAAa,GAAG;AACpC,cAAM,WAAW,MAAM,eAAe,IAAI,gBAAgB,EAAE,QAAQ,SAAS,GAAI,QAAQ,CAAC,EAAG,GAAG,OAAO;AACvG,iBAAS,KAAK,SAAS,KAAK,MAAM,KAAK,UAAkC,OAAO;AAChF;AAAA,MACF;AAEA,UAAI,WAAW,UAAU,IAAI,aAAa,sBAAsB;AAC9D,cAAM,OAAQ,MAAM,aAAa,GAAG;AACpC,cAAM,WAAW,MAAM,eAAe,IAAI,gBAAgB,EAAE,QAAQ,WAAW,GAAI,QAAQ,CAAC,EAAG,GAAG,OAAO;AACzG,iBAAS,KAAK,SAAS,KAAK,MAAM,KAAK,UAAkC,OAAO;AAChF;AAAA,MACF;AAEA,UAAI,WAAW,SAAS,IAAI,aAAa,cAAc;AACrD,cAAM,WAAW,MAAM;AAAA,UACrB;AAAA,UACA;AAAA,UACA,EAAE,QAAQ,QAAQ,GAAG,iBAAiB,GAAG,EAAE;AAAA,UAC3C;AAAA,QACF;AACA,iBAAS,KAAK,SAAS,KAAK,MAAM,KAAK,UAAkC,OAAO;AAChF;AAAA,MACF;AAEA,UAAI,WAAW,UAAU,IAAI,aAAa,mBAAmB;AAC3D,cAAM,OAAQ,MAAM,aAAa,GAAG;AACpC,cAAM,WAAW,MAAM,eAAe,IAAI,sBAAsB,QAAQ,CAAC,GAAG,OAAO;AACnF,iBAAS,KAAK,SAAS,KAAK,MAAM,KAAK,UAAkC,OAAO;AAChF;AAAA,MACF;AAEA,UAAI,WAAW,UAAU,IAAI,aAAa,qBAAqB;AAC7D,cAAM,OAAQ,MAAM,aAAa,GAAG;AACpC,cAAM,WAAW,MAAM,eAAe,IAAI,wBAAwB,QAAQ,CAAC,GAAG,OAAO;AACrF,iBAAS,KAAK,SAAS,KAAK,MAAM,KAAK,UAAkC,OAAO;AAChF;AAAA,MACF;AAEA,UAAI,WAAW,UAAU,IAAI,aAAa,oBAAoB;AAC5D,cAAM,OAAQ,MAAM,aAAa,GAAG;AACpC,cAAM,WAAW,MAAM,eAAe,IAAI,uBAAuB,QAAQ,CAAC,GAAG,OAAO;AACpF,iBAAS,KAAK,SAAS,KAAK,MAAM,KAAK,UAAkC,OAAO;AAChF;AAAA,MACF;AAEA,UAAI,WAAW,UAAU,IAAI,aAAa,wBAAwB;AAChE,cAAM,OAAQ,MAAM,aAAa,GAAG;AACpC,cAAM,WAAW,MAAM,eAAe,IAAI,kBAAkB,EAAE,QAAQ,YAAY,GAAI,QAAQ,CAAC,EAAG,GAAG,OAAO;AAC5G,iBAAS,KAAK,SAAS,KAAK,MAAM,KAAK,UAAkC,OAAO;AAChF;AAAA,MACF;AAEA,UAAI,WAAW,UAAU,IAAI,aAAa,qBAAqB;AAC7D,cAAM,OAAQ,MAAM,aAAa,GAAG;AACpC,cAAM,WAAW,MAAM,eAAe,IAAI,kBAAkB,EAAE,QAAQ,SAAS,GAAI,QAAQ,CAAC,EAAG,GAAG,OAAO;AACzG,iBAAS,KAAK,SAAS,KAAK,MAAM,KAAK,UAAkC,OAAO;AAChF;AAAA,MACF;AAEA,UAAI,WAAW,SAAS,IAAI,aAAa,oBAAoB;AAC3D,cAAM,WAAW,MAAM;AAAA,UACrB;AAAA,UACA;AAAA,UACA,EAAE,QAAQ,QAAQ,GAAG,iBAAiB,GAAG,EAAE;AAAA,UAC3C;AAAA,QACF;AACA,iBAAS,KAAK,SAAS,KAAK,MAAM,KAAK,UAAkC,OAAO;AAChF;AAAA,MACF;AAEA,oBAAc,KAAK,KAAK,aAAa,aAAa,OAAO;AAAA,IAC3D,SAAS,OAAO;AACd,UAAI,iBAAiB,kBAAkB;AACrC,sBAAc,KAAK,MAAM,QAAQ,MAAM,MAAM,MAAM,SAAS,OAAO;AACnE;AAAA,MACF;AAEA,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,oBAAc,KAAK,KAAK,kBAAkB,SAAS,OAAO;AAAA,IAC5D;AAAA,EACF,CAAC;AACH;AAEA,eAAsB,gBACpB,IACA,SACsB;AACtB,QAAM,iBAAiB,cAAc,QAAQ,IAAI;AAEjD,MAAI,CAAC,gBAAgB,cAAc,GAAG;AACpC,QAAI,CAAC,QAAQ,UAAU;AACrB,YAAM,IAAI,MAAM,wDAAwD;AAAA,IAC1E;AAEA,UAAM,eAAe,kBAAkB,gBAAgB,QAAQ,YAAY;AAC3E,QAAI,aAAa,SAAS,GAAG;AAC3B,YAAM,IAAI,MAAM,wDAAwD;AAAA,IAC1E;AAAA,EACF;AAEA,QAAM,SAAS,iBAAiB,IAAI,OAAO;AAC3C,QAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,WAAO,OAAO,QAAQ,MAAM,QAAQ,MAAM,MAAM,QAAQ,CAAC;AACzD,WAAO,GAAG,SAAS,MAAM;AAAA,EAC3B,CAAC;AACD,SAAO;AACT;;;ADvfA,IAAMA,aAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAExD,SAAS,iBAA2B;AAClC,QAAM,gBAAgB,KAAKA,YAAW,MAAM,MAAM,YAAY;AAC9D,SAAO,sBAAsB,aAAa;AAC5C;AAEA,SAAS,YAAqF;AAC5F,QAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,MAAI;AACJ,MAAI,OAAO;AACX,MAAI,OAAO;AACX,QAAM,eAAyB,CAAC;AAEhC,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,QAAQ,KAAK,IAAI,CAAC;AACxB,QAAI,KAAK,CAAC,MAAM,UAAU,OAAO;AAC/B,eAAS;AACT;AAAA,IACF,WAAW,KAAK,CAAC,MAAM,YAAY,OAAO;AACxC,aAAO;AACP;AAAA,IACF,WAAW,KAAK,CAAC,MAAM,YAAY,OAAO;AACxC,aAAO,OAAO,KAAK;AACnB;AAAA,IACF,WAAW,KAAK,CAAC,MAAM,oBAAoB,OAAO;AAChD,mBAAa,KAAK,KAAK;AACvB;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,QAAQ,MAAM,MAAM,aAAa;AAC5C;AAEA,eAAe,OAAsB;AACnC,QAAM,EAAE,QAAQ,MAAM,MAAM,aAAa,IAAI,UAAU;AACvD,QAAM,KAAK,oBAAoB,MAAM;AACrC,QAAM,WAAW,QAAQ,IAAI;AAC7B,QAAM,mBAAmB,QAAQ,IAAI,gCAAgC,IAClE,MAAM,GAAG,EACT,IAAI,CAAC,cAAc,UAAU,KAAK,CAAC,EACnC,OAAO,OAAO;AAEjB,MAAI;AACF,UAAM,aAAa,eAAe;AAClC,OAAG,WAAW,UAAU;AAAA,EAC1B,SAAS,OAAO;AACd,QAAI,EAAE,iBAAiB,SAAS,MAAM,QAAQ,SAAS,gBAAgB,IAAI;AACzE,cAAQ,MAAM,6BAA6B,KAAK;AAAA,IAClD;AAAA,EACF;AAEA,QAAM,gBAAgB,IAAI;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc,CAAC,GAAG,iBAAiB,GAAG,YAAY;AAAA,EACpD,CAAC;AACD,UAAQ,MAAM,gDAAgD,IAAI,IAAI,IAAI,EAAE;AAC5E,UAAQ,MAAM,aAAa,UAAU,iBAAiB,CAAC,EAAE;AACzD,UAAQ,MAAM,yBAAyB;AACvC,UAAQ,MAAM,6BAA6B;AAC7C;AAEA,KAAK,EAAE,MAAM,CAAC,UAAU;AACtB,UAAQ,MAAM,gBAAgB,KAAK;AACnC,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["__dirname"]}
@@ -1,6 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/http/client-cli.ts
4
+ var REQUIRED_TOOLS = [
5
+ "collab_session_start",
6
+ "collab_session_end",
7
+ "collab_session_list",
8
+ "collab_session_update",
9
+ "collab_config",
10
+ "collab_status",
11
+ "collab_claim",
12
+ "collab_memory_save",
13
+ "collab_memory_recall",
14
+ "collab_memory_clear",
15
+ "collab_protect"
16
+ ];
4
17
  function parseArgs() {
5
18
  const argv = process.argv.slice(2);
6
19
  let baseUrl = "http://127.0.0.1:8765";
@@ -25,14 +38,71 @@ function printHelp() {
25
38
  console.log(`Usage:
26
39
  session-collab health [--base-url URL]
27
40
  session-collab tools [--base-url URL]
41
+ session-collab doctor [--base-url URL]
28
42
  session-collab call --name TOOL --args JSON [--base-url URL]
29
43
 
30
44
  Examples:
31
45
  session-collab health
32
46
  session-collab tools
47
+ session-collab doctor
33
48
  session-collab call --name collab_session_start --args '{"project_root":"/repo","name":"demo"}'
34
49
  `);
35
50
  }
51
+ function getHeaders() {
52
+ const headers = {};
53
+ const token = process.env.SESSION_COLLAB_HTTP_TOKEN;
54
+ if (token) {
55
+ headers.Authorization = `Bearer ${token}`;
56
+ }
57
+ return headers;
58
+ }
59
+ async function fetchText(url, init = {}) {
60
+ const headers = {
61
+ ...getHeaders(),
62
+ ...init.headers
63
+ };
64
+ const res = await fetch(url, { ...init, headers });
65
+ return { status: res.status, text: await res.text() };
66
+ }
67
+ async function runDoctor(baseUrl) {
68
+ const checks = [];
69
+ try {
70
+ const health = await fetchText(`${baseUrl}/health`);
71
+ checks.push({
72
+ name: "health",
73
+ ok: health.status === 200,
74
+ detail: health.text
75
+ });
76
+ } catch (error) {
77
+ checks.push({
78
+ name: "health",
79
+ ok: false,
80
+ detail: error instanceof Error ? error.message : String(error)
81
+ });
82
+ }
83
+ try {
84
+ const tools = await fetchText(`${baseUrl}/v1/tools`);
85
+ const body = JSON.parse(tools.text);
86
+ const toolNames = new Set(body.data?.tools?.map((tool) => tool.name) ?? []);
87
+ const missingTools = REQUIRED_TOOLS.filter((tool) => !toolNames.has(tool));
88
+ checks.push({
89
+ name: "tools",
90
+ ok: tools.status === 200 && missingTools.length === 0,
91
+ detail: missingTools.length > 0 ? `Missing tools: ${missingTools.join(", ")}` : `${toolNames.size} tools available`
92
+ });
93
+ } catch (error) {
94
+ checks.push({
95
+ name: "tools",
96
+ ok: false,
97
+ detail: error instanceof Error ? error.message : String(error)
98
+ });
99
+ }
100
+ const ok = checks.every((check) => check.ok);
101
+ console.log(JSON.stringify({ ok, base_url: baseUrl, checks }, null, 2));
102
+ if (!ok) {
103
+ process.exit(1);
104
+ }
105
+ }
36
106
  async function main() {
37
107
  const { baseUrl, command, name, args } = parseArgs();
38
108
  if (command === "help" || command === "--help" || command === "-h") {
@@ -40,13 +110,17 @@ async function main() {
40
110
  return;
41
111
  }
42
112
  if (command === "health") {
43
- const res = await fetch(`${baseUrl}/health`);
44
- console.log(await res.text());
113
+ const res = await fetchText(`${baseUrl}/health`);
114
+ console.log(res.text);
45
115
  return;
46
116
  }
47
117
  if (command === "tools") {
48
- const res = await fetch(`${baseUrl}/v1/tools`);
49
- console.log(await res.text());
118
+ const res = await fetchText(`${baseUrl}/v1/tools`);
119
+ console.log(res.text);
120
+ return;
121
+ }
122
+ if (command === "doctor") {
123
+ await runDoctor(baseUrl);
50
124
  return;
51
125
  }
52
126
  if (command === "call") {
@@ -55,12 +129,12 @@ async function main() {
55
129
  process.exit(1);
56
130
  }
57
131
  const parsedArgs = args ? JSON.parse(args) : {};
58
- const res = await fetch(`${baseUrl}/v1/tools/call`, {
132
+ const res = await fetchText(`${baseUrl}/v1/tools/call`, {
59
133
  method: "POST",
60
134
  headers: { "Content-Type": "application/json" },
61
135
  body: JSON.stringify({ name, args: parsedArgs })
62
136
  });
63
- console.log(await res.text());
137
+ console.log(res.text);
64
138
  return;
65
139
  }
66
140
  console.error(`Unknown command: ${command}`);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/http/client-cli.ts"],"sourcesContent":["#!/usr/bin/env node\n// Minimal HTTP client wrapper for Session Collab HTTP API\n\ntype Args = {\n baseUrl: string;\n command: string;\n name?: string;\n args?: string;\n};\n\nfunction parseArgs(): Args {\n const argv = process.argv.slice(2);\n let baseUrl = 'http://127.0.0.1:8765';\n let command = argv[0] ?? 'help';\n let name: string | undefined;\n let args: string | undefined;\n\n for (let i = 1; i < argv.length; i++) {\n if (argv[i] === '--base-url' && argv[i + 1]) {\n baseUrl = argv[i + 1];\n i++;\n } else if (argv[i] === '--name' && argv[i + 1]) {\n name = argv[i + 1];\n i++;\n } else if (argv[i] === '--args' && argv[i + 1]) {\n args = argv[i + 1];\n i++;\n }\n }\n\n return { baseUrl, command, name, args };\n}\n\nfunction printHelp(): void {\n console.log(`Usage:\n session-collab health [--base-url URL]\n session-collab tools [--base-url URL]\n session-collab call --name TOOL --args JSON [--base-url URL]\n\nExamples:\n session-collab health\n session-collab tools\n session-collab call --name collab_session_start --args '{\"project_root\":\"/repo\",\"name\":\"demo\"}'\n`);\n}\n\nasync function main(): Promise<void> {\n const { baseUrl, command, name, args } = parseArgs();\n\n if (command === 'help' || command === '--help' || command === '-h') {\n printHelp();\n return;\n }\n\n if (command === 'health') {\n const res = await fetch(`${baseUrl}/health`);\n console.log(await res.text());\n return;\n }\n\n if (command === 'tools') {\n const res = await fetch(`${baseUrl}/v1/tools`);\n console.log(await res.text());\n return;\n }\n\n if (command === 'call') {\n if (!name) {\n console.error('Missing --name');\n process.exit(1);\n }\n const parsedArgs = args ? JSON.parse(args) : {};\n const res = await fetch(`${baseUrl}/v1/tools/call`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ name, args: parsedArgs }),\n });\n console.log(await res.text());\n return;\n }\n\n console.error(`Unknown command: ${command}`);\n printHelp();\n process.exit(1);\n}\n\nmain().catch((error) => {\n console.error('Fatal error:', error instanceof Error ? error.message : String(error));\n process.exit(1);\n});\n"],"mappings":";;;AAUA,SAAS,YAAkB;AACzB,QAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,MAAI,UAAU;AACd,MAAI,UAAU,KAAK,CAAC,KAAK;AACzB,MAAI;AACJ,MAAI;AAEJ,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,QAAI,KAAK,CAAC,MAAM,gBAAgB,KAAK,IAAI,CAAC,GAAG;AAC3C,gBAAU,KAAK,IAAI,CAAC;AACpB;AAAA,IACF,WAAW,KAAK,CAAC,MAAM,YAAY,KAAK,IAAI,CAAC,GAAG;AAC9C,aAAO,KAAK,IAAI,CAAC;AACjB;AAAA,IACF,WAAW,KAAK,CAAC,MAAM,YAAY,KAAK,IAAI,CAAC,GAAG;AAC9C,aAAO,KAAK,IAAI,CAAC;AACjB;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,SAAS,SAAS,MAAM,KAAK;AACxC;AAEA,SAAS,YAAkB;AACzB,UAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CASb;AACD;AAEA,eAAe,OAAsB;AACnC,QAAM,EAAE,SAAS,SAAS,MAAM,KAAK,IAAI,UAAU;AAEnD,MAAI,YAAY,UAAU,YAAY,YAAY,YAAY,MAAM;AAClE,cAAU;AACV;AAAA,EACF;AAEA,MAAI,YAAY,UAAU;AACxB,UAAM,MAAM,MAAM,MAAM,GAAG,OAAO,SAAS;AAC3C,YAAQ,IAAI,MAAM,IAAI,KAAK,CAAC;AAC5B;AAAA,EACF;AAEA,MAAI,YAAY,SAAS;AACvB,UAAM,MAAM,MAAM,MAAM,GAAG,OAAO,WAAW;AAC7C,YAAQ,IAAI,MAAM,IAAI,KAAK,CAAC;AAC5B;AAAA,EACF;AAEA,MAAI,YAAY,QAAQ;AACtB,QAAI,CAAC,MAAM;AACT,cAAQ,MAAM,gBAAgB;AAC9B,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,UAAM,aAAa,OAAO,KAAK,MAAM,IAAI,IAAI,CAAC;AAC9C,UAAM,MAAM,MAAM,MAAM,GAAG,OAAO,kBAAkB;AAAA,MAClD,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,EAAE,MAAM,MAAM,WAAW,CAAC;AAAA,IACjD,CAAC;AACD,YAAQ,IAAI,MAAM,IAAI,KAAK,CAAC;AAC5B;AAAA,EACF;AAEA,UAAQ,MAAM,oBAAoB,OAAO,EAAE;AAC3C,YAAU;AACV,UAAQ,KAAK,CAAC;AAChB;AAEA,KAAK,EAAE,MAAM,CAAC,UAAU;AACtB,UAAQ,MAAM,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AACpF,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
1
+ {"version":3,"sources":["../../src/http/client-cli.ts"],"sourcesContent":["#!/usr/bin/env node\n// Minimal HTTP client wrapper for Session Collab HTTP API\n\ntype Args = {\n baseUrl: string;\n command: string;\n name?: string;\n args?: string;\n};\n\nconst REQUIRED_TOOLS = [\n 'collab_session_start',\n 'collab_session_end',\n 'collab_session_list',\n 'collab_session_update',\n 'collab_config',\n 'collab_status',\n 'collab_claim',\n 'collab_memory_save',\n 'collab_memory_recall',\n 'collab_memory_clear',\n 'collab_protect',\n];\n\nfunction parseArgs(): Args {\n const argv = process.argv.slice(2);\n let baseUrl = 'http://127.0.0.1:8765';\n let command = argv[0] ?? 'help';\n let name: string | undefined;\n let args: string | undefined;\n\n for (let i = 1; i < argv.length; i++) {\n if (argv[i] === '--base-url' && argv[i + 1]) {\n baseUrl = argv[i + 1];\n i++;\n } else if (argv[i] === '--name' && argv[i + 1]) {\n name = argv[i + 1];\n i++;\n } else if (argv[i] === '--args' && argv[i + 1]) {\n args = argv[i + 1];\n i++;\n }\n }\n\n return { baseUrl, command, name, args };\n}\n\nfunction printHelp(): void {\n console.log(`Usage:\n session-collab health [--base-url URL]\n session-collab tools [--base-url URL]\n session-collab doctor [--base-url URL]\n session-collab call --name TOOL --args JSON [--base-url URL]\n\nExamples:\n session-collab health\n session-collab tools\n session-collab doctor\n session-collab call --name collab_session_start --args '{\"project_root\":\"/repo\",\"name\":\"demo\"}'\n`);\n}\n\nfunction getHeaders(): Record<string, string> {\n const headers: Record<string, string> = {};\n const token = process.env.SESSION_COLLAB_HTTP_TOKEN;\n if (token) {\n headers.Authorization = `Bearer ${token}`;\n }\n return headers;\n}\n\nasync function fetchText(url: string, init: RequestInit = {}): Promise<{ status: number; text: string }> {\n const headers = {\n ...getHeaders(),\n ...(init.headers as Record<string, string> | undefined),\n };\n const res = await fetch(url, { ...init, headers });\n return { status: res.status, text: await res.text() };\n}\n\nasync function runDoctor(baseUrl: string): Promise<void> {\n const checks: Array<{ name: string; ok: boolean; detail: string }> = [];\n\n try {\n const health = await fetchText(`${baseUrl}/health`);\n checks.push({\n name: 'health',\n ok: health.status === 200,\n detail: health.text,\n });\n } catch (error) {\n checks.push({\n name: 'health',\n ok: false,\n detail: error instanceof Error ? error.message : String(error),\n });\n }\n\n try {\n const tools = await fetchText(`${baseUrl}/v1/tools`);\n const body = JSON.parse(tools.text) as { data?: { tools?: Array<{ name: string }> } };\n const toolNames = new Set(body.data?.tools?.map((tool) => tool.name) ?? []);\n const missingTools = REQUIRED_TOOLS.filter((tool) => !toolNames.has(tool));\n checks.push({\n name: 'tools',\n ok: tools.status === 200 && missingTools.length === 0,\n detail: missingTools.length > 0 ? `Missing tools: ${missingTools.join(', ')}` : `${toolNames.size} tools available`,\n });\n } catch (error) {\n checks.push({\n name: 'tools',\n ok: false,\n detail: error instanceof Error ? error.message : String(error),\n });\n }\n\n const ok = checks.every((check) => check.ok);\n console.log(JSON.stringify({ ok, base_url: baseUrl, checks }, null, 2));\n if (!ok) {\n process.exit(1);\n }\n}\n\nasync function main(): Promise<void> {\n const { baseUrl, command, name, args } = parseArgs();\n\n if (command === 'help' || command === '--help' || command === '-h') {\n printHelp();\n return;\n }\n\n if (command === 'health') {\n const res = await fetchText(`${baseUrl}/health`);\n console.log(res.text);\n return;\n }\n\n if (command === 'tools') {\n const res = await fetchText(`${baseUrl}/v1/tools`);\n console.log(res.text);\n return;\n }\n\n if (command === 'doctor') {\n await runDoctor(baseUrl);\n return;\n }\n\n if (command === 'call') {\n if (!name) {\n console.error('Missing --name');\n process.exit(1);\n }\n const parsedArgs = args ? JSON.parse(args) : {};\n const res = await fetchText(`${baseUrl}/v1/tools/call`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ name, args: parsedArgs }),\n });\n console.log(res.text);\n return;\n }\n\n console.error(`Unknown command: ${command}`);\n printHelp();\n process.exit(1);\n}\n\nmain().catch((error) => {\n console.error('Fatal error:', error instanceof Error ? error.message : String(error));\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "session-collab-mcp",
3
- "version": "2.2.0",
3
+ "version": "2.3.1",
4
4
  "description": "Provider-agnostic MCP collaboration server for agent sessions - prevents conflicts, persists context, and protects important files",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -32,7 +32,9 @@
32
32
  "prepublishOnly": "npm run build",
33
33
  "typecheck": "tsc --noEmit",
34
34
  "lint": "eslint src/",
35
- "test": "vitest"
35
+ "test": "vitest",
36
+ "test:http": "SESSION_COLLAB_HTTP_TESTS=true vitest run src/http/__tests__/server-integration.test.ts",
37
+ "test:release": "npm run typecheck && npm run lint && npm test -- --run && npm run test:http && npm run build && npm pack --dry-run"
36
38
  },
37
39
  "dependencies": {
38
40
  "better-sqlite3": "^11.7.0",
@@ -47,6 +49,6 @@
47
49
  "tsup": "^8.5.1",
48
50
  "tsx": "^4.19.2",
49
51
  "typescript": "^5.7.2",
50
- "vitest": "^2.1.8"
52
+ "vitest": "^4.1.5"
51
53
  }
52
54
  }