@nex-ai/nex 0.1.34 → 0.1.36
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 +18 -4
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/workspace-registry.d.ts +47 -0
- package/dist/lib/workspace-registry.js +240 -0
- package/dist/lib/workspace-registry.js.map +1 -0
- package/dist/mcp/channel.d.ts +28 -0
- package/dist/mcp/channel.js +135 -0
- package/dist/mcp/channel.js.map +1 -0
- package/dist/mcp/client.d.ts +20 -0
- package/dist/mcp/client.js +130 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/config.d.ts +14 -0
- package/dist/mcp/config.js +33 -0
- package/dist/mcp/config.js.map +1 -0
- package/dist/mcp/context-files.d.ts +9 -0
- package/dist/mcp/context-files.js +90 -0
- package/dist/mcp/context-files.js.map +1 -0
- package/dist/mcp/file-manifest.d.ts +27 -0
- package/dist/mcp/file-manifest.js +64 -0
- package/dist/mcp/file-manifest.js.map +1 -0
- package/dist/mcp/file-scanner.d.ts +17 -0
- package/dist/mcp/file-scanner.js +77 -0
- package/dist/mcp/file-scanner.js.map +1 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.js +51 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/rate-limiter.d.ts +14 -0
- package/dist/mcp/rate-limiter.js +60 -0
- package/dist/mcp/rate-limiter.js.map +1 -0
- package/dist/mcp/server.d.ts +6 -0
- package/dist/mcp/server.js +42 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/session-store.d.ts +16 -0
- package/dist/mcp/session-store.js +70 -0
- package/dist/mcp/session-store.js.map +1 -0
- package/dist/mcp/tools/context.d.ts +3 -0
- package/dist/mcp/tools/context.js +65 -0
- package/dist/mcp/tools/context.js.map +1 -0
- package/dist/mcp/tools/insights.d.ts +3 -0
- package/dist/mcp/tools/insights.js +24 -0
- package/dist/mcp/tools/insights.js.map +1 -0
- package/dist/mcp/tools/integrations.d.ts +3 -0
- package/dist/mcp/tools/integrations.js +27 -0
- package/dist/mcp/tools/integrations.js.map +1 -0
- package/dist/mcp/tools/lists.d.ts +3 -0
- package/dist/mcp/tools/lists.js +101 -0
- package/dist/mcp/tools/lists.js.map +1 -0
- package/dist/mcp/tools/notes.d.ts +3 -0
- package/dist/mcp/tools/notes.js +52 -0
- package/dist/mcp/tools/notes.js.map +1 -0
- package/dist/mcp/tools/records.d.ts +3 -0
- package/dist/mcp/tools/records.js +74 -0
- package/dist/mcp/tools/records.js.map +1 -0
- package/dist/mcp/tools/register.d.ts +3 -0
- package/dist/mcp/tools/register.js +30 -0
- package/dist/mcp/tools/register.js.map +1 -0
- package/dist/mcp/tools/relationships.d.ts +3 -0
- package/dist/mcp/tools/relationships.js +47 -0
- package/dist/mcp/tools/relationships.js.map +1 -0
- package/dist/mcp/tools/scan.d.ts +3 -0
- package/dist/mcp/tools/scan.js +37 -0
- package/dist/mcp/tools/scan.js.map +1 -0
- package/dist/mcp/tools/schema.d.ts +3 -0
- package/dist/mcp/tools/schema.js +108 -0
- package/dist/mcp/tools/schema.js.map +1 -0
- package/dist/mcp/tools/search.d.ts +3 -0
- package/dist/mcp/tools/search.js +8 -0
- package/dist/mcp/tools/search.js.map +1 -0
- package/dist/mcp/tools/tasks.d.ts +3 -0
- package/dist/mcp/tools/tasks.js +88 -0
- package/dist/mcp/tools/tasks.js.map +1 -0
- package/package.json +6 -3
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { loadConfig } from "./config.js";
|
|
2
|
+
function getBaseUrl() {
|
|
3
|
+
const config = loadConfig();
|
|
4
|
+
const base = process.env.NEX_API_BASE_URL || config.base_url || config.dev_url || "https://app.nex.ai";
|
|
5
|
+
return `${base.replace(/\/+$/, "")}/api/developers`;
|
|
6
|
+
}
|
|
7
|
+
function getRegisterUrl() {
|
|
8
|
+
const config = loadConfig();
|
|
9
|
+
const base = process.env.NEX_API_BASE_URL || config.base_url || config.dev_url || "https://app.nex.ai";
|
|
10
|
+
return `${base.replace(/\/+$/, "")}/api/v1/agents/register`;
|
|
11
|
+
}
|
|
12
|
+
export class NexApiError extends Error {
|
|
13
|
+
status;
|
|
14
|
+
statusText;
|
|
15
|
+
body;
|
|
16
|
+
constructor(status, statusText, body) {
|
|
17
|
+
super(`Nex API error ${status}: ${statusText}`);
|
|
18
|
+
this.status = status;
|
|
19
|
+
this.statusText = statusText;
|
|
20
|
+
this.body = body;
|
|
21
|
+
this.name = "NexApiError";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export class NexApiClient {
|
|
25
|
+
apiKey;
|
|
26
|
+
constructor(apiKey) {
|
|
27
|
+
this.apiKey = apiKey;
|
|
28
|
+
}
|
|
29
|
+
get isAuthenticated() {
|
|
30
|
+
return this.apiKey !== undefined && this.apiKey.length > 0;
|
|
31
|
+
}
|
|
32
|
+
setApiKey(key) {
|
|
33
|
+
this.apiKey = key;
|
|
34
|
+
}
|
|
35
|
+
requireAuth() {
|
|
36
|
+
if (!this.isAuthenticated) {
|
|
37
|
+
throw new NexApiError(401, "Not registered", {
|
|
38
|
+
message: "No API key configured. Call the 'register' tool first with your email to get an API key.",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async request(method, path, body) {
|
|
43
|
+
this.requireAuth();
|
|
44
|
+
const url = `${getBaseUrl()}${path}`;
|
|
45
|
+
const headers = {
|
|
46
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
47
|
+
};
|
|
48
|
+
if (body !== undefined) {
|
|
49
|
+
headers["Content-Type"] = "application/json";
|
|
50
|
+
}
|
|
51
|
+
const res = await fetch(url, {
|
|
52
|
+
method,
|
|
53
|
+
headers,
|
|
54
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
55
|
+
signal: AbortSignal.timeout(120_000),
|
|
56
|
+
});
|
|
57
|
+
if (res.status === 401 || res.status === 403) {
|
|
58
|
+
throw new NexApiError(res.status, res.statusText, {
|
|
59
|
+
message: "API key expired or invalid. Run 'nex register --email <email>' to get a new key.",
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (!res.ok) {
|
|
63
|
+
let errorBody;
|
|
64
|
+
try {
|
|
65
|
+
errorBody = await res.json();
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
errorBody = await res.text();
|
|
69
|
+
}
|
|
70
|
+
throw new NexApiError(res.status, res.statusText, errorBody);
|
|
71
|
+
}
|
|
72
|
+
const text = await res.text();
|
|
73
|
+
if (!text)
|
|
74
|
+
return {};
|
|
75
|
+
try {
|
|
76
|
+
return JSON.parse(text);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return { message: text };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async register(email, name, companyName, source) {
|
|
83
|
+
const body = {
|
|
84
|
+
email,
|
|
85
|
+
source: source ?? "mcp",
|
|
86
|
+
};
|
|
87
|
+
if (name !== undefined)
|
|
88
|
+
body.name = name;
|
|
89
|
+
if (companyName !== undefined)
|
|
90
|
+
body.company_name = companyName;
|
|
91
|
+
const res = await fetch(getRegisterUrl(), {
|
|
92
|
+
method: "POST",
|
|
93
|
+
headers: { "Content-Type": "application/json" },
|
|
94
|
+
body: JSON.stringify(body),
|
|
95
|
+
signal: AbortSignal.timeout(120_000),
|
|
96
|
+
});
|
|
97
|
+
if (!res.ok) {
|
|
98
|
+
let errorBody;
|
|
99
|
+
try {
|
|
100
|
+
errorBody = await res.json();
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
errorBody = await res.text();
|
|
104
|
+
}
|
|
105
|
+
throw new NexApiError(res.status, res.statusText, errorBody);
|
|
106
|
+
}
|
|
107
|
+
const data = await res.json();
|
|
108
|
+
const apiKey = data.api_key;
|
|
109
|
+
if (typeof apiKey === "string" && apiKey.length > 0) {
|
|
110
|
+
this.apiKey = apiKey;
|
|
111
|
+
}
|
|
112
|
+
return data;
|
|
113
|
+
}
|
|
114
|
+
async get(path) {
|
|
115
|
+
return this.request("GET", path);
|
|
116
|
+
}
|
|
117
|
+
async post(path, body) {
|
|
118
|
+
return this.request("POST", path, body);
|
|
119
|
+
}
|
|
120
|
+
async put(path, body) {
|
|
121
|
+
return this.request("PUT", path, body);
|
|
122
|
+
}
|
|
123
|
+
async patch(path, body) {
|
|
124
|
+
return this.request("PATCH", path, body);
|
|
125
|
+
}
|
|
126
|
+
async delete(path) {
|
|
127
|
+
return this.request("DELETE", path);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/mcp/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
declare const CONFIG_PATH: string;
|
|
2
|
+
interface NexMcpConfig {
|
|
3
|
+
api_key?: string;
|
|
4
|
+
base_url?: string;
|
|
5
|
+
dev_url?: string;
|
|
6
|
+
workspace_id?: string;
|
|
7
|
+
workspace_slug?: string;
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
export declare function loadConfig(): NexMcpConfig;
|
|
11
|
+
export declare function saveConfig(config: NexMcpConfig): void;
|
|
12
|
+
export declare function loadApiKey(): string | undefined;
|
|
13
|
+
export declare function persistRegistration(data: Record<string, unknown>): void;
|
|
14
|
+
export { CONFIG_PATH };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join, dirname } from "node:path";
|
|
4
|
+
const CONFIG_PATH = join(homedir(), ".nex", "config.json");
|
|
5
|
+
export function loadConfig() {
|
|
6
|
+
try {
|
|
7
|
+
const raw = readFileSync(CONFIG_PATH, "utf-8");
|
|
8
|
+
return JSON.parse(raw);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return {};
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function saveConfig(config) {
|
|
15
|
+
mkdirSync(dirname(CONFIG_PATH), { recursive: true });
|
|
16
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
17
|
+
}
|
|
18
|
+
export function loadApiKey() {
|
|
19
|
+
return process.env.NEX_API_KEY || loadConfig().api_key || undefined;
|
|
20
|
+
}
|
|
21
|
+
export function persistRegistration(data) {
|
|
22
|
+
const existing = loadConfig();
|
|
23
|
+
if (typeof data.api_key === "string")
|
|
24
|
+
existing.api_key = data.api_key;
|
|
25
|
+
if (typeof data.workspace_id === "string" || typeof data.workspace_id === "number") {
|
|
26
|
+
existing.workspace_id = String(data.workspace_id);
|
|
27
|
+
}
|
|
28
|
+
if (typeof data.workspace_slug === "string")
|
|
29
|
+
existing.workspace_slug = data.workspace_slug;
|
|
30
|
+
saveConfig(existing);
|
|
31
|
+
}
|
|
32
|
+
export { CONFIG_PATH };
|
|
33
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/mcp/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;AAW3D,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAC/C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAiB,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAoB;IAC7C,SAAS,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,UAAU,EAAE,CAAC,OAAO,IAAI,SAAS,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAA6B;IAC/D,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAC;IAC9B,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ;QAAE,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IACtE,IAAI,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;QACnF,QAAQ,CAAC,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACpD,CAAC;IACD,IAAI,OAAO,IAAI,CAAC,cAAc,KAAK,QAAQ;QAAE,QAAQ,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC;IAC3F,UAAU,CAAC,QAAQ,CAAC,CAAC;AACvB,CAAC;AAED,OAAO,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NexApiClient } from "./client.js";
|
|
2
|
+
import type { RateLimiter } from "./rate-limiter.js";
|
|
3
|
+
export interface ContextFilesResult {
|
|
4
|
+
ingested: number;
|
|
5
|
+
skipped: number;
|
|
6
|
+
errors: number;
|
|
7
|
+
files: string[];
|
|
8
|
+
}
|
|
9
|
+
export declare function ingestContextFiles(client: NexApiClient, rateLimiter: RateLimiter, cwd: string): Promise<ContextFilesResult>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ingests Claude Code context files (CLAUDE.md + memory files) into Nex.
|
|
3
|
+
*
|
|
4
|
+
* Reads from both global and project-level locations:
|
|
5
|
+
* - ~/.claude/CLAUDE.md (global instructions)
|
|
6
|
+
* - {cwd}/CLAUDE.md (project instructions)
|
|
7
|
+
* - ~/.claude/projects/{project-key}/memory/*.md (memory files)
|
|
8
|
+
*
|
|
9
|
+
* Uses the file manifest for change detection — unchanged files are skipped.
|
|
10
|
+
*/
|
|
11
|
+
import { existsSync, readdirSync, statSync, readFileSync } from "node:fs";
|
|
12
|
+
import { join, extname, basename } from "node:path";
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
import { readManifest, writeManifest, isChanged, markIngested } from "./file-manifest.js";
|
|
15
|
+
const CLAUDE_DIR = join(homedir(), ".claude");
|
|
16
|
+
const INGEST_TIMEOUT_MS = 10_000;
|
|
17
|
+
const MAX_FILE_SIZE = 100_000;
|
|
18
|
+
function projectKey(cwd) {
|
|
19
|
+
return cwd.replace(/\//g, "-");
|
|
20
|
+
}
|
|
21
|
+
function collectContextFiles(cwd) {
|
|
22
|
+
const files = [];
|
|
23
|
+
const key = projectKey(cwd);
|
|
24
|
+
const globalClaude = join(CLAUDE_DIR, "CLAUDE.md");
|
|
25
|
+
if (existsSync(globalClaude)) {
|
|
26
|
+
files.push({ path: globalClaude, contextTag: "claude-md:global" });
|
|
27
|
+
}
|
|
28
|
+
const projectClaude = join(cwd, "CLAUDE.md");
|
|
29
|
+
if (existsSync(projectClaude)) {
|
|
30
|
+
files.push({ path: projectClaude, contextTag: "claude-md:project" });
|
|
31
|
+
}
|
|
32
|
+
const memoryDir = join(CLAUDE_DIR, "projects", key, "memory");
|
|
33
|
+
if (existsSync(memoryDir)) {
|
|
34
|
+
try {
|
|
35
|
+
const entries = readdirSync(memoryDir, { withFileTypes: true });
|
|
36
|
+
for (const entry of entries) {
|
|
37
|
+
if (!entry.isFile())
|
|
38
|
+
continue;
|
|
39
|
+
if (extname(entry.name).toLowerCase() !== ".md")
|
|
40
|
+
continue;
|
|
41
|
+
const fullPath = join(memoryDir, entry.name);
|
|
42
|
+
const name = basename(entry.name, ".md");
|
|
43
|
+
files.push({ path: fullPath, contextTag: `claude-memory:${name}` });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// memoryDir unreadable — skip
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return files;
|
|
51
|
+
}
|
|
52
|
+
export async function ingestContextFiles(client, rateLimiter, cwd) {
|
|
53
|
+
const result = { ingested: 0, skipped: 0, errors: 0, files: [] };
|
|
54
|
+
const manifest = readManifest();
|
|
55
|
+
const candidates = collectContextFiles(cwd);
|
|
56
|
+
let dirty = false;
|
|
57
|
+
for (const { path, contextTag } of candidates) {
|
|
58
|
+
try {
|
|
59
|
+
const stat = statSync(path);
|
|
60
|
+
if (!isChanged(path, stat, manifest)) {
|
|
61
|
+
result.skipped++;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (!rateLimiter.canProceed()) {
|
|
65
|
+
process.stderr.write("[nex-context-files] Rate limited — stopping context file ingest\n");
|
|
66
|
+
result.skipped += candidates.length - result.ingested - result.skipped - result.errors;
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
let content = readFileSync(path, "utf-8");
|
|
70
|
+
if (content.length > MAX_FILE_SIZE) {
|
|
71
|
+
content = content.slice(0, MAX_FILE_SIZE) + "\n[...truncated]";
|
|
72
|
+
}
|
|
73
|
+
await client.post("/v1/context/text", { content, context: contextTag });
|
|
74
|
+
rateLimiter.recordRequest();
|
|
75
|
+
markIngested(path, stat, contextTag, manifest);
|
|
76
|
+
result.ingested++;
|
|
77
|
+
result.files.push(contextTag);
|
|
78
|
+
dirty = true;
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
process.stderr.write(`[nex-context-files] Failed to ingest ${contextTag}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
82
|
+
result.errors++;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (dirty) {
|
|
86
|
+
writeManifest(manifest);
|
|
87
|
+
}
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=context-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-files.js","sourceRoot":"","sources":["../../src/mcp/context-files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent file manifest — tracks which files have been ingested
|
|
3
|
+
* using mtime + size as change detection.
|
|
4
|
+
*
|
|
5
|
+
* Stored at ~/.nex/file-scan-manifest.json.
|
|
6
|
+
*/
|
|
7
|
+
import { type Stats } from "node:fs";
|
|
8
|
+
export interface FileManifestEntry {
|
|
9
|
+
mtime: number;
|
|
10
|
+
size: number;
|
|
11
|
+
ingestedAt: number;
|
|
12
|
+
context: string;
|
|
13
|
+
}
|
|
14
|
+
export interface FileManifest {
|
|
15
|
+
version: 1;
|
|
16
|
+
lastScanAt?: number;
|
|
17
|
+
files: Record<string, FileManifestEntry>;
|
|
18
|
+
}
|
|
19
|
+
export declare function readManifest(): FileManifest;
|
|
20
|
+
export declare function writeManifest(manifest: FileManifest): void;
|
|
21
|
+
export declare function isChanged(path: string, stat: Stats, manifest: FileManifest): boolean;
|
|
22
|
+
export declare function markIngested(path: string, stat: Stats, context: string, manifest: FileManifest): void;
|
|
23
|
+
/**
|
|
24
|
+
* Record that a full scan completed at this moment.
|
|
25
|
+
*/
|
|
26
|
+
export declare function markScanned(manifest: FileManifest): void;
|
|
27
|
+
export declare function isScanFresh(manifest: FileManifest, windowMs?: number): boolean;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent file manifest — tracks which files have been ingested
|
|
3
|
+
* using mtime + size as change detection.
|
|
4
|
+
*
|
|
5
|
+
* Stored at ~/.nex/file-scan-manifest.json.
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
const DATA_DIR = join(homedir(), ".nex");
|
|
11
|
+
const MANIFEST_PATH = join(DATA_DIR, "file-scan-manifest.json");
|
|
12
|
+
export function readManifest() {
|
|
13
|
+
try {
|
|
14
|
+
const raw = readFileSync(MANIFEST_PATH, "utf-8");
|
|
15
|
+
const data = JSON.parse(raw);
|
|
16
|
+
if (data && data.version === 1 && data.files) {
|
|
17
|
+
return data;
|
|
18
|
+
}
|
|
19
|
+
return { version: 1, files: {} };
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return { version: 1, files: {} };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function writeManifest(manifest) {
|
|
26
|
+
try {
|
|
27
|
+
mkdirSync(DATA_DIR, { recursive: true });
|
|
28
|
+
writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2), "utf-8");
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Best-effort — if we can't write, next scan re-ingests
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function isChanged(path, stat, manifest) {
|
|
35
|
+
const entry = manifest.files[path];
|
|
36
|
+
if (!entry)
|
|
37
|
+
return true;
|
|
38
|
+
return entry.mtime !== stat.mtimeMs || entry.size !== stat.size;
|
|
39
|
+
}
|
|
40
|
+
export function markIngested(path, stat, context, manifest) {
|
|
41
|
+
manifest.files[path] = {
|
|
42
|
+
mtime: stat.mtimeMs,
|
|
43
|
+
size: stat.size,
|
|
44
|
+
ingestedAt: Date.now(),
|
|
45
|
+
context,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Record that a full scan completed at this moment.
|
|
50
|
+
*/
|
|
51
|
+
export function markScanned(manifest) {
|
|
52
|
+
manifest.lastScanAt = Date.now();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Check if a scan completed recently (within the given window).
|
|
56
|
+
* Default window: 1 hour.
|
|
57
|
+
*/
|
|
58
|
+
const DEFAULT_SCAN_FRESHNESS_MS = 60 * 60 * 1000; // 1 hour
|
|
59
|
+
export function isScanFresh(manifest, windowMs = DEFAULT_SCAN_FRESHNESS_MS) {
|
|
60
|
+
if (!manifest.lastScanAt)
|
|
61
|
+
return false;
|
|
62
|
+
return Date.now() - manifest.lastScanAt < windowMs;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=file-manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-manifest.js","sourceRoot":"","sources":["../../src/mcp/file-manifest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAc,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC,CAAC;AACzC,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ,EAAE,yBAAyB,CAAC,CAAC;AAehE,MAAM,UAAU,YAAY;IAC1B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QACjD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,IAAI,IAAI,IAAI,CAAC,OAAO,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC7C,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IACnC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAsB;IAClD,IAAI,CAAC;QACH,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,aAAa,CAAC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC3E,CAAC;IAAC,MAAM,CAAC;QACP,wDAAwD;IAC1D,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,IAAW,EAAE,QAAsB;IACzE,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO,KAAK,CAAC,KAAK,KAAK,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,IAAW,EAAE,OAAe,EAAE,QAAsB;IAC7F,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG;QACrB,KAAK,EAAE,IAAI,CAAC,OAAO;QACnB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;QACtB,OAAO;KACR,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,QAAsB;IAChD,QAAQ,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AACnC,CAAC;AAED;;;GAGG;AACH,MAAM,yBAAyB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,SAAS;AAE3D,MAAM,UAAU,WAAW,CAAC,QAAsB,EAAE,QAAQ,GAAG,yBAAyB;IACtF,IAAI,CAAC,QAAQ,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IACvC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,UAAU,GAAG,QAAQ,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { NexApiClient } from "./client.js";
|
|
2
|
+
import type { RateLimiter } from "./rate-limiter.js";
|
|
3
|
+
export interface ScanConfig {
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
extensions: string[];
|
|
6
|
+
maxFileSize: number;
|
|
7
|
+
maxFilesPerScan: number;
|
|
8
|
+
scanDepth: number;
|
|
9
|
+
ignoreDirs: string[];
|
|
10
|
+
}
|
|
11
|
+
export interface ScanResult {
|
|
12
|
+
scanned: number;
|
|
13
|
+
ingested: number;
|
|
14
|
+
skipped: number;
|
|
15
|
+
errors: number;
|
|
16
|
+
}
|
|
17
|
+
export declare function scanAndIngest(client: NexApiClient, rateLimiter: RateLimiter, cwd: string, config: ScanConfig): Promise<ScanResult>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core file scanner — walks project directories, detects changed files,
|
|
3
|
+
* and ingests them into Nex via the developer API.
|
|
4
|
+
*/
|
|
5
|
+
import { readdirSync, statSync, readFileSync } from "node:fs";
|
|
6
|
+
import { join, relative, extname } from "node:path";
|
|
7
|
+
import { readManifest, writeManifest, isChanged, markIngested } from "./file-manifest.js";
|
|
8
|
+
function walkDir(dir, cwd, config, depth, results) {
|
|
9
|
+
if (depth > config.scanDepth)
|
|
10
|
+
return;
|
|
11
|
+
let entries;
|
|
12
|
+
try {
|
|
13
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
for (const entry of entries) {
|
|
19
|
+
const fullPath = join(dir, entry.name);
|
|
20
|
+
if (entry.isDirectory()) {
|
|
21
|
+
if (config.ignoreDirs.includes(entry.name))
|
|
22
|
+
continue;
|
|
23
|
+
walkDir(fullPath, cwd, config, depth + 1, results);
|
|
24
|
+
}
|
|
25
|
+
else if (entry.isFile()) {
|
|
26
|
+
const ext = extname(entry.name).toLowerCase();
|
|
27
|
+
if (!config.extensions.includes(ext))
|
|
28
|
+
continue;
|
|
29
|
+
try {
|
|
30
|
+
const stat = statSync(fullPath);
|
|
31
|
+
results.push({ absolutePath: fullPath, relativePath: relative(cwd, fullPath), stat });
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// stat failed — skip
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export async function scanAndIngest(client, rateLimiter, cwd, config) {
|
|
40
|
+
const result = { scanned: 0, ingested: 0, skipped: 0, errors: 0 };
|
|
41
|
+
if (!config.enabled)
|
|
42
|
+
return result;
|
|
43
|
+
const manifest = readManifest();
|
|
44
|
+
const candidates = [];
|
|
45
|
+
walkDir(cwd, cwd, config, 0, candidates);
|
|
46
|
+
result.scanned = candidates.length;
|
|
47
|
+
const changed = candidates
|
|
48
|
+
.filter((f) => isChanged(f.absolutePath, f.stat, manifest))
|
|
49
|
+
.sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs)
|
|
50
|
+
.slice(0, config.maxFilesPerScan);
|
|
51
|
+
result.skipped = candidates.length - changed.length;
|
|
52
|
+
for (const file of changed) {
|
|
53
|
+
if (!rateLimiter.canProceed()) {
|
|
54
|
+
process.stderr.write(`[nex-scan] Rate limited — stopping after ${result.ingested} files\n`);
|
|
55
|
+
result.skipped += changed.length - result.ingested - result.errors;
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
let content = readFileSync(file.absolutePath, "utf-8");
|
|
60
|
+
if (content.length > config.maxFileSize) {
|
|
61
|
+
content = content.slice(0, config.maxFileSize) + "\n[...truncated]";
|
|
62
|
+
}
|
|
63
|
+
const context = `file-scan:${file.relativePath}`;
|
|
64
|
+
await client.post("/v1/context/text", { content, context });
|
|
65
|
+
rateLimiter.recordRequest();
|
|
66
|
+
markIngested(file.absolutePath, file.stat, context, manifest);
|
|
67
|
+
result.ingested++;
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
process.stderr.write(`[nex-scan] Failed to ingest ${file.relativePath}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
71
|
+
result.errors++;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
writeManifest(manifest);
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=file-scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-scanner.js","sourceRoot":"","sources":["../../src/mcp/file-scanner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
4
|
+
import { createServer } from "./server.js";
|
|
5
|
+
import { createServer as createHttpServer } from "node:http";
|
|
6
|
+
import { loadApiKey } from "./config.js";
|
|
7
|
+
import { startChannel } from "./channel.js";
|
|
8
|
+
const apiKey = loadApiKey();
|
|
9
|
+
if (!apiKey) {
|
|
10
|
+
console.error("No API key found (checked NEX_API_KEY env and ~/.nex/config.json). Starting in registration-only mode. Use the 'register' tool to create an account and get an API key. Once registered, all context, search, and scan tools become available.");
|
|
11
|
+
}
|
|
12
|
+
const transport = process.env.MCP_TRANSPORT ?? "stdio";
|
|
13
|
+
async function main() {
|
|
14
|
+
const { server, client } = createServer(apiKey);
|
|
15
|
+
if (transport === "http") {
|
|
16
|
+
const port = parseInt(process.env.MCP_PORT ?? "3001", 10);
|
|
17
|
+
const httpTransport = new StreamableHTTPServerTransport({
|
|
18
|
+
sessionIdGenerator: undefined,
|
|
19
|
+
});
|
|
20
|
+
const httpServer = createHttpServer(async (req, res) => {
|
|
21
|
+
const url = new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
22
|
+
if (url.pathname === "/mcp") {
|
|
23
|
+
await httpTransport.handleRequest(req, res);
|
|
24
|
+
}
|
|
25
|
+
else if (url.pathname === "/health") {
|
|
26
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
27
|
+
res.end(JSON.stringify({ status: "ok" }));
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
res.writeHead(404);
|
|
31
|
+
res.end("Not found");
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
await server.connect(httpTransport);
|
|
35
|
+
startChannel(server.server, client);
|
|
36
|
+
httpServer.listen(port, () => {
|
|
37
|
+
console.error(`Nex MCP server running on http://localhost:${port}/mcp`);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
const stdioTransport = new StdioServerTransport();
|
|
42
|
+
await server.connect(stdioTransport);
|
|
43
|
+
startChannel(server.server, client);
|
|
44
|
+
console.error("Nex MCP server running on stdio");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
main().catch((err) => {
|
|
48
|
+
console.error("Fatal error:", err);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
});
|
|
51
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;AAC5B,IAAI,CAAC,MAAM,EAAE,CAAC;IACZ,OAAO,CAAC,KAAK,CAAC,gPAAgP,CAAC,CAAC;AAClQ,CAAC;AAED,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,OAAO,CAAC;AAEvD,KAAK,UAAU,IAAI;IACjB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAEhD,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;QAE1D,MAAM,aAAa,GAAG,IAAI,6BAA6B,CAAC;YACtD,kBAAkB,EAAE,SAAS;SAC9B,CAAC,CAAC;QAEH,MAAM,UAAU,GAAG,gBAAgB,CAAC,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;YACrD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,oBAAoB,IAAI,EAAE,CAAC,CAAC;YAChE,IAAI,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;gBAC5B,MAAM,aAAa,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;YAC9C,CAAC;iBAAM,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;gBACtC,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;gBAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAC5C,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACnB,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YACvB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QACpC,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACpC,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;YAC3B,OAAO,CAAC,KAAK,CAAC,8CAA8C,IAAI,MAAM,CAAC,CAAC;QAC1E,CAAC,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,MAAM,cAAc,GAAG,IAAI,oBAAoB,EAAE,CAAC;QAClD,MAAM,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACrC,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACpC,OAAO,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACnD,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;IACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface RateLimiterConfig {
|
|
2
|
+
maxRequests: number;
|
|
3
|
+
windowMs: number;
|
|
4
|
+
dataDir: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class RateLimiter {
|
|
7
|
+
private config;
|
|
8
|
+
private filePath;
|
|
9
|
+
constructor(config?: Partial<RateLimiterConfig>);
|
|
10
|
+
private readTimestamps;
|
|
11
|
+
private writeTimestamps;
|
|
12
|
+
canProceed(): boolean;
|
|
13
|
+
recordRequest(): void;
|
|
14
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based sliding window rate limiter.
|
|
3
|
+
* Designed for Nex /text endpoint (10 req/min).
|
|
4
|
+
*
|
|
5
|
+
* Persists timestamps to a JSON file so rate limits are respected
|
|
6
|
+
* across invocations.
|
|
7
|
+
*/
|
|
8
|
+
import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
const DEFAULTS = {
|
|
12
|
+
maxRequests: 10,
|
|
13
|
+
windowMs: 60_000,
|
|
14
|
+
dataDir: join(homedir(), ".nex"),
|
|
15
|
+
};
|
|
16
|
+
export class RateLimiter {
|
|
17
|
+
config;
|
|
18
|
+
filePath;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.config = { ...DEFAULTS, ...config };
|
|
21
|
+
this.filePath = join(this.config.dataDir, "rate-limiter.json");
|
|
22
|
+
mkdirSync(this.config.dataDir, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
readTimestamps() {
|
|
25
|
+
try {
|
|
26
|
+
const raw = readFileSync(this.filePath, "utf-8");
|
|
27
|
+
const data = JSON.parse(raw);
|
|
28
|
+
if (Array.isArray(data))
|
|
29
|
+
return data;
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
writeTimestamps(timestamps) {
|
|
37
|
+
try {
|
|
38
|
+
writeFileSync(this.filePath, JSON.stringify(timestamps), "utf-8");
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Best-effort
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
canProceed() {
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
const timestamps = this.readTimestamps().filter((t) => now - t < this.config.windowMs);
|
|
47
|
+
if (timestamps.length >= this.config.maxRequests) {
|
|
48
|
+
this.writeTimestamps(timestamps);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
recordRequest() {
|
|
54
|
+
const now = Date.now();
|
|
55
|
+
const timestamps = this.readTimestamps().filter((t) => now - t < this.config.windowMs);
|
|
56
|
+
timestamps.push(now);
|
|
57
|
+
this.writeTimestamps(timestamps);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/mcp/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAQlC,MAAM,QAAQ,GAAsB;IAClC,WAAW,EAAE,EAAE;IACf,QAAQ,EAAE,MAAM;IAChB,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC;CACjC,CAAC;AAEF,MAAM,OAAO,WAAW;IACd,MAAM,CAAoB;IAC1B,QAAQ,CAAS;IAEzB,YAAY,MAAmC;QAC7C,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,MAAM,EAAE,CAAC;QACzC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;QAC/D,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;IAEO,cAAc;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACjD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC7B,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;YACrC,OAAO,EAAE,CAAC;QACZ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAEO,eAAe,CAAC,UAAoB;QAC1C,IAAI,CAAC;YACH,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC;QACpE,CAAC;QAAC,MAAM,CAAC;YACP,cAAc;QAChB,CAAC;IACH,CAAC;IAED,UAAU;QACR,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACvF,IAAI,UAAU,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACjD,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;YACjC,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,aAAa;QACX,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACvF,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrB,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;IACnC,CAAC;CACF"}
|