@nex-ai/nex 0.1.34 → 0.1.35
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/index.js +5 -0
- package/dist/index.js.map +1 -1
- 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
package/dist/index.js
CHANGED
|
@@ -98,6 +98,11 @@ When no command is given, launches the interactive TUI.`);
|
|
|
98
98
|
// Interactive commands → fall through to Commander (rich TUI with pickers, spinners, workflows)
|
|
99
99
|
const INTERACTIVE_COMMANDS = new Set(["setup", "integrate", "scan", "register", "status"]);
|
|
100
100
|
const firstArg = cleanArgs[0]?.toLowerCase();
|
|
101
|
+
// nex mcp → start embedded MCP server
|
|
102
|
+
if (firstArg === "mcp") {
|
|
103
|
+
await import("./mcp/index.js");
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
101
106
|
if (firstArg && !INTERACTIVE_COMMANDS.has(firstArg)) {
|
|
102
107
|
// Non-interactive subcommand → dispatch and exit
|
|
103
108
|
const result = await dispatchTokens(cleanArgs, ctx);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nex Channel — pushes daily digests and proactive notifications
|
|
3
|
+
* into active Claude Code sessions via the Channels API.
|
|
4
|
+
*
|
|
5
|
+
* Uses the existing Nex API client to query context and insights,
|
|
6
|
+
* then forwards results as channel notifications.
|
|
7
|
+
*/
|
|
8
|
+
import type { NexApiClient } from "./client.js";
|
|
9
|
+
/**
|
|
10
|
+
* Minimal interface for the underlying MCP Server's notification method.
|
|
11
|
+
* We use this instead of importing the full Server type to avoid coupling
|
|
12
|
+
* to the SDK's internal type hierarchy.
|
|
13
|
+
*/
|
|
14
|
+
interface NotificationSender {
|
|
15
|
+
notification(notification: {
|
|
16
|
+
method: string;
|
|
17
|
+
params?: Record<string, unknown>;
|
|
18
|
+
}): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Start the Nex notification channel. Call this after the MCP server
|
|
22
|
+
* has connected to its transport.
|
|
23
|
+
*
|
|
24
|
+
* @param server The underlying MCP `Server` instance (mcpServer.server)
|
|
25
|
+
* @param client An authenticated NexApiClient
|
|
26
|
+
*/
|
|
27
|
+
export declare function startChannel(server: NotificationSender, client: NexApiClient): void;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nex Channel — pushes daily digests and proactive notifications
|
|
3
|
+
* into active Claude Code sessions via the Channels API.
|
|
4
|
+
*
|
|
5
|
+
* Uses the existing Nex API client to query context and insights,
|
|
6
|
+
* then forwards results as channel notifications.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Constants
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
const STATE_DIR = join(homedir(), ".nex");
|
|
15
|
+
const STATE_PATH = join(STATE_DIR, "channel-state.json");
|
|
16
|
+
/** How often to check whether a daily digest is due (1 hour). */
|
|
17
|
+
const DIGEST_CHECK_INTERVAL_MS = 60 * 60 * 1000;
|
|
18
|
+
/** Minimum gap between daily digests (24 hours). */
|
|
19
|
+
const DIGEST_COOLDOWN_MS = 24 * 60 * 60 * 1000;
|
|
20
|
+
/** How often to poll for proactive notifications (5 minutes). */
|
|
21
|
+
const NOTIFY_INTERVAL_MS = 5 * 60 * 1000;
|
|
22
|
+
/** Delay before the first digest check after startup (10 seconds). */
|
|
23
|
+
const INITIAL_DIGEST_DELAY_MS = 10_000;
|
|
24
|
+
/** Delay before the first notification check after startup (15 seconds). */
|
|
25
|
+
const INITIAL_NOTIFY_DELAY_MS = 15_000;
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// State persistence
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
function loadState() {
|
|
30
|
+
try {
|
|
31
|
+
if (existsSync(STATE_PATH)) {
|
|
32
|
+
return JSON.parse(readFileSync(STATE_PATH, "utf-8"));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Corrupted or unreadable — start fresh
|
|
37
|
+
}
|
|
38
|
+
return { lastDigestAt: 0, lastNotifyCheckAt: 0 };
|
|
39
|
+
}
|
|
40
|
+
function saveState(state) {
|
|
41
|
+
try {
|
|
42
|
+
if (!existsSync(STATE_DIR))
|
|
43
|
+
mkdirSync(STATE_DIR, { recursive: true });
|
|
44
|
+
writeFileSync(STATE_PATH, JSON.stringify(state));
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Best-effort — don't crash if write fails
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Push helper
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
async function pushChannelEvent(server, content, meta = {}) {
|
|
54
|
+
await server.notification({
|
|
55
|
+
method: "notifications/claude/channel",
|
|
56
|
+
params: { content, meta },
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// Digest logic
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
async function checkDigest(server, client, state) {
|
|
63
|
+
const now = Date.now();
|
|
64
|
+
if (now - state.lastDigestAt < DIGEST_COOLDOWN_MS)
|
|
65
|
+
return;
|
|
66
|
+
try {
|
|
67
|
+
const result = (await client.post("/v1/context/ask", {
|
|
68
|
+
query: "Provide a comprehensive daily digest: summarize all key context " +
|
|
69
|
+
"collected in the last 24 hours, including important updates, new " +
|
|
70
|
+
"relationships, deal changes, upcoming events, and any actionable " +
|
|
71
|
+
"items. Be specific with names, dates, and numbers.",
|
|
72
|
+
}));
|
|
73
|
+
if (result.answer && result.answer.trim().length > 0) {
|
|
74
|
+
await pushChannelEvent(server, result.answer, {
|
|
75
|
+
type: "daily_digest",
|
|
76
|
+
period: "24h",
|
|
77
|
+
});
|
|
78
|
+
state.lastDigestAt = now;
|
|
79
|
+
saveState(state);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
console.error("[nex-channel] digest check failed:", err);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// Proactive notification logic
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
async function checkNotifications(server, client, state) {
|
|
90
|
+
const now = Date.now();
|
|
91
|
+
try {
|
|
92
|
+
const lastCheck = state.lastNotifyCheckAt || now - NOTIFY_INTERVAL_MS;
|
|
93
|
+
const minutesSinceLastCheck = Math.max(5, Math.ceil((now - lastCheck) / 60_000));
|
|
94
|
+
const result = (await client.get(`/v1/insights?last=${minutesSinceLastCheck}m&limit=10`));
|
|
95
|
+
state.lastNotifyCheckAt = now;
|
|
96
|
+
saveState(state);
|
|
97
|
+
if (result.insights && result.insights.length > 0) {
|
|
98
|
+
const summary = result.insights
|
|
99
|
+
.map((i) => `- [${i.type || "update"}${i.importance ? ` | ${i.importance}` : ""}] ${i.content || JSON.stringify(i)}`)
|
|
100
|
+
.join("\n");
|
|
101
|
+
await pushChannelEvent(server, summary, {
|
|
102
|
+
type: "proactive_notification",
|
|
103
|
+
count: String(result.insights.length),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
console.error("[nex-channel] notification check failed:", err);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Entry point
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
/**
|
|
115
|
+
* Start the Nex notification channel. Call this after the MCP server
|
|
116
|
+
* has connected to its transport.
|
|
117
|
+
*
|
|
118
|
+
* @param server The underlying MCP `Server` instance (mcpServer.server)
|
|
119
|
+
* @param client An authenticated NexApiClient
|
|
120
|
+
*/
|
|
121
|
+
export function startChannel(server, client) {
|
|
122
|
+
if (!client.isAuthenticated)
|
|
123
|
+
return;
|
|
124
|
+
const state = loadState();
|
|
125
|
+
// --- Daily digest ---
|
|
126
|
+
// Initial check after a short delay to let the session settle
|
|
127
|
+
setTimeout(() => checkDigest(server, client, state), INITIAL_DIGEST_DELAY_MS);
|
|
128
|
+
// Then check hourly (only fires digest if 24h have passed)
|
|
129
|
+
setInterval(() => checkDigest(server, client, state), DIGEST_CHECK_INTERVAL_MS);
|
|
130
|
+
// --- Proactive notifications ---
|
|
131
|
+
setTimeout(() => checkNotifications(server, client, state), INITIAL_NOTIFY_DELAY_MS);
|
|
132
|
+
setInterval(() => checkNotifications(server, client, state), NOTIFY_INTERVAL_MS);
|
|
133
|
+
console.error("[nex-channel] started (digest: 24h, notifications: 5m)");
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=channel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel.js","sourceRoot":"","sources":["../../src/mcp/channel.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare class NexApiError extends Error {
|
|
2
|
+
status: number;
|
|
3
|
+
statusText: string;
|
|
4
|
+
body: unknown;
|
|
5
|
+
constructor(status: number, statusText: string, body: unknown);
|
|
6
|
+
}
|
|
7
|
+
export declare class NexApiClient {
|
|
8
|
+
private apiKey;
|
|
9
|
+
constructor(apiKey?: string);
|
|
10
|
+
get isAuthenticated(): boolean;
|
|
11
|
+
setApiKey(key: string): void;
|
|
12
|
+
private requireAuth;
|
|
13
|
+
private request;
|
|
14
|
+
register(email: string, name?: string, companyName?: string, source?: string): Promise<unknown>;
|
|
15
|
+
get(path: string): Promise<unknown>;
|
|
16
|
+
post(path: string, body?: unknown): Promise<unknown>;
|
|
17
|
+
put(path: string, body?: unknown): Promise<unknown>;
|
|
18
|
+
patch(path: string, body?: unknown): Promise<unknown>;
|
|
19
|
+
delete(path: string): Promise<unknown>;
|
|
20
|
+
}
|
|
@@ -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>;
|