From f410f1aa948966113e25fc6a91b827fdaebbd628 Mon Sep 17 00:00:00 2001 From: hl Date: Tue, 14 Apr 2026 20:03:57 +0800 Subject: [PATCH] fix: address multiple open issues (#50, #49, #46, #44-related) - fix(llm): support LlmConfig object as first arg in createCompleteFn so direct-maintain.ts works without signature mismatch (#49) - fix(recaller): add embedReady promise to eliminate race condition between createEmbedFn and recall(), ensuring embedFn is always awaited before vector search (#50) - fix(build): migrate to NodeNext ESM + .js extensions for Node.js v24 compatibility, output dist/ and point package main there (#46) - chore(ports): searched codebase; no hardcoded 18787/18789 found, port mismatch lives outside this repo (#44) Tests: 80 passed. Build: dist/ generated successfully. --- direct-maintain.ts | 37 +++++++++++++++++++++++++ index.ts | 28 ++++++++++--------- package.json | 4 +-- src/engine/embed.ts | 2 +- src/engine/llm.ts | 52 +++++++++++++++++++++++++++++------ src/extractor/extract.ts | 4 +-- src/format/assemble.ts | 4 +-- src/graph/community.ts | 8 +++--- src/graph/dedup.ts | 4 +-- src/graph/maintenance.ts | 12 ++++---- src/graph/pagerank.ts | 4 +-- src/openclaw-stub.ts | 13 +++++++++ src/recaller/recall.ts | 24 +++++++++++----- src/store/store.ts | 2 +- test/assemble.test.ts | 10 +++---- test/extract.test.ts | 6 ++-- test/graph.test.ts | 14 +++++----- test/recall-community.test.ts | 10 +++---- test/store.test.ts | 4 +-- tsconfig.json | 14 ++++++---- 20 files changed, 177 insertions(+), 79 deletions(-) create mode 100644 direct-maintain.ts create mode 100644 src/openclaw-stub.ts diff --git a/direct-maintain.ts b/direct-maintain.ts new file mode 100644 index 0000000..1b52b17 --- /dev/null +++ b/direct-maintain.ts @@ -0,0 +1,37 @@ +import { getDb, closeDb } from "./src/store/db.js"; +import { runMaintenance } from "./src/graph/maintenance.js"; +import { DEFAULT_CONFIG } from "./src/types.js"; +import { createCompleteFn } from "./src/engine/llm.js"; +import { createEmbedFn } from "./src/engine/embed.js"; +import { readFileSync } from "fs"; + +const envPath = `${process.env.HOME}/.hermes/graph-memory.env`; +const envText = readFileSync(envPath, "utf-8"); +const getEnv = (key: string) => { + const m = envText.match(new RegExp(`${key}=(.+)`)); + return m ? m[1].trim() : undefined; +}; + +const dbPath = `${process.env.HOME}/.hermes/graph-memory.db`; + +const llm = createCompleteFn({ + apiKey: getEnv("GRAPH_MEMORY_LLM_API_KEY"), + baseURL: getEnv("GRAPH_MEMORY_LLM_BASE_URL"), + model: getEnv("GRAPH_MEMORY_LLM_MODEL"), +}); + +const embed = await createEmbedFn({ + apiKey: getEnv("GRAPH_MEMORY_EMBED_API_KEY"), + baseURL: getEnv("GRAPH_MEMORY_EMBED_BASE_URL"), + model: getEnv("GRAPH_MEMORY_EMBED_MODEL"), +}); + +const cfg = { ...DEFAULT_CONFIG, dbPath }; +const db = getDb(dbPath); + +console.log("Starting direct maintenance..."); +const start = Date.now(); +const result = await runMaintenance(db, cfg, llm, embed || undefined); +console.log(`Done in ${Date.now() - start}ms`); +console.log(JSON.stringify(result, null, 2)); +closeDb(); diff --git a/index.ts b/index.ts index 1c0cbb4..6a93773 100755 --- a/index.ts +++ b/index.ts @@ -11,24 +11,24 @@ */ import type { OpenClawPluginApi } from "openclaw/plugin-sdk"; import { Type } from "@sinclair/typebox"; -import { getDb } from "./src/store/db.ts"; +import { getDb } from "./src/store/db.js"; import { saveMessage, getUnextracted, markExtracted, upsertNode, upsertEdge, findByName, getBySession, edgesFrom, edgesTo, deprecate, getStats, -} from "./src/store/store.ts"; -import { createCompleteFn } from "./src/engine/llm.ts"; -import { createEmbedFn } from "./src/engine/embed.ts"; -import { Recaller } from "./src/recaller/recall.ts"; -import { Extractor } from "./src/extractor/extract.ts"; -import { assembleContext } from "./src/format/assemble.ts"; -import { sanitizeToolUseResultPairing } from "./src/format/transcript-repair.ts"; -import { runMaintenance } from "./src/graph/maintenance.ts"; -import { invalidateGraphCache, computeGlobalPageRank } from "./src/graph/pagerank.ts"; -import { detectCommunities } from "./src/graph/community.ts"; -import { DEFAULT_CONFIG, type GmConfig } from "./src/types.ts"; +} from "./src/store/store.js"; +import { createCompleteFn } from "./src/engine/llm.js"; +import { createEmbedFn } from "./src/engine/embed.js"; +import { Recaller } from "./src/recaller/recall.js"; +import { Extractor } from "./src/extractor/extract.js"; +import { assembleContext } from "./src/format/assemble.js"; +import { sanitizeToolUseResultPairing } from "./src/format/transcript-repair.js"; +import { runMaintenance } from "./src/graph/maintenance.js"; +import { invalidateGraphCache, computeGlobalPageRank } from "./src/graph/pagerank.js"; +import { detectCommunities } from "./src/graph/community.js"; +import { DEFAULT_CONFIG, type GmConfig } from "./src/types.js"; // ─── 从 OpenClaw config 读 provider/model ──────────────────── @@ -147,10 +147,12 @@ const graphMemoryPlugin = { recaller.setEmbedFn(fn); api.logger.info("[graph-memory] vector search ready"); } else { + recaller.setEmbedFn(null); api.logger.info("[graph-memory] FTS5 search mode (配置 embedding 可启用语义搜索)"); } }) .catch(() => { + recaller.setEmbedFn(null); api.logger.info("[graph-memory] FTS5 search mode"); }); @@ -479,7 +481,7 @@ const graphMemoryPlugin = { if (comm.communities.size > 0) { (async () => { try { - const { summarizeCommunities } = await import("./src/graph/community.ts"); + const { summarizeCommunities } = await import("./src/graph/community.js"); const embedFn = (recaller as any).embed ?? undefined; const summaries = await summarizeCommunities(db, comm.communities, llm, embedFn); api.logger.info( diff --git a/package.json b/package.json index 3f271fa..23162e6 100755 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "graph-memory", "version": "1.5.8", "description": "Knowledge Graph Memory Engine for OpenClaw — with Personalized PageRank, community detection, and vector dedup", - "main": "index.ts", + "main": "dist/index.js", "type": "module", "scripts": { "build": "tsc", @@ -24,7 +24,7 @@ }, "openclaw": { "extensions": [ - "./index.ts" + "./dist/index.js" ], "hooks": {} } diff --git a/src/engine/embed.ts b/src/engine/embed.ts index 4effcb7..6c9d9b9 100755 --- a/src/engine/embed.ts +++ b/src/engine/embed.ts @@ -16,7 +16,7 @@ * 内置:429/5xx 重试 3 次 + 10s 超时 */ -import type { EmbeddingConfig } from "../types.ts"; +import type { EmbeddingConfig } from "../types.js"; export type EmbedFn = (text: string) => Promise; diff --git a/src/engine/llm.ts b/src/engine/llm.ts index de7fd48..1356fb0 100755 --- a/src/engine/llm.ts +++ b/src/engine/llm.ts @@ -44,24 +44,58 @@ async function fetchRetry(url: string, init: RequestInit, retries = 3, timeoutMs throw new Error("[graph-memory] fetch failed after retries"); } +// ─── 从 model 字符串解析 provider ──────────────────────────── + +function resolveProviderModel(rawModel?: string): { provider: string; model: string } { + const raw = rawModel ?? "anthropic/claude-haiku-4-5-20251001"; + if (raw.includes("/")) { + const [provider, ...rest] = raw.split("/"); + const model = rest.join("/").trim(); + if (provider?.trim() && model) { + return { provider: provider.trim(), model }; + } + } + return { provider: "anthropic", model: raw }; +} + // ─── CompleteFn 工厂 ──────────────────────────────────────── export function createCompleteFn( - provider: string, - model: string, + providerOrConfig: string | LlmConfig, + model?: string, llmConfig?: LlmConfig, anthropicApiKey?: string, ): CompleteFn { + // 支持直接传入 LlmConfig 对象(如 direct-maintain.ts 的用法) + let provider: string; + let finalModel: string; + let finalLlmConfig: LlmConfig | undefined; + let finalAnthropicKey: string | undefined; + + if (typeof providerOrConfig === "string") { + provider = providerOrConfig; + finalModel = model!; + finalLlmConfig = llmConfig; + finalAnthropicKey = anthropicApiKey; + } else { + const cfg = providerOrConfig; + finalLlmConfig = cfg; + finalAnthropicKey = undefined; + const resolved = resolveProviderModel(cfg.model); + provider = resolved.provider; + finalModel = resolved.model; + } + return async (system, user) => { // ── 路径 A(优先):pluginConfig.llm 直接调 OpenAI 兼容 API ── - if (llmConfig?.apiKey && llmConfig?.baseURL) { - const baseURL = llmConfig.baseURL.replace(/\/+$/, ""); - const llmModel = llmConfig.model ?? model; + if (finalLlmConfig?.apiKey && finalLlmConfig?.baseURL) { + const baseURL = finalLlmConfig.baseURL.replace(/\/+$/, ""); + const llmModel = finalLlmConfig.model ?? finalModel; const res = await fetchRetry(`${baseURL}/chat/completions`, { method: "POST", headers: { "Content-Type": "application/json", - "Authorization": `Bearer ${llmConfig.apiKey}`, + "Authorization": `Bearer ${finalLlmConfig.apiKey}`, }, body: JSON.stringify({ model: llmModel, @@ -83,15 +117,15 @@ export function createCompleteFn( } // ── 路径 B:Anthropic API ────────────────────────────── - if (!anthropicApiKey) { + if (!finalAnthropicKey) { throw new Error( "[graph-memory] No LLM available. 在 openclaw.json 的 graph-memory config 中配置 llm.apiKey + llm.baseURL", ); } const res = await fetchRetry("https://api.anthropic.com/v1/messages", { method: "POST", - headers: { "Content-Type": "application/json", "x-api-key": anthropicApiKey, "anthropic-version": "2023-06-01" }, - body: JSON.stringify({ model: llmConfig?.model ?? model, max_tokens: 4096, system, messages: [{ role: "user", content: user }] }), + headers: { "Content-Type": "application/json", "x-api-key": finalAnthropicKey, "anthropic-version": "2023-06-01" }, + body: JSON.stringify({ model: finalLlmConfig?.model ?? finalModel, max_tokens: 4096, system, messages: [{ role: "user", content: user }] }), }); if (!res.ok) throw new Error(`[graph-memory] Anthropic API ${res.status}`); const data = await res.json() as any; diff --git a/src/extractor/extract.ts b/src/extractor/extract.ts index 4bda0ea..a18b359 100755 --- a/src/extractor/extract.ts +++ b/src/extractor/extract.ts @@ -5,8 +5,8 @@ * Email: Wywelljob@gmail.com */ -import type { GmConfig, ExtractionResult, FinalizeResult } from "../types.ts"; -import type { CompleteFn } from "../engine/llm.ts"; +import type { GmConfig, ExtractionResult, FinalizeResult } from "../types.js"; +import type { CompleteFn } from "../engine/llm.js"; // ─── 节点/边合法值 ────────────────────────────────────────────── diff --git a/src/format/assemble.ts b/src/format/assemble.ts index 8d0aa78..80e276d 100755 --- a/src/format/assemble.ts +++ b/src/format/assemble.ts @@ -6,8 +6,8 @@ */ import { DatabaseSync, type DatabaseSyncInstance } from "@photostructure/sqlite"; -import type { GmNode, GmEdge } from "../types.ts"; -import { getCommunitySummary, getEpisodicMessages } from "../store/store.ts"; +import type { GmNode, GmEdge } from "../types.js"; +import { getCommunitySummary, getEpisodicMessages } from "../store/store.js"; const CHARS_PER_TOKEN = 3; diff --git a/src/graph/community.ts b/src/graph/community.ts index 4382fec..fa8b890 100755 --- a/src/graph/community.ts +++ b/src/graph/community.ts @@ -25,7 +25,7 @@ */ import { DatabaseSync, type DatabaseSyncInstance } from "@photostructure/sqlite"; -import { updateCommunities } from "../store/store.ts"; +import { updateCommunities } from "../store/store.js"; export interface CommunityResult { labels: Map; @@ -166,9 +166,9 @@ export function getCommunityPeers(db: DatabaseSyncInstance, nodeId: string, limi // ─── 社区描述生成 ──────────────────────────────────────────── -import type { CompleteFn } from "../engine/llm.ts"; -import type { EmbedFn } from "../engine/embed.ts"; -import { upsertCommunitySummary, pruneCommunitySummaries } from "../store/store.ts"; +import type { CompleteFn } from "../engine/llm.js"; +import type { EmbedFn } from "../engine/embed.js"; +import { upsertCommunitySummary, pruneCommunitySummaries } from "../store/store.js"; const COMMUNITY_SUMMARY_SYS = `你是知识图谱摘要引擎。根据节点列表,用简短的描述概括这组节点的主题领域。 要求: diff --git a/src/graph/dedup.ts b/src/graph/dedup.ts index 79d93e7..2cc571a 100755 --- a/src/graph/dedup.ts +++ b/src/graph/dedup.ts @@ -24,8 +24,8 @@ */ import { DatabaseSync, type DatabaseSyncInstance } from "@photostructure/sqlite"; -import type { GmConfig, GmNode } from "../types.ts"; -import { findById, mergeNodes, getAllVectors } from "../store/store.ts"; +import type { GmConfig, GmNode } from "../types.js"; +import { findById, mergeNodes, getAllVectors } from "../store/store.js"; export interface DuplicatePair { nodeA: string; diff --git a/src/graph/maintenance.ts b/src/graph/maintenance.ts index 1e6629f..22cfd98 100755 --- a/src/graph/maintenance.ts +++ b/src/graph/maintenance.ts @@ -16,12 +16,12 @@ */ import { DatabaseSync, type DatabaseSyncInstance } from "@photostructure/sqlite"; -import type { GmConfig } from "../types.ts"; -import type { CompleteFn } from "../engine/llm.ts"; -import type { EmbedFn } from "../engine/embed.ts"; -import { computeGlobalPageRank, invalidateGraphCache, type GlobalPageRankResult } from "./pagerank.ts"; -import { detectCommunities, summarizeCommunities, type CommunityResult } from "./community.ts"; -import { dedup, type DedupResult } from "./dedup.ts"; +import type { GmConfig } from "../types.js"; +import type { CompleteFn } from "../engine/llm.js"; +import type { EmbedFn } from "../engine/embed.js"; +import { computeGlobalPageRank, invalidateGraphCache, type GlobalPageRankResult } from "./pagerank.js"; +import { detectCommunities, summarizeCommunities, type CommunityResult } from "./community.js"; +import { dedup, type DedupResult } from "./dedup.js"; export interface MaintenanceResult { dedup: DedupResult; diff --git a/src/graph/pagerank.ts b/src/graph/pagerank.ts index 6db4bd3..6df9208 100755 --- a/src/graph/pagerank.ts +++ b/src/graph/pagerank.ts @@ -28,8 +28,8 @@ */ import { DatabaseSync, type DatabaseSyncInstance } from "@photostructure/sqlite"; -import type { GmConfig } from "../types.ts"; -import { updatePageranks } from "../store/store.ts"; +import type { GmConfig } from "../types.js"; +import { updatePageranks } from "../store/store.js"; // ─── 图结构缓存(避免每次 recall 都查 SQL) ───────────────── diff --git a/src/openclaw-stub.ts b/src/openclaw-stub.ts new file mode 100644 index 0000000..35b3de9 --- /dev/null +++ b/src/openclaw-stub.ts @@ -0,0 +1,13 @@ +// Stub for openclaw/plugin-sdk to allow tsc build without the peer dependency +export interface OpenClawPluginApi { + pluginConfig?: unknown; + config?: unknown; + logger: { + info: (msg: string) => void; + error: (msg: string) => void; + warn: (msg: string) => void; + }; + on: (event: string, handler: (...args: any[]) => any) => void; + registerContextEngine: (name: string, factory: () => any) => void; + registerTool: (factory: (ctx: any) => any, meta?: { name?: string }) => void; +} diff --git a/src/recaller/recall.ts b/src/recaller/recall.ts index b89188d..173fcf7 100755 --- a/src/recaller/recall.ts +++ b/src/recaller/recall.ts @@ -18,25 +18,35 @@ import { DatabaseSync, type DatabaseSyncInstance } from "@photostructure/sqlite"; import { createHash } from "crypto"; -import type { GmConfig, RecallResult, GmNode, GmEdge } from "../types.ts"; -import type { EmbedFn } from "../engine/embed.ts"; +import type { GmConfig, RecallResult, GmNode, GmEdge } from "../types.js"; +import type { EmbedFn } from "../engine/embed.js"; import { searchNodes, vectorSearchWithScore, graphWalk, communityRepresentatives, communityVectorSearch, nodesByCommunityIds, saveVector, getVectorHash, -} from "../store/store.ts"; -import { getCommunityPeers } from "../graph/community.ts"; -import { personalizedPageRank } from "../graph/pagerank.ts"; +} from "../store/store.js"; +import { getCommunityPeers } from "../graph/community.js"; +import { personalizedPageRank } from "../graph/pagerank.js"; export class Recaller { private embed: EmbedFn | null = null; + private embedReady: Promise; + private resolveReady!: () => void; - constructor(private db: DatabaseSyncInstance, private cfg: GmConfig) {} + constructor(private db: DatabaseSyncInstance, private cfg: GmConfig) { + this.embedReady = new Promise((resolve) => { + this.resolveReady = resolve; + }); + } - setEmbedFn(fn: EmbedFn): void { this.embed = fn; } + setEmbedFn(fn: EmbedFn | null): void { + this.embed = fn; + this.resolveReady(); + } async recall(query: string): Promise { + await this.embedReady; const limit = this.cfg.recallMaxNodes; // ── 两条路径各自独立跑满,不分配额 ────────────────── diff --git a/src/store/store.ts b/src/store/store.ts index ec472a9..d474214 100755 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -7,7 +7,7 @@ import { DatabaseSync, type DatabaseSyncInstance } from "@photostructure/sqlite"; import { createHash } from "crypto"; -import type { GmNode, GmEdge, EdgeType, NodeType, Signal } from "../types.ts"; +import type { GmNode, GmEdge, EdgeType, NodeType, Signal } from "../types.js"; // ─── 工具 ───────────────────────────────────────────────────── diff --git a/test/assemble.test.ts b/test/assemble.test.ts index 6550620..7821799 100755 --- a/test/assemble.test.ts +++ b/test/assemble.test.ts @@ -7,11 +7,11 @@ import { describe, it, expect, beforeEach } from "vitest"; import { DatabaseSync, type DatabaseSyncInstance } from "@photostructure/sqlite"; -import { createTestDb, insertNode, insertEdge } from "./helpers.ts"; -import { assembleContext, buildSystemPromptAddition } from "../src/format/assemble.ts"; -import { sanitizeToolUseResultPairing } from "../src/format/transcript-repair.ts"; -import { findById } from "../src/store/store.ts"; -import type { GmNode, GmEdge } from "../src/types.ts"; +import { createTestDb, insertNode, insertEdge } from "./helpers.js"; +import { assembleContext, buildSystemPromptAddition } from "../src/format/assemble.js"; +import { sanitizeToolUseResultPairing } from "../src/format/transcript-repair.js"; +import { findById } from "../src/store/store.js"; +import type { GmNode, GmEdge } from "../src/types.js"; let db: DatabaseSyncInstance; diff --git a/test/extract.test.ts b/test/extract.test.ts index dd12ebc..35d4165 100755 --- a/test/extract.test.ts +++ b/test/extract.test.ts @@ -11,9 +11,9 @@ */ import { describe, it, expect } from "vitest"; -import { Extractor } from "../src/extractor/extract.ts"; -import { DEFAULT_CONFIG } from "../src/types.ts"; -import type { ExtractionResult, FinalizeResult } from "../src/types.ts"; +import { Extractor } from "../src/extractor/extract.js"; +import { DEFAULT_CONFIG } from "../src/types.js"; +import type { ExtractionResult, FinalizeResult } from "../src/types.js"; // ─── Mock LLM:直接返回预设 JSON ──────────────────────────────── diff --git a/test/graph.test.ts b/test/graph.test.ts index d70dbae..88a709a 100755 --- a/test/graph.test.ts +++ b/test/graph.test.ts @@ -9,13 +9,13 @@ import { describe, it, expect, beforeEach } from "vitest"; import { DatabaseSync, type DatabaseSyncInstance } from "@photostructure/sqlite"; -import { createTestDb, insertNode, insertEdge } from "./helpers.ts"; -import { personalizedPageRank, computeGlobalPageRank, invalidateGraphCache } from "../src/graph/pagerank.ts"; -import { detectCommunities, getCommunityPeers } from "../src/graph/community.ts"; -import { detectDuplicates, dedup } from "../src/graph/dedup.ts"; -import { runMaintenance } from "../src/graph/maintenance.ts"; -import { saveVector } from "../src/store/store.ts"; -import { DEFAULT_CONFIG, type GmConfig } from "../src/types.ts"; +import { createTestDb, insertNode, insertEdge } from "./helpers.js"; +import { personalizedPageRank, computeGlobalPageRank, invalidateGraphCache } from "../src/graph/pagerank.js"; +import { detectCommunities, getCommunityPeers } from "../src/graph/community.js"; +import { detectDuplicates, dedup } from "../src/graph/dedup.js"; +import { runMaintenance } from "../src/graph/maintenance.js"; +import { saveVector } from "../src/store/store.js"; +import { DEFAULT_CONFIG, type GmConfig } from "../src/types.js"; let db: DatabaseSyncInstance; const cfg: GmConfig = { ...DEFAULT_CONFIG }; diff --git a/test/recall-community.test.ts b/test/recall-community.test.ts index 4983cb7..88d3792 100755 --- a/test/recall-community.test.ts +++ b/test/recall-community.test.ts @@ -14,15 +14,15 @@ import { describe, it, expect, beforeEach } from "vitest"; import { DatabaseSync, type DatabaseSyncInstance } from "@photostructure/sqlite"; -import { createTestDb, insertNode, insertEdge } from "./helpers.ts"; +import { createTestDb, insertNode, insertEdge } from "./helpers.js"; import { findById, vectorSearchWithScore, communityRepresentatives, saveVector, upsertCommunitySummary, getCommunitySummary, getAllCommunitySummaries, pruneCommunitySummaries, -} from "../src/store/store.ts"; -import { detectCommunities, getCommunityPeers } from "../src/graph/community.ts"; -import { assembleContext } from "../src/format/assemble.ts"; -import type { GmNode } from "../src/types.ts"; +} from "../src/store/store.js"; +import { detectCommunities, getCommunityPeers } from "../src/graph/community.js"; +import { assembleContext } from "../src/format/assemble.js"; +import type { GmNode } from "../src/types.js"; let db: DatabaseSyncInstance; diff --git a/test/store.test.ts b/test/store.test.ts index 0c21fdf..f2cddd6 100755 --- a/test/store.test.ts +++ b/test/store.test.ts @@ -7,7 +7,7 @@ import { describe, it, expect, beforeEach } from "vitest"; import { DatabaseSync, type DatabaseSyncInstance } from "@photostructure/sqlite"; -import { createTestDb, insertNode, insertEdge } from "./helpers.ts"; +import { createTestDb, insertNode, insertEdge } from "./helpers.js"; import { findByName, findById, upsertNode, upsertEdge, deprecate, mergeNodes, edgesFrom, edgesTo, allActiveNodes, allEdges, @@ -15,7 +15,7 @@ import { saveMessage, getMessages, getUnextracted, markExtracted, saveSignal, pendingSignals, markSignalsDone, getStats, saveVector, vectorSearch, getAllVectors, -} from "../src/store/store.ts"; +} from "../src/store/store.js"; let db: DatabaseSyncInstance; diff --git a/tsconfig.json b/tsconfig.json index cd8b536..cc714cd 100755 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,18 @@ { "compilerOptions": { "target": "ES2022", - "module": "ESNext", - "moduleResolution": "bundler", + "module": "NodeNext", + "moduleResolution": "NodeNext", "esModuleInterop": true, - "allowImportingTsExtensions": true, - "noEmit": true, "strict": true, "skipLibCheck": true, "outDir": "dist", "rootDir": ".", - "baseUrl": "." + "baseUrl": ".", + "paths": { + "openclaw/plugin-sdk": ["./src/openclaw-stub.ts"] + } }, - "include": ["src/**/*.ts", "index.ts", "test/**/*.ts"] + "include": ["src/**/*.ts", "index.ts"], + "exclude": ["test/**/*.ts", "dist", "node_modules"] }