@mnemonik/shared 7.100.0 → 7.101.1

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.
@@ -53,6 +53,7 @@ export function hostAdapterConformance(
53
53
  runtimeRoot,
54
54
  credentialFamily: 'hook-family',
55
55
  runtimeEntry: join(runtimeRoot, '1.0.0', 'dist', 'hook.js'),
56
+ installationId: '11111111-1111-4111-8111-111111111111',
56
57
  };
57
58
  const deps: AdapterDependencies = {
58
59
  target,
@@ -391,55 +392,10 @@ export function hostAdapterConformance(
391
392
  assert.deepEqual(await readFile(configPath('user')), before);
392
393
  }
393
394
  );
394
- check(
395
- 'parses connected listings without inferring account and uses execFile arguments',
396
- async ({ deps, project, target }) => {
397
- let stdout = 'mnemonik: https://api.mnemonik.dev/mcp - ✔ Connected\n';
398
- const calls: unknown[][] = [];
399
- const host = create({
400
- ...deps,
401
- target: { ...target, component: 'mcp' },
402
- execFile: async (...args) => {
403
- if (args[1][0] === '--version') return { stdout: versionOutput, stderr: '' };
404
- calls.push(args);
405
- return { stdout, stderr: '' };
406
- },
407
- });
408
- const inspection = await host.verify();
409
- if (options.name === 'cursor') {
410
- assert.equal(inspection.authenticatedTools, false);
411
- assert.equal(inspection.grant, undefined);
412
- assert.deepEqual(calls, []);
413
- return;
414
- }
415
- assert.equal(inspection.authenticatedTools, true);
416
- assert.equal(inspection.grant, undefined);
417
- assert.deepEqual(calls[0], [
418
- options.name === 'claude-code'
419
- ? 'claude'
420
- : options.name === 'cursor'
421
- ? 'agent'
422
- : options.name,
423
- ['mcp', 'list'],
424
- { env: deps.env, cwd: project, timeout: 5000, maxBuffer: 64 * 1024, windowsHide: true },
425
- ]);
426
- for (const listing of [
427
- 'mnemonik: not connected',
428
- 'foreign: ✔ Connected',
429
- 'mnemonik: enabled OAuth',
430
- 'mnemonik-foreign: Connected',
431
- 'mnemonik: warning: was previously connected',
432
- 'unrecognized format',
433
- ]) {
434
- stdout = listing;
435
- assert.equal((await host.verify()).authenticatedTools, false);
436
- }
437
- }
438
- );
439
395
  if (options.mcpConfig)
440
396
  for (const scope of options.scopes)
