neo-mcp 1.1.11

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 ADDED
@@ -0,0 +1,221 @@
1
+ # neo-mcp-daemon
2
+
3
+ Local execution daemon for [Neo](https://heyneo.so) — written in TypeScript/Node.js, **no Python required**.
4
+
5
+ The daemon polls the Neo backend for commands (write files, run scripts, list files) and executes them on your local machine. It can also run as a full MCP server, replacing the Python `neo-mcp` package entirely.
6
+
7
+ Get your API key at [app.heyneo.so](https://app.heyneo.so) → Settings → API Keys.
8
+
9
+ ---
10
+
11
+ ## Two modes
12
+
13
+ ### Mode 1: MCP server + daemon (recommended if you don't use Python)
14
+
15
+ Runs as a full MCP server over stdio — exposes all 7 Neo tools to your editor and handles local execution in the same process. No Python required.
16
+
17
+ ```bash
18
+ NEO_SECRET_KEY=sk-v1-YOUR_KEY npx --yes neo-mcp-daemon --mcp /path/to/workspace
19
+ ```
20
+
21
+ ### Mode 2: Daemon only
22
+
23
+ Runs only the local execution daemon. Use this alongside the Python `neo-mcp` MCP server — the pip package auto-starts this daemon when you submit your first task.
24
+
25
+ ```bash
26
+ NEO_SECRET_KEY=sk-v1-YOUR_KEY npx --yes neo-mcp-daemon /path/to/workspace
27
+ ```
28
+
29
+ ---
30
+
31
+ ## Requirements
32
+
33
+ - Node.js 18+
34
+ - A Neo API key (`sk-v1-...`)
35
+
36
+ ---
37
+
38
+ ## Connecting to editors (MCP server mode)
39
+
40
+ All editors below use `--mcp` mode. Replace `sk-v1-YOUR_KEY` with your actual key and `/path/to/workspace` with your project root.
41
+
42
+ ---
43
+
44
+ ### Claude Code
45
+
46
+ ```bash
47
+ claude mcp add --scope user neo \
48
+ -e NEO_SECRET_KEY=sk-v1-YOUR_KEY \
49
+ -- npx --yes neo-mcp-daemon --mcp /path/to/workspace
50
+ ```
51
+
52
+ Open a **new Claude Code session** after running this command. Neo tools load at session start.
53
+
54
+ > **Scope options:** `--scope user` (global, recommended) · `--scope project` (writes `.mcp.json` in current repo) · `--scope local` (this machine only)
55
+
56
+ ---
57
+
58
+ ### Cursor
59
+
60
+ Edit `~/.cursor/mcp.json` (create if it doesn't exist):
61
+
62
+ ```json
63
+ {
64
+ "mcpServers": {
65
+ "neo": {
66
+ "command": "npx",
67
+ "args": ["--yes", "neo-mcp-daemon", "--mcp", "/path/to/workspace"],
68
+ "env": {
69
+ "NEO_SECRET_KEY": "sk-v1-YOUR_KEY"
70
+ }
71
+ }
72
+ }
73
+ }
74
+ ```
75
+
76
+ Restart Cursor after editing.
77
+
78
+ ---
79
+
80
+ ### Windsurf
81
+
82
+ Edit `~/.codeium/windsurf/mcp_config.json`:
83
+
84
+ ```json
85
+ {
86
+ "mcpServers": {
87
+ "neo": {
88
+ "command": "npx",
89
+ "args": ["--yes", "neo-mcp-daemon", "--mcp", "/path/to/workspace"],
90
+ "env": {
91
+ "NEO_SECRET_KEY": "sk-v1-YOUR_KEY"
92
+ }
93
+ }
94
+ }
95
+ }
96
+ ```
97
+
98
+ Restart Windsurf after editing.
99
+
100
+ ---
101
+
102
+ ### VS Code (GitHub Copilot)
103
+
104
+ Requires VS Code 1.99+. Edit `.vscode/mcp.json` in your workspace root:
105
+
106
+ ```json
107
+ {
108
+ "servers": {
109
+ "neo": {
110
+ "type": "stdio",
111
+ "command": "npx",
112
+ "args": ["--yes", "neo-mcp-daemon", "--mcp", "/path/to/workspace"],
113
+ "env": {
114
+ "NEO_SECRET_KEY": "sk-v1-YOUR_KEY"
115
+ }
116
+ }
117
+ }
118
+ }
119
+ ```
120
+
121
+ ---
122
+
123
+ ### Zed
124
+
125
+ Edit `~/.config/zed/settings.json`:
126
+
127
+ ```json
128
+ {
129
+ "context_servers": {
130
+ "neo": {
131
+ "source": "custom",
132
+ "command": {
133
+ "path": "npx",
134
+ "args": ["--yes", "neo-mcp-daemon", "--mcp", "/path/to/workspace"],
135
+ "env": {
136
+ "NEO_SECRET_KEY": "sk-v1-YOUR_KEY"
137
+ }
138
+ }
139
+ }
140
+ }
141
+ }
142
+ ```
143
+
144
+ ---
145
+
146
+ ### Continue.dev
147
+
148
+ Edit `~/.continue/config.json`:
149
+
150
+ ```json
151
+ {
152
+ "mcpServers": [
153
+ {
154
+ "name": "neo",
155
+ "transport": {
156
+ "type": "stdio",
157
+ "command": "npx",
158
+ "args": ["--yes", "neo-mcp-daemon", "--mcp", "/path/to/workspace"],
159
+ "env": {
160
+ "NEO_SECRET_KEY": "sk-v1-YOUR_KEY"
161
+ }
162
+ }
163
+ }
164
+ ]
165
+ }
166
+ ```
167
+
168
+ ---
169
+
170
+ ### OpenAI Codex CLI
171
+
172
+ Edit `~/.codex/config.json`:
173
+
174
+ ```json
175
+ {
176
+ "mcpServers": {
177
+ "neo": {
178
+ "command": "npx",
179
+ "args": ["--yes", "neo-mcp-daemon", "--mcp", "/path/to/workspace"],
180
+ "env": {
181
+ "NEO_SECRET_KEY": "sk-v1-YOUR_KEY"
182
+ }
183
+ }
184
+ }
185
+ }
186
+ ```
187
+
188
+ ---
189
+
190
+ ## Environment variables
191
+
192
+ | Variable | Required | Description |
193
+ |---|---|---|
194
+ | `NEO_SECRET_KEY` | **Yes** | Your Neo API key (`sk-v1-...`) from [app.heyneo.so](https://app.heyneo.so) |
195
+ | `NEO_DEPLOYMENT_ID` | No | Pin a specific deployment UUID (auto-generated and persisted by default) |
196
+
197
+ ---
198
+
199
+ ## How it works
200
+
201
+ In **MCP server mode** (`--mcp`):
202
+ 1. Starts an MCP stdio server exposing 7 tools to your editor
203
+ 2. Starts the daemon polling loop in the background
204
+ 3. On `neo_submit_task`, POSTs to Neo's `/v2/thread/init-chat-direct` and returns `thread_id`
205
+ 4. Background poller monitors status via `/v2/thread/status/{thread_id}`
206
+ 5. Daemon polls `/v2/poll/{deployment_id}` for file/subprocess commands and executes them locally
207
+
208
+ In **daemon-only mode** (no `--mcp`):
209
+ 1. Registers with the Neo backend under a persistent deployment UUID
210
+ 2. Polls for commands: `write_code`, `run_subprocess`, `get_file`, `list_files`, `create_session`
211
+ 3. Executes all commands locally and returns results to the backend
212
+
213
+ ---
214
+
215
+ ## Path safety
216
+
217
+ All file operations are restricted to:
218
+ - The workspace directory passed on the command line
219
+ - The system temp directory (`/tmp` on Linux/macOS)
220
+
221
+ Paths outside these locations are blocked — traversal attempts (e.g. `../../etc/passwd`) are rejected.
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ // Warn if no key — daemon will start but will be unable to poll.
5
+ if (!process.env.NEO_SECRET_KEY && !process.env.NEO_TOKEN) {
6
+ console.error(
7
+ '[neo-mcp-daemon] Warning: NEO_SECRET_KEY is not set.\n' +
8
+ ' The daemon needs your API key to receive tasks from Neo.\n' +
9
+ ' Set it before starting: NEO_SECRET_KEY=sk-v1-... npx neo-mcp-daemon'
10
+ );
11
+ }
12
+
13
+ require('../dist/index.js');
package/dist/auth.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Derive a stable deployment UUID from an API key.
3
+ *
4
+ * Algorithm matches Python daemon exactly:
5
+ * SHA-256(key)[:16] formatted as UUID with RFC 4122 version=5, variant=1.
6
+ * Same key → same UUID on every call, across Python and Node daemons.
7
+ */
8
+ export declare function deriveDeploymentId(secretKey: string): string;
9
+ /** Return the NEO_SECRET_KEY API key used for all requests. */
10
+ export declare function getAuthToken(): string;
11
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAgB5D;AAED,+DAA+D;AAC/D,wBAAgB,YAAY,IAAI,MAAM,CAErC"}
package/dist/auth.js ADDED
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deriveDeploymentId = deriveDeploymentId;
4
+ exports.getAuthToken = getAuthToken;
5
+ const crypto_1 = require("crypto");
6
+ /**
7
+ * Derive a stable deployment UUID from an API key.
8
+ *
9
+ * Algorithm matches Python daemon exactly:
10
+ * SHA-256(key)[:16] formatted as UUID with RFC 4122 version=5, variant=1.
11
+ * Same key → same UUID on every call, across Python and Node daemons.
12
+ */
13
+ function deriveDeploymentId(secretKey) {
14
+ const hash = (0, crypto_1.createHash)('sha256').update(secretKey).digest();
15
+ const bytes = Buffer.from(hash.subarray(0, 16));
16
+ // Apply RFC 4122 version 5 and variant bits — mirrors Python's uuid.UUID(bytes=..., version=5)
17
+ bytes[6] = (bytes[6] & 0x0f) | 0x50; // version 5 (0101xxxx)
18
+ bytes[8] = (bytes[8] & 0x3f) | 0x80; // variant 1 (10xxxxxx)
19
+ const hex = bytes.toString('hex');
20
+ return [
21
+ hex.slice(0, 8),
22
+ hex.slice(8, 12),
23
+ hex.slice(12, 16),
24
+ hex.slice(16, 20),
25
+ hex.slice(20, 32),
26
+ ].join('-');
27
+ }
28
+ /** Return the NEO_SECRET_KEY API key used for all requests. */
29
+ function getAuthToken() {
30
+ return process.env['NEO_SECRET_KEY'] ?? '';
31
+ }
32
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":";;AASA,gDAgBC;AAGD,oCAEC;AA9BD,mCAAoC;AAEpC;;;;;;GAMG;AACH,SAAgB,kBAAkB,CAAC,SAAiB;IAClD,MAAM,IAAI,GAAG,IAAA,mBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,CAAC;IAC7D,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAEhD,+FAA+F;IAC/F,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,uBAAuB;IAC5D,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,uBAAuB;IAE5D,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClC,OAAO;QACL,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;QACf,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QAChB,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC;QACjB,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC;QACjB,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC;KAClB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACd,CAAC;AAED,+DAA+D;AAC/D,SAAgB,YAAY;IAC1B,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Environment configuration.
3
+ * NEO_ENV=staging → https://alpha.heyneo.so
4
+ * NEO_ENV=prod → https://master.heyneo.so (default)
5
+ * NEO_API_URL → explicit override (takes priority)
6
+ */
7
+ export declare const NEO_API_URL: string;
8
+ export declare const NEO_ENV: string;
9
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,eAAO,MAAM,WAAW,QAA4C,CAAC;AACrE,eAAO,MAAM,OAAO,QAAO,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ /**
3
+ * Environment configuration.
4
+ * NEO_ENV=staging → https://alpha.heyneo.so
5
+ * NEO_ENV=prod → https://master.heyneo.so (default)
6
+ * NEO_API_URL → explicit override (takes priority)
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.NEO_ENV = exports.NEO_API_URL = void 0;
10
+ const _env = (process.env['NEO_ENV'] ?? 'prod').toLowerCase();
11
+ const _defaultUrl = _env === 'staging' ? 'https://alpha.heyneo.so' : 'https://master.heyneo.so';
12
+ exports.NEO_API_URL = process.env['NEO_API_URL'] ?? _defaultUrl;
13
+ exports.NEO_ENV = _env;
14
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEH,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;AAC9D,MAAM,WAAW,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,0BAA0B,CAAC;AAEnF,QAAA,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC;AACxD,QAAA,OAAO,GAAG,IAAI,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Neo npm daemon — simple poll-execute loop.
3
+ *
4
+ * Polls the Neo backend for commands, executes them locally, sends results back.
5
+ * Thread → workspace mapping persisted in ~/.neo/daemon/thread-workspaces.json.
6
+ */
7
+ import { Command } from './executor.js';
8
+ export declare function getOrCreateDeploymentId(): string;
9
+ export declare function loadThreadWorkspaces(): Record<string, string>;
10
+ export declare function registerThreadWorkspace(threadId: string, workspace: string): void;
11
+ export declare function saveThreadWorkspaces(workspaces: Record<string, string>): void;
12
+ export declare function pollBackend(depId: string, token: string, waitTime?: number): Promise<Command[]>;
13
+ export declare function sendResponse(depId: string, token: string, response: Record<string, unknown>): Promise<void>;
14
+ export declare function runDaemon(opts?: {
15
+ workspace?: string;
16
+ deploymentId?: string;
17
+ signal?: AbortSignal;
18
+ }): Promise<void>;
19
+ //# sourceMappingURL=daemon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,EAAE,OAAO,EAAY,MAAM,eAAe,CAAC;AAyBlD,wBAAgB,uBAAuB,IAAI,MAAM,CA0BhD;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAc7D;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAIjF;AAED,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAiC7E;AAYD,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,SAAI,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAiBhG;AAKD,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBjH;AAMD,wBAAsB,SAAS,CAAC,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAyG7H"}
package/dist/daemon.js ADDED
@@ -0,0 +1,276 @@
1
+ "use strict";
2
+ /**
3
+ * Neo npm daemon — simple poll-execute loop.
4
+ *
5
+ * Polls the Neo backend for commands, executes them locally, sends results back.
6
+ * Thread → workspace mapping persisted in ~/.neo/daemon/thread-workspaces.json.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.getOrCreateDeploymentId = getOrCreateDeploymentId;
10
+ exports.loadThreadWorkspaces = loadThreadWorkspaces;
11
+ exports.registerThreadWorkspace = registerThreadWorkspace;
12
+ exports.saveThreadWorkspaces = saveThreadWorkspaces;
13
+ exports.pollBackend = pollBackend;
14
+ exports.sendResponse = sendResponse;
15
+ exports.runDaemon = runDaemon;
16
+ const crypto_1 = require("crypto");
17
+ const fs_1 = require("fs");
18
+ const path_1 = require("path");
19
+ const auth_js_1 = require("./auth.js");
20
+ const config_js_1 = require("./config.js");
21
+ const executor_js_1 = require("./executor.js");
22
+ const paths_js_1 = require("./paths.js");
23
+ const MAX_THREAD_WORKSPACES = Number(process.env['NEO_THREAD_WORKSPACES_MAX'] ?? 500);
24
+ const THREAD_WORKSPACES_TTL_MS = Number(process.env['NEO_THREAD_WORKSPACES_TTL_SECONDS'] ?? 7 * 24 * 60 * 60) * 1000;
25
+ function writeSandboxLog(deploymentId) {
26
+ (0, fs_1.mkdirSync)(paths_js_1.DAEMON_DIR, { recursive: true });
27
+ (0, fs_1.appendFileSync)(paths_js_1.DAEMON_LOG, `${JSON.stringify({ sandboxId: deploymentId, source: 'npm-daemon' })}\n`);
28
+ }
29
+ function writePidFiles(deploymentId) {
30
+ (0, fs_1.mkdirSync)(paths_js_1.DAEMON_DIR, { recursive: true });
31
+ (0, fs_1.writeFileSync)(paths_js_1.NPM_PID_FILE, String(process.pid));
32
+ (0, fs_1.writeFileSync)((0, paths_js_1.pidFileForDeployment)(deploymentId), String(process.pid));
33
+ }
34
+ function cleanupPidFiles(deploymentId) {
35
+ try {
36
+ (0, fs_1.unlinkSync)(paths_js_1.NPM_PID_FILE);
37
+ }
38
+ catch { /* ignore */ }
39
+ try {
40
+ (0, fs_1.unlinkSync)((0, paths_js_1.pidFileForDeployment)(deploymentId));
41
+ }
42
+ catch { /* ignore */ }
43
+ }
44
+ function getOrCreateDeploymentId() {
45
+ if (process.env['NEO_DEPLOYMENT_ID'])
46
+ return process.env['NEO_DEPLOYMENT_ID'];
47
+ const mode = (process.env['NEO_DEPLOYMENT_ID_MODE'] ?? '').trim().toLowerCase();
48
+ const token = (0, auth_js_1.getAuthToken)();
49
+ if ((mode === 'key-derived' || mode === 'key' || mode === 'deterministic') && token) {
50
+ return (0, auth_js_1.deriveDeploymentId)(token);
51
+ }
52
+ (0, fs_1.mkdirSync)(paths_js_1.DAEMON_DIR, { recursive: true });
53
+ // Always prefer a persisted machine-specific UUID over key-derived.
54
+ // This prevents command-queue collision when the same API key is used on
55
+ // multiple machines simultaneously — each machine must have its own sandbox.
56
+ // Mirrors what the VS Code extension does (unique UUID per user+machine).
57
+ if ((0, fs_1.existsSync)(paths_js_1.STANDALONE_UUID_FILE)) {
58
+ const uid = (0, fs_1.readFileSync)(paths_js_1.STANDALONE_UUID_FILE, 'utf8').trim();
59
+ if (uid)
60
+ return uid;
61
+ }
62
+ // No persisted UUID yet — generate a fresh random one for this machine.
63
+ // Do NOT derive from the API key: same key on two machines would produce
64
+ // identical deployment IDs, causing the backend to split commands between
65
+ // daemons (files on one machine, folders on another).
66
+ const uid = (0, crypto_1.randomUUID)();
67
+ (0, fs_1.writeFileSync)(paths_js_1.STANDALONE_UUID_FILE, uid);
68
+ return uid;
69
+ }
70
+ function loadThreadWorkspaces() {
71
+ try {
72
+ const data = JSON.parse((0, fs_1.readFileSync)(paths_js_1.WORKSPACES_FILE, 'utf8'));
73
+ const out = {};
74
+ for (const [tid, value] of Object.entries(data)) {
75
+ if (typeof value === 'string')
76
+ out[tid] = value;
77
+ else if (value && typeof value === 'object' && typeof value.workspace === 'string') {
78
+ out[tid] = value.workspace;
79
+ }
80
+ }
81
+ return out;
82
+ }
83
+ catch {
84
+ return {};
85
+ }
86
+ }
87
+ function registerThreadWorkspace(threadId, workspace) {
88
+ const existing = loadThreadWorkspaces();
89
+ existing[threadId] = workspace;
90
+ saveThreadWorkspaces(existing);
91
+ }
92
+ function saveThreadWorkspaces(workspaces) {
93
+ const now = Date.now();
94
+ const minTs = now - THREAD_WORKSPACES_TTL_MS;
95
+ let previous = {};
96
+ try {
97
+ const raw = JSON.parse((0, fs_1.readFileSync)(paths_js_1.WORKSPACES_FILE, 'utf8'));
98
+ for (const [tid, value] of Object.entries(raw)) {
99
+ if (!value || typeof value !== 'object')
100
+ continue;
101
+ const workspace = value.workspace;
102
+ const updated = value.updated_at;
103
+ if (typeof workspace === 'string' && typeof updated === 'number') {
104
+ previous[tid] = { workspace, updated_at: updated };
105
+ }
106
+ }
107
+ }
108
+ catch {
109
+ previous = {};
110
+ }
111
+ let entries = Object.entries(workspaces).map(([tid, workspace]) => {
112
+ const prev = previous[tid];
113
+ const prevTs = prev && prev.workspace === workspace ? prev.updated_at * 1000 : now;
114
+ return { tid, workspace, updatedAt: prevTs };
115
+ });
116
+ entries = entries.filter((e) => e.updatedAt >= minTs && !!e.workspace);
117
+ if (entries.length > MAX_THREAD_WORKSPACES) {
118
+ entries = entries.slice(entries.length - MAX_THREAD_WORKSPACES);
119
+ }
120
+ (0, fs_1.mkdirSync)(paths_js_1.DAEMON_DIR, { recursive: true });
121
+ const payload = Object.fromEntries(entries.map((e) => [e.tid, { workspace: e.workspace, updated_at: Math.floor(e.updatedAt / 1000) }]));
122
+ const tmpFile = `${paths_js_1.WORKSPACES_FILE}.tmp-${process.pid}`;
123
+ (0, fs_1.writeFileSync)(tmpFile, JSON.stringify(payload, null, 2));
124
+ (0, fs_1.renameSync)(tmpFile, paths_js_1.WORKSPACES_FILE);
125
+ }
126
+ async function fetchWithTimeout(url, init, timeoutMs) {
127
+ const controller = new AbortController();
128
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
129
+ try {
130
+ return await fetch(url, { ...init, signal: controller.signal });
131
+ }
132
+ finally {
133
+ clearTimeout(timeout);
134
+ }
135
+ }
136
+ async function pollBackend(depId, token, waitTime = 5) {
137
+ try {
138
+ const res = await fetchWithTimeout(`${config_js_1.NEO_API_URL}/v2/poll/${depId}?max_messages=20&wait_time=${waitTime}`, { headers: { 'Authorization': `Bearer ${token}` } }, Math.max(waitTime * 2, 10) * 1_000);
139
+ if (res.status === 401) {
140
+ console.error(`Auth rejected for deployment ${depId} (401).`);
141
+ return [];
142
+ }
143
+ if (res.status === 404 || !res.ok)
144
+ return [];
145
+ const data = await res.json();
146
+ return Array.isArray(data) ? data : (data.messages ?? []);
147
+ }
148
+ catch {
149
+ return [];
150
+ }
151
+ }
152
+ // Retry sendResponse up to 3 times — silent failure was the root cause of stalled file creation.
153
+ // The backend only generates the next write_code command after receiving the previous response.
154
+ // If sendResponse silently failed, the backend would stall waiting for a confirmation that never arrived.
155
+ async function sendResponse(depId, token, response) {
156
+ const body = JSON.stringify({ ...response, sandbox_id: response['sandbox_id'] ?? depId });
157
+ let delayMs = 500;
158
+ for (let attempt = 1; attempt <= 3; attempt++) {
159
+ try {
160
+ await fetchWithTimeout(`${config_js_1.NEO_API_URL}/v2/poll/response`, {
161
+ method: 'POST',
162
+ headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
163
+ body,
164
+ }, 30_000);
165
+ return; // success
166
+ }
167
+ catch (err) {
168
+ if (attempt < 3) {
169
+ await sleep(delayMs);
170
+ delayMs *= 2; // 500ms → 1000ms
171
+ }
172
+ else {
173
+ console.error(`[sendResponse] Failed after 3 attempts for request_id=${response['request_id'] ?? '?'}:`, err);
174
+ }
175
+ }
176
+ }
177
+ }
178
+ function sleep(ms) {
179
+ return new Promise((resolve) => setTimeout(resolve, ms));
180
+ }
181
+ async function runDaemon(opts = {}) {
182
+ const workspace = (0, path_1.resolve)(opts.workspace ?? process.cwd());
183
+ const depId = opts.deploymentId ?? getOrCreateDeploymentId();
184
+ const token = (0, auth_js_1.getAuthToken)();
185
+ if (!token) {
186
+ console.error('ERROR: NEO_SECRET_KEY is not set.\nSet your API key: export NEO_SECRET_KEY=sk-v1-...');
187
+ process.exit(1);
188
+ }
189
+ (0, fs_1.mkdirSync)(paths_js_1.DAEMON_DIR, { recursive: true });
190
+ writePidFiles(depId);
191
+ writeSandboxLog(depId);
192
+ console.log('Neo daemon ready');
193
+ console.log(` deployment_id : ${depId}`);
194
+ console.log(` workspace : ${workspace}`);
195
+ console.log(` backend : ${config_js_1.NEO_API_URL}`);
196
+ console.log(` pid : ${process.pid}`);
197
+ const threadWorkspaces = loadThreadWorkspaces();
198
+ let backoffMs = 1_000;
199
+ let running = true;
200
+ const stop = () => { running = false; cleanupPidFiles(depId); };
201
+ process.on('SIGTERM', () => { stop(); process.exit(0); });
202
+ process.on('SIGINT', () => { stop(); process.exit(0); });
203
+ opts.signal?.addEventListener('abort', stop, { once: true });
204
+ let lastCommandTime = 0; // Date.now() ms, 0 = never
205
+ while (running) {
206
+ // During active execution use wait_time=1 so the poll returns quickly after the
207
+ // backend queues the next command. wait_time=5 is fine when idle (reduces poll traffic).
208
+ const recentlyActive = (Date.now() - lastCommandTime) < 60_000;
209
+ const waitTime = recentlyActive ? 1 : 5;
210
+ const commands = await pollBackend(depId, token, waitTime);
211
+ if (commands.length === 0) {
212
+ if (recentlyActive) {
213
+ // Small yield so the event loop can process signals/timers before next poll.
214
+ await sleep(100);
215
+ }
216
+ else {
217
+ await sleep(backoffMs);
218
+ backoffMs = Math.min(Math.floor(backoffMs * 1.5), 3_000);
219
+ }
220
+ continue;
221
+ }
222
+ backoffMs = 1_000;
223
+ lastCommandTime = Date.now();
224
+ // Dispatch all commands in this batch concurrently — each runs in its own
225
+ // thread's workspace so there is no ordering dependency between them.
226
+ await Promise.all(commands.map(async (cmd) => {
227
+ const tid = cmd.thread_id;
228
+ // Resolve the local workspace for this thread. The MCP server writes
229
+ // thread→workspace to the shared file right after submit; the file may
230
+ // not exist yet if this command raced the registration. Retry with
231
+ // back-off before falling back so all 10 concurrent projects each land
232
+ // in their own directory instead of sharing the daemon's default.
233
+ let ws = tid ? threadWorkspaces[tid] : undefined;
234
+ if (tid && !ws) {
235
+ // Attempt 1 — synchronous file reload (usually sufficient in stdio mode)
236
+ Object.assign(threadWorkspaces, loadThreadWorkspaces());
237
+ ws = threadWorkspaces[tid];
238
+ }
239
+ if (tid && !ws) {
240
+ // Attempt 2 — wait 250 ms for MCP server to finish writing the file
241
+ await sleep(250);
242
+ Object.assign(threadWorkspaces, loadThreadWorkspaces());
243
+ ws = threadWorkspaces[tid];
244
+ }
245
+ if (tid && !ws) {
246
+ // Attempt 3 — one final reload at 500 ms
247
+ await sleep(500);
248
+ Object.assign(threadWorkspaces, loadThreadWorkspaces());
249
+ ws = threadWorkspaces[tid];
250
+ }
251
+ if (tid && !ws) {
252
+ // Still nothing — log a warning so the user can diagnose misrouted files.
253
+ // Do NOT persist the fallback: if the registration arrives later we want
254
+ // the next command to pick up the real workspace, not a cached default.
255
+ console.warn(`[daemon] workspace not registered for thread ${tid} — using default: ${workspace}`);
256
+ }
257
+ const resolvedWs = ws ?? workspace;
258
+ const result = await (0, executor_js_1.dispatch)(cmd, resolvedWs);
259
+ if (tid) {
260
+ result['thread_id'] = tid;
261
+ // Only persist the mapping when it came from an explicit registration
262
+ // (not the fallback default) so a late-arriving registration can still
263
+ // override a previously saved default.
264
+ if (ws && !threadWorkspaces[tid]) {
265
+ threadWorkspaces[tid] = ws;
266
+ saveThreadWorkspaces(threadWorkspaces);
267
+ }
268
+ }
269
+ if (cmd.response_queue_name) {
270
+ result['response_queue_name'] = cmd.response_queue_name;
271
+ }
272
+ await sendResponse(depId, token, result);
273
+ }));
274
+ }
275
+ }
276
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}