@yugabytedb/meko-mcp 1.0.5 → 1.0.6
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/bin/create-meko-setup.mjs +6 -1
- package/lib/migrate/orchestrator.mjs +45 -3
- package/lib/prompt.mjs +13 -9
- package/package.json +1 -1
|
@@ -374,6 +374,8 @@ async function main() {
|
|
|
374
374
|
// Step 1 — connection type. Local Meko only makes sense for users who
|
|
375
375
|
// have the server repo cloned locally (docker-compose stack on :8000);
|
|
376
376
|
// npm/npx users have no way to run it, so hide the option for them.
|
|
377
|
+
// When only Cloud is available, skip the prompt entirely — asking to
|
|
378
|
+
// pick between one option is pointless UX.
|
|
377
379
|
const connectionOptions = [
|
|
378
380
|
{ label: 'Cloud Meko', description: 'Hosted at mekodev.com (requires URL + API key)' },
|
|
379
381
|
];
|
|
@@ -383,7 +385,10 @@ async function main() {
|
|
|
383
385
|
description: 'Local stack at localhost:8000',
|
|
384
386
|
});
|
|
385
387
|
}
|
|
386
|
-
const connectionType =
|
|
388
|
+
const connectionType =
|
|
389
|
+
connectionOptions.length === 1
|
|
390
|
+
? connectionOptions[0].label
|
|
391
|
+
: await askChoice('Connection type:', connectionOptions);
|
|
387
392
|
|
|
388
393
|
if (connectionType === 'Cloud Meko') {
|
|
389
394
|
// Step 2 — MCP URL
|
|
@@ -283,11 +283,17 @@ export async function runMigration(opts) {
|
|
|
283
283
|
const [, memRes] = await Promise.all([addMsgPromise, memPromise]);
|
|
284
284
|
messagesSent++;
|
|
285
285
|
if (!opts.noExtract) {
|
|
286
|
-
if (memRes && memRes.ok) {
|
|
286
|
+
if (memRes && memRes.ok && !isErrorText(memRes.text)) {
|
|
287
287
|
memoryPairsSent++;
|
|
288
288
|
prog.counts.memoryPairsSent++;
|
|
289
289
|
} else if (memRes) {
|
|
290
290
|
prog.counts.memoryErrors++;
|
|
291
|
+
log.warn("memory_add_failed", {
|
|
292
|
+
agentId: ref.agentId,
|
|
293
|
+
sessionUuid: ref.sessionUuid,
|
|
294
|
+
userUuid: ex.user_uuid,
|
|
295
|
+
detail: errorDetail(memRes),
|
|
296
|
+
});
|
|
291
297
|
}
|
|
292
298
|
}
|
|
293
299
|
lastUuid = ex.user_uuid;
|
|
@@ -361,7 +367,7 @@ export async function runMigration(opts) {
|
|
|
361
367
|
metadata: JSON.stringify(item.metadata),
|
|
362
368
|
}, { fireAndForget: true });
|
|
363
369
|
|
|
364
|
-
if (res.ok) {
|
|
370
|
+
if (res.ok && !isErrorText(res.text)) {
|
|
365
371
|
state.markMemory(st, item.agentId, item.hash, {
|
|
366
372
|
memoryId: extractId(res.text) || "",
|
|
367
373
|
name: item.name,
|
|
@@ -370,7 +376,12 @@ export async function runMigration(opts) {
|
|
|
370
376
|
prog.counts.memoriesFromMD++;
|
|
371
377
|
} else {
|
|
372
378
|
prog.counts.memoryErrors++;
|
|
373
|
-
log.warn("memory_md_failed", {
|
|
379
|
+
log.warn("memory_md_failed", {
|
|
380
|
+
agentId: item.agentId,
|
|
381
|
+
name: item.name,
|
|
382
|
+
error: res.error?.message,
|
|
383
|
+
detail: errorDetail(res),
|
|
384
|
+
});
|
|
374
385
|
}
|
|
375
386
|
if (++opsSinceFlush >= FLUSH_EVERY) flushState();
|
|
376
387
|
writeProgress();
|
|
@@ -441,3 +452,34 @@ function extractId(text) {
|
|
|
441
452
|
return null;
|
|
442
453
|
}
|
|
443
454
|
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* The MCP transport returns `{ok:true}` whenever the HTTP call itself
|
|
458
|
+
* succeeded, even if the tool's body encoded a failure. memory_add in
|
|
459
|
+
* older server builds returned plain "Error adding memory: ..." strings
|
|
460
|
+
* on exception; newer builds return JSON `{"error":"memory_add_failed",...}`.
|
|
461
|
+
* Either way, the text content is the authoritative success signal.
|
|
462
|
+
*/
|
|
463
|
+
function isErrorText(text) {
|
|
464
|
+
if (!text) return false;
|
|
465
|
+
const trimmed = text.trim();
|
|
466
|
+
if (trimmed.startsWith("Error adding memory")) return true;
|
|
467
|
+
if (trimmed.startsWith("{")) {
|
|
468
|
+
try {
|
|
469
|
+
const obj = JSON.parse(trimmed);
|
|
470
|
+
if (obj && typeof obj === "object" && obj.error) return true;
|
|
471
|
+
} catch { /* not JSON; fall through */ }
|
|
472
|
+
}
|
|
473
|
+
return false;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function errorDetail(res) {
|
|
477
|
+
if (!res) return null;
|
|
478
|
+
if (res.error?.message) return res.error.message;
|
|
479
|
+
if (!res.text) return null;
|
|
480
|
+
try {
|
|
481
|
+
const obj = JSON.parse(res.text);
|
|
482
|
+
if (obj?.error) return obj.detail || obj.error;
|
|
483
|
+
} catch { /* not JSON */ }
|
|
484
|
+
return res.text.slice(0, 200);
|
|
485
|
+
}
|
package/lib/prompt.mjs
CHANGED
|
@@ -164,16 +164,21 @@ export async function askText(questionText, defaultValue) {
|
|
|
164
164
|
* @returns {Promise<string>}
|
|
165
165
|
*/
|
|
166
166
|
export async function askSecret(questionText) {
|
|
167
|
+
// Fully close the singleton readline BEFORE printing the prompt or
|
|
168
|
+
// entering raw mode. pause() alone was insufficient — readline left
|
|
169
|
+
// stdin in a state where the terminal echo would still leak keystrokes
|
|
170
|
+
// before our raw-mode handler wrote its `*` mask. close() forcefully
|
|
171
|
+
// releases stdin back to the OS, which lets setRawMode actually
|
|
172
|
+
// suppress echo. getRL() will recreate the interface on the next
|
|
173
|
+
// askText/askYesNo call, so downstream prompts still work.
|
|
174
|
+
if (rl) {
|
|
175
|
+
rl.close();
|
|
176
|
+
rl = null;
|
|
177
|
+
}
|
|
178
|
+
|
|
167
179
|
// Print the prompt without a trailing newline.
|
|
168
180
|
process.stdout.write(` ${questionText} `);
|
|
169
181
|
|
|
170
|
-
// Pause the singleton readline so its data listener doesn't echo each
|
|
171
|
-
// keystroke in parallel with our manual "*" write. Without this, users
|
|
172
|
-
// see `d*j*f*k*...` (raw char + mask interleaved) and backspace fights
|
|
173
|
-
// with readline over cursor position.
|
|
174
|
-
const pausedRL = rl;
|
|
175
|
-
if (pausedRL) pausedRL.pause();
|
|
176
|
-
|
|
177
182
|
return new Promise((resolve, reject) => {
|
|
178
183
|
let secret = "";
|
|
179
184
|
const stdin = process.stdin;
|
|
@@ -185,7 +190,6 @@ export async function askSecret(questionText) {
|
|
|
185
190
|
const fallbackRL = createInterface({ input: stdin, output: process.stdout });
|
|
186
191
|
fallbackRL.question("", (answer) => {
|
|
187
192
|
fallbackRL.close();
|
|
188
|
-
if (pausedRL) pausedRL.resume();
|
|
189
193
|
resolve(answer.trim());
|
|
190
194
|
});
|
|
191
195
|
return;
|
|
@@ -200,8 +204,8 @@ export async function askSecret(questionText) {
|
|
|
200
204
|
function cleanup() {
|
|
201
205
|
stdin.setRawMode(wasRaw);
|
|
202
206
|
stdin.removeListener("data", onData);
|
|
207
|
+
stdin.pause();
|
|
203
208
|
process.stdout.write("\n");
|
|
204
|
-
if (pausedRL) pausedRL.resume();
|
|
205
209
|
}
|
|
206
210
|
|
|
207
211
|
function onData(ch) {
|