UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

1,125 lines (1,124 loc) • 44.7 kB
import { C as asNullableRecord, S as resolveGlobalSingleton, g as normalizeLowercaseStringOrEmpty, w as asOptionalObjectRecord, y as normalizeOptionalString } from "./number-coercion-CmpLVzZ6.mjs"; import { M as isMissingPathError, it as pruneMapToMaxSize } from "./redact-TLu2QTmL.mjs"; import "./errors-BwnhmTMH.mjs"; import { t as LruCache } from "./lru-cache-C3Xgvc4Q.mjs"; import { t as createDeferredCore } from "./deferred-D0La5CRk.mjs"; import { i as trackAsyncWork, t as AsyncWorkScope } from "./async-work-scope-6N1oTppj.mjs"; import { n as registerListener } from "./listeners-BogSNJ-R.mjs"; import { createRequire } from "node:module"; import fsSync, { default as openClawRootFsSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { readFileWindowFullySync } from "@openclaw/fs-safe/advanced"; import { AsyncLocalStorage } from "node:async_hooks"; import "@openclaw/fs-safe/path"; //#region src/infra/sqlite-error-diagnostics.ts const inspectionOperations = resolveGlobalSingleton(Symbol.for("openclaw.sqliteInspectionOperations"), () => /* @__PURE__ */ new WeakMap()); const nativeOpenFailures = resolveGlobalSingleton(Symbol.for("openclaw.sqliteNativeOpenFailures"), () => /* @__PURE__ */ new WeakSet()); function markSqliteNativeOpenFailure(error) { if (error !== null && typeof error === "object") nativeOpenFailures.add(error); } /** Record the native effect without changing its error or tagging surrounding authority checks. */ function withSqliteNativeOpen(open) { try { return open(); } catch (error) { markSqliteNativeOpenFailure(error); throw error; } } function isSqliteNativeOpenFailure(error) { return error !== null && typeof error === "object" && nativeOpenFailures.has(error); } function markSqliteInspectionOperation(error, operation) { if (error !== null && typeof error === "object" && !inspectionOperations.has(error)) inspectionOperations.set(error, operation); return error; } const SQLITE_LOCK_ERROR_CODES = /* @__PURE__ */ new Set(["SQLITE_BUSY", "SQLITE_LOCKED"]); const SQLITE_BUSY_RESULT_CODE = 5; const SQLITE_LOCKED_RESULT_CODE = 6; const SQLITE_CORRUPT_RESULT_CODE = 11; const SQLITE_NOTADB_RESULT_CODE = 26; const SQLITE_PRIMARY_RESULT_CODE_MASK = 255; function sqliteErrorCode(error) { const code = asOptionalObjectRecord(error)?.code; return typeof code === "string" ? code : void 0; } function sqliteExtendedResultCode(error) { const errcode = asOptionalObjectRecord(error)?.errcode; return typeof errcode === "number" && Number.isInteger(errcode) ? errcode : void 0; } function sqlitePrimaryResultCode(error) { const errcode = sqliteExtendedResultCode(error); return errcode === void 0 ? void 0 : errcode & SQLITE_PRIMARY_RESULT_CODE_MASK; } function isSqliteLockError(error) { const code = sqliteErrorCode(error); if (code !== void 0 && SQLITE_LOCK_ERROR_CODES.has(code)) return true; const primaryCode = sqlitePrimaryResultCode(error); return primaryCode === SQLITE_BUSY_RESULT_CODE || primaryCode === SQLITE_LOCKED_RESULT_CODE; } /** Report proven file damage (corrupt page or non-database header), not transient failure. */ function isSqliteCorruptionError(error) { const primaryCode = sqlitePrimaryResultCode(error); return primaryCode === SQLITE_CORRUPT_RESULT_CODE || primaryCode === SQLITE_NOTADB_RESULT_CODE; } //#endregion //#region src/state/openclaw-state-db-contract.ts const DOCTOR_OWNED_STATE_TABLES = ["agent_deletion_journal"]; const FIRST_USE_STATE_TABLES = [ "user_profile_identities", "local_workspace_projections", "update_runs", "session_repository_workspaces", "github_repository_publication_requests", "github_publication_session_lifecycles", "skill_library_entries", "skill_library_revisions", "skill_library_events", "skill_library_uploads", "github_personal_publication_requests", "cron_job_runtime_authorities", "cron_run_trigger_state_retirements", "execution_identity_contexts", "mcp_oauth_pending_authorizations", "node_worker_launch_containers", "node_worker_launch_cleanup", "node_worker_launch_process_scopes", "node_worker_launches", "node_worker_prepared_workspaces", "node_worker_turns", "operator_approval_execution_identities", "operator_approval_standing_grants", "operator_approval_standing_grant_generations", "web_push_approval_deliveries", "execution_decision_facts", "execution_owner_lifecycle_bindings", "outbound_message_execution_bindings", "outbound_message_progress" ]; const FIRST_USE_STATE_INDEXES = [ "idx_user_profile_identities_profile_id", "idx_user_profile_identities_authorization", "idx_update_runs_created", "idx_update_runs_active", "idx_github_repository_publication_shared_request", "idx_github_repository_publication_personal_request", "idx_github_personal_publication_owner_session", "idx_github_personal_publication_pending", "idx_node_worker_launches_terminal_completed", "idx_node_worker_turns_terminal_completed", "idx_node_worker_turns_active_owner", "idx_operator_approval_standing_grants_binding", "idx_web_push_approval_deliveries_subscription", "execution_identity_contexts_run_created_idx", "execution_decision_facts_context_occurred_idx", "execution_decision_facts_run_occurred_idx", "outbound_message_execution_bindings_execution_event_idx", "outbound_message_progress_occurred_idx", "outbound_message_progress_run_occurred_idx" ]; const LAZY_ADDITIVE_STATE_TABLES = [ ...FIRST_USE_STATE_TABLES, "agent_provenance", "cron_run_receipts", "config_revision_keys", "secret_store_entries", "projects", "worktree_templates", "user_preferences", "device_pair_setup_completions", "github_publication_requests", "device_pairing_join_codes", "skill_workshop_proposal_events", "skill_workshop_collection_reviews", "skill_workshop_proposal_rollbacks", "skill_workshop_proposals", "worker_environment_ssh_fallback_ports", "worker_session_placement_moves" ]; const LAZY_ADDITIVE_STATE_INDEXES = [ ...FIRST_USE_STATE_INDEXES, "idx_cron_run_receipts_active_job", "idx_cron_run_receipts_job_history", "idx_github_publication_requests_pending", "secret_store_entries_live_idx", "idx_skill_workshop_collection_reviews_owner_time" ]; /** Maximum time one synchronous SQLite call may wait for a lock. */ const OPENCLAW_SQLITE_BUSY_TIMEOUT_MS = 5e3; /** User-facing guide for schema refusals; lives here so error sites avoid import cycles. */ const OPENCLAW_DATABASE_SCHEMA_DOCS_URL = "https://docs.openclaw.ai/reference/database-schemas"; //#endregion //#region src/infra/git-root.ts function walkUpFrom(startDir, opts, resolveAtDir) { let current = path.resolve(startDir); for (let i = 0; opts.maxDepth === void 0 || i < opts.maxDepth; i += 1) { const resolved = resolveAtDir(current); if (resolved !== null && resolved !== void 0) return resolved; const parent = path.dirname(current); if (parent === current) break; current = parent; } return null; } function resolveGitDirFromMarker(repoRoot) { const gitPath = path.join(repoRoot, ".git"); try { const stat = fsSync.statSync(gitPath); if (stat.isDirectory()) return gitPath; if (!stat.isFile()) return null; const match = fsSync.readFileSync(gitPath, "utf-8").match(/gitdir:\s*(.+)/i); if (!match?.[1]) return null; return path.resolve(repoRoot, match[1].trim()); } catch { return null; } } function resolveGitHeadPath(startDir, opts = {}) { return walkUpFrom(startDir, opts, (repoRoot) => { const gitDir = resolveGitDirFromMarker(repoRoot); return gitDir ? path.join(gitDir, "HEAD") : null; }); } /** Read at most `limit` bytes from Git or build metadata. */ function readGitMetadataPrefix(filePath, limit = 256) { const fd = fsSync.openSync(filePath, "r"); try { const buf = Buffer.alloc(limit); const bytesRead = readFileWindowFullySync(fd, buf, 0); return buf.subarray(0, bytesRead).toString("utf-8"); } finally { fsSync.closeSync(fd); } } function readGitHead(startDir, opts = {}) { const headPath = resolveGitHeadPath(startDir, opts); if (!headPath) return; const head = fsSync.readFileSync(headPath, "utf-8").trim(); if (!head.startsWith("ref:")) return { headPath, ref: null, value: head || null }; const ref = head.replace(/^ref:\s*/i, "").trim(); const refsBase = resolveGitRefsBase(headPath); return { headPath, ref, value: readGitRefs(refsBase, [ref]).get(ref) ?? null, refsBase }; } function resolveGitRefsBase(headPath) { const gitDir = path.dirname(headPath); try { const commonDir = readGitMetadataPrefix(path.join(gitDir, "commondir")).trim(); if (commonDir) return path.resolve(gitDir, commonDir); } catch (error) { if (!isMissingPathError(error)) throw error; } return gitDir; } /** Raw ref contents, sharing one packed inventory across the requested names. */ function readGitRefs(refsBase, refs) { const values = new Map(refs.map((ref) => [ref, null])); const missing = /* @__PURE__ */ new Set(); for (const ref of refs) { const refPath = resolveRefPath(refsBase, ref); if (!refPath) continue; try { values.set(ref, readGitMetadataPrefix(refPath).trim()); } catch (error) { if (!isMissingPathError(error)) throw error; missing.add(ref); } } if (missing.size === 0) return values; try { const packedRefs = fsSync.readFileSync(path.join(refsBase, "packed-refs"), "utf-8"); for (const line of packedRefs.split("\n")) { if (!line || line.startsWith("#") || line.startsWith("^")) continue; const [value, packedRef] = line.trim().split(/\s+/, 2); if (packedRef && missing.delete(packedRef)) values.set(packedRef, value ?? null); } } catch (error) { if (!isMissingPathError(error)) throw error; } return values; } /** Safely resolve a Git ref path, rejecting traversal from a crafted HEAD file. */ function resolveRefPath(refsBase, ref) { if (!ref.startsWith("refs/")) return null; if (path.isAbsolute(ref)) return null; if (ref.split(/[/]/).includes("..")) return null; const resolved = path.resolve(refsBase, ref); const rel = path.relative(refsBase, resolved); if (!rel || rel.startsWith("..") || path.isAbsolute(rel)) return null; return resolved; } //#endregion //#region src/plugins/host-hook-cleanup-result.ts /** Merge raw instance outcomes without duplicating their existing host or instance report. */ function appendPluginInstanceCleanupFailures(failures, pluginId, result) { for (const error of result.errors) { if (failures.some((failure) => failure.pluginId === pluginId && failure.error === error && (failure.hookId === "instance" || result.hostCleanupErrors?.includes(error)))) continue; failures.push({ pluginId, hookId: "instance", error }); } } //#endregion //#region src/plugins/plugin-cache-artifacts.ts function createPluginCacheArtifacts() { return { moduleLoaders: /* @__PURE__ */ new Map(), sources: /* @__PURE__ */ new Map(), sourceAliases: /* @__PURE__ */ new Map(), runtimeRecordRoots: /* @__PURE__ */ new WeakMap() }; } function createPluginRootArtifacts() { return { artifactLoadsInProgress: /* @__PURE__ */ new Set(), artifacts: /* @__PURE__ */ new Map(), runtimeArtifacts: /* @__PURE__ */ new Map(), entryBoundaries: /* @__PURE__ */ new Map(), entryPaths: /* @__PURE__ */ new Map() }; } //#endregion //#region src/plugins/plugin-cache-sdk.ts /** Derived SDK facts share the plugin cache lifetime; none owns a separate expiry. */ function createPluginCacheSdk() { return { hosts: /* @__PURE__ */ new Map(), contexts: /* @__PURE__ */ new Map(), packageNames: /* @__PURE__ */ new Map(), packageSearches: /* @__PURE__ */ new Map(), argvDirectories: /* @__PURE__ */ new Map(), devSourceRoots: /* @__PURE__ */ new Map(), runtimeModules: /* @__PURE__ */ new Map(), usableDistArtifacts: /* @__PURE__ */ new Map(), normalizedJitiAliases: /* @__PURE__ */ new Map(), aliasFacts: /* @__PURE__ */ new WeakMap(), native: { sdkProviders: /* @__PURE__ */ new Map(), nextSdkProviderOrder: 0, aliases: /* @__PURE__ */ new Map(), registeredHosts: /* @__PURE__ */ new Set(), hostRoots: /* @__PURE__ */ new Map(), nearestPackageRoots: /* @__PURE__ */ new Map(), loaderPackageRoots: /* @__PURE__ */ new Map(), allowedParentRoots: /* @__PURE__ */ new Map() } }; } resolveGlobalSingleton(Symbol.for("openclaw.pluginSourceRecoveryUnavailableError"), () => class SourceRecoveryUnavailableError extends Error { constructor(cause) { super("Captured plugin source is missing; its previous code cannot be recovered.", { cause }); this.name = "PluginSourceRecoveryUnavailableError"; } }); const PluginInstanceDrainTimeoutError = resolveGlobalSingleton(Symbol.for("openclaw.pluginInstanceDrainTimeoutError"), () => class DrainTimeoutError extends Error { constructor(message, settled, options, forcedRetirement) { super(message, options); this.settled = settled; this.forcedRetirement = forcedRetirement; this.name = "PluginInstanceDrainTimeoutError"; } }); //#endregion //#region src/plugins/plugin-instance-invocation.ts var InvocationFrame = class InvocationFrame { constructor(scopes, invocation = scopes.invocation) { this.invocation = invocation; this.metadataScope = scopes.metadataScope; this.cacheScope = scopes.cacheScope; this.sourceCaptureStorage = scopes.sourceCaptureStorage; } withScopes(scopes) { return new InvocationFrame(scopes); } }; /** Copy core scopes through the current owner so runtime-only fields survive. */ function createPluginExecutionFrame(scopes, current) { return current ? current.withScopes(scopes) : new InvocationFrame(scopes); } const pluginExecutionContext = resolveGlobalSingleton(Symbol.for("openclaw.pluginInstanceInvocation"), () => { const frames = new AsyncLocalStorage(); return { invocation: { getStore() { return frames.getStore()?.invocation; }, run(invocation, run) { const current = frames.getStore(); return frames.run(current?.invocation === invocation ? current : createPluginExecutionFrame({ ...current, invocation }, current), run); }, exit(run) { const current = frames.getStore(); return current?.invocation ? frames.run(current.withScopes({ ...current, invocation: void 0 }), run) : run(); } }, getFrame() { return frames.getStore(); }, runFrame(frame, run) { return frames.run(frame, run); } }; }); pluginExecutionContext.invocation; const getPluginExecutionFrame = pluginExecutionContext.getFrame; pluginExecutionContext.runFrame; //#endregion //#region src/plugins/plugin-cache.ts const PLUGIN_CACHE_FACT_INVALIDATED = "PLUGIN_CACHE_FACT_INVALIDATED"; /** Cached diagnostics must not retain the caller through V8's lazy stack frames. */ function materializePluginCacheError(failure) { let error = failure; const seen = /* @__PURE__ */ new Set(); while (error instanceof Error && !seen.has(error)) { seen.add(error); try { error.stack = String(error.stack); } catch { error.stack = "Stack trace unavailable: custom formatter failed"; } error = error.cause; } } /** Explicit fact invalidation cancels its preparation. */ var PluginCacheFactInvalidatedError = class extends Error { constructor(..._args) { super(..._args); this.code = PLUGIN_CACHE_FACT_INVALIDATED; } }; const state = resolveGlobalSingleton(Symbol.for("openclaw.pluginCache"), () => ({ snapshotOwners: /* @__PURE__ */ new WeakMap(), retirements: [] })); const cacheRetainers = resolveGlobalSingleton(Symbol.for("openclaw.pluginCacheRetainers"), () => /* @__PURE__ */ new WeakMap()); const instanceCacheOwners = resolveGlobalSingleton(Symbol.for("openclaw.pluginInstanceCacheOwners"), () => /* @__PURE__ */ new WeakMap()); /** A retiring inventory releases its own custody; terminal disposal releases every birth cache. */ function releasePluginCacheInstance(instance, cache) { const owners = instanceCacheOwners.get(instance); if (cache) { cache.instances.delete(instance); owners?.delete(cache); } else { for (const owner of owners ?? []) owner.instances.delete(instance); owners?.clear(); } if (owners?.size === 0) instanceCacheOwners.delete(instance); } function getPluginCacheRetainers(cache) { let retained = cacheRetainers.get(cache); if (!retained) { retained = { references: /* @__PURE__ */ new Set(), controller: new AbortController(), settled: createDeferredCore() }; cacheRetainers.set(cache, retained); } return retained; } /** Cache retirement revokes cached publications before waiting for admitted borrowers. */ function getPluginCacheRetirementSignal(cache) { return getPluginCacheRetainers(cache).controller.signal; } function createPluginMetadataCache() { return { current: { snapshot: void 0, owner: "operation", configFingerprint: void 0, agentWorkspaceFingerprint: void 0, envFingerprint: void 0, defaultDiscoveryCompatible: false, compatiblePolicyHashes: void 0, compatibleConfigFingerprints: void 0, revision: Symbol("plugin-metadata-snapshot"), configIdentities: /* @__PURE__ */ new WeakSet() }, snapshots: /* @__PURE__ */ new Map(), discovery: /* @__PURE__ */ new Map(), sharedDiscovery: /* @__PURE__ */ new Map(), projections: /* @__PURE__ */ new WeakMap(), projectionSources: /* @__PURE__ */ new WeakMap(), completions: /* @__PURE__ */ new WeakMap(), indexFacts: /* @__PURE__ */ new WeakMap(), providerPolicyOwners: /* @__PURE__ */ new WeakMap(), channelAdapters: /* @__PURE__ */ new WeakMap(), bundledChannelCatalogs: /* @__PURE__ */ new Map(), bundledProviderPolicySurfaces: /* @__PURE__ */ new Map(), staticCatalogStates: /* @__PURE__ */ new WeakMap(), modelSuppressionResolvers: /* @__PURE__ */ new WeakMap(), mcpAuthDeclarations: /* @__PURE__ */ new WeakMap() }; } /** Invalidate discovery facts without retiring callbacks owned by this operation. */ function invalidatePluginCacheMetadata(cache) { cache.sourceAdmissions?.invalidate(); cache.metadata = createPluginMetadataCache(); for (const root of cache.roots.values()) { root.files.clear(); root.checkedEntries.clear(); root.paths.clear(); root.directory = void 0; root.artifacts.clear(); root.runtimeArtifacts.clear(); root.entryBoundaries.clear(); root.entryPaths.clear(); } cache.rootAliases.clear(); cache.installRecords.clear(); cache.persistedInstalledIndex.clear(); cache.preparedBundledDiscoveryModes.clear(); cache.dependencyStatus = /* @__PURE__ */ new WeakMap(); } /** Each inventory owns its acquired facts and reusable load results; publication owns activation. */ function createPluginCache(options = {}) { return { async [Symbol.asyncDispose]() { await retirePluginCache(this); }, kind: options.kind ?? "operation", roots: /* @__PURE__ */ new Map(), rootAliases: /* @__PURE__ */ new Map(), sdk: createPluginCacheSdk(), setupModules: /* @__PURE__ */ new Map(), instances: /* @__PURE__ */ new Set(), metadata: createPluginMetadataCache(), installRecords: /* @__PURE__ */ new Map(), persistedInstalledIndex: /* @__PURE__ */ new Map(), preparedBundledDiscoveryModes: /* @__PURE__ */ new Map(), dependencyStatus: /* @__PURE__ */ new WeakMap(), ...createPluginCacheArtifacts() }; } function getProcessPluginCache() { return state.current ??= createPluginCache({ kind: "process" }); } function getScopedPluginCache() { return getPluginExecutionFrame()?.cacheScope?.cache; } /** Installation refreshes every enclosing operation, including callers outside metadata phases. */ function getScopedPluginCaches() { const caches = []; for (let scope = getPluginExecutionFrame()?.cacheScope; scope; scope = scope.parent) caches.push(scope.cache); return caches; } function getPluginCache() { return getScopedPluginCache() ?? getProcessPluginCache(); } /** Stop new setup calls immediately; the owner awaits in-flight calls and graph cleanup. */ function retirePluginCache(cache, beforeRetire) { const retained = getPluginCacheRetainers(cache); if (retained.retirement) return retained.retirement; const completion = createDeferredCore(); retained.retirement = completion.promise; const trackRetirement = async (run) => { const work = new AsyncWorkScope(); try { return await work.track(run); } finally { await work.run(() => work.drain()); } }; retained.beginRetirement = (track = trackAsyncWork) => { let admitted = false; track(() => { admitted = true; retained.beginRetirement = void 0; return beginPluginCacheRetirement(cache, beforeRetire); }).then(completion.resolve, (error) => { if (admitted) completion.reject(error); else retained.beginRetirement?.(trackRetirement); }); }; retained.controller.abort(); materializePluginCacheError(retained.controller.signal.reason); if (retained.references.size === 0) retained.beginRetirement?.(); else retained.settled.promise.then(() => { retained.beginRetirement?.(trackRetirement); }); return completion.promise; } function beginPluginCacheRetirement(cache, beforeRetire) { if (cache.retirement) return cache.retirement; const retirement = createDeferredCore(); cache.retirement = retirement.promise; const cleanup = async () => { beforeRetire?.(); const registries = cache.retireRegistryLoads?.(); const resources = /* @__PURE__ */ new Set([...cache.setupModules.values(), ...cache.instances]); for (const resource of resources) resource.quiesce(); const [registry] = await Promise.allSettled([registries]); const outcomes = await Promise.allSettled([...resources].map(async (resource) => ({ resource, result: await resource.dispose() }))); const nativeAdmissions = cache.sourceAdmissions; if (nativeAdmissions) { const pending = outcomes.flatMap((outcome) => outcome.status === "fulfilled" ? outcome.value.result.errors.flatMap((error) => error instanceof PluginInstanceDrainTimeoutError ? [error.settled] : []) : []); if (pending.length > 0) Promise.allSettled(pending).then(() => nativeAdmissions.dispose()).catch((error) => process.emitWarning(`Plugin native capture cleanup failed: ${String(error)}`)); else await nativeAdmissions.dispose(); } cache.setupModules.clear(); for (const instance of cache.instances) releasePluginCacheInstance(instance, cache); const unexpected = [...registry.status === "rejected" ? [registry.reason] : [], ...outcomes.flatMap((result) => result.status === "rejected" ? [result.reason] : [])]; if (unexpected.length) throw new AggregateError(unexpected, "Plugin cache resources failed to retire"); const host = registry.status === "fulfilled" ? registry.value : void 0; const failures = [...host?.failures ?? []]; for (const outcome of outcomes) { if (outcome.status !== "fulfilled") continue; const { resource, result } = outcome.value; appendPluginInstanceCleanupFailures(failures, resource.pluginId, result); } return { cleanupCount: host?.cleanupCount ?? 0, failures }; }; cleanup().then(retirement.resolve, retirement.reject); return retirement.promise; } function getPluginCacheRoot(rootDir) { const cache = getPluginCache(); const cached = cache.roots.get(cache.rootAliases.get(rootDir) ?? rootDir); if (cached) return cached; const lexical = path.resolve(rootDir); const key = cache.rootAliases.get(lexical) ?? lexical; let root = cache.roots.get(key); if (!root) { root = { rootDir: key, files: /* @__PURE__ */ new Map(), checkedEntries: /* @__PURE__ */ new Map(), paths: /* @__PURE__ */ new Map(), ...createPluginRootArtifacts() }; cache.roots.set(key, root); } return root; } function mergeRootFacts(target, source) { for (const [key, value] of source) if (!target.has(key)) target.set(key, value); } /** Bind aliases only after a checked file establishes their shared package boundary. */ function bindPluginCacheRoot(rootDir, canonicalRoot) { const cache = getPluginCache(); const lexical = path.resolve(rootDir); const canonical = path.resolve(canonicalRoot); const root = getPluginCacheRoot(lexical); root.rootDir = canonical; const existing = cache.roots.get(canonical); if (existing && existing !== root) { mergeRootFacts(root.files, existing.files); mergeRootFacts(root.checkedEntries, existing.checkedEntries); mergeRootFacts(root.paths, existing.paths); mergeRootFacts(root.artifacts, existing.artifacts); mergeRootFacts(root.runtimeArtifacts, existing.runtimeArtifacts); mergeRootFacts(root.entryBoundaries, existing.entryBoundaries); mergeRootFacts(root.entryPaths, existing.entryPaths); root.directory ??= existing.directory; root.publicSurfaceBoundary ??= existing.publicSurfaceBoundary; for (const artifact of existing.artifactLoadsInProgress) root.artifactLoadsInProgress.add(artifact); Object.assign(existing, root); } cache.roots.set(canonical, root); cache.rootAliases.set(lexical, canonical); return root; } //#endregion //#region src/infra/openclaw-root.ts function parsePackageName(raw) { const parsed = JSON.parse(raw); return typeof parsed.name === "string" ? parsed.name : null; } function readPackageNameSync(dir) { const packageNameCache = getPluginCache().sdk.packageNames; const packageJsonPath = path.join(path.resolve(dir), "package.json"); if (packageNameCache.has(packageJsonPath)) return packageNameCache.get(packageJsonPath) ?? null; try { const name = parsePackageName(openClawRootFsSync.readFileSync(packageJsonPath, "utf-8")); packageNameCache.set(packageJsonPath, name); return name; } catch { packageNameCache.set(packageJsonPath, null); return null; } } function findPackageRootSync(startDir, maxDepth = 12) { for (const current of iterAncestorDirs(startDir, maxDepth)) if (readPackageNameSync(current) === "openclaw") return current; return null; } function* iterAncestorDirs(startDir, maxDepth) { let current = path.resolve(startDir); for (let i = 0; i < maxDepth; i += 1) { yield current; if (path.basename(current) === "node_modules") break; const parent = path.dirname(current); if (parent === current) break; current = parent; } } function candidateDirsFromArgv1(argv1) { const argv1CandidateCache = getPluginCache().sdk.argvDirectories; const cacheKey = path.resolve(argv1); const cached = argv1CandidateCache.get(cacheKey); if (cached) return [...cached]; const normalized = cacheKey; const candidates = []; try { const resolved = openClawRootFsSync.realpathSync(normalized); if (resolved !== normalized) candidates.push(path.dirname(resolved)); } catch {} candidates.push(path.dirname(normalized)); const parts = normalized.split(path.sep); const binIndex = parts.lastIndexOf(".bin"); if (binIndex > 0 && parts[binIndex - 1] === "node_modules") { const binName = path.basename(normalized); const nodeModulesDir = parts.slice(0, binIndex).join(path.sep); candidates.push(path.join(nodeModulesDir, binName)); } const deduped = dedupeCandidates(candidates); argv1CandidateCache.set(cacheKey, deduped); return [...deduped]; } function resolveOpenClawPackageRootSync(opts) { const candidates = buildCandidates(opts); const cacheKey = candidates.join("\0"); const searches = getPluginCache().sdk.packageSearches; const cached = searches.get(cacheKey); if (cached?.all) return cached.all[0] ?? null; if (cached?.first !== void 0) return cached.first; for (const candidate of candidates) { const found = findPackageRootSync(candidate); if (found) { searches.set(cacheKey, { first: found }); return found; } } searches.set(cacheKey, { first: null }); return null; } function buildCandidates(opts) { const candidates = []; if (opts.moduleUrl) try { candidates.push(path.dirname(fileURLToPath(opts.moduleUrl))); } catch {} if (opts.argv1) candidates.push(...candidateDirsFromArgv1(opts.argv1)); if (opts.cwd) candidates.push(opts.cwd); return dedupeCandidates(candidates); } function dedupeCandidates(candidates) { return [...new Set(candidates.map((candidate) => path.resolve(candidate)))]; } //#endregion //#region src/infra/git-commit.ts const formatCommit = (value) => { const match = value?.match(/[0-9a-fA-F]{7,40}/); return match ? normalizeLowercaseStringOrEmpty(match[0].slice(0, 7)) : null; }; const cachedGitCommitBySearchDir = new LruCache(256); const resolveCommitSearchDir = (options) => { if (options.cwd) return path.resolve(options.cwd); if (options.moduleUrl) try { return path.dirname(fileURLToPath(options.moduleUrl)); } catch {} return process.cwd(); }; const cacheGitCommit = (searchDir, commit) => { cachedGitCommitBySearchDir.set(searchDir, commit); return commit; }; const resolveGitLookupDepth = (searchDir, packageRoot) => { if (!packageRoot) return; const relative = path.relative(packageRoot, searchDir); if (relative.startsWith("..") || path.isAbsolute(relative)) return; return (relative ? relative.split(path.sep).filter(Boolean).length : 0) + 1; }; const readCommitFromGit = (searchDir, packageRoot) => { const head = readGitHead(searchDir, { maxDepth: resolveGitLookupDepth(searchDir, packageRoot) }); return head === void 0 ? void 0 : formatCommit(head.value); }; const readCommitFromPackageJson = () => { try { const pkg = createRequire(import.meta.url)("../../package.json"); return formatCommit(pkg.gitHead ?? pkg.githead ?? null); } catch { return null; } }; const readCommitProbe = (moduleUrl, candidates, field) => { try { for (const candidate of candidates) { const filePath = fileURLToPath(new URL(candidate, moduleUrl)); let raw; try { raw = readGitMetadataPrefix(filePath, 1024); } catch (error) { if (isMissingPathError(error)) continue; return null; } try { const value = asNullableRecord(JSON.parse(raw))?.[field]; return typeof value === "string" ? formatCommit(value) : null; } catch { return null; } } } catch {} }; const readCommitFromBuildInfo = (moduleUrl = import.meta.url) => { return readCommitProbe(moduleUrl, ["../build-info.json", "./build-info.json"], "commit") ?? null; }; const readLoadedCommit = (moduleUrl) => { const buildStamp = readCommitProbe(moduleUrl, ["../.buildstamp", "./.buildstamp"], "head"); const runtimeStamp = readCommitProbe(moduleUrl, ["../.runtime-postbuildstamp", "./.runtime-postbuildstamp"], "head"); if (buildStamp !== void 0 || runtimeStamp !== void 0) { if (buildStamp || runtimeStamp) return buildStamp && buildStamp === runtimeStamp ? buildStamp : null; return readCommitFromBuildInfo(moduleUrl); } return readCommitProbe(moduleUrl, ["../build-info.json", "./build-info.json"], "commit"); }; const resolveCommitHash = (options = {}) => { const env = options.env ?? process.env; const readers = options.readers ?? {}; const readGitCommit = readers.readGitCommit ?? readCommitFromGit; const envCommit = env.GIT_COMMIT?.trim() || env.GIT_SHA?.trim(); const normalized = formatCommit(envCommit); if (normalized) return normalized; const searchDir = resolveCommitSearchDir(options); const cached = cachedGitCommitBySearchDir.get(searchDir); if (cached !== void 0) return cached; const packageRoot = resolveOpenClawPackageRootSync({ cwd: options.cwd, moduleUrl: options.moduleUrl }); try { const gitCommit = readGitCommit(searchDir, packageRoot); if (gitCommit !== void 0) return cacheGitCommit(searchDir, gitCommit); } catch {} const buildInfoCommit = readers.readBuildInfoCommit?.() ?? readCommitFromBuildInfo(); if (buildInfoCommit) return cacheGitCommit(searchDir, buildInfoCommit); const pkgCommit = readers.readPackageJsonCommit?.() ?? readCommitFromPackageJson(); if (pkgCommit) return cacheGitCommit(searchDir, pkgCommit); try { return cacheGitCommit(searchDir, readGitCommit(searchDir, packageRoot) ?? null); } catch { return cacheGitCommit(searchDir, null); } }; /** Resolve the commit that produced the loaded artifact, not the checkout's current revision. */ function resolveLoadedCommitHash(options = {}) { const moduleUrl = options.moduleUrl ?? import.meta.url; const loaded = readLoadedCommit(moduleUrl); return loaded === void 0 ? resolveCommitHash({ moduleUrl, ...options.env ? { env: options.env } : {} }) : loaded; } //#endregion //#region src/version.ts const CORE_PACKAGE_NAME = "openclaw"; const PACKAGE_JSON_CANDIDATES = [ "../package.json", "../../package.json", "../../../package.json", "./package.json" ]; const BUILD_INFO_CANDIDATES = [ "../build-info.json", "../../build-info.json", "./build-info.json" ]; function readVersionFromJsonCandidates(moduleUrl, candidates, opts = {}) { try { const require = createRequire(moduleUrl); for (const candidate of candidates) try { const parsed = require(candidate); const version = normalizeOptionalString(parsed.version); if (!version) continue; if (opts.requirePackageName && parsed.name !== CORE_PACKAGE_NAME) continue; return version; } catch {} return null; } catch { return null; } } function readBuildIdFromJsonCandidates(moduleUrl) { try { const require = createRequire(moduleUrl); for (const candidate of BUILD_INFO_CANDIDATES) try { const parsed = require(candidate); const buildId = normalizeOptionalString(parsed.buildId); if (buildId && buildId.length <= 96) return buildId; } catch {} return null; } catch { return null; } } function firstNonEmpty(...values) { for (const value of values) { const trimmed = normalizeOptionalString(value); if (trimmed && trimmed.toLowerCase() !== "undefined" && trimmed.toLowerCase() !== "null") return trimmed; } } function readVersionFromPackageJsonForModuleUrl(moduleUrl) { return readVersionFromJsonCandidates(moduleUrl, PACKAGE_JSON_CANDIDATES, { requirePackageName: true }); } function readVersionFromBuildInfoForModuleUrl(moduleUrl) { return readVersionFromJsonCandidates(moduleUrl, BUILD_INFO_CANDIDATES); } function readBuildIdFromBuildInfoForModuleUrl(moduleUrl) { return readBuildIdFromJsonCandidates(moduleUrl); } function resolveVersionFromModuleUrl(moduleUrl) { return readVersionFromBuildInfoForModuleUrl(moduleUrl) || readVersionFromPackageJsonForModuleUrl(moduleUrl); } function resolveBinaryVersion(params) { return resolveVersionFromModuleUrl(params.moduleUrl) || firstNonEmpty(params.bundledVersion) || params.fallback || "0.0.0"; } const RUNTIME_SERVICE_VERSION_FALLBACK = "unknown"; function resolveUsableRuntimeVersion(version) { const trimmed = normalizeOptionalString(version); if (!trimmed || trimmed === "0.0.0") return; return trimmed; } function resolveVersionFromRuntimeSources(params) { return firstNonEmpty(...params.preference === "env-first" ? [params.env["OPENCLAW_VERSION"], params.runtimeVersion] : [params.runtimeVersion, params.env["OPENCLAW_VERSION"]], params.env["npm_package_version"]) ?? params.fallback; } readBuildIdFromBuildInfoForModuleUrl(import.meta.url); const RUNTIME_SERVICE_COMMIT = resolveLoadedCommitHash({ moduleUrl: import.meta.url }); function resolveRuntimeServiceCommit() { return RUNTIME_SERVICE_COMMIT; } function resolveCompatibilityHostVersion(env = process.env, fallback = RUNTIME_SERVICE_VERSION_FALLBACK) { const explicitCompatibilityVersion = firstNonEmpty(env.OPENCLAW_COMPATIBILITY_HOST_VERSION); if (explicitCompatibilityVersion) return explicitCompatibilityVersion; return resolveVersionFromRuntimeSources({ env, runtimeVersion: resolveUsableRuntimeVersion(VERSION), fallback, preference: env === process.env ? "runtime-first" : "env-first" }); } const VERSION = resolveBinaryVersion({ moduleUrl: import.meta.url, bundledVersion: process.env.OPENCLAW_BUNDLED_VERSION }); //#endregion //#region src/infra/kysely-sync-cache-state.ts const { kyselyByDatabase, queryErrorHandlerByDatabase } = resolveGlobalSingleton(Symbol.for("openclaw.sqliteKyselyCacheState"), () => ({ kyselyByDatabase: /* @__PURE__ */ new WeakMap(), queryErrorHandlerByDatabase: /* @__PURE__ */ new WeakMap() })); const statementCacheSymbol = Symbol.for("openclaw.kyselySyncStatementCache"); const statementInvalidationSymbol = Symbol.for("openclaw.kyselySyncStatementInvalidation"); const statementCacheEnabledSymbol = Symbol.for("openclaw.kyselySyncStatementCacheEnabled"); const authorizerActiveSymbol = Symbol.for("openclaw.kyselySyncAuthorizerActive"); const disposeCallbacksSymbol = Symbol.for("openclaw.sqliteDisposeCallbacks"); const statementCacheCapacity = 64; const statementCacheEntryBytes = 65536; /** Retire dependent native resources before this connection closes or is replaced. */ function registerNodeSqliteDisposeCallback(db, callback) { const owner = db; installStatementInvalidation(owner); return registerListener(owner[disposeCallbacksSymbol] ??= /* @__PURE__ */ new Set(), callback); } function disposeNodeSqliteDependents(owner, reason = "close") { for (const callback of owner[disposeCallbacksSymbol] ?? []) callback(reason); } /** Register the lifecycle owner's handler for synchronous Kysely query failures. */ function registerNodeSqliteKyselyQueryErrorHandler(db, handler) { queryErrorHandlerByDatabase.set(db, handler); } /** Drop cached Kysely state for a DatabaseSync. */ function clearNodeSqliteKyselyCacheForDatabase(db) { delete db[statementCacheSymbol]; kyselyByDatabase.delete(db); queryErrorHandlerByDatabase.delete(db); } function installStatementInvalidation(owner) { if (owner[statementInvalidationSymbol]) return; if (typeof owner.setAuthorizer === "function") { const setAuthorizer = owner.setAuthorizer.bind(owner); Object.defineProperty(owner, "setAuthorizer", { configurable: true, writable: true, value(callback) { setAuthorizer(callback); this[authorizerActiveSymbol] = callback !== null; delete this[statementCacheSymbol]; } }); } if (typeof owner.deserialize === "function") { const deserialize = owner.deserialize.bind(owner); Object.defineProperty(owner, "deserialize", { configurable: true, writable: true, value(...args) { disposeNodeSqliteDependents(this, "replace"); try { deserialize(...args); } finally { delete this[statementCacheSymbol]; } } }); } for (const method of ["close", Symbol.dispose]) if (typeof owner[method] === "function") { const dispose = owner[method].bind(owner); Object.defineProperty(owner, method, { configurable: true, writable: true, value() { disposeNodeSqliteDependents(this); clearNodeSqliteKyselyCacheForDatabase(this); return dispose(); } }); } Object.defineProperty(owner, statementInvalidationSymbol, { configurable: true, value: true }); } /** * Enable bounded statement caching for a lifecycle-owned database that has not * installed an authorizer before this call. */ function enableNodeSqliteKyselyStatementCache(db) { const owner = db; installStatementInvalidation(owner); owner[statementCacheEnabledSymbol] = true; } function queryFitsStatementCache(sql, parameters) { let bytes = Buffer.byteLength(sql); if (bytes > statementCacheEntryBytes) return false; for (const parameter of parameters) { if (typeof parameter === "string") { if (parameter.length > statementCacheEntryBytes - bytes) return false; bytes += Buffer.byteLength(parameter); } else if (ArrayBuffer.isView(parameter)) bytes += parameter.byteLength; if (bytes > statementCacheEntryBytes) return false; } return true; } function executeWithCachedStatement(db, sql, parameters, execute) { const owner = db; if (!owner[statementCacheEnabledSymbol] || owner[authorizerActiveSymbol] || !queryFitsStatementCache(sql, parameters)) return execute(db.prepare(sql)); let cache = owner[statementCacheSymbol]; if (!cache) { cache = { statements: /* @__PURE__ */ new Map(), candidates: /* @__PURE__ */ new Set(), active: /* @__PURE__ */ new WeakSet() }; Object.defineProperty(owner, statementCacheSymbol, { configurable: true, value: cache }); } const cached = cache.statements.get(sql); let statement; if (cached && !cache.active.has(cached)) { cache.statements.delete(sql); cache.statements.set(sql, cached); statement = cached; } else { statement = db.prepare(sql); if (!cached && cache.candidates.delete(sql)) { cache.statements.set(sql, statement); pruneMapToMaxSize(cache.statements, statementCacheCapacity); } else if (!cached) { cache.candidates.add(sql); if (cache.candidates.size > statementCacheCapacity) { const oldestCandidate = cache.candidates.values().next().value; if (oldestCandidate !== void 0) cache.candidates.delete(oldestCandidate); } } } cache.active.add(statement); try { return execute(statement); } finally { cache.active.delete(statement); } } //#endregion //#region src/infra/startup-maintenance-required.ts const GATEWAY_STARTUP_MAINTENANCE_REQUIRED_REASON = "gateway.maintenance_required"; const maintenanceReasons = { "state-migrations": "state migration", "newer-schema": "a newer OpenClaw build", "agent-media": "offline media migration", "audit-events-v2": "state database schema migration", "legacy-workshop-review-index": "state database schema migration", "legacy-cron-run-logs": "cron run history migration", "legacy-workspace": "workspace setup state migration", "legacy-session-store": "session store migration" }; function isStartupMaintenanceKind(value) { return typeof value === "string" && Object.hasOwn(maintenanceReasons, value); } var StartupMaintenanceRequiredError = class extends Error { constructor(kind, message, options) { super(message, options); this.kind = kind; this.code = GATEWAY_STARTUP_MAINTENANCE_REQUIRED_REASON; this.name = "StartupMaintenanceRequiredError"; } get reason() { return maintenanceReasons[this.kind]; } }; //#endregion //#region src/infra/sqlite-user-version.ts const SQLITE_SCHEMA_VERSION_ERROR_NAME = "SqliteSchemaVersionError"; var SqliteSchemaVersionError = class extends StartupMaintenanceRequiredError { constructor(message) { super("newer-schema", message); this.name = SQLITE_SCHEMA_VERSION_ERROR_NAME; } }; function isSqliteSchemaVersionError(error) { return error instanceof SqliteSchemaVersionError || error instanceof Error && error.name === SQLITE_SCHEMA_VERSION_ERROR_NAME; } function readSqliteUserVersion(db) { const row = executeWithCachedStatement(db, "PRAGMA user_version", [], (statement) => statement.get()); return Number(row?.user_version ?? 0); } /** * Name the refusing build from immutable loaded metadata, plus its install root. * The path remains actionable when multiple installs share a version or build. */ function describeRunningOpenClawBuild() { const commit = resolveRuntimeServiceCommit(); const root = resolveOpenClawPackageRootSync({ moduleUrl: import.meta.url }); const identity = commit ? `OpenClaw ${VERSION} (${commit})` : `OpenClaw ${VERSION}`; return root ? `${identity} installed at ${root}` : identity; } function createNewerSqliteSchemaVersionError(databaseLabel, pathname, schemaVersion, supportedVersion) { return new SqliteSchemaVersionError(`This OpenClaw build cannot open your existing data. ${databaseLabel} ${pathname} uses newer schema version ${schemaVersion}; this build supports ${supportedVersion}.\nRefused by ${describeRunningOpenClawBuild()}.\nUse a build that supports schema ${schemaVersion} or newer with this state directory. To use an older build, restore your pre-update backup created with openclaw backup create.\nSee ${OPENCLAW_DATABASE_SCHEMA_DOCS_URL}.`); } //#endregion export { DOCTOR_OWNED_STATE_TABLES as A, markSqliteInspectionOperation as B, getProcessPluginCache as C, materializePluginCacheError as D, invalidatePluginCacheMetadata as E, OPENCLAW_DATABASE_SCHEMA_DOCS_URL as F, withSqliteNativeOpen as G, sqliteErrorCode as H, OPENCLAW_SQLITE_BUSY_TIMEOUT_MS as I, isSqliteCorruptionError as L, FIRST_USE_STATE_TABLES as M, LAZY_ADDITIVE_STATE_INDEXES as N, InvocationFrame as O, LAZY_ADDITIVE_STATE_TABLES as P, isSqliteLockError as R, getPluginCacheRoot as S, getScopedPluginCaches as T, sqliteExtendedResultCode as U, markSqliteNativeOpenFailure as V, sqlitePrimaryResultCode as W, resolveOpenClawPackageRootSync as _, StartupMaintenanceRequiredError as a, getPluginCache as b, enableNodeSqliteKyselyStatementCache as c, kyselyByDatabase as d, queryErrorHandlerByDatabase as f, resolveCompatibilityHostVersion as g, VERSION as h, readSqliteUserVersion as i, FIRST_USE_STATE_INDEXES as j, getPluginExecutionFrame as k, executeWithCachedStatement as l, registerNodeSqliteKyselyQueryErrorHandler as m, createNewerSqliteSchemaVersionError as n, isStartupMaintenanceKind as o, registerNodeSqliteDisposeCallback as p, isSqliteSchemaVersionError as r, clearNodeSqliteKyselyCacheForDatabase as s, SqliteSchemaVersionError as t, installStatementInvalidation as u, PluginCacheFactInvalidatedError as v, getScopedPluginCache as w, getPluginCacheRetirementSignal as x, bindPluginCacheRoot as y, isSqliteNativeOpenFailure as z };