open-context-engine 0.1.3 → 0.1.5
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/docs/QUICKSTART.md +28 -4
- package/package.json +3 -2
- package/scripts/retrieval-server.py +65 -16
- package/src/client.mjs +6 -2
- package/src/config.mjs +1 -1
- package/src/eval/remote-models.mjs +1 -1
- package/src/mcp.mjs +5 -3
- package/src/retrieval/batched.py +1 -1
- package/src/retrieval/cascade.py +1 -1
- package/src/retrieval/engine.py +2 -2
- package/src/retrieval/entities.py +1 -1
- package/src/retrieval/evidence.py +312 -0
- package/src/retrieval/languages/__init__.py +33 -4
- package/src/retrieval/languages/go.py +3 -1
- package/src/retrieval/languages/go_ast.go +11 -0
- package/src/retrieval/languages/python.py +23 -13
- package/src/retrieval/languages/python_calls.py +188 -0
- package/src/retrieval/languages/schema.py +9 -2
- package/src/retrieval/languages/typescript.mjs +10 -4
- package/src/retrieval/languages/typescript.py +3 -0
- package/src/retrieval/live.py +11 -5
- package/src/retrieval/planning.py +15 -0
- package/src/retrieval/reranker.py +12 -6
- package/src/retrieval/shared_worker.py +115 -0
- package/src/retrieval/writer_lock.py +5 -1
- package/src/service.mjs +19 -3
- package/src/setup.mjs +2 -0
- package/src/shared-service.mjs +142 -0
- package/src/workspaces.mjs +7 -5
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { createHash, createHmac, randomBytes, randomUUID } from 'node:crypto';
|
|
2
|
+
import { readFile, mkdir, realpath } from 'node:fs/promises';
|
|
3
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
6
|
+
import { projectRoot } from './config.mjs';
|
|
7
|
+
import { startService } from './service.mjs';
|
|
8
|
+
|
|
9
|
+
const protocol = 1;
|
|
10
|
+
function stable(value) {
|
|
11
|
+
if (Array.isArray(value)) return value.map(stable);
|
|
12
|
+
if (value && typeof value === 'object') return Object.fromEntries(Object.keys(value).sort()
|
|
13
|
+
.filter(key => value[key] !== undefined).map(key => [key, stable(value[key])]));
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
function runtimeIdentity() {
|
|
17
|
+
const hash = createHash('sha256');
|
|
18
|
+
function add(path) {
|
|
19
|
+
for (const entry of readdirSync(join(projectRoot, path), {withFileTypes:true}).sort((a,b) => a.name.localeCompare(b.name))) {
|
|
20
|
+
const name = `${path}/${entry.name}`;
|
|
21
|
+
if (entry.isDirectory() && entry.name !== '__pycache__') add(name);
|
|
22
|
+
else if (entry.isFile() && /\.(py|mjs|go)$/.test(name)) hash.update(name).update(readFileSync(join(projectRoot, name)));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
add('src/retrieval');
|
|
26
|
+
for (const name of ['scripts/retrieval-server.py', 'requirements.txt']) hash.update(readFileSync(join(projectRoot, name)));
|
|
27
|
+
hash.update(JSON.parse(readFileSync(join(projectRoot, 'package.json'))).version);
|
|
28
|
+
return hash.digest('hex');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// A handle owns a renewable lease, never the lifetime of the shared child process.
|
|
32
|
+
export function startSharedService(settings, {startupMs = 15000, leaseSeconds = 15, idleSeconds = 30, start = startService} = {}) {
|
|
33
|
+
const runtime = runtimeIdentity();
|
|
34
|
+
let configured, connection, pending, timer, closed = false, closePromise;
|
|
35
|
+
const leaseId = randomUUID();
|
|
36
|
+
const initialized = (async () => {
|
|
37
|
+
await mkdir(settings.config.state, {recursive:true, mode:0o700});
|
|
38
|
+
const root = await realpath(settings.config.root), state = await realpath(settings.config.state);
|
|
39
|
+
const config = {...settings.config, root, state};
|
|
40
|
+
const {serviceKey, port, shared, ...behavior} = config;
|
|
41
|
+
const identity = JSON.stringify(stable({protocol, runtime, config:behavior,
|
|
42
|
+
python:settings.python, workerEnv:settings.workerEnv || {}}));
|
|
43
|
+
configured = {config, identity, record:join(state, 'worker.json')};
|
|
44
|
+
})();
|
|
45
|
+
|
|
46
|
+
function fingerprint(key) {
|
|
47
|
+
// Credentials participate in compatibility without a reusable plaintext/hash in discovery files.
|
|
48
|
+
return createHmac('sha256', key).update(configured.identity).digest('hex');
|
|
49
|
+
}
|
|
50
|
+
async function readRecord() {
|
|
51
|
+
let record;
|
|
52
|
+
try {record = JSON.parse(await readFile(configured.record, 'utf8'));}
|
|
53
|
+
catch (error) {
|
|
54
|
+
if (error.code === 'ENOENT' || error instanceof SyntaxError) return;
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
if (!record || !Number.isInteger(record.port) || record.port < 1 || record.port > 65535 ||
|
|
58
|
+
typeof record.apiKey !== 'string' || !/^[a-f0-9]{64}$/.test(record.apiKey) ||
|
|
59
|
+
typeof record.instanceId !== 'string') return;
|
|
60
|
+
// Discovery cannot redirect credentials or requests off this machine.
|
|
61
|
+
return {...record, baseUrl:`http://127.0.0.1:${record.port}`};
|
|
62
|
+
}
|
|
63
|
+
async function lease(record, release = false) {
|
|
64
|
+
const response = await fetch(record.baseUrl + (release ? '/release' : '/lease'), {
|
|
65
|
+
method:'POST', redirect:'error', signal:AbortSignal.timeout(1500),
|
|
66
|
+
headers:{'content-type':'application/json', authorization:`Bearer ${record.apiKey}`},
|
|
67
|
+
body:JSON.stringify({instanceId:record.instanceId, fingerprint:fingerprint(record.apiKey), leaseId}),
|
|
68
|
+
});
|
|
69
|
+
const body = await response.json();
|
|
70
|
+
if (response.status === 409 && body.code === 'CONFIG_MISMATCH') {
|
|
71
|
+
throw Object.assign(new Error(body.error), {code:'CONFIG_MISMATCH'});
|
|
72
|
+
}
|
|
73
|
+
if (!response.ok || body.instanceId !== record.instanceId || record.protocol !== protocol ||
|
|
74
|
+
!Number.isFinite(body.leaseSeconds) || body.leaseSeconds <= 0) throw new Error('Shared worker handshake failed');
|
|
75
|
+
return {...record, leaseSeconds:body.leaseSeconds};
|
|
76
|
+
}
|
|
77
|
+
function schedule(record) {
|
|
78
|
+
clearTimeout(timer);
|
|
79
|
+
timer = setTimeout(() => {void get().catch(() => {});}, record.leaseSeconds * 1000 / 3);
|
|
80
|
+
timer.unref();
|
|
81
|
+
}
|
|
82
|
+
async function connect() {
|
|
83
|
+
await initialized;
|
|
84
|
+
const deadline = performance.now() + startupMs;
|
|
85
|
+
let nextSpawn = 0, lastBusy;
|
|
86
|
+
while (!closed && performance.now() < deadline) {
|
|
87
|
+
const record = connection || await readRecord();
|
|
88
|
+
if (record) {
|
|
89
|
+
try {
|
|
90
|
+
connection = await lease(record);
|
|
91
|
+
if (closed) {await lease(connection, true).catch(() => {}); break;}
|
|
92
|
+
schedule(connection);
|
|
93
|
+
return {baseUrl:connection.baseUrl, apiKey:connection.apiKey};
|
|
94
|
+
} catch (error) {
|
|
95
|
+
connection = undefined;
|
|
96
|
+
if (error.code === 'CONFIG_MISMATCH') throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (performance.now() >= nextSpawn) {
|
|
100
|
+
if (runtimeIdentity() !== runtime) throw new Error('Worker runtime changed on disk; restart this MCP client');
|
|
101
|
+
const key = randomBytes(32).toString('hex');
|
|
102
|
+
const worker = start({...settings, config:{...configured.config, port:0, serviceKey:key,
|
|
103
|
+
shared:{fingerprint:fingerprint(key), leaseSeconds, idleSeconds}}}, {log:() => {}, startupMs:Math.max(1, deadline - performance.now())});
|
|
104
|
+
try {await worker.ready;}
|
|
105
|
+
catch (error) {
|
|
106
|
+
await worker.close();
|
|
107
|
+
// A retry near the overall deadline may have too little time to import
|
|
108
|
+
// Python. Preserve the writer conflict already established by an earlier attempt.
|
|
109
|
+
if (error.code === 'STARTUP_TIMEOUT' && lastBusy) break;
|
|
110
|
+
if (error.code !== 'INDEX_LOCKED') throw error;
|
|
111
|
+
lastBusy = error;
|
|
112
|
+
}
|
|
113
|
+
nextSpawn = performance.now() + 1000;
|
|
114
|
+
}
|
|
115
|
+
await delay(100);
|
|
116
|
+
}
|
|
117
|
+
if (closed) throw new Error('MCP workspace manager is shutting down');
|
|
118
|
+
throw new Error(lastBusy
|
|
119
|
+
? `${lastBusy.message}. No compatible shared worker became available. An older or manually started worker may own this index; close its clients or connect to it explicitly.`
|
|
120
|
+
: 'Shared retrieval worker startup or connection timed out');
|
|
121
|
+
}
|
|
122
|
+
function get() {
|
|
123
|
+
if (closed) return Promise.reject(new Error('MCP workspace manager is shutting down'));
|
|
124
|
+
if (!pending) {
|
|
125
|
+
pending = connect().finally(() => {pending = undefined;});
|
|
126
|
+
}
|
|
127
|
+
return pending;
|
|
128
|
+
}
|
|
129
|
+
function close() {
|
|
130
|
+
if (!closePromise) {
|
|
131
|
+
closed = true;
|
|
132
|
+
clearTimeout(timer);
|
|
133
|
+
closePromise = (async () => {
|
|
134
|
+
await pending?.catch(() => {});
|
|
135
|
+
if (connection) await lease(connection, true).catch(() => {});
|
|
136
|
+
connection = undefined;
|
|
137
|
+
})();
|
|
138
|
+
}
|
|
139
|
+
return closePromise;
|
|
140
|
+
}
|
|
141
|
+
return {ready:get(), get, close};
|
|
142
|
+
}
|
package/src/workspaces.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { realpathSync, statSync } from 'node:fs';
|
|
2
2
|
import { isAbsolute, resolve, join } from 'node:path';
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
|
-
import { serviceConfig
|
|
4
|
+
import { serviceConfig } from './service.mjs';
|
|
5
|
+
import { startSharedService } from './shared-service.mjs';
|
|
5
6
|
|
|
6
7
|
function directory(path) {
|
|
7
8
|
const canonical = realpathSync.native(path);
|
|
@@ -9,8 +10,8 @@ function directory(path) {
|
|
|
9
10
|
return canonical;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
// Each
|
|
13
|
-
export function createWorkspaceManager({root, state} = {}, {configure = serviceConfig, start =
|
|
13
|
+
// Each manager holds one lease per canonical repository, including while connecting.
|
|
14
|
+
export function createWorkspaceManager({root, state} = {}, {configure = serviceConfig, start = startSharedService} = {}) {
|
|
14
15
|
const fixedRoot = root ? directory(resolve(root)) : undefined;
|
|
15
16
|
const workers = new Map();
|
|
16
17
|
let closing = false, closed;
|
|
@@ -35,13 +36,14 @@ export function createWorkspaceManager({root, state} = {}, {configure = serviceC
|
|
|
35
36
|
entry = {worker};
|
|
36
37
|
workers.set(repository, entry);
|
|
37
38
|
const remove = () => {if (workers.get(repository) === entry) workers.delete(repository);};
|
|
38
|
-
worker.child
|
|
39
|
+
worker.child?.once('exit', remove);
|
|
39
40
|
entry.ready = worker.ready.catch(async error => {
|
|
40
41
|
try {await worker.close();} finally {remove();}
|
|
41
42
|
throw error;
|
|
42
43
|
});
|
|
43
44
|
}
|
|
44
|
-
|
|
45
|
+
await entry.ready;
|
|
46
|
+
return entry.worker.get ? entry.worker.get() : entry.ready;
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
function close() {
|