@cnkang/qdrant-memory-launcher 0.1.0-rc.1 → 0.1.0-rc.2

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 CHANGED
@@ -13,7 +13,7 @@ are tracked separately for each operating system.
13
13
  For a client that already supplies Node.js 20 or newer:
14
14
 
15
15
  ```sh
16
- npm install --global @cnkang/qdrant-memory-launcher@0.1.0-rc.1
16
+ npm install --global @cnkang/qdrant-memory-launcher@0.1.0-rc.2
17
17
  qdrant-memory-launcher prepare dsh
18
18
  qdrant-memory-launcher mcp dsh
19
19
  ```
@@ -21,8 +21,8 @@ qdrant-memory-launcher mcp dsh
21
21
  Preparation needs network access and may take several minutes. It downloads
22
22
  private Python dependencies and about 252 MB of model artifacts. No system
23
23
  Python, uv/uvx, Ollama or Qdrant Server is required. The local preset needs no
24
- API keys. DSH must finish preparation before enabling the MCP connection;
25
- its temporary MCP probe never runs installation.
24
+ API keys. DSH must finish preparation before you enable the MCP connection.
25
+ Its temporary MCP probe never runs installation.
26
26
 
27
27
  WorkBuddy's managed Connector declares Node.js 20 and requires WorkBuddy
28
28
  5.0.0 or newer. It runs `mcp workbuddy --auto-setup`. Installing that connection
@@ -31,7 +31,7 @@ authorizes downloads and creation of its private profile.
31
31
  ## First connection and recovery
32
32
 
33
33
  The four tools appear while preparation runs. A connection indicator means the
34
- MCP transport is available; it does not mean the memory backend is ready.
34
+ MCP transport is available. It does not mean the memory backend is ready.
35
35
  During preparation, tool calls report that no operation occurred. Wait before
36
36
  asking the agent to save a fact. Writes are never queued.
37
37
 
@@ -64,7 +64,7 @@ Program versions and dependency locks select separate installation directories.
64
64
  Existing profiles retain their model-cache settings during an upgrade.
65
65
 
66
66
  Disconnect the client before upgrading or using maintenance commands.
67
- Uninstalling the npm package removes the launcher; it does not remove profiles
67
+ Uninstalling the npm package removes the launcher. It does not remove profiles
68
68
  or models. Back up the profile before a rollback and retain the same embedding
69
69
  pipeline. Do not copy a running embedded store or automatically migrate it.
70
70
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cnkang/qdrant-memory-launcher",
3
- "version": "0.1.0-rc.1",
3
+ "version": "0.1.0-rc.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Private Python and CPU memory runtime for MCP clients",
package/provision.mjs CHANGED
@@ -120,6 +120,22 @@ async function regularFile(path) {
120
120
  return path;
121
121
  }
122
122
 
123
+ // Windows can briefly hold a freshly written file open: a real-time scan of a
124
+ // new executable, or a terminating child that still maps it after a client
125
+ // stopped its probe. Retry only transient lock errors, within bounds.
126
+ export async function publishRename(from, to, signal) {
127
+ const delays = [50, 100, 200, 400, 800, 1500, 1500, 1500, 1500, 1500];
128
+ for (let attempt = 0; ; attempt++) {
129
+ try {
130
+ await rename(from, to);
131
+ return;
132
+ } catch (error) {
133
+ if (!['EPERM', 'EACCES', 'EBUSY'].includes(error.code) || attempt >= delays.length || signal?.aborted) throw error;
134
+ await new Promise(resolve => setTimeout(resolve, delays[attempt]));
135
+ }
136
+ }
137
+ }
138
+
123
139
  // Caller must hold the program's provisioning lock. Only a prefix of the exact
124
140
  // expected snapshot can be left by an interrupted legacy write; other changes
125
141
  // remain errors. Publish on the same filesystem only after writing and flushing.
@@ -136,7 +152,7 @@ export async function publishSnapshot(path, content) {
136
152
  const file = await open(temporary, 'wx', 0o600);
137
153
  try { await file.writeFile(content); await file.sync(); }
138
154
  finally { await file.close(); }
139
- await rename(temporary, path);
155
+ await publishRename(temporary, path);
140
156
  } finally { await rm(temporary, { force: true }); }
141
157
  }
142
158
 
@@ -186,7 +202,7 @@ export async function prepare(paths, host, signal, onStage = () => {}) {
186
202
  await rm(temporary, { force: true });
187
203
  await writeFile(temporary, bytes, { flag: 'wx', mode: 0o700 });
188
204
  if (await lstat(uv).catch(error => { if (error.code !== 'ENOENT') throw error; })) await regularFile(uv);
189
- await rename(temporary, uv);
205
+ await publishRename(temporary, uv, signal);
190
206
  enter('install-python');
191
207
  await run(uv, ['python', 'install', PYTHON_VERSION, '--no-bin'], { env, signal });
192
208
  const python = await run(uv, ['python', 'find', '--system', PYTHON_VERSION], { env, signal });
@@ -208,7 +224,7 @@ export async function prepare(paths, host, signal, onStage = () => {}) {
208
224
  const temporaryArchive = archive + '.pending';
209
225
  await rm(temporaryArchive, { force: true });
210
226
  await writeFile(temporaryArchive, await download(runtime, signal), { mode: 0o600 });
211
- await rename(temporaryArchive, archive);
227
+ await publishRename(temporaryArchive, archive, signal);
212
228
  }
213
229
  const environmentPython = join(paths.program, 'env', process.platform === 'win32' ? 'Scripts/python.exe' : 'bin/python');
214
230
  // Install only published wheels. No source builds or build environment.