@mem-weave/server 0.3.3 → 0.3.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/dist/cli-entry.js CHANGED
@@ -5,7 +5,18 @@
5
5
  * This file is the executable for the `memweave` bin command. It parses
6
6
  * argv, dispatches to the right command, and prints the result.
7
7
  */
8
+ import { existsSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+ import { homedir } from 'node:os';
8
11
  import { parseCli, runCli, CliError } from './cli.js';
12
+ // Same fallback as bootstrap.ts: $MEMWEAVE_CONFIG > ~/.memweave/config.jsonc
13
+ function resolveConfigPath() {
14
+ const explicit = process.env.MEMWEAVE_CONFIG;
15
+ if (explicit)
16
+ return explicit;
17
+ const fallback = join(homedir(), '.memweave', 'config.jsonc');
18
+ return existsSync(fallback) ? fallback : undefined;
19
+ }
9
20
  async function main() {
10
21
  let parsed;
11
22
  try {
@@ -23,7 +34,7 @@ async function main() {
23
34
  command: parsed.command,
24
35
  args: parsed.args,
25
36
  env: process.env,
26
- configPath: process.env.MEMWEAVE_CONFIG
37
+ configPath: resolveConfigPath()
27
38
  });
28
39
  // Print message + data
29
40
  if (result.message) {
@@ -76,7 +76,11 @@ export const doctorCommand = async (ctx) => {
76
76
  // 5. LLM
77
77
  if (config.llm.provider === 'openai-compatible') {
78
78
  const hasKey = Boolean(config.llm.apiKey);
79
- results.push({ name: 'llm', ok: hasKey, detail: hasKey ? 'apiKey set' : 'apiKey missing' });
79
+ results.push({
80
+ name: 'llm',
81
+ ok: true,
82
+ detail: hasKey ? 'apiKey set' : 'configured (no apiKey → falls back to noop)'
83
+ });
80
84
  }
81
85
  else {
82
86
  results.push({ name: 'llm', ok: true, detail: 'noop' });
@@ -12,7 +12,7 @@ export const startCommand = async (ctx) => {
12
12
  // Lazy import to avoid pulling fastify in tests of the parser itself.
13
13
  const { createHttpServer } = await import('../server/http.js');
14
14
  const { startConsolidationScheduler } = await import('../server/scheduler.js');
15
- const app = await createHttpServer({ dbPath });
15
+ const app = await createHttpServer({ dbPath, configPath: ctx.configPath });
16
16
  if (config.consolidation.enabled) {
17
17
  startConsolidationScheduler({
18
18
  dbPath,
@@ -2,8 +2,15 @@ import { OpenaiLlmProvider } from './openai.js';
2
2
  import { NoopLlmProvider } from './noop.js';
3
3
  export function createLlmProvider(kind, config) {
4
4
  switch (kind) {
5
- case 'openai-compatible':
6
- return new OpenaiLlmProvider(config);
5
+ case 'openai-compatible': {
6
+ const provider = new OpenaiLlmProvider(config);
7
+ // Graceful degrade: openai-compatible without an apiKey is a noop.
8
+ // This keeps the "no external LLM required" guarantee while still
9
+ // letting users wire the provider in advance of having a key.
10
+ if (!provider.isConfigured)
11
+ return new NoopLlmProvider();
12
+ return provider;
13
+ }
7
14
  case 'noop':
8
15
  return new NoopLlmProvider();
9
16
  default:
@@ -1,7 +1,9 @@
1
1
  import { z } from 'zod';
2
2
  const OpenaiConfigSchema = z.object({
3
3
  baseUrl: z.string().url().default('https://api.openai.com/v1'),
4
- apiKey: z.string(),
4
+ // Optional: if missing/empty, the provider degrades to a noop (matches the
5
+ // "no external LLM required" boundary declared in src/providers/AGENTS.md).
6
+ apiKey: z.string().optional(),
5
7
  model: z.string().default('gpt-4o-mini'),
6
8
  temperature: z.number().min(0).max(2).default(0.2),
7
9
  maxTokens: z.number().int().positive().default(2048)
@@ -11,6 +13,10 @@ export class OpenaiLlmProvider {
11
13
  constructor(raw) {
12
14
  this.config = OpenaiConfigSchema.parse(raw);
13
15
  }
16
+ /** True only when a real remote endpoint is configured. */
17
+ get isConfigured() {
18
+ return typeof this.config.apiKey === 'string' && this.config.apiKey.length > 0;
19
+ }
14
20
  async call(systemPrompt, userPrompt) {
15
21
  try {
16
22
  const url = `${this.config.baseUrl.replace(/\/+$/, '')}/chat/completions`;
@@ -1,8 +1,19 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { homedir } from 'node:os';
1
4
  import { expandPath, loadConfig } from '../core/config.js';
2
5
  import { createHttpServer } from './http.js';
3
6
  import { startConsolidationScheduler } from './scheduler.js';
4
7
  import { logger } from './logger.js';
5
- const configPath = process.env.MEMWEAVE_CONFIG;
8
+ // Resolution order for the config file:
9
+ // 1. $MEMWEAVE_CONFIG (explicit override)
10
+ // 2. ~/.memweave/config.jsonc (the default `memweave init` target)
11
+ // Without step 2, a global `npm i -g @mem-weave/server` install would silently
12
+ // fall back to schema defaults (llm/embedding = noop) and the user has no
13
+ // signal that their config file was ignored.
14
+ const explicit = process.env.MEMWEAVE_CONFIG;
15
+ const fallback = join(homedir(), '.memweave', 'config.jsonc');
16
+ const configPath = explicit ?? (existsSync(fallback) ? fallback : undefined);
6
17
  const config = loadConfig(configPath);
7
18
  const dbPath = expandPath(config.storage.path);
8
19
  const app = await createHttpServer({ dbPath, configPath });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mem-weave/server",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "MemWeave local-first memory infrastructure for AI agents: structured memory, 4-layer retrieval (BM25 + vector + graph + causal), token-budgeted injection, server-side write deduplication, and background consolidation. Server process: Fastify REST API + CLI.",
5
5
  "type": "module",
6
6
  "main": "./dist/cli-entry.js",