@poprobertdaniel/openclaw-memory 0.1.2 → 0.1.3
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/{chunk-VB5GGBGB.js → chunk-7DNVIKQ3.js} +88 -4
- package/dist/chunk-7DNVIKQ3.js.map +1 -0
- package/dist/{chunk-RZPYOMPO.js → chunk-BTR4T5L3.js} +155 -25
- package/dist/chunk-BTR4T5L3.js.map +1 -0
- package/dist/{chunk-L2KRIMDA.cjs → chunk-CSGZH2SG.cjs} +5 -5
- package/dist/chunk-CSGZH2SG.cjs.map +1 -0
- package/dist/{chunk-5SZWJKD5.js → chunk-J2C5USXH.js} +2 -2
- package/dist/{chunk-HPGHPKK3.cjs → chunk-JYQB2DOK.cjs} +159 -29
- package/dist/chunk-JYQB2DOK.cjs.map +1 -0
- package/dist/chunk-LA5OP5VI.cjs.map +1 -1
- package/dist/{chunk-MQEBVCH5.cjs → chunk-NYZMAY73.cjs} +90 -6
- package/dist/chunk-NYZMAY73.cjs.map +1 -0
- package/dist/cli/index.cjs +3 -3
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +3 -3
- package/dist/index.cjs +4 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +52 -2
- package/dist/index.d.ts +52 -2
- package/dist/index.js +3 -3
- package/dist/memory-service-B2BAEKR2.cjs +9 -0
- package/dist/memory-service-B2BAEKR2.cjs.map +1 -0
- package/dist/memory-service-ZTLGPIUH.js +9 -0
- package/dist/{server-D-3OqU-T.d.ts → server-CtNlCow7.d.cts} +50 -0
- package/dist/{server-D-3OqU-T.d.cts → server-CtNlCow7.d.ts} +50 -0
- package/dist/server.cjs +3 -3
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-HPGHPKK3.cjs.map +0 -1
- package/dist/chunk-L2KRIMDA.cjs.map +0 -1
- package/dist/chunk-MQEBVCH5.cjs.map +0 -1
- package/dist/chunk-RZPYOMPO.js.map +0 -1
- package/dist/chunk-VB5GGBGB.js.map +0 -1
- package/dist/memory-service-4ZPYUN4L.js +0 -9
- package/dist/memory-service-LURM3FBB.cjs +0 -9
- package/dist/memory-service-LURM3FBB.cjs.map +0 -1
- /package/dist/{chunk-5SZWJKD5.js.map → chunk-J2C5USXH.js.map} +0 -0
- /package/dist/{memory-service-4ZPYUN4L.js.map → memory-service-ZTLGPIUH.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/robertpop/work/personal/openclaw-memory/dist/chunk-LA5OP5VI.cjs","../src/config/index.ts","../src/core/errors.ts","../src/config/defaults.ts"],"names":[],"mappings":"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;AACT,EAAA;AACI,EAAA;AACK,IAAA;AACT,EAAA;AAGM,EAAA;AAGA,EAAA;AAGF,EAAA;AACA,EAAA;AACF,IAAA;AACO,MAAA;AACL,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEI,EAAA;AACA,EAAA;AACG,IAAA;AACH,MAAA;AACF,IAAA;AACM,IAAA;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEI,EAAA;AACA,EAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEI,EAAA;AACA,EAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACL,IAAA;AACM,IAAA;AACA,IAAA;AACA,IAAA;AACJ,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAIgB;AACP,EAAA;AACT;AAIgB;AACR,EAAA;AACJ,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AAEI,EAAA;AACI,IAAA;AACR,EAAA;AACI,EAAA;AACI,IAAA;AACR,EAAA;AACI,EAAA;AACI,IAAA;AACR,EAAA;AACI,EAAA;AACI,IAAA;AACR,EAAA;AAEO,EAAA;AACT;ADZW;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/Users/robertpop/work/personal/openclaw-memory/dist/chunk-LA5OP5VI.cjs","sourcesContent":[null,"import path from \"node:path\";\nimport os from \"node:os\";\nimport fs from \"node:fs\";\nimport type { Tier } from \"../core/types.js\";\nimport { ConfigError } from \"../core/errors.js\";\nimport { DEFAULTS } from \"./defaults.js\";\nimport type { Config, ResolvedConfig, QdrantConfig, AgeConfig, EmbeddingConfig, ExtractionConfig } from \"./schema.js\";\n\nexport type { Config, ResolvedConfig } from \"./schema.js\";\n\n// ── Helper: expand ~ to home dir ────────────────────────────────────────\n\nfunction expandHome(p: string): string {\n if (p.startsWith(\"~/\") || p === \"~\") {\n return path.join(os.homedir(), p.slice(1));\n }\n return p;\n}\n\n// ── Data Directory ──────────────────────────────────────────────────────\n\nexport function getDataDir(): string {\n return process.env.OPENCLAW_MEMORY_DATA_DIR || path.join(os.homedir(), \".openclaw-memory\");\n}\n\nexport function getDefaultSqlitePath(): string {\n return path.join(getDataDir(), \"memory.sqlite\");\n}\n\nexport function getPidFilePath(): string {\n return path.join(getDataDir(), \"server.pid\");\n}\n\n// ── Config File Discovery ───────────────────────────────────────────────\n\nfunction getConfigSearchPaths(cwd: string): string[] {\n return [\n path.join(cwd, \"openclaw-memory.config.ts\"),\n path.join(cwd, \"openclaw-memory.config.js\"),\n path.join(cwd, \"openclaw-memory.config.json\"),\n path.join(getDataDir(), \"config.ts\"),\n path.join(getDataDir(), \"config.json\"),\n ];\n}\n\nasync function loadConfigFile(configPath?: string): Promise<Config | null> {\n const searchPaths = configPath ? [configPath] : getConfigSearchPaths(process.cwd());\n\n for (const p of searchPaths) {\n if (fs.existsSync(p)) {\n if (p.endsWith(\".json\")) {\n const content = fs.readFileSync(p, \"utf-8\");\n return JSON.parse(content) as Config;\n }\n // For .ts and .js files, try dynamic import\n try {\n const mod = await import(p);\n return (mod.default || mod) as Config;\n } catch {\n // Can't import .ts outside of Bun — skip\n }\n }\n }\n\n return null;\n}\n\n// ── Environment Variable Loading ────────────────────────────────────────\n\nfunction loadFromEnv(): Partial<Config> {\n const config: Partial<Config> = {};\n\n // Load .env file if dotenv is available\n try {\n const dotenvPath = path.join(process.cwd(), \".env\");\n if (fs.existsSync(dotenvPath)) {\n const envContent = fs.readFileSync(dotenvPath, \"utf-8\");\n for (const line of envContent.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n const eqIdx = trimmed.indexOf(\"=\");\n if (eqIdx === -1) continue;\n const key = trimmed.slice(0, eqIdx).trim();\n const value = trimmed.slice(eqIdx + 1).trim().replace(/^[\"']|[\"']$/g, \"\");\n if (!process.env[key]) {\n process.env[key] = value;\n }\n }\n }\n } catch {\n // No .env file — that's fine\n }\n\n const env = process.env;\n\n // Tier\n const tier = env.OPENCLAW_MEMORY_TIER || env.MEMORY_TIER;\n if (tier && [\"lite\", \"standard\", \"full\"].includes(tier)) {\n config.tier = tier as Tier;\n }\n\n // Port/Host\n const port = env.OPENCLAW_MEMORY_PORT || env.PORT;\n if (port) config.port = parseInt(port, 10);\n\n const host = env.OPENCLAW_MEMORY_HOST;\n if (host) config.host = host;\n\n // Auth\n const authToken = env.OPENCLAW_MEMORY_AUTH__TOKEN || env.AUTH_TOKEN || env.MEMORY_TOKEN;\n if (authToken) {\n config.auth = { token: authToken, enabled: true };\n }\n\n // SQLite\n const sqlitePath = env.OPENCLAW_MEMORY_SQLITE__PATH || env.SQLITE_PATH;\n if (sqlitePath) {\n config.sqlite = { path: sqlitePath };\n }\n\n // Qdrant\n const qdrantUrl = env.OPENCLAW_MEMORY_QDRANT__URL || env.QDRANT_URL;\n if (qdrantUrl) {\n config.qdrant = {\n url: qdrantUrl,\n collection: env.OPENCLAW_MEMORY_QDRANT__COLLECTION || env.QDRANT_COLLECTION || DEFAULTS.qdrant.collection,\n apiKey: env.OPENCLAW_MEMORY_QDRANT__API_KEY,\n };\n }\n\n // AGE\n const ageHost = env.OPENCLAW_MEMORY_AGE__HOST || env.PGHOST;\n if (ageHost) {\n config.age = {\n host: ageHost,\n port: parseInt(env.OPENCLAW_MEMORY_AGE__PORT || env.PGPORT || String(DEFAULTS.age.port), 10),\n user: env.OPENCLAW_MEMORY_AGE__USER || env.PGUSER || \"\",\n password: env.OPENCLAW_MEMORY_AGE__PASSWORD || env.PGPASSWORD || \"\",\n database: env.OPENCLAW_MEMORY_AGE__DATABASE || env.PGDATABASE || \"\",\n graph: env.OPENCLAW_MEMORY_AGE__GRAPH || env.AGE_GRAPH || DEFAULTS.age.graph,\n };\n }\n\n // Embedding\n const embeddingApiKey = env.OPENCLAW_MEMORY_EMBEDDING__API_KEY || env.OPENROUTER_API_KEY || env.OPENAI_API_KEY;\n if (embeddingApiKey) {\n config.embedding = {\n apiKey: embeddingApiKey,\n baseUrl: env.OPENCLAW_MEMORY_EMBEDDING__BASE_URL || env.EMBEDDING_BASE_URL,\n model: env.OPENCLAW_MEMORY_EMBEDDING__MODEL || env.EMBEDDING_MODEL || DEFAULTS.embedding.model,\n dimensions: parseInt(env.OPENCLAW_MEMORY_EMBEDDING__DIMENSIONS || String(DEFAULTS.embedding.dimensions), 10),\n };\n }\n\n // Extraction\n const extractionApiKey = env.OPENCLAW_MEMORY_EXTRACTION__API_KEY || env.OPENROUTER_API_KEY || env.OPENAI_API_KEY;\n if (extractionApiKey) {\n config.extraction = {\n apiKey: extractionApiKey,\n baseUrl: env.OPENCLAW_MEMORY_EXTRACTION__BASE_URL || env.EXTRACTION_BASE_URL,\n model: env.OPENCLAW_MEMORY_EXTRACTION__MODEL || env.EXTRACTION_MODEL || DEFAULTS.extraction.model,\n enabled: env.OPENCLAW_MEMORY_EXTRACTION__ENABLED !== \"false\",\n };\n }\n\n return config;\n}\n\n// ── Tier Inference ──────────────────────────────────────────────────────\n\nfunction inferTier(config: Partial<Config>): Tier {\n if (config.tier) return config.tier;\n if (config.qdrant && config.age) return \"full\";\n if (config.qdrant) return \"standard\";\n return \"lite\";\n}\n\n// ── Config Resolution ───────────────────────────────────────────────────\n\nexport async function loadConfig(configPath?: string): Promise<ResolvedConfig> {\n // 1. Load from file\n const fileConfig = await loadConfigFile(configPath);\n\n // 2. Load from env\n const envConfig = loadFromEnv();\n\n // 3. Merge: file < env (env wins)\n const merged: Partial<Config> = {\n ...fileConfig,\n ...envConfig,\n // Deep merge nested objects\n auth: { ...fileConfig?.auth, ...envConfig.auth },\n sqlite: { ...fileConfig?.sqlite, ...envConfig.sqlite },\n };\n\n if (envConfig.qdrant || fileConfig?.qdrant) {\n merged.qdrant = { ...fileConfig?.qdrant, ...envConfig.qdrant } as Config[\"qdrant\"];\n }\n if (envConfig.age || fileConfig?.age) {\n merged.age = { ...fileConfig?.age, ...envConfig.age } as Config[\"age\"];\n }\n if (envConfig.embedding || fileConfig?.embedding) {\n merged.embedding = { ...fileConfig?.embedding, ...envConfig.embedding } as Config[\"embedding\"];\n }\n if (envConfig.extraction || fileConfig?.extraction) {\n merged.extraction = { ...fileConfig?.extraction, ...envConfig.extraction } as Config[\"extraction\"];\n }\n\n // 4. Infer tier\n const tier = inferTier(merged);\n\n // 5. Resolve defaults\n const sqlitePath = expandHome(merged.sqlite?.path || DEFAULTS.sqlite.path);\n\n // 6. Resolve optional layers based on tier\n let qdrant: QdrantConfig | null = null;\n if (tier !== \"lite\" && merged.qdrant?.url) {\n qdrant = {\n url: merged.qdrant.url,\n collection: merged.qdrant.collection || DEFAULTS.qdrant.collection,\n apiKey: merged.qdrant.apiKey,\n };\n }\n\n let age: AgeConfig | null = null;\n if (tier === \"full\" && merged.age?.host) {\n if (!merged.age.user || !merged.age.password || !merged.age.database) {\n throw new ConfigError(\"Full tier requires age.user, age.password, and age.database\");\n }\n age = {\n host: merged.age.host,\n port: merged.age.port || DEFAULTS.age.port,\n user: merged.age.user,\n password: merged.age.password,\n database: merged.age.database,\n graph: merged.age.graph || DEFAULTS.age.graph,\n };\n }\n\n let embedding: EmbeddingConfig | null = null;\n if (tier !== \"lite\" && merged.embedding?.apiKey) {\n embedding = {\n apiKey: merged.embedding.apiKey,\n baseUrl: merged.embedding.baseUrl,\n model: merged.embedding.model || DEFAULTS.embedding.model,\n dimensions: merged.embedding.dimensions || DEFAULTS.embedding.dimensions,\n };\n }\n\n let extraction: ExtractionConfig | null = null;\n if (tier !== \"lite\" && merged.extraction?.apiKey) {\n extraction = {\n apiKey: merged.extraction.apiKey,\n baseUrl: merged.extraction.baseUrl,\n model: merged.extraction.model || DEFAULTS.extraction.model,\n enabled: merged.extraction.enabled ?? DEFAULTS.extraction.enabled,\n };\n }\n\n return {\n tier,\n port: merged.port || DEFAULTS.port,\n host: merged.host || DEFAULTS.host,\n auth: {\n token: merged.auth?.token,\n enabled: merged.auth?.enabled ?? DEFAULTS.auth.enabled,\n },\n sqlite: { path: sqlitePath },\n qdrant,\n age,\n embedding,\n extraction,\n agents: merged.agents || fileConfig?.agents || [],\n };\n}\n\n// ── defineConfig helper for config files ────────────────────────────────\n\nexport function defineConfig(config: Config): Config {\n return config;\n}\n\n// ── Config summary (redacted) for logging ───────────────────────────────\n\nexport function configSummary(config: ResolvedConfig): string {\n const lines: string[] = [\n `Tier: ${config.tier}`,\n `Port: ${config.port}`,\n `Host: ${config.host}`,\n `SQLite: ${config.sqlite.path}`,\n `Auth: ${config.auth.enabled ? \"enabled\" : \"disabled\"}`,\n ];\n\n if (config.qdrant) {\n lines.push(`Qdrant: ${config.qdrant.url} (collection: ${config.qdrant.collection})`);\n }\n if (config.age) {\n lines.push(`AGE: ${config.age.host}:${config.age.port}/${config.age.database}`);\n }\n if (config.embedding) {\n lines.push(`Embedding: ${config.embedding.model}${config.embedding.baseUrl ? ` via ${config.embedding.baseUrl}` : \"\"}`);\n }\n if (config.extraction) {\n lines.push(`Extraction: ${config.extraction.model}${config.extraction.enabled ? \"\" : \" (disabled)\"}`);\n }\n\n return lines.join(\"\\n\");\n}\n","// ── Custom Error Classes ────────────────────────────────────────────────\n\nexport class MemoryError extends Error {\n constructor(message: string, public readonly code: string) {\n super(message);\n this.name = \"MemoryError\";\n }\n}\n\nexport class ConfigError extends MemoryError {\n constructor(message: string) {\n super(message, \"CONFIG_ERROR\");\n this.name = \"ConfigError\";\n }\n}\n\nexport class StorageError extends MemoryError {\n constructor(message: string, public readonly layer: \"sqlite\" | \"qdrant\" | \"age\") {\n super(message, \"STORAGE_ERROR\");\n this.name = \"StorageError\";\n }\n}\n\nexport class ValidationError extends MemoryError {\n constructor(message: string, public readonly details?: unknown) {\n super(message, \"VALIDATION_ERROR\");\n this.name = \"ValidationError\";\n }\n}\n\nexport class NotFoundError extends MemoryError {\n constructor(resource: string, id: string) {\n super(`${resource} not found: ${id}`, \"NOT_FOUND\");\n this.name = \"NotFoundError\";\n }\n}\n\nexport class AuthError extends MemoryError {\n constructor(message: string = \"Unauthorized\") {\n super(message, \"AUTH_ERROR\");\n this.name = \"AuthError\";\n }\n}\n","// ── Default Configuration Values ────────────────────────────────────────\n\nexport const DEFAULTS = {\n port: 7777,\n host: \"0.0.0.0\",\n sqlite: {\n path: \"~/.openclaw-memory/memory.sqlite\",\n },\n qdrant: {\n collection: \"openclaw_memories\",\n },\n age: {\n port: 5432,\n graph: \"agent_memory\",\n },\n embedding: {\n model: \"text-embedding-3-small\",\n dimensions: 1536,\n },\n extraction: {\n model: \"gpt-5-nano\",\n enabled: true,\n },\n auth: {\n enabled: true,\n },\n} as const;\n"]}
|
|
1
|
+
{"version":3,"sources":["/private/tmp/openclaw-memory-publish/dist/chunk-LA5OP5VI.cjs","../src/config/index.ts","../src/core/errors.ts","../src/config/defaults.ts"],"names":[],"mappings":"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;AACT,EAAA;AACI,EAAA;AACK,IAAA;AACT,EAAA;AAGM,EAAA;AAGA,EAAA;AAGF,EAAA;AACA,EAAA;AACF,IAAA;AACO,MAAA;AACL,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEI,EAAA;AACA,EAAA;AACG,IAAA;AACH,MAAA;AACF,IAAA;AACM,IAAA;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEI,EAAA;AACA,EAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEI,EAAA;AACA,EAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AAEO,EAAA;AACL,IAAA;AACM,IAAA;AACA,IAAA;AACA,IAAA;AACJ,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAIgB;AACP,EAAA;AACT;AAIgB;AACR,EAAA;AACJ,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AAEI,EAAA;AACI,IAAA;AACR,EAAA;AACI,EAAA;AACI,IAAA;AACR,EAAA;AACI,EAAA;AACI,IAAA;AACR,EAAA;AACI,EAAA;AACI,IAAA;AACR,EAAA;AAEO,EAAA;AACT;ADZW;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/private/tmp/openclaw-memory-publish/dist/chunk-LA5OP5VI.cjs","sourcesContent":[null,"import path from \"node:path\";\nimport os from \"node:os\";\nimport fs from \"node:fs\";\nimport type { Tier } from \"../core/types.js\";\nimport { ConfigError } from \"../core/errors.js\";\nimport { DEFAULTS } from \"./defaults.js\";\nimport type { Config, ResolvedConfig, QdrantConfig, AgeConfig, EmbeddingConfig, ExtractionConfig } from \"./schema.js\";\n\nexport type { Config, ResolvedConfig } from \"./schema.js\";\n\n// ── Helper: expand ~ to home dir ────────────────────────────────────────\n\nfunction expandHome(p: string): string {\n if (p.startsWith(\"~/\") || p === \"~\") {\n return path.join(os.homedir(), p.slice(1));\n }\n return p;\n}\n\n// ── Data Directory ──────────────────────────────────────────────────────\n\nexport function getDataDir(): string {\n return process.env.OPENCLAW_MEMORY_DATA_DIR || path.join(os.homedir(), \".openclaw-memory\");\n}\n\nexport function getDefaultSqlitePath(): string {\n return path.join(getDataDir(), \"memory.sqlite\");\n}\n\nexport function getPidFilePath(): string {\n return path.join(getDataDir(), \"server.pid\");\n}\n\n// ── Config File Discovery ───────────────────────────────────────────────\n\nfunction getConfigSearchPaths(cwd: string): string[] {\n return [\n path.join(cwd, \"openclaw-memory.config.ts\"),\n path.join(cwd, \"openclaw-memory.config.js\"),\n path.join(cwd, \"openclaw-memory.config.json\"),\n path.join(getDataDir(), \"config.ts\"),\n path.join(getDataDir(), \"config.json\"),\n ];\n}\n\nasync function loadConfigFile(configPath?: string): Promise<Config | null> {\n const searchPaths = configPath ? [configPath] : getConfigSearchPaths(process.cwd());\n\n for (const p of searchPaths) {\n if (fs.existsSync(p)) {\n if (p.endsWith(\".json\")) {\n const content = fs.readFileSync(p, \"utf-8\");\n return JSON.parse(content) as Config;\n }\n // For .ts and .js files, try dynamic import\n try {\n const mod = await import(p);\n return (mod.default || mod) as Config;\n } catch {\n // Can't import .ts outside of Bun — skip\n }\n }\n }\n\n return null;\n}\n\n// ── Environment Variable Loading ────────────────────────────────────────\n\nfunction loadFromEnv(): Partial<Config> {\n const config: Partial<Config> = {};\n\n // Load .env file if dotenv is available\n try {\n const dotenvPath = path.join(process.cwd(), \".env\");\n if (fs.existsSync(dotenvPath)) {\n const envContent = fs.readFileSync(dotenvPath, \"utf-8\");\n for (const line of envContent.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n const eqIdx = trimmed.indexOf(\"=\");\n if (eqIdx === -1) continue;\n const key = trimmed.slice(0, eqIdx).trim();\n const value = trimmed.slice(eqIdx + 1).trim().replace(/^[\"']|[\"']$/g, \"\");\n if (!process.env[key]) {\n process.env[key] = value;\n }\n }\n }\n } catch {\n // No .env file — that's fine\n }\n\n const env = process.env;\n\n // Tier\n const tier = env.OPENCLAW_MEMORY_TIER || env.MEMORY_TIER;\n if (tier && [\"lite\", \"standard\", \"full\"].includes(tier)) {\n config.tier = tier as Tier;\n }\n\n // Port/Host\n const port = env.OPENCLAW_MEMORY_PORT || env.PORT;\n if (port) config.port = parseInt(port, 10);\n\n const host = env.OPENCLAW_MEMORY_HOST;\n if (host) config.host = host;\n\n // Auth\n const authToken = env.OPENCLAW_MEMORY_AUTH__TOKEN || env.AUTH_TOKEN || env.MEMORY_TOKEN;\n if (authToken) {\n config.auth = { token: authToken, enabled: true };\n }\n\n // SQLite\n const sqlitePath = env.OPENCLAW_MEMORY_SQLITE__PATH || env.SQLITE_PATH;\n if (sqlitePath) {\n config.sqlite = { path: sqlitePath };\n }\n\n // Qdrant\n const qdrantUrl = env.OPENCLAW_MEMORY_QDRANT__URL || env.QDRANT_URL;\n if (qdrantUrl) {\n config.qdrant = {\n url: qdrantUrl,\n collection: env.OPENCLAW_MEMORY_QDRANT__COLLECTION || env.QDRANT_COLLECTION || DEFAULTS.qdrant.collection,\n apiKey: env.OPENCLAW_MEMORY_QDRANT__API_KEY,\n };\n }\n\n // AGE\n const ageHost = env.OPENCLAW_MEMORY_AGE__HOST || env.PGHOST;\n if (ageHost) {\n config.age = {\n host: ageHost,\n port: parseInt(env.OPENCLAW_MEMORY_AGE__PORT || env.PGPORT || String(DEFAULTS.age.port), 10),\n user: env.OPENCLAW_MEMORY_AGE__USER || env.PGUSER || \"\",\n password: env.OPENCLAW_MEMORY_AGE__PASSWORD || env.PGPASSWORD || \"\",\n database: env.OPENCLAW_MEMORY_AGE__DATABASE || env.PGDATABASE || \"\",\n graph: env.OPENCLAW_MEMORY_AGE__GRAPH || env.AGE_GRAPH || DEFAULTS.age.graph,\n };\n }\n\n // Embedding\n const embeddingApiKey = env.OPENCLAW_MEMORY_EMBEDDING__API_KEY || env.OPENROUTER_API_KEY || env.OPENAI_API_KEY;\n if (embeddingApiKey) {\n config.embedding = {\n apiKey: embeddingApiKey,\n baseUrl: env.OPENCLAW_MEMORY_EMBEDDING__BASE_URL || env.EMBEDDING_BASE_URL,\n model: env.OPENCLAW_MEMORY_EMBEDDING__MODEL || env.EMBEDDING_MODEL || DEFAULTS.embedding.model,\n dimensions: parseInt(env.OPENCLAW_MEMORY_EMBEDDING__DIMENSIONS || String(DEFAULTS.embedding.dimensions), 10),\n };\n }\n\n // Extraction\n const extractionApiKey = env.OPENCLAW_MEMORY_EXTRACTION__API_KEY || env.OPENROUTER_API_KEY || env.OPENAI_API_KEY;\n if (extractionApiKey) {\n config.extraction = {\n apiKey: extractionApiKey,\n baseUrl: env.OPENCLAW_MEMORY_EXTRACTION__BASE_URL || env.EXTRACTION_BASE_URL,\n model: env.OPENCLAW_MEMORY_EXTRACTION__MODEL || env.EXTRACTION_MODEL || DEFAULTS.extraction.model,\n enabled: env.OPENCLAW_MEMORY_EXTRACTION__ENABLED !== \"false\",\n };\n }\n\n return config;\n}\n\n// ── Tier Inference ──────────────────────────────────────────────────────\n\nfunction inferTier(config: Partial<Config>): Tier {\n if (config.tier) return config.tier;\n if (config.qdrant && config.age) return \"full\";\n if (config.qdrant) return \"standard\";\n return \"lite\";\n}\n\n// ── Config Resolution ───────────────────────────────────────────────────\n\nexport async function loadConfig(configPath?: string): Promise<ResolvedConfig> {\n // 1. Load from file\n const fileConfig = await loadConfigFile(configPath);\n\n // 2. Load from env\n const envConfig = loadFromEnv();\n\n // 3. Merge: file < env (env wins)\n const merged: Partial<Config> = {\n ...fileConfig,\n ...envConfig,\n // Deep merge nested objects\n auth: { ...fileConfig?.auth, ...envConfig.auth },\n sqlite: { ...fileConfig?.sqlite, ...envConfig.sqlite },\n };\n\n if (envConfig.qdrant || fileConfig?.qdrant) {\n merged.qdrant = { ...fileConfig?.qdrant, ...envConfig.qdrant } as Config[\"qdrant\"];\n }\n if (envConfig.age || fileConfig?.age) {\n merged.age = { ...fileConfig?.age, ...envConfig.age } as Config[\"age\"];\n }\n if (envConfig.embedding || fileConfig?.embedding) {\n merged.embedding = { ...fileConfig?.embedding, ...envConfig.embedding } as Config[\"embedding\"];\n }\n if (envConfig.extraction || fileConfig?.extraction) {\n merged.extraction = { ...fileConfig?.extraction, ...envConfig.extraction } as Config[\"extraction\"];\n }\n\n // 4. Infer tier\n const tier = inferTier(merged);\n\n // 5. Resolve defaults\n const sqlitePath = expandHome(merged.sqlite?.path || DEFAULTS.sqlite.path);\n\n // 6. Resolve optional layers based on tier\n let qdrant: QdrantConfig | null = null;\n if (tier !== \"lite\" && merged.qdrant?.url) {\n qdrant = {\n url: merged.qdrant.url,\n collection: merged.qdrant.collection || DEFAULTS.qdrant.collection,\n apiKey: merged.qdrant.apiKey,\n };\n }\n\n let age: AgeConfig | null = null;\n if (tier === \"full\" && merged.age?.host) {\n if (!merged.age.user || !merged.age.password || !merged.age.database) {\n throw new ConfigError(\"Full tier requires age.user, age.password, and age.database\");\n }\n age = {\n host: merged.age.host,\n port: merged.age.port || DEFAULTS.age.port,\n user: merged.age.user,\n password: merged.age.password,\n database: merged.age.database,\n graph: merged.age.graph || DEFAULTS.age.graph,\n };\n }\n\n let embedding: EmbeddingConfig | null = null;\n if (tier !== \"lite\" && merged.embedding?.apiKey) {\n embedding = {\n apiKey: merged.embedding.apiKey,\n baseUrl: merged.embedding.baseUrl,\n model: merged.embedding.model || DEFAULTS.embedding.model,\n dimensions: merged.embedding.dimensions || DEFAULTS.embedding.dimensions,\n };\n }\n\n let extraction: ExtractionConfig | null = null;\n if (tier !== \"lite\" && merged.extraction?.apiKey) {\n extraction = {\n apiKey: merged.extraction.apiKey,\n baseUrl: merged.extraction.baseUrl,\n model: merged.extraction.model || DEFAULTS.extraction.model,\n enabled: merged.extraction.enabled ?? DEFAULTS.extraction.enabled,\n };\n }\n\n return {\n tier,\n port: merged.port || DEFAULTS.port,\n host: merged.host || DEFAULTS.host,\n auth: {\n token: merged.auth?.token,\n enabled: merged.auth?.enabled ?? DEFAULTS.auth.enabled,\n },\n sqlite: { path: sqlitePath },\n qdrant,\n age,\n embedding,\n extraction,\n agents: merged.agents || fileConfig?.agents || [],\n };\n}\n\n// ── defineConfig helper for config files ────────────────────────────────\n\nexport function defineConfig(config: Config): Config {\n return config;\n}\n\n// ── Config summary (redacted) for logging ───────────────────────────────\n\nexport function configSummary(config: ResolvedConfig): string {\n const lines: string[] = [\n `Tier: ${config.tier}`,\n `Port: ${config.port}`,\n `Host: ${config.host}`,\n `SQLite: ${config.sqlite.path}`,\n `Auth: ${config.auth.enabled ? \"enabled\" : \"disabled\"}`,\n ];\n\n if (config.qdrant) {\n lines.push(`Qdrant: ${config.qdrant.url} (collection: ${config.qdrant.collection})`);\n }\n if (config.age) {\n lines.push(`AGE: ${config.age.host}:${config.age.port}/${config.age.database}`);\n }\n if (config.embedding) {\n lines.push(`Embedding: ${config.embedding.model}${config.embedding.baseUrl ? ` via ${config.embedding.baseUrl}` : \"\"}`);\n }\n if (config.extraction) {\n lines.push(`Extraction: ${config.extraction.model}${config.extraction.enabled ? \"\" : \" (disabled)\"}`);\n }\n\n return lines.join(\"\\n\");\n}\n","// ── Custom Error Classes ────────────────────────────────────────────────\n\nexport class MemoryError extends Error {\n constructor(message: string, public readonly code: string) {\n super(message);\n this.name = \"MemoryError\";\n }\n}\n\nexport class ConfigError extends MemoryError {\n constructor(message: string) {\n super(message, \"CONFIG_ERROR\");\n this.name = \"ConfigError\";\n }\n}\n\nexport class StorageError extends MemoryError {\n constructor(message: string, public readonly layer: \"sqlite\" | \"qdrant\" | \"age\") {\n super(message, \"STORAGE_ERROR\");\n this.name = \"StorageError\";\n }\n}\n\nexport class ValidationError extends MemoryError {\n constructor(message: string, public readonly details?: unknown) {\n super(message, \"VALIDATION_ERROR\");\n this.name = \"ValidationError\";\n }\n}\n\nexport class NotFoundError extends MemoryError {\n constructor(resource: string, id: string) {\n super(`${resource} not found: ${id}`, \"NOT_FOUND\");\n this.name = \"NotFoundError\";\n }\n}\n\nexport class AuthError extends MemoryError {\n constructor(message: string = \"Unauthorized\") {\n super(message, \"AUTH_ERROR\");\n this.name = \"AuthError\";\n }\n}\n","// ── Default Configuration Values ────────────────────────────────────────\n\nexport const DEFAULTS = {\n port: 7777,\n host: \"0.0.0.0\",\n sqlite: {\n path: \"~/.openclaw-memory/memory.sqlite\",\n },\n qdrant: {\n collection: \"openclaw_memories\",\n },\n age: {\n port: 5432,\n graph: \"agent_memory\",\n },\n embedding: {\n model: \"text-embedding-3-small\",\n dimensions: 1536,\n },\n extraction: {\n model: \"gpt-5-nano\",\n enabled: true,\n },\n auth: {\n enabled: true,\n },\n} as const;\n"]}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
var _chunkJYQB2DOKcjs = require('./chunk-JYQB2DOK.cjs');
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
|
|
@@ -146,7 +147,7 @@ function memoriesRoutes(orchestrator) {
|
|
|
146
147
|
// src/api/search.ts
|
|
147
148
|
|
|
148
149
|
function searchRoutes(orchestrator) {
|
|
149
|
-
const searchEngine = new (0,
|
|
150
|
+
const searchEngine = new (0, _chunkJYQB2DOKcjs.SearchEngine)(orchestrator);
|
|
150
151
|
return new (0, _elysia.Elysia)({ prefix: "/api/search" }).post(
|
|
151
152
|
"/",
|
|
152
153
|
async ({ body, set }) => {
|
|
@@ -296,7 +297,7 @@ function searchRoutes(orchestrator) {
|
|
|
296
297
|
// src/api/conversations.ts
|
|
297
298
|
|
|
298
299
|
function conversationRoutes(orchestrator, config) {
|
|
299
|
-
const summarizer = config.extraction ? new (0,
|
|
300
|
+
const summarizer = config.extraction ? new (0, _chunkJYQB2DOKcjs.ConversationSummarizer)({
|
|
300
301
|
apiKey: config.extraction.apiKey,
|
|
301
302
|
baseUrl: config.extraction.baseUrl,
|
|
302
303
|
model: config.extraction.model
|
|
@@ -503,7 +504,63 @@ function adminRoutes(orchestrator) {
|
|
|
503
504
|
details: error instanceof Error ? error.message : String(error)
|
|
504
505
|
};
|
|
505
506
|
}
|
|
506
|
-
}).
|
|
507
|
+
}).post(
|
|
508
|
+
"/api/admin/resync",
|
|
509
|
+
async ({ query, set }) => {
|
|
510
|
+
const layerParam = parseLayerParam(query.layer);
|
|
511
|
+
if (!layerParam) {
|
|
512
|
+
set.status = 400;
|
|
513
|
+
return {
|
|
514
|
+
error: "Invalid layer parameter",
|
|
515
|
+
details: "Use ?layer=qdrant|age|both"
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
const batch = parseBatchParam(query.batch);
|
|
519
|
+
if (batch === null) {
|
|
520
|
+
set.status = 400;
|
|
521
|
+
return {
|
|
522
|
+
error: "Invalid batch parameter",
|
|
523
|
+
details: "Use a positive integer for ?batch=50"
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
const layers = layerParam === "both" ? ["qdrant", "age"] : [layerParam];
|
|
527
|
+
try {
|
|
528
|
+
const memories = listAllMemories(orchestrator);
|
|
529
|
+
let queuedItems = 0;
|
|
530
|
+
for (const memory of memories) {
|
|
531
|
+
for (const layer of layers) {
|
|
532
|
+
orchestrator.sqlite.addToSyncQueue(memory.id, layer, "upsert");
|
|
533
|
+
queuedItems++;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
const syncResults = await _chunkJYQB2DOKcjs.SyncQueueProcessor.withBatchSize(batch, async () => {
|
|
537
|
+
return await orchestrator.retrySyncQueue();
|
|
538
|
+
});
|
|
539
|
+
return {
|
|
540
|
+
layer: layerParam,
|
|
541
|
+
layers,
|
|
542
|
+
batch,
|
|
543
|
+
memory_count: memories.length,
|
|
544
|
+
queued_items: queuedItems,
|
|
545
|
+
sync_results: syncResults
|
|
546
|
+
};
|
|
547
|
+
} catch (error) {
|
|
548
|
+
set.status = 500;
|
|
549
|
+
return {
|
|
550
|
+
error: "Resync failed",
|
|
551
|
+
details: error instanceof Error ? error.message : String(error)
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
query: _elysia.t.Object({
|
|
557
|
+
layer: _elysia.t.Optional(
|
|
558
|
+
_elysia.t.Union([_elysia.t.Literal("qdrant"), _elysia.t.Literal("age"), _elysia.t.Literal("both")])
|
|
559
|
+
),
|
|
560
|
+
batch: _elysia.t.Optional(_elysia.t.String())
|
|
561
|
+
})
|
|
562
|
+
}
|
|
563
|
+
).get("/api/sync/queue", () => {
|
|
507
564
|
const items = orchestrator.sqlite.getSyncQueue(100);
|
|
508
565
|
return { items, count: items.length };
|
|
509
566
|
}).post(
|
|
@@ -532,6 +589,33 @@ function adminRoutes(orchestrator) {
|
|
|
532
589
|
return { error: "Not yet implemented" };
|
|
533
590
|
});
|
|
534
591
|
}
|
|
592
|
+
function parseLayerParam(layer) {
|
|
593
|
+
if (!layer) return "both";
|
|
594
|
+
if (layer === "qdrant" || layer === "age" || layer === "both") return layer;
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
function parseBatchParam(batch) {
|
|
598
|
+
if (!batch) return 50;
|
|
599
|
+
const parsed = Number.parseInt(batch, 10);
|
|
600
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return null;
|
|
601
|
+
return parsed;
|
|
602
|
+
}
|
|
603
|
+
function listAllMemories(orchestrator) {
|
|
604
|
+
const pageSize = 5e3;
|
|
605
|
+
const memories = [];
|
|
606
|
+
let offset = 0;
|
|
607
|
+
while (true) {
|
|
608
|
+
const page = orchestrator.sqlite.listMemories({
|
|
609
|
+
limit: pageSize,
|
|
610
|
+
offset,
|
|
611
|
+
order: "asc"
|
|
612
|
+
});
|
|
613
|
+
memories.push(...page);
|
|
614
|
+
if (page.length < pageSize) break;
|
|
615
|
+
offset += pageSize;
|
|
616
|
+
}
|
|
617
|
+
return memories;
|
|
618
|
+
}
|
|
535
619
|
async function migrateMarkdownFiles(orchestrator, request) {
|
|
536
620
|
const { markdown_paths, agent_id, dry_run } = request;
|
|
537
621
|
let migrated = 0;
|
|
@@ -706,7 +790,7 @@ function createApp(orchestrator, config) {
|
|
|
706
790
|
// src/server.ts
|
|
707
791
|
async function createServer(configPath) {
|
|
708
792
|
const config = await _chunkLA5OP5VIcjs.loadConfig.call(void 0, configPath);
|
|
709
|
-
const orchestrator = new (0,
|
|
793
|
+
const orchestrator = new (0, _chunkJYQB2DOKcjs.StorageOrchestrator)(config);
|
|
710
794
|
await orchestrator.init();
|
|
711
795
|
const app = createApp(orchestrator, config);
|
|
712
796
|
return { app, orchestrator, config };
|
|
@@ -755,4 +839,4 @@ if (isMainModule()) {
|
|
|
755
839
|
|
|
756
840
|
|
|
757
841
|
exports.createApp = createApp; exports.createServer = createServer;
|
|
758
|
-
//# sourceMappingURL=chunk-
|
|
842
|
+
//# sourceMappingURL=chunk-NYZMAY73.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/private/tmp/openclaw-memory-publish/dist/chunk-NYZMAY73.cjs","../src/server.ts","../src/api/router.ts","../src/api/memories.ts","../src/api/search.ts","../src/api/conversations.ts","../src/api/entities.ts","../src/api/admin.ts"],"names":["Elysia","t","path"],"mappings":"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","file":"/private/tmp/openclaw-memory-publish/dist/chunk-NYZMAY73.cjs","sourcesContent":[null,"import { fileURLToPath } from \"node:url\";\nimport { resolve } from \"node:path\";\nimport { loadConfig, configSummary } from \"./config/index.js\";\nimport { StorageOrchestrator } from \"./storage/orchestrator.js\";\nimport { createApp } from \"./api/router.js\";\n\n// ── Server Entry Point ──────────────────────────────────────────────────\n\nexport async function createServer(configPath?: string) {\n const config = await loadConfig(configPath);\n const orchestrator = new StorageOrchestrator(config);\n await orchestrator.init();\n\n const app = createApp(orchestrator, config);\n\n return { app, orchestrator, config };\n}\n\n// ── Main ────────────────────────────────────────────────────────────────\n\nasync function main() {\n console.log(\"┌──────────────────────────────────────────┐\");\n console.log(\"│ openclaw-memory service v0.1.0 │\");\n console.log(\"│ Triple-Layer Memory System │\");\n console.log(\"└──────────────────────────────────────────┘\");\n\n const { app, orchestrator, config } = await createServer();\n\n console.log(\"[config]\");\n console.log(configSummary(config).split(\"\\n\").map((l) => ` ${l}`).join(\"\\n\"));\n\n app.listen(config.port);\n console.log(`[server] Listening on http://${config.host}:${config.port}`);\n console.log(`[server] Health check: http://localhost:${config.port}/api/health`);\n\n const shutdown = async () => {\n console.log(\"\\n[server] Shutting down...\");\n await orchestrator.close();\n process.exit(0);\n };\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n}\n\n// Run if executed directly (works in both Bun and Node ESM)\nfunction isMainModule(): boolean {\n try {\n // Bun runtime\n const bun = (globalThis as typeof globalThis & { Bun?: { main?: string } }).Bun;\n if (typeof bun !== \"undefined\") {\n return bun.main === fileURLToPath(import.meta.url);\n }\n // Node.js\n if (process.argv[1]) {\n return resolve(process.argv[1]) === resolve(fileURLToPath(import.meta.url));\n }\n return false;\n } catch {\n return false;\n }\n}\n\nif (isMainModule()) {\n main().catch((error) => {\n console.error(\"[fatal]\", error);\n process.exit(1);\n });\n}\n","import { Elysia } from \"elysia\";\nimport { cors } from \"@elysiajs/cors\";\nimport type { ResolvedConfig } from \"../config/index.js\";\nimport type { StorageOrchestrator } from \"../storage/orchestrator.js\";\nimport { AuthError, MemoryError, NotFoundError, ValidationError } from \"../core/errors.js\";\nimport { memoriesRoutes } from \"./memories.js\";\nimport { searchRoutes } from \"./search.js\";\nimport { conversationRoutes } from \"./conversations.js\";\nimport { entityRoutes } from \"./entities.js\";\nimport { adminRoutes } from \"./admin.js\";\n\n// ── App Router ──────────────────────────────────────────────────────────\n\nexport function createApp(orchestrator: StorageOrchestrator, config: ResolvedConfig) {\n const app = new Elysia()\n .use(cors())\n\n // ── Auth Middleware ────────────────────────────────────────────────\n .derive(({ headers, set, path }) => {\n // Skip auth for health check\n if (path === \"/api/health\" || path === \"/health\") return {};\n\n // Skip auth if disabled\n if (!config.auth.enabled || !config.auth.token) return {};\n\n const authHeader = headers.authorization;\n if (!authHeader || !authHeader.startsWith(\"Bearer \")) {\n set.status = 401;\n throw new AuthError(\"Missing or invalid Authorization header\");\n }\n\n const token = authHeader.slice(7);\n if (token !== config.auth.token) {\n set.status = 403;\n throw new AuthError(\"Invalid token\");\n }\n\n return {};\n })\n\n // ── Error Handler ─────────────────────────────────────────────────\n .onError(({ code, error, set }) => {\n // Handle AuthError\n if (error instanceof AuthError) {\n const status = error.message.includes(\"Invalid token\") ? 403 : 401;\n set.status = status;\n return { error: error.message, code: error.code };\n }\n\n // Handle other custom errors\n if (error instanceof NotFoundError) {\n set.status = 404;\n return { error: error.message, code: error.code };\n }\n\n if (error instanceof ValidationError) {\n set.status = 400;\n return { error: error.message, code: error.code, details: error.details };\n }\n\n if (error instanceof MemoryError) {\n set.status = 500;\n return { error: error.message, code: error.code };\n }\n\n // Elysia validation errors\n if (code === \"VALIDATION\") {\n const msg = error && \"message\" in error ? (error as Error).message : String(error);\n set.status = 400;\n return { error: \"Validation error\", details: msg };\n }\n\n // Elysia built-in not found errors\n if (code === \"NOT_FOUND\") {\n const msg =\n error && \"message\" in error ? (error as Error).message : \"Route not found\";\n set.status = 404;\n return { error: msg, code };\n }\n\n // Unknown errors\n const msg = error instanceof Error ? error.stack || error.message : String(error);\n console.error(`[api] Unhandled error: ${msg}`);\n set.status = 500;\n return { error: \"Internal server error\" };\n })\n\n // ── Mount Routes ──────────────────────────────────────────────────\n .use(memoriesRoutes(orchestrator))\n .use(searchRoutes(orchestrator))\n .use(conversationRoutes(orchestrator, config))\n .use(entityRoutes(orchestrator))\n .use(adminRoutes(orchestrator));\n\n return app;\n}\n","import { Elysia, t } from \"elysia\";\nimport type { StorageOrchestrator } from \"../storage/orchestrator.js\";\nimport type { CreateMemoryRequest, UpdateMemoryRequest, MemoryScope, MemorySource } from \"../core/types.js\";\n\n// ── Memory CRUD Routes ──────────────────────────────────────────────────\n\nexport function memoriesRoutes(orchestrator: StorageOrchestrator) {\n return new Elysia({ prefix: \"/api/memories\" })\n // POST /api/memories — Create a new memory\n .post(\n \"/\",\n async ({ body, set }) => {\n try {\n const result = await orchestrator.createMemory(body as CreateMemoryRequest);\n set.status = 201;\n return result;\n } catch (error) {\n set.status = 500;\n return {\n error: \"Failed to create memory\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n agent_id: t.String(),\n scope: t.Union([\n t.Literal(\"user\"),\n t.Literal(\"agent\"),\n t.Literal(\"global\"),\n t.Literal(\"project\"),\n t.Literal(\"session\"),\n ]),\n subject_id: t.Optional(t.Nullable(t.String())),\n content: t.String(),\n tags: t.Optional(t.Array(t.String())),\n source: t.Optional(\n t.Union([\n t.Literal(\"explicit\"),\n t.Literal(\"derived\"),\n t.Literal(\"observation\"),\n t.Literal(\"conversation_summary\"),\n t.Literal(\"entity_extraction\"),\n t.Literal(\"daily_digest\"),\n t.Literal(\"migration\"),\n ])\n ),\n created_by: t.Optional(t.Nullable(t.String())),\n extract_entities: t.Optional(t.Boolean()),\n expires_at: t.Optional(t.Nullable(t.String())),\n }),\n }\n )\n\n // GET /api/memories/:id — Get a memory by ID\n .get(\"/:id\", ({ params, set }) => {\n const memory = orchestrator.sqlite.getMemory(params.id);\n if (!memory) {\n set.status = 404;\n return { error: \"Memory not found\" };\n }\n return memory;\n })\n\n // PUT /api/memories/:id — Update a memory\n .put(\n \"/:id\",\n async ({ params, body, set }) => {\n try {\n const result = await orchestrator.updateMemory(\n params.id,\n body as UpdateMemoryRequest\n );\n if (!result) {\n set.status = 404;\n return { error: \"Memory not found\" };\n }\n return result;\n } catch (error) {\n set.status = 500;\n return {\n error: \"Failed to update memory\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n content: t.Optional(t.String()),\n tags: t.Optional(t.Array(t.String())),\n scope: t.Optional(\n t.Union([\n t.Literal(\"user\"),\n t.Literal(\"agent\"),\n t.Literal(\"global\"),\n t.Literal(\"project\"),\n t.Literal(\"session\"),\n ])\n ),\n subject_id: t.Optional(t.Nullable(t.String())),\n expires_at: t.Optional(t.Nullable(t.String())),\n extract_entities: t.Optional(t.Boolean()),\n }),\n }\n )\n\n // DELETE /api/memories/:id — Delete a memory\n .delete(\"/:id\", async ({ params, set }) => {\n try {\n const deleted = await orchestrator.deleteMemory(params.id);\n if (!deleted) {\n set.status = 404;\n return { error: \"Memory not found\" };\n }\n return { deleted: true, id: params.id };\n } catch (error) {\n set.status = 500;\n return {\n error: \"Failed to delete memory\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n })\n\n // GET /api/memories — List memories with filters\n .get(\"/\", ({ query }) => {\n const memories = orchestrator.sqlite.listMemories({\n agent_id: query.agent_id as string | undefined,\n scope: query.scope as MemoryScope | undefined,\n subject_id: query.subject_id as string | undefined,\n source: query.source as MemorySource | undefined,\n tags: query.tags as string | undefined,\n limit: query.limit ? parseInt(String(query.limit), 10) : undefined,\n offset: query.offset ? parseInt(String(query.offset), 10) : undefined,\n order: (query.order as \"asc\" | \"desc\") || \"desc\",\n });\n return { memories, count: memories.length };\n });\n}\n","import { Elysia, t } from \"elysia\";\nimport { SearchEngine } from \"../search/engine.js\";\nimport type { StorageOrchestrator } from \"../storage/orchestrator.js\";\nimport type { SearchRequest } from \"../core/types.js\";\n\n// ── Search Routes ───────────────────────────────────────────────────────\n\nexport function searchRoutes(orchestrator: StorageOrchestrator) {\n const searchEngine = new SearchEngine(orchestrator);\n\n return new Elysia({ prefix: \"/api/search\" })\n // POST /api/search — Smart search (auto-selects layers)\n .post(\n \"/\",\n async ({ body, set }) => {\n try {\n const req = body as SearchRequest;\n if (!req.agent_id) req.cross_agent = true;\n return await searchEngine.search(req);\n } catch (error) {\n set.status = 500;\n return {\n error: \"Search failed\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n agent_id: t.Optional(t.String()),\n query: t.String(),\n scopes: t.Optional(\n t.Array(\n t.Union([\n t.Literal(\"user\"),\n t.Literal(\"agent\"),\n t.Literal(\"global\"),\n t.Literal(\"project\"),\n t.Literal(\"session\"),\n ])\n )\n ),\n subject_id: t.Optional(t.Nullable(t.String())),\n limit: t.Optional(t.Number()),\n include_graph: t.Optional(t.Boolean()),\n cross_agent: t.Optional(t.Boolean()),\n strategy: t.Optional(\n t.Union([\n t.Literal(\"auto\"),\n t.Literal(\"semantic\"),\n t.Literal(\"fulltext\"),\n t.Literal(\"graph\"),\n t.Literal(\"all\"),\n ])\n ),\n }),\n }\n )\n\n // POST /api/search/semantic — Force Qdrant semantic search\n .post(\n \"/semantic\",\n async ({ body, set }) => {\n try {\n return await searchEngine.search({\n ...(body as SearchRequest),\n strategy: \"semantic\",\n });\n } catch (error) {\n set.status = 500;\n return {\n error: \"Semantic search failed\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n agent_id: t.Optional(t.String()),\n query: t.String(),\n scopes: t.Optional(\n t.Array(\n t.Union([\n t.Literal(\"user\"),\n t.Literal(\"agent\"),\n t.Literal(\"global\"),\n t.Literal(\"project\"),\n t.Literal(\"session\"),\n ])\n )\n ),\n subject_id: t.Optional(t.Nullable(t.String())),\n limit: t.Optional(t.Number()),\n cross_agent: t.Optional(t.Boolean()),\n }),\n }\n )\n\n // POST /api/search/graph — Force AGE graph traversal\n .post(\n \"/graph\",\n async ({ body, set }) => {\n try {\n return await searchEngine.search({\n ...(body as SearchRequest),\n strategy: \"graph\",\n include_graph: true,\n });\n } catch (error) {\n set.status = 500;\n return {\n error: \"Graph search failed\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n agent_id: t.Optional(t.String()),\n query: t.String(),\n scopes: t.Optional(\n t.Array(\n t.Union([\n t.Literal(\"user\"),\n t.Literal(\"agent\"),\n t.Literal(\"global\"),\n t.Literal(\"project\"),\n t.Literal(\"session\"),\n ])\n )\n ),\n subject_id: t.Optional(t.Nullable(t.String())),\n limit: t.Optional(t.Number()),\n }),\n }\n )\n\n // POST /api/search/fulltext — Force SQLite FTS search\n .post(\n \"/fulltext\",\n async ({ body, set }) => {\n try {\n return await searchEngine.search({\n ...(body as SearchRequest),\n strategy: \"fulltext\",\n include_graph: false,\n });\n } catch (error) {\n set.status = 500;\n return {\n error: \"Fulltext search failed\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n agent_id: t.Optional(t.String()),\n query: t.String(),\n scopes: t.Optional(t.Array(t.String())),\n subject_id: t.Optional(t.Nullable(t.String())),\n limit: t.Optional(t.Number()),\n }),\n }\n );\n}\n","import { Elysia, t } from \"elysia\";\nimport type { StorageOrchestrator } from \"../storage/orchestrator.js\";\nimport type { ResolvedConfig } from \"../config/index.js\";\nimport { ConversationSummarizer } from \"../extraction/summarizer.js\";\nimport type { ConversationLogEntry, CreateMemoryRequest, SummarizeResponse } from \"../core/types.js\";\n\n// ── Conversation Routes ─────────────────────────────────────────────────\n\nexport function conversationRoutes(\n orchestrator: StorageOrchestrator,\n config: ResolvedConfig\n) {\n // Summarizer is only available if extraction config is present\n const summarizer = config.extraction\n ? new ConversationSummarizer({\n apiKey: config.extraction.apiKey,\n baseUrl: config.extraction.baseUrl,\n model: config.extraction.model,\n })\n : null;\n\n return new Elysia({ prefix: \"/api/conversations\" })\n // POST /api/conversations/log — Append conversation entry\n .post(\n \"/log\",\n ({ body, set }) => {\n try {\n orchestrator.sqlite.appendConversationLog(body as ConversationLogEntry);\n set.status = 201;\n return { ok: true };\n } catch (error) {\n set.status = 500;\n return {\n error: \"Failed to append conversation log\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n agent_id: t.String(),\n session_id: t.String(),\n user_id: t.String(),\n channel: t.String(),\n role: t.Union([\n t.Literal(\"user\"),\n t.Literal(\"assistant\"),\n t.Literal(\"system\"),\n ]),\n content: t.String(),\n timestamp: t.String(),\n }),\n }\n )\n\n // POST /api/conversations/summarize — Summarize a session\n .post(\n \"/summarize\",\n async ({ body, set }) => {\n if (!summarizer) {\n set.status = 501;\n return { error: \"Summarization not available — extraction config required\" };\n }\n\n try {\n const { agent_id, session_id, user_id, channel, messages } = body;\n\n const summary = await summarizer.summarize(messages);\n if (!summary) {\n set.status = 422;\n return { error: \"Failed to generate summary\" };\n }\n\n const memoryReq: CreateMemoryRequest = {\n agent_id,\n scope: \"session\",\n subject_id: user_id,\n content: summary,\n tags: [\"conversation_summary\", channel, `session:${session_id}`],\n source: \"conversation_summary\",\n created_by: agent_id,\n extract_entities: true,\n };\n\n const result = await orchestrator.createMemory(memoryReq);\n\n const response: SummarizeResponse = {\n memory_id: result.id,\n summary,\n entities_extracted: result.entities,\n relationships_created: 0,\n };\n\n set.status = 201;\n return response;\n } catch (error) {\n set.status = 500;\n return {\n error: \"Summarization failed\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n agent_id: t.String(),\n session_id: t.String(),\n user_id: t.String(),\n channel: t.String(),\n messages: t.Array(\n t.Object({\n role: t.Union([\n t.Literal(\"user\"),\n t.Literal(\"assistant\"),\n t.Literal(\"system\"),\n ]),\n content: t.String(),\n timestamp: t.String(),\n })\n ),\n reason: t.Optional(t.String()),\n }),\n }\n );\n}\n","import { Elysia, t } from \"elysia\";\nimport type { StorageOrchestrator } from \"../storage/orchestrator.js\";\n\n// ── Entity Routes ───────────────────────────────────────────────────────\n\nexport function entityRoutes(orchestrator: StorageOrchestrator) {\n return new Elysia({ prefix: \"/api/entities\" })\n // GET /api/entities/:type — List entities by type\n .get(\"/:type\", async ({ params, query, set }) => {\n if (!orchestrator.age) {\n set.status = 501;\n return { error: \"Graph layer not available — requires Full tier\" };\n }\n\n try {\n const entities = await orchestrator.age.listEntities(\n params.type,\n query.agent_id as string | undefined,\n query.limit ? parseInt(String(query.limit), 10) : 50\n );\n return { entities, count: entities.length };\n } catch (error) {\n set.status = 500;\n return {\n error: \"Failed to list entities\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n })\n\n // GET /api/entities/:type/:id — Get entity with relationships\n .get(\"/:type/:id\", async ({ params, set }) => {\n if (!orchestrator.age) {\n set.status = 501;\n return { error: \"Graph layer not available — requires Full tier\" };\n }\n\n try {\n const result = await orchestrator.age.getEntityWithRelationships(\n params.type,\n params.id\n );\n if (!result.entity) {\n set.status = 404;\n return { error: \"Entity not found\" };\n }\n return result;\n } catch (error) {\n set.status = 500;\n return {\n error: \"Failed to get entity\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n })\n\n // GET /api/entities/:type/:id/related — Get related entities\n .get(\"/:type/:id/related\", async ({ params, query, set }) => {\n if (!orchestrator.age) {\n set.status = 501;\n return { error: \"Graph layer not available — requires Full tier\" };\n }\n\n try {\n const depth = query.depth ? parseInt(String(query.depth), 10) : 2;\n const related = await orchestrator.age.getRelatedEntities(\n params.id,\n depth\n );\n return { related, count: related.length };\n } catch (error) {\n set.status = 500;\n return {\n error: \"Failed to get related entities\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n })\n\n // POST /api/entities/extract — Manually trigger entity extraction\n .post(\n \"/extract\",\n async ({ body, set }) => {\n if (!orchestrator.entityExtractor) {\n set.status = 501;\n return { error: \"Entity extraction not available — requires extraction config\" };\n }\n\n try {\n const result = await orchestrator.entityExtractor.extract(body.text);\n return result;\n } catch (error) {\n set.status = 500;\n return {\n error: \"Entity extraction failed\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n text: t.String(),\n }),\n }\n );\n}\n","import { Elysia, t } from \"elysia\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { StorageOrchestrator } from \"../storage/orchestrator.js\";\nimport type { CreateMemoryRequest, MigrateMarkdownRequest } from \"../core/types.js\";\nimport { SyncQueueProcessor } from \"../storage/sync-queue.js\";\n\n// ── Admin Routes ────────────────────────────────────────────────────────\n\nexport function adminRoutes(orchestrator: StorageOrchestrator) {\n const healthHandler = async () => {\n return await orchestrator.healthCheck();\n };\n\n return new Elysia()\n // GET /api/health and /health — Health checks\n .get(\"/api/health\", healthHandler)\n .get(\"/health\", healthHandler)\n\n // POST /api/sync/retry — Retry failed L2/L3 syncs\n .post(\"/api/sync/retry\", async ({ set }) => {\n try {\n const result = await orchestrator.retrySyncQueue();\n return result;\n } catch (error) {\n set.status = 500;\n return {\n error: \"Sync retry failed\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n })\n\n // POST /api/admin/resync — Requeue all memories for L2/L3 sync\n .post(\n \"/api/admin/resync\",\n async ({ query, set }) => {\n const layerParam = parseLayerParam(query.layer as string | undefined);\n if (!layerParam) {\n set.status = 400;\n return {\n error: \"Invalid layer parameter\",\n details: \"Use ?layer=qdrant|age|both\",\n };\n }\n\n const batch = parseBatchParam(query.batch as string | undefined);\n if (batch === null) {\n set.status = 400;\n return {\n error: \"Invalid batch parameter\",\n details: \"Use a positive integer for ?batch=50\",\n };\n }\n\n const layers: Array<\"qdrant\" | \"age\"> =\n layerParam === \"both\" ? [\"qdrant\", \"age\"] : [layerParam];\n\n try {\n const memories = listAllMemories(orchestrator);\n let queuedItems = 0;\n\n for (const memory of memories) {\n for (const layer of layers) {\n orchestrator.sqlite.addToSyncQueue(memory.id, layer, \"upsert\");\n queuedItems++;\n }\n }\n\n const syncResults = await SyncQueueProcessor.withBatchSize(batch, async () => {\n return await orchestrator.retrySyncQueue();\n });\n\n return {\n layer: layerParam,\n layers,\n batch,\n memory_count: memories.length,\n queued_items: queuedItems,\n sync_results: syncResults,\n };\n } catch (error) {\n set.status = 500;\n return {\n error: \"Resync failed\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n query: t.Object({\n layer: t.Optional(\n t.Union([t.Literal(\"qdrant\"), t.Literal(\"age\"), t.Literal(\"both\")])\n ),\n batch: t.Optional(t.String()),\n }),\n }\n )\n\n // GET /api/sync/queue — View pending sync queue\n .get(\"/api/sync/queue\", () => {\n const items = orchestrator.sqlite.getSyncQueue(100);\n return { items, count: items.length };\n })\n\n // POST /api/admin/migrate-markdown — Migrate markdown files\n .post(\n \"/api/admin/migrate-markdown\",\n async ({ body, set }) => {\n try {\n const results = await migrateMarkdownFiles(orchestrator, body);\n return results;\n } catch (error) {\n set.status = 500;\n return {\n error: \"Migration failed\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n markdown_paths: t.Array(t.String()),\n agent_id: t.String(),\n dry_run: t.Optional(t.Boolean()),\n }),\n }\n )\n\n // POST /api/admin/daily-digest — Trigger daily digest\n .post(\"/api/admin/daily-digest\", async ({ set }) => {\n set.status = 501;\n return { error: \"Not yet implemented\" };\n });\n}\n\ntype ResyncLayer = \"qdrant\" | \"age\" | \"both\";\n\nfunction parseLayerParam(layer: string | undefined): ResyncLayer | null {\n if (!layer) return \"both\";\n if (layer === \"qdrant\" || layer === \"age\" || layer === \"both\") return layer;\n return null;\n}\n\nfunction parseBatchParam(batch: string | undefined): number | null {\n if (!batch) return 50;\n const parsed = Number.parseInt(batch, 10);\n if (!Number.isFinite(parsed) || parsed <= 0) return null;\n return parsed;\n}\n\nfunction listAllMemories(orchestrator: StorageOrchestrator) {\n const pageSize = 5_000;\n const memories: ReturnType<typeof orchestrator.sqlite.listMemories> = [];\n let offset = 0;\n\n while (true) {\n const page = orchestrator.sqlite.listMemories({\n limit: pageSize,\n offset,\n order: \"asc\",\n });\n\n memories.push(...page);\n if (page.length < pageSize) break;\n offset += pageSize;\n }\n\n return memories;\n}\n\n// ── Markdown Migration ──────────────────────────────────────────────────\n\nasync function migrateMarkdownFiles(\n orchestrator: StorageOrchestrator,\n request: MigrateMarkdownRequest\n): Promise<{\n migrated: number;\n skipped: number;\n errors: string[];\n memories: Array<{ id: string; content_preview: string }>;\n}> {\n const { markdown_paths, agent_id, dry_run } = request;\n let migrated = 0;\n let skipped = 0;\n const errors: string[] = [];\n const memories: Array<{ id: string; content_preview: string }> = [];\n\n for (const filePath of markdown_paths) {\n try {\n if (!fs.existsSync(filePath)) {\n errors.push(`File not found: ${filePath}`);\n skipped++;\n continue;\n }\n\n const content = fs.readFileSync(filePath, \"utf-8\");\n const fileName = path.basename(filePath, \".md\");\n const sections = parseMarkdownSections(content);\n\n for (const section of sections) {\n if (section.content.trim().length < 10) {\n skipped++;\n continue;\n }\n\n if (dry_run) {\n memories.push({\n id: \"(dry-run)\",\n content_preview: section.content.slice(0, 100),\n });\n migrated++;\n continue;\n }\n\n const scope = inferScope(section.heading, fileName);\n const source = inferSource(fileName);\n const tags = inferTags(section.heading, fileName);\n\n const req: CreateMemoryRequest = {\n agent_id,\n scope,\n subject_id: null,\n content: section.content.trim(),\n tags,\n source: source || \"migration\",\n created_by: \"migration\",\n extract_entities: true,\n };\n\n try {\n const result = await orchestrator.createMemory(req);\n memories.push({\n id: result.id,\n content_preview: section.content.slice(0, 100),\n });\n migrated++;\n } catch (error) {\n errors.push(\n `Failed to migrate section \"${section.heading}\": ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n } catch (error) {\n errors.push(\n `Failed to process ${filePath}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n return { migrated, skipped, errors, memories };\n}\n\n// ── Markdown Parsing Helpers ────────────────────────────────────────────\n\ninterface MarkdownSection {\n heading: string;\n content: string;\n level: number;\n}\n\nfunction parseMarkdownSections(markdown: string): MarkdownSection[] {\n const lines = markdown.split(\"\\n\");\n const sections: MarkdownSection[] = [];\n let currentHeading = \"root\";\n let currentLevel = 0;\n let currentContent: string[] = [];\n\n for (const line of lines) {\n const headingMatch = line.match(/^(#{1,3})\\s+(.+)/);\n if (headingMatch) {\n if (currentContent.length > 0) {\n sections.push({\n heading: currentHeading,\n content: currentContent.join(\"\\n\").trim(),\n level: currentLevel,\n });\n }\n currentHeading = headingMatch[2].trim();\n currentLevel = headingMatch[1].length;\n currentContent = [];\n } else {\n currentContent.push(line);\n }\n }\n\n if (currentContent.length > 0) {\n sections.push({\n heading: currentHeading,\n content: currentContent.join(\"\\n\").trim(),\n level: currentLevel,\n });\n }\n\n return sections;\n}\n\nfunction inferScope(heading: string, fileName: string): CreateMemoryRequest[\"scope\"] {\n const h = heading.toLowerCase();\n const f = fileName.toLowerCase();\n\n if (h.includes(\"about\") || h.includes(\"personal\")) return \"user\";\n if (h.includes(\"project\")) return \"project\";\n if (h.includes(\"agent\")) return \"agent\";\n if (h.includes(\"session\") || f.match(/^\\d{4}-\\d{2}-\\d{2}/)) return \"session\";\n return \"global\";\n}\n\nfunction inferSource(fileName: string): CreateMemoryRequest[\"source\"] | null {\n if (fileName.match(/^\\d{4}-\\d{2}-\\d{2}/)) return \"daily_digest\";\n return \"migration\";\n}\n\nfunction inferTags(heading: string, fileName: string): string[] {\n const tags: string[] = [\"migration\"];\n if (fileName.match(/^\\d{4}-\\d{2}-\\d{2}/)) {\n tags.push(\"daily\", fileName);\n }\n if (heading !== \"root\") {\n tags.push(heading.toLowerCase().replace(/[^a-z0-9]+/g, \"-\"));\n }\n return tags;\n}\n"]}
|
package/dist/cli/index.cjs
CHANGED
|
@@ -519,7 +519,7 @@ function storeCommand() {
|
|
|
519
519
|
const result = await apiPost(baseUrl, "/api/memories", body, config.auth.token);
|
|
520
520
|
output(result, opts.format);
|
|
521
521
|
} else {
|
|
522
|
-
const { MemoryService } = await Promise.resolve().then(() => _interopRequireWildcard(require("../memory-service-
|
|
522
|
+
const { MemoryService } = await Promise.resolve().then(() => _interopRequireWildcard(require("../memory-service-B2BAEKR2.cjs")));
|
|
523
523
|
const service = new MemoryService();
|
|
524
524
|
await service.init();
|
|
525
525
|
try {
|
|
@@ -562,7 +562,7 @@ function searchCommand() {
|
|
|
562
562
|
if (serverUp) {
|
|
563
563
|
result = await apiPost(baseUrl, "/api/search", body, config.auth.token);
|
|
564
564
|
} else {
|
|
565
|
-
const { MemoryService } = await Promise.resolve().then(() => _interopRequireWildcard(require("../memory-service-
|
|
565
|
+
const { MemoryService } = await Promise.resolve().then(() => _interopRequireWildcard(require("../memory-service-B2BAEKR2.cjs")));
|
|
566
566
|
const service = new MemoryService();
|
|
567
567
|
await service.init();
|
|
568
568
|
try {
|
|
@@ -618,7 +618,7 @@ function migrateCommand() {
|
|
|
618
618
|
if (serverUp) {
|
|
619
619
|
result = await apiPost(baseUrl, "/api/admin/migrate-markdown", body, config.auth.token);
|
|
620
620
|
} else {
|
|
621
|
-
const { MemoryService } = await Promise.resolve().then(() => _interopRequireWildcard(require("../memory-service-
|
|
621
|
+
const { MemoryService } = await Promise.resolve().then(() => _interopRequireWildcard(require("../memory-service-B2BAEKR2.cjs")));
|
|
622
622
|
const service = new MemoryService();
|
|
623
623
|
await service.init();
|
|
624
624
|
try {
|
package/dist/cli/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/robertpop/work/personal/openclaw-memory/dist/cli/index.cjs","../../src/cli/index.ts","../../src/cli/commands/init.ts","../../src/cli/utils.ts","../../src/cli/commands/start.ts","../../src/cli/commands/stop.ts","../../src/cli/commands/status.ts","../../src/cli/commands/store.ts","../../src/cli/commands/search.ts","../../src/cli/commands/migrate.ts","../../src/cli/commands/infra.ts"],"names":[],"mappings":"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","file":"/Users/robertpop/work/personal/openclaw-memory/dist/cli/index.cjs","sourcesContent":[null,"#!/usr/bin/env node\n\nimport { Command } from \"commander\";\nimport { initCommand } from \"./commands/init.js\";\nimport { startCommand } from \"./commands/start.js\";\nimport { stopCommand } from \"./commands/stop.js\";\nimport { statusCommand } from \"./commands/status.js\";\nimport { storeCommand } from \"./commands/store.js\";\nimport { searchCommand } from \"./commands/search.js\";\nimport { migrateCommand } from \"./commands/migrate.js\";\nimport { infraCommand } from \"./commands/infra.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"openclaw-memory\")\n .description(\"Triple-layer memory system for AI agents — SQLite + Qdrant + Postgres/AGE\")\n .version(\"0.1.0\");\n\n// Register commands\nprogram.addCommand(initCommand());\nprogram.addCommand(startCommand());\nprogram.addCommand(stopCommand());\nprogram.addCommand(statusCommand());\nprogram.addCommand(storeCommand());\nprogram.addCommand(searchCommand());\nprogram.addCommand(migrateCommand());\nprogram.addCommand(infraCommand());\n\nprogram.parse();\n","import { Command } from \"commander\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { header, success, info, warn, error as logError } from \"../utils.js\";\nimport { getDataDir, getDefaultSqlitePath } from \"../../config/index.js\";\n\nexport function initCommand(): Command {\n return new Command(\"init\")\n .description(\"Interactive setup wizard\")\n .option(\"--tier <tier>\", \"Tier: lite, standard, or full\")\n .option(\"--non-interactive\", \"Skip prompts, use defaults + flags\")\n .action(async (opts) => {\n header(\"Setup Wizard\");\n\n // For non-interactive mode, generate config from flags/defaults\n const tier = opts.tier || \"lite\";\n const dataDir = getDataDir();\n const sqlitePath = getDefaultSqlitePath();\n\n // Ensure data directory exists\n fs.mkdirSync(dataDir, { recursive: true });\n info(`Data directory: ${dataDir}`);\n\n // Auto-detect available services\n let qdrantAvailable = false;\n let ageAvailable = false;\n\n if (tier !== \"lite\") {\n info(\"Checking Qdrant connectivity...\");\n try {\n const res = await fetch(\"http://localhost:6333/collections\", {\n signal: AbortSignal.timeout(2000),\n });\n qdrantAvailable = res.ok;\n } catch { /* not available */ }\n\n if (qdrantAvailable) {\n success(\"Qdrant is reachable at http://localhost:6333\");\n } else {\n warn(\"Qdrant not reachable at http://localhost:6333\");\n }\n }\n\n if (tier === \"full\") {\n info(\"Checking PostgreSQL/AGE connectivity...\");\n try {\n // Simple TCP check\n const net = await import(\"node:net\");\n ageAvailable = await new Promise<boolean>((resolve) => {\n const socket = new net.Socket();\n socket.setTimeout(2000);\n socket.connect(5432, \"localhost\", () => {\n socket.destroy();\n resolve(true);\n });\n socket.on(\"error\", () => resolve(false));\n socket.on(\"timeout\", () => { socket.destroy(); resolve(false); });\n });\n } catch { /* not available */ }\n\n if (ageAvailable) {\n success(\"PostgreSQL is reachable at localhost:5432\");\n } else {\n warn(\"PostgreSQL not reachable at localhost:5432\");\n }\n }\n\n // Determine effective tier\n let effectiveTier = tier;\n if (tier === \"full\" && !ageAvailable) {\n effectiveTier = qdrantAvailable ? \"standard\" : \"lite\";\n warn(`Downgrading to ${effectiveTier} tier (missing dependencies)`);\n } else if (tier === \"standard\" && !qdrantAvailable) {\n effectiveTier = \"lite\";\n warn(\"Downgrading to lite tier (Qdrant not available)\");\n }\n\n // Generate config file\n const configContent = generateConfig(effectiveTier, sqlitePath);\n const configPath = path.join(process.cwd(), \"openclaw-memory.config.ts\");\n\n fs.writeFileSync(configPath, configContent, \"utf-8\");\n success(`Config written to ${configPath}`);\n\n // Generate .env.example\n const envExample = generateEnvExample(effectiveTier);\n const envPath = path.join(process.cwd(), \".env.example\");\n fs.writeFileSync(envPath, envExample, \"utf-8\");\n success(`Environment template written to ${envPath}`);\n\n console.log();\n info(\"Next steps:\");\n console.log(\" openclaw-memory start # Start the server\");\n console.log(\" openclaw-memory status # Check all layers\");\n console.log();\n });\n}\n\nfunction generateConfig(tier: string, sqlitePath: string): string {\n const lines = [\n `import { defineConfig } from '@poprobertdaniel/openclaw-memory';`,\n ``,\n `export default defineConfig({`,\n ` tier: '${tier}',`,\n ` port: 7777,`,\n ` auth: {`,\n ` token: process.env.MEMORY_AUTH_TOKEN || 'change-me',`,\n ` },`,\n ` sqlite: {`,\n ` path: '${sqlitePath}',`,\n ` },`,\n ];\n\n if (tier === \"standard\" || tier === \"full\") {\n lines.push(` qdrant: {`);\n lines.push(` url: process.env.QDRANT_URL || 'http://localhost:6333',`);\n lines.push(` collection: 'openclaw_memories',`);\n lines.push(` },`);\n lines.push(` embedding: {`);\n lines.push(` apiKey: process.env.OPENAI_API_KEY || '',`);\n lines.push(` model: 'text-embedding-3-small',`);\n lines.push(` dimensions: 1536,`);\n lines.push(` },`);\n lines.push(` extraction: {`);\n lines.push(` apiKey: process.env.OPENAI_API_KEY || '',`);\n lines.push(` model: 'gpt-5-nano',`);\n lines.push(` enabled: true,`);\n lines.push(` },`);\n }\n\n if (tier === \"full\") {\n lines.push(` age: {`);\n lines.push(` host: process.env.PGHOST || 'localhost',`);\n lines.push(` port: parseInt(process.env.PGPORT || '5432', 10),`);\n lines.push(` user: process.env.PGUSER || 'openclaw',`);\n lines.push(` password: process.env.PGPASSWORD || '',`);\n lines.push(` database: process.env.PGDATABASE || 'agent_memory',`);\n lines.push(` graph: 'agent_memory',`);\n lines.push(` },`);\n }\n\n lines.push(`});`);\n lines.push(``);\n\n return lines.join(\"\\n\");\n}\n\nfunction generateEnvExample(tier: string): string {\n const lines = [\n `# openclaw-memory environment variables`,\n ``,\n `# Server`,\n `# OPENCLAW_MEMORY_PORT=7777`,\n `# OPENCLAW_MEMORY_HOST=0.0.0.0`,\n ``,\n `# Authentication`,\n `MEMORY_AUTH_TOKEN=change-me-to-a-secure-token`,\n ``,\n `# SQLite (always required)`,\n `# SQLITE_PATH=~/.openclaw-memory/memory.sqlite`,\n ``,\n ];\n\n if (tier === \"standard\" || tier === \"full\") {\n lines.push(`# Qdrant (Standard/Full tier)`);\n lines.push(`QDRANT_URL=http://localhost:6333`);\n lines.push(``);\n lines.push(`# Embedding provider`);\n lines.push(`OPENAI_API_KEY=sk-your-key-here`);\n lines.push(`# EMBEDDING_MODEL=text-embedding-3-small`);\n lines.push(`# EMBEDDING_BASE_URL=https://api.openai.com/v1`);\n lines.push(``);\n lines.push(`# Entity extraction`);\n lines.push(`# EXTRACTION_MODEL=gpt-5-nano`);\n lines.push(``);\n }\n\n if (tier === \"full\") {\n lines.push(`# PostgreSQL + Apache AGE (Full tier)`);\n lines.push(`PGHOST=localhost`);\n lines.push(`PGPORT=5432`);\n lines.push(`PGUSER=openclaw`);\n lines.push(`PGPASSWORD=your-password`);\n lines.push(`PGDATABASE=agent_memory`);\n lines.push(`# AGE_GRAPH=agent_memory`);\n lines.push(``);\n }\n\n return lines.join(\"\\n\");\n}\n","import pc from \"picocolors\";\nimport fs from \"node:fs\";\nimport { getPidFilePath } from \"../config/index.js\";\n\n// ── CLI Utilities ───────────────────────────────────────────────────────\n\nexport function log(msg: string): void {\n console.log(msg);\n}\n\nexport function info(msg: string): void {\n console.log(pc.blue(\"ℹ\") + \" \" + msg);\n}\n\nexport function success(msg: string): void {\n console.log(pc.green(\"✓\") + \" \" + msg);\n}\n\nexport function warn(msg: string): void {\n console.log(pc.yellow(\"⚠\") + \" \" + msg);\n}\n\nexport function error(msg: string): void {\n console.error(pc.red(\"✗\") + \" \" + msg);\n}\n\nexport function header(title: string): void {\n console.log();\n console.log(pc.bold(` 🧠 OpenClaw Memory — ${title}`));\n console.log();\n}\n\nexport function bullet(label: string, value: string, status?: \"ok\" | \"error\" | \"disabled\" | \"degraded\"): void {\n const dot = status === \"ok\" ? pc.green(\"●\")\n : status === \"error\" ? pc.red(\"●\")\n : status === \"degraded\" ? pc.yellow(\"●\")\n : status === \"disabled\" ? pc.dim(\"○\")\n : \" \";\n console.log(` ${dot} ${pc.bold(label)} ${value}`);\n}\n\n// ── Server Detection ────────────────────────────────────────────────────\n\nexport function getServerPid(): number | null {\n const pidPath = getPidFilePath();\n if (!fs.existsSync(pidPath)) return null;\n\n try {\n const pid = parseInt(fs.readFileSync(pidPath, \"utf-8\").trim(), 10);\n if (isNaN(pid)) return null;\n\n // Check if process is alive\n try {\n process.kill(pid, 0);\n return pid;\n } catch {\n // Process not running, clean up stale PID file\n fs.unlinkSync(pidPath);\n return null;\n }\n } catch {\n return null;\n }\n}\n\nexport async function isServerRunning(baseUrl: string = \"http://localhost:7777\"): Promise<boolean> {\n try {\n const res = await fetch(`${baseUrl}/api/health`, {\n signal: AbortSignal.timeout(2000),\n });\n return res.ok;\n } catch {\n return false;\n }\n}\n\nexport function getBaseUrl(port?: number): string {\n return `http://localhost:${port || 7777}`;\n}\n\n// ── HTTP Client ─────────────────────────────────────────────────────────\n\nexport async function apiGet(baseUrl: string, path: string, token?: string): Promise<unknown> {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n const res = await fetch(`${baseUrl}${path}`, { headers });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`HTTP ${res.status}: ${text}`);\n }\n return res.json();\n}\n\nexport async function apiPost(baseUrl: string, path: string, body: unknown, token?: string): Promise<unknown> {\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (token) headers.Authorization = `Bearer ${token}`;\n\n const res = await fetch(`${baseUrl}${path}`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`HTTP ${res.status}: ${text}`);\n }\n return res.json();\n}\n\nexport async function apiDelete(baseUrl: string, path: string, token?: string): Promise<unknown> {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n const res = await fetch(`${baseUrl}${path}`, {\n method: \"DELETE\",\n headers,\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`HTTP ${res.status}: ${text}`);\n }\n return res.json();\n}\n\n// ── Output Formatting ───────────────────────────────────────────────────\n\nexport function output(data: unknown, format: \"json\" | \"text\" = \"json\"): void {\n if (format === \"text\") {\n console.log(JSON.stringify(data, null, 2));\n } else {\n console.log(JSON.stringify(data));\n }\n}\n\n// ── Stdin Reading ───────────────────────────────────────────────────────\n\nexport async function readStdin(): Promise<string> {\n if (process.stdin.isTTY) return \"\";\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(Buffer.from(chunk));\n }\n return Buffer.concat(chunks).toString(\"utf-8\").trim();\n}\n","import { Command } from \"commander\";\nimport fs from \"node:fs\";\nimport { spawn } from \"node:child_process\";\nimport { header, success, info, error as logError } from \"../utils.js\";\nimport { getPidFilePath, getDataDir } from \"../../config/index.js\";\n\nexport function startCommand(): Command {\n return new Command(\"start\")\n .description(\"Start the HTTP server\")\n .option(\"-p, --port <port>\", \"Server port\")\n .option(\"--bg\", \"Run in background (daemon mode)\")\n .option(\"--config <path>\", \"Path to config file\")\n .action(async (opts) => {\n if (opts.bg) {\n await startBackground(opts);\n } else {\n await startForeground(opts);\n }\n });\n}\n\nasync function startForeground(opts: { port?: string; config?: string }): Promise<void> {\n // Set port if specified\n if (opts.port) {\n process.env.OPENCLAW_MEMORY_PORT = opts.port;\n process.env.PORT = opts.port;\n }\n\n // Import and run server directly\n const { createServer } = await import(\"../../server.js\");\n const { app, config } = await createServer(opts.config);\n\n const port = opts.port ? parseInt(opts.port, 10) : config.port;\n app.listen(port);\n\n console.log(`[server] Listening on http://0.0.0.0:${port}`);\n\n // Write PID file for status/stop commands\n const dataDir = getDataDir();\n fs.mkdirSync(dataDir, { recursive: true });\n fs.writeFileSync(getPidFilePath(), String(process.pid), \"utf-8\");\n\n const cleanup = () => {\n try { fs.unlinkSync(getPidFilePath()); } catch {}\n process.exit(0);\n };\n\n process.on(\"SIGINT\", cleanup);\n process.on(\"SIGTERM\", cleanup);\n}\n\nasync function startBackground(opts: { port?: string; config?: string }): Promise<void> {\n header(\"Starting Server (background)\");\n\n const dataDir = getDataDir();\n fs.mkdirSync(dataDir, { recursive: true });\n\n // Build the command to run\n const args = [\"run\", \"src/server.ts\"];\n const env = { ...process.env };\n\n if (opts.port) {\n env.OPENCLAW_MEMORY_PORT = opts.port;\n env.PORT = opts.port;\n }\n\n // Detect runtime\n const runtime = typeof Bun !== \"undefined\" ? \"bun\" : \"node\";\n const execPath = runtime === \"bun\" ? \"bun\" : process.execPath;\n const execArgs = runtime === \"bun\" ? args : [\"--import\", \"tsx\", ...args];\n\n const child = spawn(execPath, execArgs, {\n env,\n detached: true,\n stdio: \"ignore\",\n cwd: process.cwd(),\n });\n\n child.unref();\n\n if (child.pid) {\n fs.writeFileSync(getPidFilePath(), String(child.pid), \"utf-8\");\n success(`Server started in background (PID: ${child.pid})`);\n info(`PID file: ${getPidFilePath()}`);\n info(`Stop with: openclaw-memory stop`);\n } else {\n logError(\"Failed to start server\");\n process.exit(1);\n }\n}\n","import { Command } from \"commander\";\nimport fs from \"node:fs\";\nimport { header, success, warn, error as logError } from \"../utils.js\";\nimport { getPidFilePath } from \"../../config/index.js\";\n\nexport function stopCommand(): Command {\n return new Command(\"stop\")\n .description(\"Stop the running server\")\n .action(async () => {\n header(\"Stopping Server\");\n\n const pidPath = getPidFilePath();\n if (!fs.existsSync(pidPath)) {\n warn(\"No PID file found — server may not be running\");\n return;\n }\n\n const pidStr = fs.readFileSync(pidPath, \"utf-8\").trim();\n const pid = parseInt(pidStr, 10);\n\n if (isNaN(pid)) {\n logError(`Invalid PID in ${pidPath}: ${pidStr}`);\n return;\n }\n\n try {\n // Send SIGTERM for graceful shutdown\n process.kill(pid, \"SIGTERM\");\n success(`Sent SIGTERM to PID ${pid}`);\n\n // Wait up to 3 seconds for graceful shutdown\n let alive = true;\n for (let i = 0; i < 30; i++) {\n await new Promise((r) => setTimeout(r, 100));\n try {\n process.kill(pid, 0);\n } catch {\n alive = false;\n break;\n }\n }\n\n if (alive) {\n warn(\"Process still running after 3s, sending SIGKILL...\");\n try {\n process.kill(pid, \"SIGKILL\");\n } catch {}\n }\n\n success(\"Server stopped\");\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === \"ESRCH\") {\n warn(`Process ${pid} not found — may have already stopped`);\n } else {\n logError(`Failed to stop process ${pid}: ${err}`);\n }\n }\n\n // Clean up PID file\n try {\n fs.unlinkSync(pidPath);\n } catch {}\n });\n}\n","import { Command } from \"commander\";\nimport pc from \"picocolors\";\nimport { header, bullet, info, getServerPid, isServerRunning, getBaseUrl, apiGet } from \"../utils.js\";\nimport { loadConfig } from \"../../config/index.js\";\n\nexport function statusCommand(): Command {\n return new Command(\"status\")\n .description(\"Show server and layer health status\")\n .option(\"-p, --port <port>\", \"Server port to check\")\n .option(\"--config <path>\", \"Path to config file\")\n .action(async (opts) => {\n header(\"Status\");\n\n let config;\n try {\n config = await loadConfig(opts.config);\n } catch {\n config = null;\n }\n\n const port = opts.port ? parseInt(opts.port, 10) : config?.port || 7777;\n const baseUrl = getBaseUrl(port);\n const pid = getServerPid();\n const running = await isServerRunning(baseUrl);\n\n // Server status\n if (running && pid) {\n bullet(\"Server\", `Running (PID ${pid}, port ${port})`, \"ok\");\n } else if (running) {\n bullet(\"Server\", `Running (port ${port})`, \"ok\");\n } else if (pid) {\n bullet(\"Server\", `PID file exists (${pid}) but not responding`, \"error\");\n } else {\n bullet(\"Server\", \"Not running\", \"disabled\");\n }\n\n if (config) {\n bullet(\"Tier\", config.tier, undefined);\n }\n\n console.log();\n\n // If server is running, get health from API\n if (running) {\n try {\n const health = await apiGet(baseUrl, \"/api/health\", config?.auth?.token) as Record<string, unknown>;\n\n info(\"Layers:\");\n const sqliteStatus = health.sqlite === \"ok\" ? \"ok\" as const : \"error\" as const;\n bullet(\"L1 SQLite\", String(health.sqlite), sqliteStatus);\n\n if (health.qdrant !== \"disabled\") {\n const qdrantStatus = health.qdrant === \"ok\" ? \"ok\" as const : \"error\" as const;\n bullet(\"L2 Qdrant\", String(health.qdrant), qdrantStatus);\n } else {\n bullet(\"L2 Qdrant\", \"disabled\", \"disabled\");\n }\n\n if (health.age !== \"disabled\") {\n const ageStatus = health.age === \"ok\" ? \"ok\" as const : \"error\" as const;\n bullet(\"L3 AGE\", String(health.age), ageStatus);\n } else {\n bullet(\"L3 AGE\", \"disabled\", \"disabled\");\n }\n\n if (health.uptime !== undefined) {\n const uptime = formatUptime(Number(health.uptime));\n console.log();\n info(`Uptime: ${uptime}`);\n }\n } catch (error) {\n info(\"Could not fetch health status from server\");\n }\n } else {\n // Server not running — try direct layer checks\n if (config) {\n info(\"Server not running. Checking layers directly...\");\n console.log();\n\n // SQLite — always available\n bullet(\"L1 SQLite\", config.sqlite.path, \"ok\");\n\n // Qdrant\n if (config.qdrant) {\n try {\n const res = await fetch(`${config.qdrant.url}/collections`, {\n signal: AbortSignal.timeout(2000),\n });\n bullet(\"L2 Qdrant\", config.qdrant.url, res.ok ? \"ok\" : \"error\");\n } catch {\n bullet(\"L2 Qdrant\", `${config.qdrant.url} (unreachable)`, \"error\");\n }\n } else {\n bullet(\"L2 Qdrant\", \"not configured\", \"disabled\");\n }\n\n // AGE\n if (config.age) {\n try {\n const net = await import(\"node:net\");\n const reachable = await new Promise<boolean>((resolve) => {\n const socket = new net.Socket();\n socket.setTimeout(2000);\n socket.connect(config!.age!.port, config!.age!.host, () => {\n socket.destroy();\n resolve(true);\n });\n socket.on(\"error\", () => resolve(false));\n socket.on(\"timeout\", () => { socket.destroy(); resolve(false); });\n });\n bullet(\"L3 AGE\", `${config.age.host}:${config.age.port}`, reachable ? \"ok\" : \"error\");\n } catch {\n bullet(\"L3 AGE\", \"unreachable\", \"error\");\n }\n } else {\n bullet(\"L3 AGE\", \"not configured\", \"disabled\");\n }\n }\n }\n\n console.log();\n });\n}\n\nfunction formatUptime(seconds: number): string {\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = seconds % 60;\n if (h > 0) return `${h}h ${m}m`;\n if (m > 0) return `${m}m ${s}s`;\n return `${s}s`;\n}\n","import { Command } from \"commander\";\nimport { output, readStdin, apiPost, isServerRunning, getBaseUrl } from \"../utils.js\";\nimport { loadConfig } from \"../../config/index.js\";\n\nexport function storeCommand(): Command {\n return new Command(\"store\")\n .description(\"Store a new memory\")\n .argument(\"[content]\", \"Memory content (or pipe via stdin)\")\n .requiredOption(\"--agent <id>\", \"Agent ID\")\n .requiredOption(\"--scope <scope>\", \"Memory scope (user, agent, global, project, session)\")\n .option(\"--subject <id>\", \"Subject ID\")\n .option(\"--tags <tags>\", \"Comma-separated tags\")\n .option(\"--source <source>\", \"Memory source\", \"explicit\")\n .option(\"--no-extract\", \"Skip entity extraction\")\n .option(\"--format <fmt>\", \"Output format (json, text)\", \"json\")\n .option(\"--config <path>\", \"Path to config file\")\n .action(async (contentArg, opts) => {\n const content = contentArg || await readStdin();\n if (!content) {\n console.error(\"Error: content is required (pass as argument or pipe via stdin)\");\n process.exit(1);\n }\n\n const config = await loadConfig(opts.config);\n const baseUrl = getBaseUrl(config.port);\n const serverUp = await isServerRunning(baseUrl);\n\n const body = {\n agent_id: opts.agent,\n scope: opts.scope,\n subject_id: opts.subject || null,\n content,\n tags: opts.tags ? opts.tags.split(\",\").map((t: string) => t.trim()) : [],\n source: opts.source,\n extract_entities: opts.extract !== false,\n };\n\n if (serverUp) {\n // Use HTTP API\n const result = await apiPost(baseUrl, \"/api/memories\", body, config.auth.token);\n output(result, opts.format);\n } else {\n // Direct mode — instantiate MemoryService in-process\n const { MemoryService } = await import(\"../../core/memory-service.js\");\n const service = new MemoryService();\n await service.init();\n\n try {\n const result = await service.store({\n agentId: opts.agent,\n scope: opts.scope,\n subjectId: opts.subject || null,\n content,\n tags: body.tags,\n source: opts.source,\n extractEntities: opts.extract !== false,\n });\n output(result, opts.format);\n } finally {\n await service.close();\n }\n }\n });\n}\n","import { Command } from \"commander\";\nimport { output, apiPost, isServerRunning, getBaseUrl } from \"../utils.js\";\nimport { loadConfig } from \"../../config/index.js\";\n\nexport function searchCommand(): Command {\n const cmd = new Command(\"search\")\n .description(\"Search memories\");\n\n // Default search (smart auto-select)\n cmd\n .argument(\"<query>\", \"Search query\")\n .requiredOption(\"--agent <id>\", \"Agent ID\")\n .option(\"--limit <n>\", \"Max results\", \"10\")\n .option(\"--strategy <s>\", \"Search strategy (auto, semantic, fulltext, graph, all)\", \"auto\")\n .option(\"--scopes <scopes>\", \"Comma-separated scopes\")\n .option(\"--subject <id>\", \"Subject ID filter\")\n .option(\"--cross-agent\", \"Search across all agents\")\n .option(\"--no-graph\", \"Exclude graph results\")\n .option(\"--recall\", \"Format output for LLM context injection\")\n .option(\"--format <fmt>\", \"Output format (json, text)\", \"json\")\n .option(\"--config <path>\", \"Path to config file\")\n .action(async (query, opts) => {\n const config = await loadConfig(opts.config);\n const baseUrl = getBaseUrl(config.port);\n const serverUp = await isServerRunning(baseUrl);\n\n const body = {\n agent_id: opts.agent,\n query,\n limit: parseInt(opts.limit, 10),\n strategy: opts.strategy,\n scopes: opts.scopes ? opts.scopes.split(\",\") : undefined,\n subject_id: opts.subject || null,\n cross_agent: opts.crossAgent || false,\n include_graph: opts.graph !== false,\n };\n\n let result: unknown;\n if (serverUp) {\n result = await apiPost(baseUrl, \"/api/search\", body, config.auth.token);\n } else {\n const { MemoryService } = await import(\"../../core/memory-service.js\");\n const service = new MemoryService();\n await service.init();\n try {\n result = await service.search({\n agentId: opts.agent,\n query,\n limit: parseInt(opts.limit, 10),\n strategy: opts.strategy,\n scopes: opts.scopes ? opts.scopes.split(\",\") : undefined,\n subjectId: opts.subject || null,\n crossAgent: opts.crossAgent || false,\n includeGraph: opts.graph !== false,\n });\n } finally {\n await service.close();\n }\n }\n\n // Recall mode — format for LLM\n if (opts.recall) {\n const data = result as { results?: Array<{ memory: { content: string; scope: string; created_at: string } }> };\n if (data.results && data.results.length > 0) {\n console.log(\"## Relevant Memories\");\n for (const r of data.results) {\n const date = new Date(r.memory.created_at).toLocaleDateString();\n console.log(`- ${r.memory.content} (${r.memory.scope}, ${date})`);\n }\n } else {\n console.log(\"No relevant memories found.\");\n }\n } else {\n output(result, opts.format);\n }\n });\n\n return cmd;\n}\n","import { Command } from \"commander\";\nimport { output, apiPost, isServerRunning, getBaseUrl, success, info, header } from \"../utils.js\";\nimport { loadConfig } from \"../../config/index.js\";\n\nexport function migrateCommand(): Command {\n return new Command(\"migrate\")\n .description(\"Import memories from markdown files\")\n .requiredOption(\"--paths <paths>\", \"Comma-separated file paths\")\n .requiredOption(\"--agent <id>\", \"Agent ID\")\n .option(\"--dry-run\", \"Preview without writing\")\n .option(\"--format <fmt>\", \"Output format (json, text)\", \"json\")\n .option(\"--config <path>\", \"Path to config file\")\n .action(async (opts) => {\n const config = await loadConfig(opts.config);\n const baseUrl = getBaseUrl(config.port);\n const serverUp = await isServerRunning(baseUrl);\n\n const paths = opts.paths.split(\",\").map((p: string) => p.trim());\n\n if (opts.dryRun) {\n header(\"Migration (dry run)\");\n }\n\n const body = {\n markdown_paths: paths,\n agent_id: opts.agent,\n dry_run: opts.dryRun || false,\n };\n\n let result: unknown;\n if (serverUp) {\n result = await apiPost(baseUrl, \"/api/admin/migrate-markdown\", body, config.auth.token);\n } else {\n // Direct mode\n const { MemoryService } = await import(\"../../core/memory-service.js\");\n const service = new MemoryService();\n await service.init();\n try {\n const migrated = await service.migrateMarkdown(paths, opts.agent);\n result = migrated;\n } finally {\n await service.close();\n }\n }\n\n if (opts.format === \"text\") {\n const data = result as { migrated?: number; skipped?: number; errors?: string[] };\n if (data.migrated !== undefined) {\n success(`Migrated ${data.migrated} memories`);\n }\n if (data.skipped) {\n info(`Skipped ${data.skipped} sections`);\n }\n if (data.errors && data.errors.length > 0) {\n for (const err of data.errors) {\n console.error(` ✗ ${err}`);\n }\n }\n } else {\n output(result, opts.format);\n }\n });\n}\n","import { Command } from \"commander\";\nimport { execSync } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { header, success, info, warn, error as logError } from \"../utils.js\";\nimport { getDataDir, loadConfig } from \"../../config/index.js\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nexport function infraCommand(): Command {\n const infra = new Command(\"infra\")\n .description(\"Manage Docker infrastructure\");\n\n infra\n .command(\"up\")\n .description(\"Start Docker containers for the configured tier\")\n .option(\"--tier <tier>\", \"Override tier (standard, full)\")\n .action(async (opts) => {\n header(\"Infrastructure Up\");\n\n let tier = opts.tier;\n if (!tier) {\n try {\n const config = await loadConfig();\n tier = config.tier;\n } catch {\n tier = \"standard\";\n }\n }\n\n if (tier === \"lite\") {\n info(\"Lite tier uses only SQLite — no Docker infrastructure needed.\");\n return;\n }\n\n const templateFile = tier === \"full\" ? \"full.yml\" : \"standard.yml\";\n const templatePath = findTemplate(templateFile);\n\n if (!templatePath) {\n logError(`Template not found: ${templateFile}`);\n logError(\"Expected in ./docker/ or ./templates/ directory\");\n process.exit(1);\n }\n\n const dataDir = getDataDir();\n fs.mkdirSync(dataDir, { recursive: true });\n\n const targetPath = path.join(dataDir, \"docker-compose.yml\");\n fs.copyFileSync(templatePath, targetPath);\n info(`Using template: ${templatePath}`);\n\n try {\n execSync(`docker compose -f ${targetPath} up -d`, {\n stdio: \"inherit\",\n cwd: dataDir,\n });\n success(\"Docker containers started\");\n } catch (error) {\n logError(\"Failed to start Docker containers\");\n logError(\"Make sure Docker is installed and running\");\n process.exit(1);\n }\n });\n\n infra\n .command(\"down\")\n .description(\"Stop Docker containers\")\n .action(async () => {\n header(\"Infrastructure Down\");\n\n const dataDir = getDataDir();\n const composePath = path.join(dataDir, \"docker-compose.yml\");\n\n if (!fs.existsSync(composePath)) {\n warn(\"No docker-compose.yml found in data directory\");\n return;\n }\n\n try {\n execSync(`docker compose -f ${composePath} down`, {\n stdio: \"inherit\",\n cwd: dataDir,\n });\n success(\"Docker containers stopped\");\n } catch (error) {\n logError(\"Failed to stop Docker containers\");\n process.exit(1);\n }\n });\n\n infra\n .command(\"status\")\n .description(\"Show Docker container status\")\n .action(async () => {\n header(\"Infrastructure Status\");\n\n const dataDir = getDataDir();\n const composePath = path.join(dataDir, \"docker-compose.yml\");\n\n if (!fs.existsSync(composePath)) {\n info(\"No docker-compose.yml found — infrastructure not set up\");\n info(\"Run: openclaw-memory infra up\");\n return;\n }\n\n try {\n execSync(`docker compose -f ${composePath} ps`, {\n stdio: \"inherit\",\n cwd: dataDir,\n });\n } catch {\n logError(\"Failed to get container status\");\n }\n });\n\n return infra;\n}\n\nfunction findTemplate(filename: string): string | null {\n const searchPaths = [\n path.join(process.cwd(), \"docker\", filename),\n path.join(process.cwd(), \"templates\", filename),\n // Look in the package's installed location\n path.join(__dirname, \"../../docker\", filename),\n path.join(__dirname, \"../../templates\", filename),\n ];\n\n for (const p of searchPaths) {\n if (fs.existsSync(p)) return p;\n }\n\n return null;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/private/tmp/openclaw-memory-publish/dist/cli/index.cjs","../../src/cli/index.ts","../../src/cli/commands/init.ts","../../src/cli/utils.ts","../../src/cli/commands/start.ts","../../src/cli/commands/stop.ts","../../src/cli/commands/status.ts","../../src/cli/commands/store.ts","../../src/cli/commands/search.ts","../../src/cli/commands/migrate.ts","../../src/cli/commands/infra.ts"],"names":[],"mappings":"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","file":"/private/tmp/openclaw-memory-publish/dist/cli/index.cjs","sourcesContent":[null,"#!/usr/bin/env node\n\nimport { Command } from \"commander\";\nimport { initCommand } from \"./commands/init.js\";\nimport { startCommand } from \"./commands/start.js\";\nimport { stopCommand } from \"./commands/stop.js\";\nimport { statusCommand } from \"./commands/status.js\";\nimport { storeCommand } from \"./commands/store.js\";\nimport { searchCommand } from \"./commands/search.js\";\nimport { migrateCommand } from \"./commands/migrate.js\";\nimport { infraCommand } from \"./commands/infra.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"openclaw-memory\")\n .description(\"Triple-layer memory system for AI agents — SQLite + Qdrant + Postgres/AGE\")\n .version(\"0.1.0\");\n\n// Register commands\nprogram.addCommand(initCommand());\nprogram.addCommand(startCommand());\nprogram.addCommand(stopCommand());\nprogram.addCommand(statusCommand());\nprogram.addCommand(storeCommand());\nprogram.addCommand(searchCommand());\nprogram.addCommand(migrateCommand());\nprogram.addCommand(infraCommand());\n\nprogram.parse();\n","import { Command } from \"commander\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { header, success, info, warn, error as logError } from \"../utils.js\";\nimport { getDataDir, getDefaultSqlitePath } from \"../../config/index.js\";\n\nexport function initCommand(): Command {\n return new Command(\"init\")\n .description(\"Interactive setup wizard\")\n .option(\"--tier <tier>\", \"Tier: lite, standard, or full\")\n .option(\"--non-interactive\", \"Skip prompts, use defaults + flags\")\n .action(async (opts) => {\n header(\"Setup Wizard\");\n\n // For non-interactive mode, generate config from flags/defaults\n const tier = opts.tier || \"lite\";\n const dataDir = getDataDir();\n const sqlitePath = getDefaultSqlitePath();\n\n // Ensure data directory exists\n fs.mkdirSync(dataDir, { recursive: true });\n info(`Data directory: ${dataDir}`);\n\n // Auto-detect available services\n let qdrantAvailable = false;\n let ageAvailable = false;\n\n if (tier !== \"lite\") {\n info(\"Checking Qdrant connectivity...\");\n try {\n const res = await fetch(\"http://localhost:6333/collections\", {\n signal: AbortSignal.timeout(2000),\n });\n qdrantAvailable = res.ok;\n } catch { /* not available */ }\n\n if (qdrantAvailable) {\n success(\"Qdrant is reachable at http://localhost:6333\");\n } else {\n warn(\"Qdrant not reachable at http://localhost:6333\");\n }\n }\n\n if (tier === \"full\") {\n info(\"Checking PostgreSQL/AGE connectivity...\");\n try {\n // Simple TCP check\n const net = await import(\"node:net\");\n ageAvailable = await new Promise<boolean>((resolve) => {\n const socket = new net.Socket();\n socket.setTimeout(2000);\n socket.connect(5432, \"localhost\", () => {\n socket.destroy();\n resolve(true);\n });\n socket.on(\"error\", () => resolve(false));\n socket.on(\"timeout\", () => { socket.destroy(); resolve(false); });\n });\n } catch { /* not available */ }\n\n if (ageAvailable) {\n success(\"PostgreSQL is reachable at localhost:5432\");\n } else {\n warn(\"PostgreSQL not reachable at localhost:5432\");\n }\n }\n\n // Determine effective tier\n let effectiveTier = tier;\n if (tier === \"full\" && !ageAvailable) {\n effectiveTier = qdrantAvailable ? \"standard\" : \"lite\";\n warn(`Downgrading to ${effectiveTier} tier (missing dependencies)`);\n } else if (tier === \"standard\" && !qdrantAvailable) {\n effectiveTier = \"lite\";\n warn(\"Downgrading to lite tier (Qdrant not available)\");\n }\n\n // Generate config file\n const configContent = generateConfig(effectiveTier, sqlitePath);\n const configPath = path.join(process.cwd(), \"openclaw-memory.config.ts\");\n\n fs.writeFileSync(configPath, configContent, \"utf-8\");\n success(`Config written to ${configPath}`);\n\n // Generate .env.example\n const envExample = generateEnvExample(effectiveTier);\n const envPath = path.join(process.cwd(), \".env.example\");\n fs.writeFileSync(envPath, envExample, \"utf-8\");\n success(`Environment template written to ${envPath}`);\n\n console.log();\n info(\"Next steps:\");\n console.log(\" openclaw-memory start # Start the server\");\n console.log(\" openclaw-memory status # Check all layers\");\n console.log();\n });\n}\n\nfunction generateConfig(tier: string, sqlitePath: string): string {\n const lines = [\n `import { defineConfig } from '@poprobertdaniel/openclaw-memory';`,\n ``,\n `export default defineConfig({`,\n ` tier: '${tier}',`,\n ` port: 7777,`,\n ` auth: {`,\n ` token: process.env.MEMORY_AUTH_TOKEN || 'change-me',`,\n ` },`,\n ` sqlite: {`,\n ` path: '${sqlitePath}',`,\n ` },`,\n ];\n\n if (tier === \"standard\" || tier === \"full\") {\n lines.push(` qdrant: {`);\n lines.push(` url: process.env.QDRANT_URL || 'http://localhost:6333',`);\n lines.push(` collection: 'openclaw_memories',`);\n lines.push(` },`);\n lines.push(` embedding: {`);\n lines.push(` apiKey: process.env.OPENAI_API_KEY || '',`);\n lines.push(` model: 'text-embedding-3-small',`);\n lines.push(` dimensions: 1536,`);\n lines.push(` },`);\n lines.push(` extraction: {`);\n lines.push(` apiKey: process.env.OPENAI_API_KEY || '',`);\n lines.push(` model: 'gpt-5-nano',`);\n lines.push(` enabled: true,`);\n lines.push(` },`);\n }\n\n if (tier === \"full\") {\n lines.push(` age: {`);\n lines.push(` host: process.env.PGHOST || 'localhost',`);\n lines.push(` port: parseInt(process.env.PGPORT || '5432', 10),`);\n lines.push(` user: process.env.PGUSER || 'openclaw',`);\n lines.push(` password: process.env.PGPASSWORD || '',`);\n lines.push(` database: process.env.PGDATABASE || 'agent_memory',`);\n lines.push(` graph: 'agent_memory',`);\n lines.push(` },`);\n }\n\n lines.push(`});`);\n lines.push(``);\n\n return lines.join(\"\\n\");\n}\n\nfunction generateEnvExample(tier: string): string {\n const lines = [\n `# openclaw-memory environment variables`,\n ``,\n `# Server`,\n `# OPENCLAW_MEMORY_PORT=7777`,\n `# OPENCLAW_MEMORY_HOST=0.0.0.0`,\n ``,\n `# Authentication`,\n `MEMORY_AUTH_TOKEN=change-me-to-a-secure-token`,\n ``,\n `# SQLite (always required)`,\n `# SQLITE_PATH=~/.openclaw-memory/memory.sqlite`,\n ``,\n ];\n\n if (tier === \"standard\" || tier === \"full\") {\n lines.push(`# Qdrant (Standard/Full tier)`);\n lines.push(`QDRANT_URL=http://localhost:6333`);\n lines.push(``);\n lines.push(`# Embedding provider`);\n lines.push(`OPENAI_API_KEY=sk-your-key-here`);\n lines.push(`# EMBEDDING_MODEL=text-embedding-3-small`);\n lines.push(`# EMBEDDING_BASE_URL=https://api.openai.com/v1`);\n lines.push(``);\n lines.push(`# Entity extraction`);\n lines.push(`# EXTRACTION_MODEL=gpt-5-nano`);\n lines.push(``);\n }\n\n if (tier === \"full\") {\n lines.push(`# PostgreSQL + Apache AGE (Full tier)`);\n lines.push(`PGHOST=localhost`);\n lines.push(`PGPORT=5432`);\n lines.push(`PGUSER=openclaw`);\n lines.push(`PGPASSWORD=your-password`);\n lines.push(`PGDATABASE=agent_memory`);\n lines.push(`# AGE_GRAPH=agent_memory`);\n lines.push(``);\n }\n\n return lines.join(\"\\n\");\n}\n","import pc from \"picocolors\";\nimport fs from \"node:fs\";\nimport { getPidFilePath } from \"../config/index.js\";\n\n// ── CLI Utilities ───────────────────────────────────────────────────────\n\nexport function log(msg: string): void {\n console.log(msg);\n}\n\nexport function info(msg: string): void {\n console.log(pc.blue(\"ℹ\") + \" \" + msg);\n}\n\nexport function success(msg: string): void {\n console.log(pc.green(\"✓\") + \" \" + msg);\n}\n\nexport function warn(msg: string): void {\n console.log(pc.yellow(\"⚠\") + \" \" + msg);\n}\n\nexport function error(msg: string): void {\n console.error(pc.red(\"✗\") + \" \" + msg);\n}\n\nexport function header(title: string): void {\n console.log();\n console.log(pc.bold(` 🧠 OpenClaw Memory — ${title}`));\n console.log();\n}\n\nexport function bullet(label: string, value: string, status?: \"ok\" | \"error\" | \"disabled\" | \"degraded\"): void {\n const dot = status === \"ok\" ? pc.green(\"●\")\n : status === \"error\" ? pc.red(\"●\")\n : status === \"degraded\" ? pc.yellow(\"●\")\n : status === \"disabled\" ? pc.dim(\"○\")\n : \" \";\n console.log(` ${dot} ${pc.bold(label)} ${value}`);\n}\n\n// ── Server Detection ────────────────────────────────────────────────────\n\nexport function getServerPid(): number | null {\n const pidPath = getPidFilePath();\n if (!fs.existsSync(pidPath)) return null;\n\n try {\n const pid = parseInt(fs.readFileSync(pidPath, \"utf-8\").trim(), 10);\n if (isNaN(pid)) return null;\n\n // Check if process is alive\n try {\n process.kill(pid, 0);\n return pid;\n } catch {\n // Process not running, clean up stale PID file\n fs.unlinkSync(pidPath);\n return null;\n }\n } catch {\n return null;\n }\n}\n\nexport async function isServerRunning(baseUrl: string = \"http://localhost:7777\"): Promise<boolean> {\n try {\n const res = await fetch(`${baseUrl}/api/health`, {\n signal: AbortSignal.timeout(2000),\n });\n return res.ok;\n } catch {\n return false;\n }\n}\n\nexport function getBaseUrl(port?: number): string {\n return `http://localhost:${port || 7777}`;\n}\n\n// ── HTTP Client ─────────────────────────────────────────────────────────\n\nexport async function apiGet(baseUrl: string, path: string, token?: string): Promise<unknown> {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n const res = await fetch(`${baseUrl}${path}`, { headers });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`HTTP ${res.status}: ${text}`);\n }\n return res.json();\n}\n\nexport async function apiPost(baseUrl: string, path: string, body: unknown, token?: string): Promise<unknown> {\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (token) headers.Authorization = `Bearer ${token}`;\n\n const res = await fetch(`${baseUrl}${path}`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`HTTP ${res.status}: ${text}`);\n }\n return res.json();\n}\n\nexport async function apiDelete(baseUrl: string, path: string, token?: string): Promise<unknown> {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n const res = await fetch(`${baseUrl}${path}`, {\n method: \"DELETE\",\n headers,\n });\n if (!res.ok) {\n const text = await res.text();\n throw new Error(`HTTP ${res.status}: ${text}`);\n }\n return res.json();\n}\n\n// ── Output Formatting ───────────────────────────────────────────────────\n\nexport function output(data: unknown, format: \"json\" | \"text\" = \"json\"): void {\n if (format === \"text\") {\n console.log(JSON.stringify(data, null, 2));\n } else {\n console.log(JSON.stringify(data));\n }\n}\n\n// ── Stdin Reading ───────────────────────────────────────────────────────\n\nexport async function readStdin(): Promise<string> {\n if (process.stdin.isTTY) return \"\";\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(Buffer.from(chunk));\n }\n return Buffer.concat(chunks).toString(\"utf-8\").trim();\n}\n","import { Command } from \"commander\";\nimport fs from \"node:fs\";\nimport { spawn } from \"node:child_process\";\nimport { header, success, info, error as logError } from \"../utils.js\";\nimport { getPidFilePath, getDataDir } from \"../../config/index.js\";\n\nexport function startCommand(): Command {\n return new Command(\"start\")\n .description(\"Start the HTTP server\")\n .option(\"-p, --port <port>\", \"Server port\")\n .option(\"--bg\", \"Run in background (daemon mode)\")\n .option(\"--config <path>\", \"Path to config file\")\n .action(async (opts) => {\n if (opts.bg) {\n await startBackground(opts);\n } else {\n await startForeground(opts);\n }\n });\n}\n\nasync function startForeground(opts: { port?: string; config?: string }): Promise<void> {\n // Set port if specified\n if (opts.port) {\n process.env.OPENCLAW_MEMORY_PORT = opts.port;\n process.env.PORT = opts.port;\n }\n\n // Import and run server directly\n const { createServer } = await import(\"../../server.js\");\n const { app, config } = await createServer(opts.config);\n\n const port = opts.port ? parseInt(opts.port, 10) : config.port;\n app.listen(port);\n\n console.log(`[server] Listening on http://0.0.0.0:${port}`);\n\n // Write PID file for status/stop commands\n const dataDir = getDataDir();\n fs.mkdirSync(dataDir, { recursive: true });\n fs.writeFileSync(getPidFilePath(), String(process.pid), \"utf-8\");\n\n const cleanup = () => {\n try { fs.unlinkSync(getPidFilePath()); } catch {}\n process.exit(0);\n };\n\n process.on(\"SIGINT\", cleanup);\n process.on(\"SIGTERM\", cleanup);\n}\n\nasync function startBackground(opts: { port?: string; config?: string }): Promise<void> {\n header(\"Starting Server (background)\");\n\n const dataDir = getDataDir();\n fs.mkdirSync(dataDir, { recursive: true });\n\n // Build the command to run\n const args = [\"run\", \"src/server.ts\"];\n const env = { ...process.env };\n\n if (opts.port) {\n env.OPENCLAW_MEMORY_PORT = opts.port;\n env.PORT = opts.port;\n }\n\n // Detect runtime\n const runtime = typeof Bun !== \"undefined\" ? \"bun\" : \"node\";\n const execPath = runtime === \"bun\" ? \"bun\" : process.execPath;\n const execArgs = runtime === \"bun\" ? args : [\"--import\", \"tsx\", ...args];\n\n const child = spawn(execPath, execArgs, {\n env,\n detached: true,\n stdio: \"ignore\",\n cwd: process.cwd(),\n });\n\n child.unref();\n\n if (child.pid) {\n fs.writeFileSync(getPidFilePath(), String(child.pid), \"utf-8\");\n success(`Server started in background (PID: ${child.pid})`);\n info(`PID file: ${getPidFilePath()}`);\n info(`Stop with: openclaw-memory stop`);\n } else {\n logError(\"Failed to start server\");\n process.exit(1);\n }\n}\n","import { Command } from \"commander\";\nimport fs from \"node:fs\";\nimport { header, success, warn, error as logError } from \"../utils.js\";\nimport { getPidFilePath } from \"../../config/index.js\";\n\nexport function stopCommand(): Command {\n return new Command(\"stop\")\n .description(\"Stop the running server\")\n .action(async () => {\n header(\"Stopping Server\");\n\n const pidPath = getPidFilePath();\n if (!fs.existsSync(pidPath)) {\n warn(\"No PID file found — server may not be running\");\n return;\n }\n\n const pidStr = fs.readFileSync(pidPath, \"utf-8\").trim();\n const pid = parseInt(pidStr, 10);\n\n if (isNaN(pid)) {\n logError(`Invalid PID in ${pidPath}: ${pidStr}`);\n return;\n }\n\n try {\n // Send SIGTERM for graceful shutdown\n process.kill(pid, \"SIGTERM\");\n success(`Sent SIGTERM to PID ${pid}`);\n\n // Wait up to 3 seconds for graceful shutdown\n let alive = true;\n for (let i = 0; i < 30; i++) {\n await new Promise((r) => setTimeout(r, 100));\n try {\n process.kill(pid, 0);\n } catch {\n alive = false;\n break;\n }\n }\n\n if (alive) {\n warn(\"Process still running after 3s, sending SIGKILL...\");\n try {\n process.kill(pid, \"SIGKILL\");\n } catch {}\n }\n\n success(\"Server stopped\");\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === \"ESRCH\") {\n warn(`Process ${pid} not found — may have already stopped`);\n } else {\n logError(`Failed to stop process ${pid}: ${err}`);\n }\n }\n\n // Clean up PID file\n try {\n fs.unlinkSync(pidPath);\n } catch {}\n });\n}\n","import { Command } from \"commander\";\nimport pc from \"picocolors\";\nimport { header, bullet, info, getServerPid, isServerRunning, getBaseUrl, apiGet } from \"../utils.js\";\nimport { loadConfig } from \"../../config/index.js\";\n\nexport function statusCommand(): Command {\n return new Command(\"status\")\n .description(\"Show server and layer health status\")\n .option(\"-p, --port <port>\", \"Server port to check\")\n .option(\"--config <path>\", \"Path to config file\")\n .action(async (opts) => {\n header(\"Status\");\n\n let config;\n try {\n config = await loadConfig(opts.config);\n } catch {\n config = null;\n }\n\n const port = opts.port ? parseInt(opts.port, 10) : config?.port || 7777;\n const baseUrl = getBaseUrl(port);\n const pid = getServerPid();\n const running = await isServerRunning(baseUrl);\n\n // Server status\n if (running && pid) {\n bullet(\"Server\", `Running (PID ${pid}, port ${port})`, \"ok\");\n } else if (running) {\n bullet(\"Server\", `Running (port ${port})`, \"ok\");\n } else if (pid) {\n bullet(\"Server\", `PID file exists (${pid}) but not responding`, \"error\");\n } else {\n bullet(\"Server\", \"Not running\", \"disabled\");\n }\n\n if (config) {\n bullet(\"Tier\", config.tier, undefined);\n }\n\n console.log();\n\n // If server is running, get health from API\n if (running) {\n try {\n const health = await apiGet(baseUrl, \"/api/health\", config?.auth?.token) as Record<string, unknown>;\n\n info(\"Layers:\");\n const sqliteStatus = health.sqlite === \"ok\" ? \"ok\" as const : \"error\" as const;\n bullet(\"L1 SQLite\", String(health.sqlite), sqliteStatus);\n\n if (health.qdrant !== \"disabled\") {\n const qdrantStatus = health.qdrant === \"ok\" ? \"ok\" as const : \"error\" as const;\n bullet(\"L2 Qdrant\", String(health.qdrant), qdrantStatus);\n } else {\n bullet(\"L2 Qdrant\", \"disabled\", \"disabled\");\n }\n\n if (health.age !== \"disabled\") {\n const ageStatus = health.age === \"ok\" ? \"ok\" as const : \"error\" as const;\n bullet(\"L3 AGE\", String(health.age), ageStatus);\n } else {\n bullet(\"L3 AGE\", \"disabled\", \"disabled\");\n }\n\n if (health.uptime !== undefined) {\n const uptime = formatUptime(Number(health.uptime));\n console.log();\n info(`Uptime: ${uptime}`);\n }\n } catch (error) {\n info(\"Could not fetch health status from server\");\n }\n } else {\n // Server not running — try direct layer checks\n if (config) {\n info(\"Server not running. Checking layers directly...\");\n console.log();\n\n // SQLite — always available\n bullet(\"L1 SQLite\", config.sqlite.path, \"ok\");\n\n // Qdrant\n if (config.qdrant) {\n try {\n const res = await fetch(`${config.qdrant.url}/collections`, {\n signal: AbortSignal.timeout(2000),\n });\n bullet(\"L2 Qdrant\", config.qdrant.url, res.ok ? \"ok\" : \"error\");\n } catch {\n bullet(\"L2 Qdrant\", `${config.qdrant.url} (unreachable)`, \"error\");\n }\n } else {\n bullet(\"L2 Qdrant\", \"not configured\", \"disabled\");\n }\n\n // AGE\n if (config.age) {\n try {\n const net = await import(\"node:net\");\n const reachable = await new Promise<boolean>((resolve) => {\n const socket = new net.Socket();\n socket.setTimeout(2000);\n socket.connect(config!.age!.port, config!.age!.host, () => {\n socket.destroy();\n resolve(true);\n });\n socket.on(\"error\", () => resolve(false));\n socket.on(\"timeout\", () => { socket.destroy(); resolve(false); });\n });\n bullet(\"L3 AGE\", `${config.age.host}:${config.age.port}`, reachable ? \"ok\" : \"error\");\n } catch {\n bullet(\"L3 AGE\", \"unreachable\", \"error\");\n }\n } else {\n bullet(\"L3 AGE\", \"not configured\", \"disabled\");\n }\n }\n }\n\n console.log();\n });\n}\n\nfunction formatUptime(seconds: number): string {\n const h = Math.floor(seconds / 3600);\n const m = Math.floor((seconds % 3600) / 60);\n const s = seconds % 60;\n if (h > 0) return `${h}h ${m}m`;\n if (m > 0) return `${m}m ${s}s`;\n return `${s}s`;\n}\n","import { Command } from \"commander\";\nimport { output, readStdin, apiPost, isServerRunning, getBaseUrl } from \"../utils.js\";\nimport { loadConfig } from \"../../config/index.js\";\n\nexport function storeCommand(): Command {\n return new Command(\"store\")\n .description(\"Store a new memory\")\n .argument(\"[content]\", \"Memory content (or pipe via stdin)\")\n .requiredOption(\"--agent <id>\", \"Agent ID\")\n .requiredOption(\"--scope <scope>\", \"Memory scope (user, agent, global, project, session)\")\n .option(\"--subject <id>\", \"Subject ID\")\n .option(\"--tags <tags>\", \"Comma-separated tags\")\n .option(\"--source <source>\", \"Memory source\", \"explicit\")\n .option(\"--no-extract\", \"Skip entity extraction\")\n .option(\"--format <fmt>\", \"Output format (json, text)\", \"json\")\n .option(\"--config <path>\", \"Path to config file\")\n .action(async (contentArg, opts) => {\n const content = contentArg || await readStdin();\n if (!content) {\n console.error(\"Error: content is required (pass as argument or pipe via stdin)\");\n process.exit(1);\n }\n\n const config = await loadConfig(opts.config);\n const baseUrl = getBaseUrl(config.port);\n const serverUp = await isServerRunning(baseUrl);\n\n const body = {\n agent_id: opts.agent,\n scope: opts.scope,\n subject_id: opts.subject || null,\n content,\n tags: opts.tags ? opts.tags.split(\",\").map((t: string) => t.trim()) : [],\n source: opts.source,\n extract_entities: opts.extract !== false,\n };\n\n if (serverUp) {\n // Use HTTP API\n const result = await apiPost(baseUrl, \"/api/memories\", body, config.auth.token);\n output(result, opts.format);\n } else {\n // Direct mode — instantiate MemoryService in-process\n const { MemoryService } = await import(\"../../core/memory-service.js\");\n const service = new MemoryService();\n await service.init();\n\n try {\n const result = await service.store({\n agentId: opts.agent,\n scope: opts.scope,\n subjectId: opts.subject || null,\n content,\n tags: body.tags,\n source: opts.source,\n extractEntities: opts.extract !== false,\n });\n output(result, opts.format);\n } finally {\n await service.close();\n }\n }\n });\n}\n","import { Command } from \"commander\";\nimport { output, apiPost, isServerRunning, getBaseUrl } from \"../utils.js\";\nimport { loadConfig } from \"../../config/index.js\";\n\nexport function searchCommand(): Command {\n const cmd = new Command(\"search\")\n .description(\"Search memories\");\n\n // Default search (smart auto-select)\n cmd\n .argument(\"<query>\", \"Search query\")\n .requiredOption(\"--agent <id>\", \"Agent ID\")\n .option(\"--limit <n>\", \"Max results\", \"10\")\n .option(\"--strategy <s>\", \"Search strategy (auto, semantic, fulltext, graph, all)\", \"auto\")\n .option(\"--scopes <scopes>\", \"Comma-separated scopes\")\n .option(\"--subject <id>\", \"Subject ID filter\")\n .option(\"--cross-agent\", \"Search across all agents\")\n .option(\"--no-graph\", \"Exclude graph results\")\n .option(\"--recall\", \"Format output for LLM context injection\")\n .option(\"--format <fmt>\", \"Output format (json, text)\", \"json\")\n .option(\"--config <path>\", \"Path to config file\")\n .action(async (query, opts) => {\n const config = await loadConfig(opts.config);\n const baseUrl = getBaseUrl(config.port);\n const serverUp = await isServerRunning(baseUrl);\n\n const body = {\n agent_id: opts.agent,\n query,\n limit: parseInt(opts.limit, 10),\n strategy: opts.strategy,\n scopes: opts.scopes ? opts.scopes.split(\",\") : undefined,\n subject_id: opts.subject || null,\n cross_agent: opts.crossAgent || false,\n include_graph: opts.graph !== false,\n };\n\n let result: unknown;\n if (serverUp) {\n result = await apiPost(baseUrl, \"/api/search\", body, config.auth.token);\n } else {\n const { MemoryService } = await import(\"../../core/memory-service.js\");\n const service = new MemoryService();\n await service.init();\n try {\n result = await service.search({\n agentId: opts.agent,\n query,\n limit: parseInt(opts.limit, 10),\n strategy: opts.strategy,\n scopes: opts.scopes ? opts.scopes.split(\",\") : undefined,\n subjectId: opts.subject || null,\n crossAgent: opts.crossAgent || false,\n includeGraph: opts.graph !== false,\n });\n } finally {\n await service.close();\n }\n }\n\n // Recall mode — format for LLM\n if (opts.recall) {\n const data = result as { results?: Array<{ memory: { content: string; scope: string; created_at: string } }> };\n if (data.results && data.results.length > 0) {\n console.log(\"## Relevant Memories\");\n for (const r of data.results) {\n const date = new Date(r.memory.created_at).toLocaleDateString();\n console.log(`- ${r.memory.content} (${r.memory.scope}, ${date})`);\n }\n } else {\n console.log(\"No relevant memories found.\");\n }\n } else {\n output(result, opts.format);\n }\n });\n\n return cmd;\n}\n","import { Command } from \"commander\";\nimport { output, apiPost, isServerRunning, getBaseUrl, success, info, header } from \"../utils.js\";\nimport { loadConfig } from \"../../config/index.js\";\n\nexport function migrateCommand(): Command {\n return new Command(\"migrate\")\n .description(\"Import memories from markdown files\")\n .requiredOption(\"--paths <paths>\", \"Comma-separated file paths\")\n .requiredOption(\"--agent <id>\", \"Agent ID\")\n .option(\"--dry-run\", \"Preview without writing\")\n .option(\"--format <fmt>\", \"Output format (json, text)\", \"json\")\n .option(\"--config <path>\", \"Path to config file\")\n .action(async (opts) => {\n const config = await loadConfig(opts.config);\n const baseUrl = getBaseUrl(config.port);\n const serverUp = await isServerRunning(baseUrl);\n\n const paths = opts.paths.split(\",\").map((p: string) => p.trim());\n\n if (opts.dryRun) {\n header(\"Migration (dry run)\");\n }\n\n const body = {\n markdown_paths: paths,\n agent_id: opts.agent,\n dry_run: opts.dryRun || false,\n };\n\n let result: unknown;\n if (serverUp) {\n result = await apiPost(baseUrl, \"/api/admin/migrate-markdown\", body, config.auth.token);\n } else {\n // Direct mode\n const { MemoryService } = await import(\"../../core/memory-service.js\");\n const service = new MemoryService();\n await service.init();\n try {\n const migrated = await service.migrateMarkdown(paths, opts.agent);\n result = migrated;\n } finally {\n await service.close();\n }\n }\n\n if (opts.format === \"text\") {\n const data = result as { migrated?: number; skipped?: number; errors?: string[] };\n if (data.migrated !== undefined) {\n success(`Migrated ${data.migrated} memories`);\n }\n if (data.skipped) {\n info(`Skipped ${data.skipped} sections`);\n }\n if (data.errors && data.errors.length > 0) {\n for (const err of data.errors) {\n console.error(` ✗ ${err}`);\n }\n }\n } else {\n output(result, opts.format);\n }\n });\n}\n","import { Command } from \"commander\";\nimport { execSync } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { header, success, info, warn, error as logError } from \"../utils.js\";\nimport { getDataDir, loadConfig } from \"../../config/index.js\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nexport function infraCommand(): Command {\n const infra = new Command(\"infra\")\n .description(\"Manage Docker infrastructure\");\n\n infra\n .command(\"up\")\n .description(\"Start Docker containers for the configured tier\")\n .option(\"--tier <tier>\", \"Override tier (standard, full)\")\n .action(async (opts) => {\n header(\"Infrastructure Up\");\n\n let tier = opts.tier;\n if (!tier) {\n try {\n const config = await loadConfig();\n tier = config.tier;\n } catch {\n tier = \"standard\";\n }\n }\n\n if (tier === \"lite\") {\n info(\"Lite tier uses only SQLite — no Docker infrastructure needed.\");\n return;\n }\n\n const templateFile = tier === \"full\" ? \"full.yml\" : \"standard.yml\";\n const templatePath = findTemplate(templateFile);\n\n if (!templatePath) {\n logError(`Template not found: ${templateFile}`);\n logError(\"Expected in ./docker/ or ./templates/ directory\");\n process.exit(1);\n }\n\n const dataDir = getDataDir();\n fs.mkdirSync(dataDir, { recursive: true });\n\n const targetPath = path.join(dataDir, \"docker-compose.yml\");\n fs.copyFileSync(templatePath, targetPath);\n info(`Using template: ${templatePath}`);\n\n try {\n execSync(`docker compose -f ${targetPath} up -d`, {\n stdio: \"inherit\",\n cwd: dataDir,\n });\n success(\"Docker containers started\");\n } catch (error) {\n logError(\"Failed to start Docker containers\");\n logError(\"Make sure Docker is installed and running\");\n process.exit(1);\n }\n });\n\n infra\n .command(\"down\")\n .description(\"Stop Docker containers\")\n .action(async () => {\n header(\"Infrastructure Down\");\n\n const dataDir = getDataDir();\n const composePath = path.join(dataDir, \"docker-compose.yml\");\n\n if (!fs.existsSync(composePath)) {\n warn(\"No docker-compose.yml found in data directory\");\n return;\n }\n\n try {\n execSync(`docker compose -f ${composePath} down`, {\n stdio: \"inherit\",\n cwd: dataDir,\n });\n success(\"Docker containers stopped\");\n } catch (error) {\n logError(\"Failed to stop Docker containers\");\n process.exit(1);\n }\n });\n\n infra\n .command(\"status\")\n .description(\"Show Docker container status\")\n .action(async () => {\n header(\"Infrastructure Status\");\n\n const dataDir = getDataDir();\n const composePath = path.join(dataDir, \"docker-compose.yml\");\n\n if (!fs.existsSync(composePath)) {\n info(\"No docker-compose.yml found — infrastructure not set up\");\n info(\"Run: openclaw-memory infra up\");\n return;\n }\n\n try {\n execSync(`docker compose -f ${composePath} ps`, {\n stdio: \"inherit\",\n cwd: dataDir,\n });\n } catch {\n logError(\"Failed to get container status\");\n }\n });\n\n return infra;\n}\n\nfunction findTemplate(filename: string): string | null {\n const searchPaths = [\n path.join(process.cwd(), \"docker\", filename),\n path.join(process.cwd(), \"templates\", filename),\n // Look in the package's installed location\n path.join(__dirname, \"../../docker\", filename),\n path.join(__dirname, \"../../templates\", filename),\n ];\n\n for (const p of searchPaths) {\n if (fs.existsSync(p)) return p;\n }\n\n return null;\n}\n"]}
|