441
397
  check(
442
- `${scope}: MCP URL only, foreign server preserved, staged lifecycle`,
398
+ `${scope}: MCP declaration has the correct machine header and preserves foreign fields`,
443
399
  async ({ root, project, deps, target: base }) => {
444
400
  const target = { ...base, component: 'mcp' as const, scope };
445
401
  const adapter = create({ ...deps, target });
@@ -458,14 +414,14 @@ export function hostAdapterConformance(
458
414
  const raw = plan.changes[0]?.content.toString();
459
415
  assert.ok(raw);
460
416
  assert.ok(raw.includes('https://api.mnemonik.dev/mcp'));
461
- const sessionHeader = 'headers = { "x-mcp-session-id" = "{{session_id}}" }\n';
417
+ const sessionHeader = /headers\s*=.*"x-mcp-session-id"\s*=\s*"\{\{session_id\}\}"/u;
462
418
  const withoutSessionHeader =
463
- options.name === 'grok' && config.toml ? raw.replace(sessionHeader, '') : raw;
464
- if (options.name === 'grok') assert.ok(raw.includes(sessionHeader));
465
- assert.doesNotMatch(
466
- withoutSessionHeader,
467
- /token|secret|headers|credential|bearer|runtime|client.?id/i
468
- );
419
+ options.name === 'grok' && config.toml
420
+ ? raw.replace(/,?\s*"x-mcp-session-id"\s*=\s*"\{\{session_id\}\}"/u, '')
421
+ : raw;
422
+ if (options.name === 'grok') assert.match(raw, sessionHeader);
423
+ if (scope === 'user') assert.match(withoutSessionHeader, /x-mnemonik-installation-id/);
424
+ else assert.doesNotMatch(withoutSessionHeader, /x-mnemonik-installation-id/);
469
425
  if (config.toml) assert.ok(raw.startsWith(original));
470
426
  else {
471
427
  const parsed = JSON.parse(raw);
@@ -473,6 +429,13 @@ export function hostAdapterConformance(
473
429
  assert.deepEqual(parsed.mcpServers.mnemonik, {
474
430
  ...(config.type ? { type: config.type } : {}),
475
431
  url: 'https://api.mnemonik.dev/mcp',
432
+ ...(scope === 'user'
433
+ ? {
434
+ headers: {
435
+ 'x-mnemonik-installation-id': '11111111-1111-4111-8111-111111111111',
436
+ },
437
+ }
438
+ : {}),
476
439
  });
477
440
  }
478
441
  const staged: FileChange[] = [];
@@ -487,6 +450,52 @@ export function hostAdapterConformance(
487
450
  await writeChanges(staged);
488
451
  assert.equal((await adapter.inspect()).declarationPresent, true);
489
452
  assert.deepEqual((await adapter.plan()).changes, plan.changes);
453
+
454
+ if (scope === 'user') {
455
+ const installed = await readFile(path, 'utf8');
456
+ if (config.toml)
457
+ await writeFile(
458
+ path,
459
+ installed
460
+ .replace(/,?\s*"x-mnemonik-installation-id"\s*=\s*"[^"]*"/u, '')
461
+ .replace(/\{\s*,/u, '{')
462
+ .replace(/,\s*\}/u, ' }')
463
+ .replace(
464
+ 'url = "https://api.mnemonik.dev/mcp"\n',
465
+ 'url = "https://api.mnemonik.dev/mcp"\ncustom = "keep exactly"\n'
466
+ )
467
+ );
468
+ else {
469
+ const missing = JSON.parse(installed);
470
+ missing.mcpServers.mnemonik.custom = 'keep exactly';
471
+ missing.mcpServers.mnemonik.headers['x-foreign'] = 'keep exactly';
472
+ delete missing.mcpServers.mnemonik.headers['x-mnemonik-installation-id'];
473
+ await writeFile(path, JSON.stringify(missing, null, 2) + '\n');
474
+ }
475
+ const missingHeader = await readFile(path, 'utf8');
476
+ staged.length = 0;
477
+ await adapter.repair(writer, target);
478
+ await writeChanges(staged);
479
+ const repaired = await readFile(path, 'utf8');
480
+ assert.match(repaired, /x-mnemonik-installation-id/);
481
+ assert.match(repaired, /keep exactly/);
482
+ if (options.name === 'grok') assert.match(repaired, /x-mcp-session-id/);
483
+ if (config.toml) {
484
+ const withoutManagedHeader = repaired
485
+ .replace(
486
+ /(?:,\s*)?(?:["']x-mnemonik-installation-id["']|x-mnemonik-installation-id)\s*=\s*["'][^"']*["']\s*,?/u,
487
+ ''
488
+ )
489
+ .replace(/\{\s*,/u, '{')
490
+ .replace(/,\s*\}/u, ' }');
491
+ assert.equal(withoutManagedHeader, missingHeader);
492
+ } else {
493
+ const withoutManagedHeader = JSON.parse(repaired);
494
+ delete withoutManagedHeader.mcpServers.mnemonik.headers['x-mnemonik-installation-id'];
495
+ assert.equal(JSON.stringify(withoutManagedHeader, null, 2) + '\n', missingHeader);
496
+ }
497
+ }
498
+
490
499
  staged.length = 0;
491
500
  await adapter.uninstall(writer, target);
492
501
  await writeChanges(staged);
@@ -13,7 +13,6 @@ const cases: Array<[ReadinessCondition, ReadinessState]> = [
13
13
  [{ kind: 'host_trust_pending', reason: 'Approve Cursor.' }, 'ACTION_REQUIRED'],
14
14
  [{ kind: 'vendor_policy_pending', reason: 'Allow the extension.' }, 'ACTION_REQUIRED'],
15
15
  [{ kind: 'hooks_missing', reason: 'Hook declaration is missing.' }, 'ACTION_REQUIRED'],
16
- [{ kind: 'host_not_connected', reason: 'Codex has not connected.' }, 'LIMITED'],
17
16
  [{ kind: 'scanner_omitted', reason: 'Scanner was omitted.' }, 'LIMITED'],
18
17
  [{ kind: 'project_uncovered', reason: 'Project is outside approved roots.' }, 'LIMITED'],
19
18
  [{ kind: 'post_commit_upload_failed', reason: 'Result upload failed.' }, 'FAILED'],
@@ -100,7 +99,7 @@ test('the CLI sentence names the concrete reason and action', () => {
100
99
  },
101
100
  ])
102
101
  ),
103
- 'Done, with one thing left. Cursor was skipped. Connect it later: mnemonik connect cursor'
102
+ 'Done, with one thing left. Cursor was skipped.'
104
103
  );
105
104
  });
106
105