@xopcai/xopc 0.0.93 → 0.0.94
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/browser-ext/manifest.json +1 -1
- package/dist/extensions/feishu/src/outbound/media-load.js +1 -1
- package/dist/extensions/feishu/src/workflow-progress.js +1 -1
- package/dist/extensions/telegram/src/plugin.js +1 -1
- package/dist/extensions/telegram/src/routing-integration.js +2 -2
- package/dist/extensions/telegram/src/workflow-progress.js +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/extensions/weixin/src/api/api.js +2 -2
- package/dist/extensions/weixin/src/auth/accounts.js +1 -1
- package/dist/extensions/weixin/src/cdn/upload.js +1 -1
- package/dist/extensions/weixin/src/media/data-url.js +1 -1
- package/dist/extensions/weixin/src/messaging/debug-mode.js +1 -1
- package/dist/extensions/weixin/src/messaging/inbound.js +1 -1
- package/dist/extensions/weixin/src/messaging/process-message.js +1 -1
- package/dist/extensions/weixin/src/plugin.js +1 -1
- package/dist/extensions/weixin/src/storage/sync-buf.js +1 -1
- package/dist/extensions/weixin/src/workflow-progress.js +1 -1
- package/dist/gateway/static/root/assets/agents-OqhbJkMf.js +222 -0
- package/dist/gateway/static/root/assets/apps-page-OHXW9XP8.js +1 -0
- package/dist/gateway/static/root/assets/channels-settings-4N2R-jof.js +1 -0
- package/dist/gateway/static/root/assets/{channels-status-swr-CsGkK9h9.js → channels-status-swr-Bv6f9kDq.js} +1 -1
- package/dist/gateway/static/root/assets/{cron-api-CyAm0xJT.js → cron-api-BtaQaHJq.js} +1 -1
- package/dist/gateway/static/root/assets/cron-page-Dah32HJK.js +1 -0
- package/dist/gateway/static/root/assets/{dist-DHwVV8XK.js → dist-BJfD9Qvs.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-BK8kcc4F.js → extension-debug-page-DnYuMzmH.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-Cf8X_QUc.js → extension-page-CJfc-6XV.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-C5-YLMmy.js → extension-settings-page-BxdfYQMG.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-BAPnkYbC.js → fetch-B0aeeY0q.js} +1 -1
- package/dist/gateway/static/root/assets/{field-primitives-8p7ucXa1.js → field-primitives-DOLHwowi.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-CpgW2sGp.js → heartbeat-config-api-Bj2INAf5.js} +1 -1
- package/dist/gateway/static/root/assets/index-Bj_l8QDp.css +1 -0
- package/dist/gateway/static/root/assets/{index-Do52EfZK.js → index-DuQ1XPoA.js} +99 -98
- package/dist/gateway/static/root/assets/logs-page-AsOgLNJE.js +2 -0
- package/dist/gateway/static/root/assets/{note-detail-page-WLM6FUIi.js → note-detail-page-24J4mVP-.js} +3 -3
- package/dist/gateway/static/root/assets/{note-time-EFyIVhec.js → note-time-JBszYV3s.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-page-BYPVYcYn.js → notes-page-BApAirFB.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-DX9huWsA.js +1 -0
- package/dist/gateway/static/root/assets/{settings-advanced-gate-CEs8pGh6.js → settings-advanced-gate-DWvhsTuz.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-C6cGTVwK.js → settings-form-section-CxMjaMiy.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-4VmUTzQs.js +3 -0
- package/dist/gateway/static/root/assets/{share-preview-page-tnIfJ4K6.js → share-preview-page-IX0TJvRd.js} +1 -1
- package/dist/gateway/static/root/assets/skills-page-CGKGKfwe.js +2 -0
- package/dist/gateway/static/root/assets/{theme-store-D6EsNTPr.js → theme-store-Cg_SuBw0.js} +1 -1
- package/dist/gateway/static/root/assets/{url-CTjpm0Uz.js → url-BHHmdJYc.js} +2 -2
- package/dist/gateway/static/root/assets/{utils-C86AVfY-.js → utils-BmlcxR2j.js} +1 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-DaGm2N4J.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-page.utils-DsEriMFW.js → workflow-page.utils-D0vsIGHD.js} +1 -1
- package/dist/gateway/static/root/assets/workflows-page-BFCrD3nw.js +27 -0
- package/dist/gateway/static/root/index.html +5 -5
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.js +7 -7
- package/dist/src/agent/agent-scope.js +1 -1
- package/dist/src/agent/bootstrap/load-bootstrap-files.js +1 -1
- package/dist/src/agent/context/workspace-seed.js +2 -2
- package/dist/src/agent/goals/goal-run-store.js +4 -4
- package/dist/src/agent/goals/persistent-goal-service.js +1 -1
- package/dist/src/agent/goals/post-turn.js +2 -2
- package/dist/src/agent/image/load-image-media.js +2 -2
- package/dist/src/agent/inbound/turn-dispatcher.d.ts +1 -0
- package/dist/src/agent/inbound/turn-dispatcher.js +3 -0
- package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
- package/dist/src/agent/ipc/bus.js +1 -1
- package/dist/src/agent/ipc/inbox.js +2 -2
- package/dist/src/agent/ipc/socket.js +1 -1
- package/dist/src/agent/lifecycle/handlers/compaction.js +1 -1
- package/dist/src/agent/lifecycle/handlers/compaction.js.map +1 -1
- package/dist/src/agent/mcp/bundle-mcp-materialize.js +2 -2
- package/dist/src/agent/mcp/bundle-mcp-materialize.js.map +1 -1
- package/dist/src/agent/mcp/bundle-mcp-runtime.js +18 -5
- package/dist/src/agent/mcp/bundle-mcp-runtime.js.map +1 -1
- package/dist/src/agent/mcp/mcp-transport-config.js +11 -4
- package/dist/src/agent/mcp/mcp-transport-config.js.map +1 -1
- package/dist/src/agent/mcp/mcp-transport.js +2 -2
- package/dist/src/agent/mcp/mcp-transport.js.map +1 -1
- package/dist/src/agent/memory/builtin-memory-store.js +1 -1
- package/dist/src/agent/memory/dreaming/deep-promotion.js +1 -1
- package/dist/src/agent/memory/dreaming/events.js +1 -1
- package/dist/src/agent/memory/dreaming/last-run.js +1 -1
- package/dist/src/agent/memory/dreaming/light-sweep.js +1 -1
- package/dist/src/agent/memory/dreaming/preview.js +1 -1
- package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
- package/dist/src/agent/memory/dreaming/short-term-store.js +1 -1
- package/dist/src/agent/memory/dreaming/utils.js +1 -1
- package/dist/src/agent/memory/plugin-discovery.js +1 -1
- package/dist/src/agent/models/manager.js +1 -1
- package/dist/src/agent/prompt/service-prompt-builder.js +2 -2
- package/dist/src/agent/reply/post-compaction-context.js +1 -1
- package/dist/src/agent/reply/workspace-boundary-read.js +1 -1
- package/dist/src/agent/sandbox/path-policy.js +2 -2
- package/dist/src/agent/service/build-direct-message-content.js +1 -1
- package/dist/src/agent/service/process-direct-streaming.d.ts +1 -0
- package/dist/src/agent/service/process-direct-streaming.js +15 -12
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service.d.ts +4 -2
- package/dist/src/agent/service.js +24 -8
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/service.types.d.ts +3 -1
- package/dist/src/agent/session/session-inspector.js +1 -1
- package/dist/src/agent/skills/config.js +1 -1
- package/dist/src/agent/skills/hub-hash.js +2 -2
- package/dist/src/agent/skills/hub-lock.js +1 -1
- package/dist/src/agent/skills/hub-pull.js +2 -2
- package/dist/src/agent/skills/index.js +1 -1
- package/dist/src/agent/skills/managed-store.js +1 -1
- package/dist/src/agent/skills/scanner.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js +1 -1
- package/dist/src/agent/skills/skill-manager.js +1 -1
- package/dist/src/agent/tools/browser/tool/browser-use-tool.js +1 -1
- package/dist/src/agent/tools/browser/tool/browser-use-tool.js.map +1 -1
- package/dist/src/agent/tools/dreaming-tool.js +1 -1
- package/dist/src/agent/tools/factory.js +1 -1
- package/dist/src/agent/tools/image-generate-tool.js +1 -1
- package/dist/src/agent/tools/search/registry.js +1 -1
- package/dist/src/agent/tools/search/registry.js.map +1 -1
- package/dist/src/agent/tools/send-media.js +1 -1
- package/dist/src/agent/tools/session-search-tool.js +1 -1
- package/dist/src/agent/tools/session-search-tool.js.map +1 -1
- package/dist/src/agent/tools/skill-manage-tool.js +1 -1
- package/dist/src/agent/tools/workflow-tool.js +2 -2
- package/dist/src/agent/tools/workflow-tool.js.map +1 -1
- package/dist/src/agent/tools/write.js +1 -1
- package/dist/src/agent/workflow/catalog.js +1 -1
- package/dist/src/agent/workflow/progress-broker.js +1 -1
- package/dist/src/agent/workflow/progress-broker.js.map +1 -1
- package/dist/src/agent/workflow/subagent-runner.js +1 -1
- package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
- package/dist/src/auth/credentials.js +3 -3
- package/dist/src/auth/profiles/store.js +1 -1
- package/dist/src/auth/sync-provider-auth.js +1 -1
- package/dist/src/browser/cache-dir-policy.js +1 -1
- package/dist/src/browser/cdp-local-launcher.js +2 -2
- package/dist/src/browser/providers/browser-ext-install.js +3 -3
- package/dist/src/browser/providers/cloakbrowser.js +4 -4
- package/dist/src/browser/providers/playwright-doctor.js +1 -1
- package/dist/src/browser/stealth.js +1 -1
- package/dist/src/channels/attachments/inbound-persist.js +1 -1
- package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
- package/dist/src/channels/outbound/persist-store.js +1 -1
- package/dist/src/channels/pairing/allow-from-file.js +1 -1
- package/dist/src/channels/pairing/pairing-store.js +2 -2
- package/dist/src/channels/pipeline.js +3 -2
- package/dist/src/channels/pipeline.js.map +1 -1
- package/dist/src/chat-commands/agent-edit.js +2 -2
- package/dist/src/chat-commands/builtins/config.js +2 -2
- package/dist/src/chat-commands/context.js +1 -1
- package/dist/src/cli/cli-log-level-preset.d.ts +1 -1
- package/dist/src/cli/cli-log-level-preset.js +2 -2
- package/dist/src/cli/cli-log-level-preset.js.map +1 -1
- package/dist/src/cli/commands/config.js +1 -1
- package/dist/src/cli/commands/doctor/checks/config-health.js +1 -1
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
- package/dist/src/cli/commands/doctor/checks/session-integrity.js +2 -2
- package/dist/src/cli/commands/doctor/checks/state-integrity.js +1 -1
- package/dist/src/cli/commands/doctor/checks/workspace-status.js +1 -1
- package/dist/src/cli/commands/extension-dev.js +1 -1
- package/dist/src/cli/commands/extension-marketplace.js +1 -1
- package/dist/src/cli/commands/extension-pack.js +1 -1
- package/dist/src/cli/commands/gateway/logs.js +1 -1
- package/dist/src/cli/commands/image.js +1 -1
- package/dist/src/cli/commands/init.js +4 -4
- package/dist/src/cli/commands/logs.js +3 -3
- package/dist/src/cli/commands/logs.js.map +1 -1
- package/dist/src/cli/commands/onboard.js +1 -1
- package/dist/src/cli/utils/init-workspace-core.js +2 -2
- package/dist/src/commands/agents.config.js +1 -1
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/config/gateway-bind.js +1 -1
- package/dist/src/config/index.js +5 -5
- package/dist/src/config/loader.js +2 -2
- package/dist/src/config/models-json.js +2 -2
- package/dist/src/config/paths-state.js +1 -1
- package/dist/src/config/profile.js +2 -2
- package/dist/src/config/workspace-path.js +1 -1
- package/dist/src/cron/executor.js +9 -6
- package/dist/src/cron/executor.js.map +1 -1
- package/dist/src/cron/persistence.js +1 -1
- package/dist/src/cron/run-log-store.js +1 -1
- package/dist/src/daemon/constants.js +1 -1
- package/dist/src/daemon/install-plan.js +2 -2
- package/dist/src/daemon/launchd.js +2 -2
- package/dist/src/daemon/schtasks.js +2 -2
- package/dist/src/daemon/systemd.js +2 -2
- package/dist/src/extensions/bundle-mcp.js +1 -1
- package/dist/src/extensions/discover-extensions.js +1 -1
- package/dist/src/extensions/health.js +1 -1
- package/dist/src/extensions/loader.js +1 -1
- package/dist/src/extensions/lockfile.js +2 -2
- package/dist/src/extensions/update.js +1 -1
- package/dist/src/gateway/agents-admin.js +3 -3
- package/dist/src/gateway/file-path-classifier.js +2 -2
- package/dist/src/gateway/heartbeat/service.js +1 -1
- package/dist/src/gateway/hono/app.js +4 -1
- package/dist/src/gateway/hono/app.js.map +1 -1
- package/dist/src/gateway/hono/lib/config-payload.js +1 -1
- package/dist/src/gateway/hono/lib/extension-store.js +2 -2
- package/dist/src/gateway/hono/lib/route-logger.d.ts +6 -0
- package/dist/src/gateway/hono/lib/route-logger.js +31 -0
- package/dist/src/gateway/hono/lib/route-logger.js.map +1 -0
- package/dist/src/gateway/hono/lib/static-ui.js +2 -2
- package/dist/src/gateway/hono/middleware/auth.js +16 -3
- package/dist/src/gateway/hono/middleware/auth.js.map +1 -1
- package/dist/src/gateway/hono/middleware/logger.js +1 -1
- package/dist/src/gateway/hono/middleware/logger.js.map +1 -1
- package/dist/src/gateway/hono/middleware/route-errors.d.ts +5 -0
- package/dist/src/gateway/hono/middleware/route-errors.js +27 -0
- package/dist/src/gateway/hono/middleware/route-errors.js.map +1 -0
- package/dist/src/gateway/hono/oauth.js +1 -1
- package/dist/src/gateway/hono/routes/agent-stream.js +6 -0
- package/dist/src/gateway/hono/routes/agent-stream.js.map +1 -1
- package/dist/src/gateway/hono/routes/agents.js +1 -1
- package/dist/src/gateway/hono/routes/auth-registry-extensions.js +1 -1
- package/dist/src/gateway/hono/routes/browser-install.js +2 -4
- package/dist/src/gateway/hono/routes/browser-install.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +1 -1
- package/dist/src/gateway/hono/routes/config.js +25 -11
- package/dist/src/gateway/hono/routes/config.js.map +1 -1
- package/dist/src/gateway/hono/routes/cron.js +5 -0
- package/dist/src/gateway/hono/routes/cron.js.map +1 -1
- package/dist/src/gateway/hono/routes/dreaming.js +1 -1
- package/dist/src/gateway/hono/routes/host-fs.js +4 -6
- package/dist/src/gateway/hono/routes/host-fs.js.map +1 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js +14 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
- package/dist/src/gateway/hono/routes/lazy-fallback.js +3 -0
- package/dist/src/gateway/hono/routes/lazy-fallback.js.map +1 -1
- package/dist/src/gateway/hono/routes/logs.js +39 -7
- package/dist/src/gateway/hono/routes/logs.js.map +1 -1
- package/dist/src/gateway/hono/routes/mcp.d.ts +3 -0
- package/dist/src/gateway/hono/routes/mcp.js +107 -0
- package/dist/src/gateway/hono/routes/mcp.js.map +1 -0
- package/dist/src/gateway/hono/routes/models.js +1 -1
- package/dist/src/gateway/hono/routes/sessions.js +6 -0
- package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
- package/dist/src/gateway/hono/routes/shares.js +1 -1
- package/dist/src/gateway/hono/routes/update.js +2 -4
- package/dist/src/gateway/hono/routes/update.js.map +1 -1
- package/dist/src/gateway/hono/routes/voice.js +2 -4
- package/dist/src/gateway/hono/routes/voice.js.map +1 -1
- package/dist/src/gateway/hono/routes/workspace.js +4 -6
- package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
- package/dist/src/gateway/hono/sse.js +9 -2
- package/dist/src/gateway/hono/sse.js.map +1 -1
- package/dist/src/gateway/lock.js +3 -3
- package/dist/src/gateway/ports.js +1 -1
- package/dist/src/gateway/service/agent-runner.js +3 -3
- package/dist/src/gateway/service/agent-runner.js.map +1 -1
- package/dist/src/gateway/service/config-coordinator.js +14 -6
- package/dist/src/gateway/service/config-coordinator.js.map +1 -1
- package/dist/src/gateway/service/marketplace-service.js +3 -3
- package/dist/src/gateway/service/marketplace-service.js.map +1 -1
- package/dist/src/gateway/service/run-gateway-agent.js +22 -5
- package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
- package/dist/src/gateway/service/sse-hub.js +1 -1
- package/dist/src/gateway/service/sse-hub.js.map +1 -1
- package/dist/src/gateway/service.js +13 -6
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/workspace-fs-file-list.js +1 -1
- package/dist/src/heartbeat/index.js +1 -1
- package/dist/src/infra/brew.js +1 -1
- package/dist/src/infra/package-json.js +1 -1
- package/dist/src/infra/package-update-steps.js +1 -1
- package/dist/src/infra/path-env.js +2 -2
- package/dist/src/infra/restart.js +2 -2
- package/dist/src/infra/stable-node-path.js +1 -1
- package/dist/src/infra/update-check.js +1 -1
- package/dist/src/infra/update-global.js +1 -1
- package/dist/src/infra/update-lock.js +3 -3
- package/dist/src/infra/update-runner.js +1 -1
- package/dist/src/infra/update-startup.js +2 -2
- package/dist/src/infra/write-file-atomic.js +2 -2
- package/dist/src/mcp/channel-bridge.js +26 -2
- package/dist/src/mcp/channel-bridge.js.map +1 -1
- package/dist/src/mcp/gateway-http-client.js +24 -2
- package/dist/src/mcp/gateway-http-client.js.map +1 -1
- package/dist/src/notes/store.js +2 -2
- package/dist/src/providers/auth-runtime/auth-profile-store.js +1 -1
- package/dist/src/providers/index.js +2 -2
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/session/config-store.js +12 -6
- package/dist/src/session/config-store.js.map +1 -1
- package/dist/src/session/index.d.ts +1 -1
- package/dist/src/session/index.js +2 -2
- package/dist/src/session/init-session-turn.js +2 -2
- package/dist/src/session/manager.js +8 -1
- package/dist/src/session/manager.js.map +1 -1
- package/dist/src/session/parity/jsonl-transcript-io.js +2 -2
- package/dist/src/session/parity/sessions-json-file.js +1 -1
- package/dist/src/session/parity/transcript-file-lock.js +2 -2
- package/dist/src/session/parity/transcript-paths.js +1 -1
- package/dist/src/session/resolve-session.js +4 -4
- package/dist/src/session/search-index-cache.js +1 -1
- package/dist/src/session/search-index.js +1 -1
- package/dist/src/session/session-title.d.ts +19 -3
- package/dist/src/session/session-title.js +84 -9
- package/dist/src/session/session-title.js.map +1 -1
- package/dist/src/session/store.js +6 -6
- package/dist/src/share/share-auto.js +2 -2
- package/dist/src/share/share-store.js +3 -3
- package/dist/src/share/share-thumbnail.js +2 -2
- package/dist/src/share/share-zip.js +1 -1
- package/dist/src/share/site-share-store.js +3 -3
- package/dist/src/share/site-static-serve.js +1 -1
- package/dist/src/tui/clipboard-image.js +3 -3
- package/dist/src/tui/theme-manager.js +1 -1
- package/dist/src/tui/tui-keybindings-file.js +1 -1
- package/dist/src/tui/tui-scoped-models.js +2 -2
- package/dist/src/tui/tui-settings.js +1 -1
- package/dist/src/tui/tui.js +3 -3
- package/dist/src/tunnel/frpc-binary.js +3 -3
- package/dist/src/tunnel/frpc-config.js +1 -1
- package/dist/src/tunnel/frpc-extract.js +1 -1
- package/dist/src/tunnel/tunnel-state.js +1 -1
- package/dist/src/utils/index.js +4 -4
- package/dist/src/utils/logger/audit.js +1 -1
- package/dist/src/utils/logger/config.js +2 -6
- package/dist/src/utils/logger/config.js.map +1 -1
- package/dist/src/utils/logger/context.d.ts +3 -22
- package/dist/src/utils/logger/context.js +4 -32
- package/dist/src/utils/logger/context.js.map +1 -1
- package/dist/src/utils/logger/index.d.ts +4 -7
- package/dist/src/utils/logger/index.js +9 -28
- package/dist/src/utils/logger/index.js.map +1 -1
- package/dist/src/utils/logger/log-store.d.ts +14 -32
- package/dist/src/utils/logger/log-store.js +68 -119
- package/dist/src/utils/logger/log-store.js.map +1 -1
- package/dist/src/utils/logger/log-stream.d.ts +5 -70
- package/dist/src/utils/logger/log-stream.js +67 -178
- package/dist/src/utils/logger/log-stream.js.map +1 -1
- package/dist/src/utils/logger/pino-record.d.ts +8 -0
- package/dist/src/utils/logger/pino-record.js +83 -0
- package/dist/src/utils/logger/pino-record.js.map +1 -0
- package/dist/src/utils/logger/rotation.js +1 -1
- package/dist/src/utils/logger/stats.d.ts +1 -1
- package/dist/src/utils/logger/stats.js +2 -2
- package/dist/src/utils/logger/stats.js.map +1 -1
- package/dist/src/utils/logger/streams.js +18 -0
- package/dist/src/utils/logger/streams.js.map +1 -1
- package/dist/src/utils/logger/types.d.ts +0 -9
- package/dist/src/utils/logger/types.js.map +1 -1
- package/dist/src/utils/logger.js +4 -4
- package/dist/src/voice/tts/audio.js +1 -1
- package/dist/src/voice/tts/providers/edge-speech.js +2 -2
- package/dist/src/workflows/store/event-store.js +1 -1
- package/dist/src/workflows/store/run-store.js +1 -1
- package/package.json +2 -1
- package/dist/gateway/static/root/assets/agents-C7tTJLP9.js +0 -222
- package/dist/gateway/static/root/assets/apps-page-BbzdMyrg.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-B49vG2hE.js +0 -1
- package/dist/gateway/static/root/assets/cron-page-Bjx7IOdR.js +0 -1
- package/dist/gateway/static/root/assets/index-CwDdudZM.css +0 -1
- package/dist/gateway/static/root/assets/logs-page-BxukQ-J-.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-BFD2_-Cl.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-BiP5iH46.js +0 -2
- package/dist/gateway/static/root/assets/skills-page-CNDctFIn.js +0 -2
- package/dist/gateway/static/root/assets/voice-api-key-field-CalxUkxm.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-D2fRxXJG.js +0 -27
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","names":[],"sources":["../../../../src/utils/logger/context.ts"],"sourcesContent":["/**\n * Logger Context\n *
|
|
1
|
+
{"version":3,"file":"context.js","names":[],"sources":["../../../../src/utils/logger/context.ts"],"sourcesContent":["/**\n * Logger Context\n * AsyncLocalStorage correlation for structured logging\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\nimport type { LogContext } from './types.js';\n\n/** Correlation fields merged into every log line while the store is active (via pino mixin). */\nconst ASYNC_LOG_CORRELATION_KEYS = ['requestId', 'sessionId', 'userId', 'correlationId'] as const;\n\ntype AsyncLogCorrelationKey = (typeof ASYNC_LOG_CORRELATION_KEYS)[number];\n\nconst logContextStorage = new AsyncLocalStorage<{ ctx: LogContext }>();\n\n/**\n * Run *fn* with merged async log context. Propagates through async/await (Node AsyncLocalStorage).\n * Nested calls merge over the parent context (shallow per key).\n */\nexport function runWithLogContext<T>(context: LogContext, fn: () => T): T {\n const parent = logContextStorage.getStore();\n const merged = parent ? mergeContext(parent.ctx, context) : { ...context };\n return logContextStorage.run({ ctx: { ...merged } }, fn);\n}\n\n/** Current async log context, if any. */\nexport function getAsyncLogContext(): LogContext | undefined {\n return logContextStorage.getStore()?.ctx;\n}\n\n/**\n * Shallow-merge fields into the current async log context (e.g. set sessionId after parsing a body).\n * No-op when not inside {@link runWithLogContext}.\n */\nexport function updateAsyncLogContext(partial: LogContext): void {\n const ref = logContextStorage.getStore();\n if (ref) {\n ref.ctx = mergeContext(ref.ctx, partial);\n }\n}\n\n/** Keys injected from async context into log records (used by logger mixin). */\nexport function getAsyncLogCorrelationKeys(): readonly AsyncLogCorrelationKey[] {\n return ASYNC_LOG_CORRELATION_KEYS;\n}\n\n/**\n * Fields to copy from ALS onto {@link InboundMessage.metadata} when the gateway enqueues\n * a message for the agent bus. Omits `sessionId` so channel routing / sessionKey stays\n * authoritative; the agent sets log sessionId after `routeMessage`.\n */\nconst INBOUND_METADATA_FROM_ASYNC: readonly (keyof LogContext)[] = [\n 'requestId',\n 'correlationId',\n 'userId',\n];\n\nexport function inboundCorrelationMetadataFromAsyncLogContext(): Record<string, unknown> | undefined {\n const ctx = getAsyncLogContext();\n if (!ctx) return undefined;\n const meta: Record<string, unknown> = {};\n for (const key of INBOUND_METADATA_FROM_ASYNC) {\n const v = ctx[key];\n if (v !== undefined && v !== '') {\n meta[key] = v;\n }\n }\n return Object.keys(meta).length > 0 ? meta : undefined;\n}\n\n/** Shallow-merge two log context objects. */\nexport function mergeContext(base: LogContext, additional: LogContext): LogContext {\n return { ...base, ...additional };\n}\n"],"mappings":";;;;;;;;;;;AAoBA,SAAgB,kBAAqB,SAAqB,IAAgB;CACxE,MAAM,SAAS,kBAAkB,UAAU;CAC3C,MAAM,SAAS,SAAS,aAAa,OAAO,KAAK,QAAQ,GAAG,EAAE,GAAG,SAAS;AAC1E,QAAO,kBAAkB,IAAI,EAAE,KAAK,EAAE,GAAG,QAAQ,EAAE,EAAE,GAAG;;;AAI1D,SAAgB,qBAA6C;AAC3D,QAAO,kBAAkB,UAAU,EAAE;;;;;;AAOvC,SAAgB,sBAAsB,SAA2B;CAC/D,MAAM,MAAM,kBAAkB,UAAU;AACxC,KAAI,IACF,KAAI,MAAM,aAAa,IAAI,KAAK,QAAQ;;;AAK5C,SAAgB,6BAAgE;AAC9E,QAAO;;AAcT,SAAgB,gDAAqF;CACnG,MAAM,MAAM,oBAAoB;AAChC,KAAI,CAAC,IAAK,QAAO,KAAA;CACjB,MAAM,OAAgC,EAAE;AACxC,MAAK,MAAM,OAAO,6BAA6B;EAC7C,MAAM,IAAI,IAAI;AACd,MAAI,MAAM,KAAA,KAAa,MAAM,GAC3B,MAAK,OAAO;;AAGhB,QAAO,OAAO,KAAK,KAAK,CAAC,SAAS,IAAI,OAAO,KAAA;;;AAI/C,SAAgB,aAAa,MAAkB,YAAoC;AACjF,QAAO;EAAE,GAAG;EAAM,GAAG;EAAY;;;;AA/D7B,8BAA6B;EAAC;EAAa;EAAa;EAAU;EAAgB;AAIlF,qBAAoB,IAAI,mBAAwC;AAsChE,+BAA6D;EACjE;EACA;EACA;EACD"}
|
|
@@ -10,26 +10,23 @@
|
|
|
10
10
|
import pino from 'pino';
|
|
11
11
|
import type { LogLevel, LogContext, ContextualLogger } from './types.js';
|
|
12
12
|
import { getLogDir, getLoggerConfig } from './config.js';
|
|
13
|
-
import {
|
|
13
|
+
import { getRuntimeLogStats } from './stats.js';
|
|
14
14
|
import { isLoggerShuttingDown, flushAndClose, setShuttingDown } from './shutdown.js';
|
|
15
15
|
import { rotateLogs, cleanOldLogs } from './rotation.js';
|
|
16
16
|
export declare const logger: ContextualLogger;
|
|
17
|
-
export declare function createLogger(
|
|
17
|
+
export declare function createLogger(module: string, context?: LogContext): ContextualLogger;
|
|
18
18
|
export declare function createModuleLogger(moduleName: string, _modulePath?: string): ContextualLogger;
|
|
19
19
|
export declare function createExtensionLogger(extensionName: string): ContextualLogger;
|
|
20
20
|
export declare function createServiceLogger(serviceId: string): ContextualLogger;
|
|
21
|
-
export declare function createRequestLogger(requestId: string, initialContext?: LogContext): ContextualLogger;
|
|
22
21
|
export declare function setLogLevel(level: LogLevel): void;
|
|
23
22
|
export declare function getLogLevel(): LogLevel;
|
|
24
23
|
export declare function withLogLevel<T>(level: LogLevel, fn: () => T): T;
|
|
25
24
|
export declare function isLevelEnabled(level: LogLevel): boolean;
|
|
26
25
|
export type { LogLevel, LogContext, LogEntry, LoggerConfig, LogFileMeta, LogQuery, LogStats, RotationResult, ContextualLogger, } from './types.js';
|
|
27
|
-
export { getLogDir, getLoggerConfig,
|
|
26
|
+
export { getLogDir, getLoggerConfig, getRuntimeLogStats, isLoggerShuttingDown, flushAndClose, setShuttingDown, rotateLogs, cleanOldLogs, };
|
|
28
27
|
export { type LogExporter, type ExporterConfig, type LokiConfig, type ElkConfig, type DatadogConfig, type WebhookConfig, initializeExporters, exportLog, flushExporters, getExporters, } from './exporters.js';
|
|
29
28
|
export { redactSensitiveInfo, redactObject, redactPemBlock, redactSecret, isLogRedactionEnabled, } from './redact.js';
|
|
30
29
|
export { type AuditEvent, type AuditEventType, type AuditLogConfig, logAuditEvent, logAuthEvent, logConfigChange, logPermissionChange, logDataAccess, configureAuditLog, getAuditConfig, } from './audit.js';
|
|
31
|
-
export { logger as default };
|
|
32
30
|
export { logger as baseLogger };
|
|
33
31
|
export { pino as Pino };
|
|
34
|
-
export
|
|
35
|
-
export { runWithLogContext, getAsyncLogContext, updateAsyncLogContext, inboundCorrelationMetadataFromAsyncLogContext, setRequestContext as setRequestLogger, clearRequestContext as clearRequestLogger, } from './context.js';
|
|
32
|
+
export { runWithLogContext, getAsyncLogContext, updateAsyncLogContext, inboundCorrelationMetadataFromAsyncLogContext, } from './context.js';
|
|
@@ -2,10 +2,10 @@ import { __esmMin } from "../../../_virtual/_rolldown/runtime.js";
|
|
|
2
2
|
import { PACKAGE_VERSION, init_package_version } from "../../package-version.js";
|
|
3
3
|
import { config, getLogDir, getLoggerConfig, init_config } from "./config.js";
|
|
4
4
|
import { init_streams, initializeStreams } from "./streams.js";
|
|
5
|
-
import {
|
|
5
|
+
import { getRuntimeLogStats, incrementStats, init_stats } from "./stats.js";
|
|
6
6
|
import { flushAndClose, init_shutdown, isLoggerShuttingDown, setShuttingDown } from "./shutdown.js";
|
|
7
7
|
import { cleanOldLogs, init_rotation, rotateLogs } from "./rotation.js";
|
|
8
|
-
import {
|
|
8
|
+
import { getAsyncLogContext, getAsyncLogCorrelationKeys, inboundCorrelationMetadataFromAsyncLogContext, init_context, mergeContext, runWithLogContext, updateAsyncLogContext } from "./context.js";
|
|
9
9
|
import { init_redact, isLogRedactionEnabled, redactLogRecord, redactObject, redactPemBlock, redactSecret, redactSensitiveInfo } from "./redact.js";
|
|
10
10
|
import { exportLog, flushExporters, getExporters, init_exporters, initializeExporters } from "./exporters.js";
|
|
11
11
|
import { configureAuditLog, getAuditConfig, init_audit, logAuditEvent, logAuthEvent, logConfigChange, logDataAccess, logPermissionChange } from "./audit.js";
|
|
@@ -25,7 +25,8 @@ function wrapLogMethod(method, defaultContext, level) {
|
|
|
25
25
|
if (isLoggerShuttingDown()) {
|
|
26
26
|
if (level !== "error" && level !== "fatal") return;
|
|
27
27
|
}
|
|
28
|
-
|
|
28
|
+
const module = defaultContext.module;
|
|
29
|
+
incrementStats(level, module);
|
|
29
30
|
return msg !== void 0 ? method.call(this, data, msg) : method.call(this, data);
|
|
30
31
|
};
|
|
31
32
|
}
|
|
@@ -35,9 +36,6 @@ function createProxyLogger(logger, defaultContext = {}) {
|
|
|
35
36
|
if (prop === "withContext") return (context) => {
|
|
36
37
|
return createProxyLogger(target.child({ ...context }), mergeContext(defaultContext, context));
|
|
37
38
|
};
|
|
38
|
-
if (prop === "childContext") return (context) => {
|
|
39
|
-
return createProxyLogger(target.child({ ...context }), mergeContext(defaultContext, context));
|
|
40
|
-
};
|
|
41
39
|
const value = target[propKey];
|
|
42
40
|
if (typeof value === "function") {
|
|
43
41
|
if ([
|
|
@@ -52,9 +50,9 @@ function createProxyLogger(logger, defaultContext = {}) {
|
|
|
52
50
|
return value;
|
|
53
51
|
} });
|
|
54
52
|
}
|
|
55
|
-
function createLogger(
|
|
56
|
-
return createProxyLogger(baseLogger.child({
|
|
57
|
-
module
|
|
53
|
+
function createLogger(module, context) {
|
|
54
|
+
return createProxyLogger(baseLogger.child({ module }), {
|
|
55
|
+
module,
|
|
58
56
|
...context
|
|
59
57
|
});
|
|
60
58
|
}
|
|
@@ -73,13 +71,6 @@ function createServiceLogger(serviceId) {
|
|
|
73
71
|
scope: serviceId
|
|
74
72
|
});
|
|
75
73
|
}
|
|
76
|
-
function createRequestLogger(requestId, initialContext) {
|
|
77
|
-
const context = {
|
|
78
|
-
requestId,
|
|
79
|
-
...initialContext
|
|
80
|
-
};
|
|
81
|
-
return createProxyLogger(baseLogger.child({ requestId }), context);
|
|
82
|
-
}
|
|
83
74
|
function setLogLevel(level) {
|
|
84
75
|
baseLogger.level = level;
|
|
85
76
|
}
|
|
@@ -105,18 +96,8 @@ function isLevelEnabled(level) {
|
|
|
105
96
|
fatal: 60,
|
|
106
97
|
silent: Number.MAX_VALUE
|
|
107
98
|
};
|
|
108
|
-
|
|
109
|
-
trace: 10,
|
|
110
|
-
debug: 20,
|
|
111
|
-
info: 30,
|
|
112
|
-
warn: 40,
|
|
113
|
-
error: 50,
|
|
114
|
-
fatal: 60,
|
|
115
|
-
silent: Number.MAX_VALUE
|
|
116
|
-
};
|
|
117
|
-
return levelValues[level] >= currentLevelValues[getLogLevel()];
|
|
99
|
+
return levelValues[level] >= levelValues[getLogLevel()];
|
|
118
100
|
}
|
|
119
|
-
function registerShutdownHandler() {}
|
|
120
101
|
var pinoOptions, streams, baseLogger, logger;
|
|
121
102
|
var init_logger = __esmMin((() => {
|
|
122
103
|
init_config();
|
|
@@ -199,6 +180,6 @@ var init_logger = __esmMin((() => {
|
|
|
199
180
|
}));
|
|
200
181
|
//#endregion
|
|
201
182
|
init_logger();
|
|
202
|
-
export { pino as Pino, logger as baseLogger, logger
|
|
183
|
+
export { pino as Pino, logger as baseLogger, logger, cleanOldLogs, configureAuditLog, createExtensionLogger, createLogger, createModuleLogger, createServiceLogger, exportLog, flushAndClose, flushExporters, getAsyncLogContext, getAuditConfig, getExporters, getLogDir, getLogLevel, getLoggerConfig, getRuntimeLogStats, inboundCorrelationMetadataFromAsyncLogContext, init_logger, initializeExporters, isLevelEnabled, isLogRedactionEnabled, isLoggerShuttingDown, logAuditEvent, logAuthEvent, logConfigChange, logDataAccess, logPermissionChange, redactObject, redactPemBlock, redactSecret, redactSensitiveInfo, rotateLogs, runWithLogContext, setLogLevel, setShuttingDown, updateAsyncLogContext, withLogLevel };
|
|
203
184
|
|
|
204
185
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/utils/logger/index.ts"],"sourcesContent":["/**\n * Modular Logger System\n * \n * Production-grade logging with:\n * - Modular architecture\n * - Context tracking\n * - Statistics\n * - Graceful shutdown\n */\n\nimport pino from 'pino';\nimport type { Logger as PinoLogger } from 'pino';\nimport type { LogLevel, LogContext, ContextualLogger } from './types.js';\n\n// Internal modules\nimport { config, getLogDir, getLoggerConfig } from './config.js';\nimport { initializeStreams } from './streams.js';\nimport { incrementStats, getLogStats } from './stats.js';\nimport { isLoggerShuttingDown, flushAndClose, setShuttingDown } from './shutdown.js';\nimport { rotateLogs, cleanOldLogs } from './rotation.js';\nimport { getAsyncLogContext, getAsyncLogCorrelationKeys, mergeContext } from './context.js';\nimport { redactLogRecord } from './redact.js';\nimport { PACKAGE_VERSION } from '../../package-version.js';\n\n// ============================================\n// Base Logger Creation\n// ============================================\n\nconst customLevels = {\n trace: 10,\n debug: 20,\n info: 30,\n warn: 40,\n error: 50,\n fatal: 60,\n};\n\nconst pinoOptions: pino.LoggerOptions = {\n level: config.level,\n base: {\n service: 'xopc',\n version: process.env.npm_package_version || PACKAGE_VERSION,\n },\n timestamp: pino.stdTimeFunctions.isoTime,\n mixin(mergeObject, _level, logger) {\n const ctx = getAsyncLogContext();\n if (!ctx) return {};\n const bindings =\n logger && typeof logger.bindings === 'function'\n ? (logger.bindings() as Record<string, unknown>)\n : {};\n const mo = mergeObject as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const key of getAsyncLogCorrelationKeys()) {\n const v = ctx[key];\n if (v === undefined || v === '') continue;\n if (mo[key] !== undefined) continue;\n if (bindings[key] !== undefined) continue;\n out[key] = v;\n }\n return out;\n },\n formatters: {\n level: (label) => ({ level: label }),\n bindings: (bindings) => ({\n pid: bindings.pid,\n host: bindings.host,\n }),\n log: (object) => {\n // Serialize errors with full stack trace\n if (object.err && object.err instanceof Error) {\n object.err = {\n name: object.err.name,\n message: object.err.message,\n stack: object.err.stack,\n cause: object.err.cause instanceof Error \n ? { name: object.err.cause.name, message: object.err.cause.message, stack: object.err.cause.stack }\n : object.err.cause,\n };\n }\n for (const key of Object.keys(object)) {\n if (object[key] instanceof Error) {\n object[key] = {\n name: object[key].name,\n message: object[key].message,\n stack: object[key].stack,\n };\n }\n }\n return redactLogRecord(object as Record<string, unknown>);\n },\n },\n customLevels,\n};\n\n// Add pretty print for development\nif (config.prettyPrint) {\n pinoOptions.transport = {\n target: 'pino-pretty',\n options: {\n colorize: true,\n translateTime: 'SYS:standard',\n ignore: 'pid,host',\n },\n };\n}\n\nconst streams = initializeStreams();\nconst baseLogger = pino(pinoOptions, pino.multistream(streams));\n\n// ============================================\n// Contextual Logger\n// ============================================\n\nfunction wrapLogMethod(method: Function, defaultContext: LogContext, level: LogLevel) {\n return function (data: unknown, msg?: string) {\n // During shutdown, only allow error and fatal logs\n if (isLoggerShuttingDown()) {\n if (level !== 'error' && level !== 'fatal') {\n return;\n }\n }\n \n const module = (defaultContext.module || defaultContext.prefix) as string | undefined;\n incrementStats(level, module);\n \n return msg !== undefined ? method.call(this, data, msg) : method.call(this, data);\n };\n}\n\nfunction createProxyLogger(logger: PinoLogger, defaultContext: LogContext = {}): ContextualLogger {\n const proxy = new Proxy(logger, {\n get(target, prop) {\n const propKey = prop as string;\n \n if (prop === 'withContext') {\n return (context: LogContext) => {\n return createProxyLogger(target.child({ ...context }), mergeContext(defaultContext, context));\n };\n }\n if (prop === 'childContext') {\n return (context: LogContext) => {\n return createProxyLogger(target.child({ ...context }), mergeContext(defaultContext, context));\n };\n }\n \n const value = (target as unknown as Record<string, unknown>)[propKey];\n if (typeof value === 'function') {\n if (['trace', 'debug', 'info', 'warn', 'error', 'fatal'].includes(propKey)) {\n return wrapLogMethod(value, defaultContext, propKey as LogLevel);\n }\n }\n \n return value;\n },\n });\n\n return proxy as ContextualLogger;\n}\n\n// ============================================\n// Public API\n// ============================================\n\nexport const logger = createProxyLogger(baseLogger);\n\nexport function createLogger(prefix: string, context?: LogContext): ContextualLogger {\n const child = baseLogger.child({ prefix });\n return createProxyLogger(child, { module: prefix, ...context });\n}\n\nexport function createModuleLogger(moduleName: string, _modulePath?: string): ContextualLogger {\n const child = baseLogger.child({ module: moduleName });\n return createProxyLogger(child, { module: moduleName });\n}\n\nexport function createExtensionLogger(extensionName: string): ContextualLogger {\n const child = baseLogger.child({ extension: extensionName });\n return createProxyLogger(child, { extension: extensionName });\n}\n\nexport function createServiceLogger(serviceId: string): ContextualLogger {\n const child = baseLogger.child({ service: 'cron', scope: serviceId });\n return createProxyLogger(child, { service: 'cron', scope: serviceId });\n}\n\nexport function createRequestLogger(requestId: string, initialContext?: LogContext): ContextualLogger {\n const context: LogContext = { requestId, ...initialContext };\n const child = baseLogger.child({ requestId });\n return createProxyLogger(child, context);\n}\n\n// ============================================\n// Log Level Management\n// ============================================\n\nexport function setLogLevel(level: LogLevel): void {\n baseLogger.level = level;\n}\n\nexport function getLogLevel(): LogLevel {\n return baseLogger.level as LogLevel;\n}\n\nexport function withLogLevel<T>(level: LogLevel, fn: () => T): T {\n const previous = baseLogger.level;\n baseLogger.level = level;\n try {\n return fn();\n } finally {\n baseLogger.level = previous;\n }\n}\n\nexport function isLevelEnabled(level: LogLevel): boolean {\n const levelValues: Record<LogLevel, number> = {\n trace: 10, debug: 20, info: 30, warn: 40, error: 50, fatal: 60, silent: Number.MAX_VALUE,\n };\n const currentLevelValues: Record<LogLevel, number> = {\n trace: 10, debug: 20, info: 30, warn: 40, error: 50, fatal: 60, silent: Number.MAX_VALUE,\n };\n return levelValues[level] >= currentLevelValues[getLogLevel()];\n}\n\n// ============================================\n// Re-exports\n// ============================================\n\nexport type {\n LogLevel,\n LogContext,\n LogEntry,\n LoggerConfig,\n LogFileMeta,\n LogQuery,\n LogStats,\n RotationResult,\n ContextualLogger,\n} from './types.js';\n\nexport {\n // Config\n getLogDir,\n getLoggerConfig,\n \n // Stats\n getLogStats,\n \n // Shutdown\n isLoggerShuttingDown,\n flushAndClose,\n setShuttingDown,\n \n // Rotation\n rotateLogs,\n cleanOldLogs,\n};\n\n// Exporters\nexport {\n type LogExporter,\n type ExporterConfig,\n type LokiConfig,\n type ElkConfig,\n type DatadogConfig,\n type WebhookConfig,\n initializeExporters,\n exportLog,\n flushExporters,\n getExporters,\n} from './exporters.js';\n\nexport {\n redactSensitiveInfo,\n redactObject,\n redactPemBlock,\n redactSecret,\n isLogRedactionEnabled,\n} from './redact.js';\n\n// Audit Log\nexport {\n type AuditEvent,\n type AuditEventType,\n type AuditLogConfig,\n logAuditEvent,\n logAuthEvent,\n logConfigChange,\n logPermissionChange,\n logDataAccess,\n configureAuditLog,\n getAuditConfig,\n} from './audit.js';\n\nexport { logger as default };\nexport { logger as baseLogger };\nexport { pino as Pino };\n\nexport function registerShutdownHandler(): void {}\n\nexport {\n runWithLogContext,\n getAsyncLogContext,\n updateAsyncLogContext,\n inboundCorrelationMetadataFromAsyncLogContext,\n setRequestContext as setRequestLogger,\n clearRequestContext as clearRequestLogger,\n} from './context.js';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../../src/utils/logger/index.ts"],"sourcesContent":["/**\n * Modular Logger System\n * \n * Production-grade logging with:\n * - Modular architecture\n * - Context tracking\n * - Statistics\n * - Graceful shutdown\n */\n\nimport pino from 'pino';\nimport type { Logger as PinoLogger } from 'pino';\nimport type { LogLevel, LogContext, ContextualLogger } from './types.js';\n\n// Internal modules\nimport { config, getLogDir, getLoggerConfig } from './config.js';\nimport { initializeStreams } from './streams.js';\nimport { incrementStats, getRuntimeLogStats } from './stats.js';\nimport { isLoggerShuttingDown, flushAndClose, setShuttingDown } from './shutdown.js';\nimport { rotateLogs, cleanOldLogs } from './rotation.js';\nimport { getAsyncLogContext, getAsyncLogCorrelationKeys, mergeContext } from './context.js';\nimport { redactLogRecord } from './redact.js';\nimport { PACKAGE_VERSION } from '../../package-version.js';\n\n// ============================================\n// Base Logger Creation\n// ============================================\n\nconst customLevels = {\n trace: 10,\n debug: 20,\n info: 30,\n warn: 40,\n error: 50,\n fatal: 60,\n};\n\nconst pinoOptions: pino.LoggerOptions = {\n level: config.level,\n base: {\n service: 'xopc',\n version: process.env.npm_package_version || PACKAGE_VERSION,\n },\n timestamp: pino.stdTimeFunctions.isoTime,\n mixin(mergeObject, _level, logger) {\n const ctx = getAsyncLogContext();\n if (!ctx) return {};\n const bindings =\n logger && typeof logger.bindings === 'function'\n ? (logger.bindings() as Record<string, unknown>)\n : {};\n const mo = mergeObject as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const key of getAsyncLogCorrelationKeys()) {\n const v = ctx[key];\n if (v === undefined || v === '') continue;\n if (mo[key] !== undefined) continue;\n if (bindings[key] !== undefined) continue;\n out[key] = v;\n }\n return out;\n },\n formatters: {\n level: (label) => ({ level: label }),\n bindings: (bindings) => ({\n pid: bindings.pid,\n host: bindings.host,\n }),\n log: (object) => {\n // Serialize errors with full stack trace\n if (object.err && object.err instanceof Error) {\n object.err = {\n name: object.err.name,\n message: object.err.message,\n stack: object.err.stack,\n cause: object.err.cause instanceof Error \n ? { name: object.err.cause.name, message: object.err.cause.message, stack: object.err.cause.stack }\n : object.err.cause,\n };\n }\n for (const key of Object.keys(object)) {\n if (object[key] instanceof Error) {\n object[key] = {\n name: object[key].name,\n message: object[key].message,\n stack: object[key].stack,\n };\n }\n }\n return redactLogRecord(object as Record<string, unknown>);\n },\n },\n customLevels,\n};\n\n// Add pretty print for development\nif (config.prettyPrint) {\n pinoOptions.transport = {\n target: 'pino-pretty',\n options: {\n colorize: true,\n translateTime: 'SYS:standard',\n ignore: 'pid,host',\n },\n };\n}\n\nconst streams = initializeStreams();\nconst baseLogger = pino(pinoOptions, pino.multistream(streams));\n\n// ============================================\n// Contextual Logger\n// ============================================\n\nfunction wrapLogMethod(method: Function, defaultContext: LogContext, level: LogLevel) {\n return function (data: unknown, msg?: string) {\n // During shutdown, only allow error and fatal logs\n if (isLoggerShuttingDown()) {\n if (level !== 'error' && level !== 'fatal') {\n return;\n }\n }\n \n const module = defaultContext.module as string | undefined;\n incrementStats(level, module);\n \n return msg !== undefined ? method.call(this, data, msg) : method.call(this, data);\n };\n}\n\nfunction createProxyLogger(logger: PinoLogger, defaultContext: LogContext = {}): ContextualLogger {\n const proxy = new Proxy(logger, {\n get(target, prop) {\n const propKey = prop as string;\n \n if (prop === 'withContext') {\n return (context: LogContext) => {\n return createProxyLogger(target.child({ ...context }), mergeContext(defaultContext, context));\n };\n }\n \n const value = (target as unknown as Record<string, unknown>)[propKey];\n if (typeof value === 'function') {\n if (['trace', 'debug', 'info', 'warn', 'error', 'fatal'].includes(propKey)) {\n return wrapLogMethod(value, defaultContext, propKey as LogLevel);\n }\n }\n \n return value;\n },\n });\n\n return proxy as ContextualLogger;\n}\n\n// ============================================\n// Public API\n// ============================================\n\nexport const logger = createProxyLogger(baseLogger);\n\nexport function createLogger(module: string, context?: LogContext): ContextualLogger {\n const child = baseLogger.child({ module });\n return createProxyLogger(child, { module, ...context });\n}\n\nexport function createModuleLogger(moduleName: string, _modulePath?: string): ContextualLogger {\n const child = baseLogger.child({ module: moduleName });\n return createProxyLogger(child, { module: moduleName });\n}\n\nexport function createExtensionLogger(extensionName: string): ContextualLogger {\n const child = baseLogger.child({ extension: extensionName });\n return createProxyLogger(child, { extension: extensionName });\n}\n\nexport function createServiceLogger(serviceId: string): ContextualLogger {\n const child = baseLogger.child({ service: 'cron', scope: serviceId });\n return createProxyLogger(child, { service: 'cron', scope: serviceId });\n}\n\n// ============================================\n// Log Level Management\n// ============================================\n\nexport function setLogLevel(level: LogLevel): void {\n baseLogger.level = level;\n}\n\nexport function getLogLevel(): LogLevel {\n return baseLogger.level as LogLevel;\n}\n\nexport function withLogLevel<T>(level: LogLevel, fn: () => T): T {\n const previous = baseLogger.level;\n baseLogger.level = level;\n try {\n return fn();\n } finally {\n baseLogger.level = previous;\n }\n}\n\nexport function isLevelEnabled(level: LogLevel): boolean {\n const levelValues: Record<LogLevel, number> = {\n trace: 10, debug: 20, info: 30, warn: 40, error: 50, fatal: 60, silent: Number.MAX_VALUE,\n };\n return levelValues[level] >= levelValues[getLogLevel()];\n}\n\n// ============================================\n// Re-exports\n// ============================================\n\nexport type {\n LogLevel,\n LogContext,\n LogEntry,\n LoggerConfig,\n LogFileMeta,\n LogQuery,\n LogStats,\n RotationResult,\n ContextualLogger,\n} from './types.js';\n\nexport {\n // Config\n getLogDir,\n getLoggerConfig,\n \n // Stats\n getRuntimeLogStats,\n \n // Shutdown\n isLoggerShuttingDown,\n flushAndClose,\n setShuttingDown,\n \n // Rotation\n rotateLogs,\n cleanOldLogs,\n};\n\n// Exporters\nexport {\n type LogExporter,\n type ExporterConfig,\n type LokiConfig,\n type ElkConfig,\n type DatadogConfig,\n type WebhookConfig,\n initializeExporters,\n exportLog,\n flushExporters,\n getExporters,\n} from './exporters.js';\n\nexport {\n redactSensitiveInfo,\n redactObject,\n redactPemBlock,\n redactSecret,\n isLogRedactionEnabled,\n} from './redact.js';\n\n// Audit Log\nexport {\n type AuditEvent,\n type AuditEventType,\n type AuditLogConfig,\n logAuditEvent,\n logAuthEvent,\n logConfigChange,\n logPermissionChange,\n logDataAccess,\n configureAuditLog,\n getAuditConfig,\n} from './audit.js';\n\nexport { logger as baseLogger };\nexport { pino as Pino };\n\nexport {\n runWithLogContext,\n getAsyncLogContext,\n updateAsyncLogContext,\n inboundCorrelationMetadataFromAsyncLogContext,\n} from './context.js';\n"],"mappings":"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"}
|
|
@@ -22,46 +22,28 @@ export declare function getLogPath(date?: Date, type?: 'app' | 'error' | 'audit'
|
|
|
22
22
|
* Query logs across multiple files
|
|
23
23
|
*/
|
|
24
24
|
export declare function queryLogs(query?: LogQuery): Promise<LogEntry[]>;
|
|
25
|
+
export declare function getLogModules(): Promise<string[]>;
|
|
25
26
|
/**
|
|
26
|
-
* Get
|
|
27
|
+
* Get log statistics by level (sampled from recent files)
|
|
27
28
|
*/
|
|
28
|
-
export declare function
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
export declare function getFileLogStats(): Promise<LogStats>;
|
|
30
|
+
export interface LogErrorSummaryItem {
|
|
31
|
+
key: string;
|
|
32
|
+
errName: string;
|
|
33
|
+
phase?: string;
|
|
31
34
|
module?: string;
|
|
32
|
-
|
|
35
|
+
count: number;
|
|
36
|
+
lastSeen: string;
|
|
37
|
+
sampleMessage: string;
|
|
38
|
+
}
|
|
33
39
|
/**
|
|
34
|
-
*
|
|
40
|
+
* Aggregate recent error/fatal logs by err name + phase + module.
|
|
35
41
|
*/
|
|
36
|
-
export declare function
|
|
42
|
+
export declare function getLogErrorSummary(options?: {
|
|
37
43
|
from?: string;
|
|
38
44
|
to?: string;
|
|
39
45
|
limit?: number;
|
|
40
|
-
}): Promise<
|
|
41
|
-
/**
|
|
42
|
-
* Get logs for a specific request/session
|
|
43
|
-
*/
|
|
44
|
-
export declare function getLogsByContext(contextType: 'requestId' | 'sessionId', contextValue: string, limit?: number): Promise<LogEntry[]>;
|
|
45
|
-
/**
|
|
46
|
-
* Get available log levels from actual log data
|
|
47
|
-
*/
|
|
48
|
-
export declare function getLogLevelsFromData(): Promise<LogLevel[]>;
|
|
49
|
-
/**
|
|
50
|
-
* Get available modules from log data
|
|
51
|
-
*/
|
|
52
|
-
export declare function getLogModules(): Promise<string[]>;
|
|
53
|
-
/**
|
|
54
|
-
* Get log statistics by level (sampled from recent files)
|
|
55
|
-
*/
|
|
56
|
-
export declare function getLogStats(): Promise<LogStats>;
|
|
57
|
-
/**
|
|
58
|
-
* Clean old logs (actually deletes files)
|
|
59
|
-
*/
|
|
60
|
-
export declare function cleanOldLogs(keepDays?: number): {
|
|
61
|
-
deleted: number;
|
|
62
|
-
freedBytes: number;
|
|
63
|
-
errors: string[];
|
|
64
|
-
};
|
|
46
|
+
}): Promise<LogErrorSummaryItem[]>;
|
|
65
47
|
/**
|
|
66
48
|
* Get available log levels
|
|
67
49
|
*/
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { init_pino_record, logEntrySearchText, pinoRecordToLogEntry } from "./pino-record.js";
|
|
2
2
|
import { basename, join } from "path";
|
|
3
|
-
import { createReadStream, existsSync, mkdirSync, readdirSync, statSync
|
|
3
|
+
import { createReadStream, existsSync, mkdirSync, readdirSync, statSync } from "fs";
|
|
4
|
+
import { Readable } from "stream";
|
|
5
|
+
import { readFile } from "fs/promises";
|
|
4
6
|
import { gunzip } from "zlib";
|
|
5
7
|
import { promisify } from "util";
|
|
6
8
|
import { createInterface } from "readline";
|
|
7
|
-
import { Readable } from "stream";
|
|
8
9
|
//#region src/utils/logger/log-store.ts
|
|
9
10
|
/**
|
|
10
11
|
* Log Store - Enhanced File-based Log Storage
|
|
@@ -16,6 +17,7 @@ import { Readable } from "stream";
|
|
|
16
17
|
* - Statistics and analytics
|
|
17
18
|
* - Safe log cleanup with actual deletion
|
|
18
19
|
*/
|
|
20
|
+
init_pino_record();
|
|
19
21
|
const gunzipAsync = promisify(gunzip);
|
|
20
22
|
const LOG_DIR = process.env.XOPC_LOG_DIR || join(process.env.HOME || ".", ".xopc", "logs");
|
|
21
23
|
function ensureLogDir() {
|
|
@@ -69,45 +71,12 @@ function parseLogLine(line, source, lineNumber) {
|
|
|
69
71
|
const trimmed = line.trim();
|
|
70
72
|
if (!trimmed) return null;
|
|
71
73
|
try {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
level: {
|
|
77
|
-
10: "trace",
|
|
78
|
-
20: "debug",
|
|
79
|
-
30: "info",
|
|
80
|
-
40: "warn",
|
|
81
|
-
50: "error",
|
|
82
|
-
60: "fatal"
|
|
83
|
-
}[levelNum] || (parsed.level?.toString() || "info").toLowerCase(),
|
|
84
|
-
message: parsed.msg || parsed.message || "",
|
|
85
|
-
module: parsed.module,
|
|
86
|
-
prefix: parsed.prefix,
|
|
87
|
-
service: parsed.service,
|
|
88
|
-
extension: parsed.extension,
|
|
89
|
-
requestId: parsed.requestId,
|
|
90
|
-
sessionId: parsed.sessionId,
|
|
91
|
-
_source: source,
|
|
92
|
-
_lineNumber: lineNumber,
|
|
93
|
-
...parsed
|
|
94
|
-
};
|
|
74
|
+
const entry = pinoRecordToLogEntry(JSON.parse(trimmed));
|
|
75
|
+
entry._source = source;
|
|
76
|
+
entry._lineNumber = lineNumber;
|
|
77
|
+
return entry;
|
|
95
78
|
} catch {
|
|
96
|
-
|
|
97
|
-
if (match) return {
|
|
98
|
-
timestamp: match[1],
|
|
99
|
-
level: match[2].toLowerCase(),
|
|
100
|
-
message: match[3],
|
|
101
|
-
_source: source,
|
|
102
|
-
_lineNumber: lineNumber
|
|
103
|
-
};
|
|
104
|
-
return {
|
|
105
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
106
|
-
level: "info",
|
|
107
|
-
message: trimmed,
|
|
108
|
-
_source: source,
|
|
109
|
-
_lineNumber: lineNumber
|
|
110
|
-
};
|
|
79
|
+
return null;
|
|
111
80
|
}
|
|
112
81
|
}
|
|
113
82
|
/**
|
|
@@ -164,15 +133,7 @@ function matchesQuery(entry, query) {
|
|
|
164
133
|
}
|
|
165
134
|
if (query.q) {
|
|
166
135
|
const keyword = query.q.toLowerCase();
|
|
167
|
-
if (!
|
|
168
|
-
entry.message,
|
|
169
|
-
entry.module,
|
|
170
|
-
entry.prefix,
|
|
171
|
-
entry.service,
|
|
172
|
-
entry.extension,
|
|
173
|
-
entry.requestId,
|
|
174
|
-
entry.sessionId
|
|
175
|
-
].filter(Boolean).map(String).join(" ").toLowerCase().includes(keyword)) return false;
|
|
136
|
+
if (!logEntrySearchText(entry).includes(keyword)) return false;
|
|
176
137
|
}
|
|
177
138
|
if (query.module && entry.module !== query.module) return false;
|
|
178
139
|
if (query.extension && entry.extension !== query.extension) return false;
|
|
@@ -207,62 +168,16 @@ async function queryLogs(query = {}) {
|
|
|
207
168
|
const limit = query.limit || 100;
|
|
208
169
|
return results.slice(offset, offset + limit).map(({ _source, _lineNumber, ...rest }) => rest);
|
|
209
170
|
}
|
|
210
|
-
/**
|
|
211
|
-
* Get recent logs (convenience method)
|
|
212
|
-
*/
|
|
213
|
-
async function getRecentLogs(options) {
|
|
214
|
-
return queryLogs({
|
|
215
|
-
levels: options?.level ? [options.level] : void 0,
|
|
216
|
-
limit: options?.limit || 50,
|
|
217
|
-
module: options?.module,
|
|
218
|
-
order: "desc"
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* Search logs by keyword
|
|
223
|
-
*/
|
|
224
|
-
async function searchLogs(keyword, options) {
|
|
225
|
-
return queryLogs({
|
|
226
|
-
q: keyword,
|
|
227
|
-
...options,
|
|
228
|
-
limit: options?.limit || 100
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* Get logs for a specific request/session
|
|
233
|
-
*/
|
|
234
|
-
async function getLogsByContext(contextType, contextValue, limit = 100) {
|
|
235
|
-
return queryLogs({
|
|
236
|
-
[contextType]: contextValue,
|
|
237
|
-
limit,
|
|
238
|
-
order: "asc"
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Get available log levels from actual log data
|
|
243
|
-
*/
|
|
244
|
-
async function getLogLevelsFromData() {
|
|
245
|
-
const levels = /* @__PURE__ */ new Set();
|
|
246
|
-
const files = getLogFiles().slice(0, 3);
|
|
247
|
-
for (const file of files) for await (const entry of streamLogFile(file.path, { limit: 500 })) levels.add(entry.level);
|
|
248
|
-
return Array.from(levels).sort();
|
|
249
|
-
}
|
|
250
|
-
/**
|
|
251
|
-
* Get available modules from log data
|
|
252
|
-
*/
|
|
253
171
|
async function getLogModules() {
|
|
254
172
|
const modules = /* @__PURE__ */ new Set();
|
|
255
173
|
const files = getLogFiles().slice(0, 7);
|
|
256
|
-
for (const file of files) for await (const entry of streamLogFile(file.path, { limit: 1e3 }))
|
|
257
|
-
if (entry.module) modules.add(entry.module);
|
|
258
|
-
if (entry.prefix) modules.add(entry.prefix);
|
|
259
|
-
}
|
|
174
|
+
for (const file of files) for await (const entry of streamLogFile(file.path, { limit: 1e3 })) if (entry.module) modules.add(entry.module);
|
|
260
175
|
return Array.from(modules).filter(Boolean).sort();
|
|
261
176
|
}
|
|
262
177
|
/**
|
|
263
178
|
* Get log statistics by level (sampled from recent files)
|
|
264
179
|
*/
|
|
265
|
-
async function
|
|
180
|
+
async function getFileLogStats() {
|
|
266
181
|
const files = getLogFiles();
|
|
267
182
|
const byLevel = {
|
|
268
183
|
trace: 0,
|
|
@@ -276,29 +191,63 @@ async function getLogStats() {
|
|
|
276
191
|
for (const file of files.slice(0, 7)) for await (const entry of streamLogFile(file.path, { limit: 1e3 })) if (entry.level in byLevel) byLevel[entry.level]++;
|
|
277
192
|
return { byLevel };
|
|
278
193
|
}
|
|
194
|
+
function entryMeta(entry) {
|
|
195
|
+
if (!entry.meta || typeof entry.meta !== "object") return void 0;
|
|
196
|
+
return entry.meta;
|
|
197
|
+
}
|
|
198
|
+
function extractErrName(entry) {
|
|
199
|
+
const meta = entryMeta(entry);
|
|
200
|
+
const raw = entry.err ?? meta?.err;
|
|
201
|
+
if (raw && typeof raw === "object" && raw !== null) {
|
|
202
|
+
const name = raw.name;
|
|
203
|
+
if (typeof name === "string" && name.trim()) return name;
|
|
204
|
+
const message = raw.message;
|
|
205
|
+
if (typeof message === "string" && message.trim()) return message.slice(0, 120);
|
|
206
|
+
}
|
|
207
|
+
if (typeof entry.errorMessage === "string" && entry.errorMessage.trim()) return entry.errorMessage.slice(0, 120);
|
|
208
|
+
return entry.message?.slice(0, 120) || "Error";
|
|
209
|
+
}
|
|
210
|
+
function summaryKey(entry) {
|
|
211
|
+
const errName = extractErrName(entry);
|
|
212
|
+
const meta = entryMeta(entry);
|
|
213
|
+
return `${errName}::${typeof entry.phase === "string" ? entry.phase : typeof meta?.phase === "string" ? meta.phase : ""}::${entry.module || ""}`;
|
|
214
|
+
}
|
|
279
215
|
/**
|
|
280
|
-
*
|
|
216
|
+
* Aggregate recent error/fatal logs by err name + phase + module.
|
|
281
217
|
*/
|
|
282
|
-
function
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
218
|
+
async function getLogErrorSummary(options) {
|
|
219
|
+
const limit = options?.limit ?? 20;
|
|
220
|
+
const groups = /* @__PURE__ */ new Map();
|
|
221
|
+
const entries = await queryLogs({
|
|
222
|
+
levels: ["error", "fatal"],
|
|
223
|
+
from: options?.from,
|
|
224
|
+
to: options?.to,
|
|
225
|
+
limit: 5e3,
|
|
226
|
+
order: "desc"
|
|
227
|
+
});
|
|
228
|
+
for (const entry of entries) {
|
|
229
|
+
const key = summaryKey(entry);
|
|
230
|
+
const parsed = entry;
|
|
231
|
+
const existing = groups.get(key);
|
|
232
|
+
if (existing) {
|
|
233
|
+
existing.count += 1;
|
|
234
|
+
if (entry.timestamp > existing.lastSeen) {
|
|
235
|
+
existing.lastSeen = entry.timestamp;
|
|
236
|
+
existing.sampleMessage = entry.message;
|
|
237
|
+
}
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
groups.set(key, {
|
|
241
|
+
key,
|
|
242
|
+
errName: extractErrName(parsed),
|
|
243
|
+
phase: typeof parsed.phase === "string" ? parsed.phase : typeof entryMeta(parsed)?.phase === "string" ? String(entryMeta(parsed)?.phase) : void 0,
|
|
244
|
+
module: parsed.module,
|
|
245
|
+
count: 1,
|
|
246
|
+
lastSeen: entry.timestamp,
|
|
247
|
+
sampleMessage: entry.message
|
|
248
|
+
});
|
|
296
249
|
}
|
|
297
|
-
return
|
|
298
|
-
deleted,
|
|
299
|
-
freedBytes,
|
|
300
|
-
errors
|
|
301
|
-
};
|
|
250
|
+
return Array.from(groups.values()).sort((a, b) => b.count - a.count || b.lastSeen.localeCompare(a.lastSeen)).slice(0, limit);
|
|
302
251
|
}
|
|
303
252
|
const LOG_LEVELS = [
|
|
304
253
|
"trace",
|
|
@@ -315,6 +264,6 @@ function getLogLevels() {
|
|
|
315
264
|
return [...LOG_LEVELS];
|
|
316
265
|
}
|
|
317
266
|
//#endregion
|
|
318
|
-
export { LOG_DIR,
|
|
267
|
+
export { LOG_DIR, getFileLogStats, getLogErrorSummary, getLogFiles, getLogLevels, getLogModules, getLogPath, queryLogs };
|
|
319
268
|
|
|
320
269
|
//# sourceMappingURL=log-store.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log-store.js","names":[],"sources":["../../../../src/utils/logger/log-store.ts"],"sourcesContent":["/**\n * Log Store - Enhanced File-based Log Storage\n * \n * Features:\n * - Query logs across multiple files with filtering\n * - Support for compressed (.gz) log files\n * - Pagination and sorting\n * - Statistics and analytics\n * - Safe log cleanup with actual deletion\n */\n\nimport { \n existsSync, \n mkdirSync, \n readdirSync, \n statSync, \n createReadStream,\n unlinkSync,\n} from 'fs';\nimport { readFile } from 'fs/promises';\nimport { join, basename } from 'path';\nimport { createInterface } from 'readline';\nimport { gunzip } from 'zlib';\nimport { promisify } from 'util';\nimport { Readable } from 'stream';\nimport type { LogLevel, LogFileMeta, LogQuery, LogStats, LogEntry } from './types.js';\n\nconst gunzipAsync = promisify(gunzip);\n\n// ============================================\n// Types\n// ============================================\n\ninterface ParsedLogEntry extends LogEntry {\n _source?: string;\n _lineNumber?: number;\n}\n\n// ============================================\n// Configuration\n// ============================================\n\nconst LOG_DIR = process.env.XOPC_LOG_DIR || join(process.env.HOME || '.', '.xopc', 'logs');\n\nfunction ensureLogDir(): void {\n if (!existsSync(LOG_DIR)) {\n mkdirSync(LOG_DIR, { recursive: true });\n }\n}\n\n// ============================================\n// File Management\n// ============================================\n\n/**\n * Get all log files (including compressed)\n */\nexport function getLogFiles(): LogFileMeta[] {\n ensureLogDir();\n\n const files = readdirSync(LOG_DIR)\n .filter(f => f.endsWith('.log') || f.endsWith('.log.gz'))\n .map(f => {\n const filePath = join(LOG_DIR, f);\n const stats = statSync(filePath);\n \n let type: LogFileMeta['type'] = 'app';\n if (f.includes('error')) type = 'error';\n else if (f.includes('audit')) type = 'audit';\n else if (f.includes('access')) type = 'access';\n\n return {\n name: f,\n path: filePath,\n size: stats.size,\n created: stats.birthtime.toISOString(),\n modified: stats.mtime.toISOString(),\n type,\n };\n })\n .sort((a, b) => new Date(b.modified).getTime() - new Date(a.modified).getTime());\n\n return files;\n}\n\n/**\n * Get log file path for a specific date and type\n */\nexport function getLogPath(\n date: Date = new Date(), \n type: 'app' | 'error' | 'audit' | 'access' = 'app'\n): string {\n ensureLogDir();\n const dateStr = date.toISOString().split('T')[0];\n return join(LOG_DIR, `${type}-${dateStr}.log`);\n}\n\n/**\n * Get available log files for a date range\n */\nfunction getLogFilesForRange(from: Date, to: Date): LogFileMeta[] {\n const allFiles = getLogFiles();\n \n return allFiles.filter(f => {\n // Extract date from filename (e.g., app-2024-01-01.log)\n const match = f.name.match(/(\\d{4}-\\d{2}-\\d{2})/);\n if (!match) return false;\n \n const fileDate = new Date(match[1]);\n return fileDate >= from && fileDate <= to;\n });\n}\n\n// ============================================\n// Log Parsing\n// ============================================\n\n/**\n * Parse a single log line (JSON format from pino)\n */\nfunction parseLogLine(line: string, source?: string, lineNumber?: number): ParsedLogEntry | null {\n const trimmed = line.trim();\n if (!trimmed) return null;\n\n try {\n // Try JSON format (pino default)\n const parsed = JSON.parse(trimmed);\n \n // Convert pino numeric level to string (10=trace, 20=debug, 30=info, 40=warn, 50=error, 60=fatal)\n const levelNum = typeof parsed.level === 'number' ? parsed.level : 30;\n const levelMap: Record<number, string> = {\n 10: 'trace',\n 20: 'debug',\n 30: 'info',\n 40: 'warn',\n 50: 'error',\n 60: 'fatal',\n };\n\n return {\n timestamp: parsed.time || parsed.timestamp || '',\n level: levelMap[levelNum] || (parsed.level?.toString() || 'info').toLowerCase(),\n message: parsed.msg || parsed.message || '',\n module: parsed.module,\n prefix: parsed.prefix,\n service: parsed.service,\n extension: parsed.extension,\n requestId: parsed.requestId,\n sessionId: parsed.sessionId,\n _source: source,\n _lineNumber: lineNumber,\n ...parsed,\n };\n } catch {\n // Fallback: try to parse pino's text format\n const match = trimmed.match(/^\\[([^\\]]+)\\]\\s+(\\w+):\\s+(.+)$/);\n if (match) {\n return {\n timestamp: match[1],\n level: match[2].toLowerCase() as LogLevel,\n message: match[3],\n _source: source,\n _lineNumber: lineNumber,\n };\n }\n \n // Plain text fallback\n return {\n timestamp: new Date().toISOString(),\n level: 'info',\n message: trimmed,\n _source: source,\n _lineNumber: lineNumber,\n };\n }\n}\n\n// ============================================\n// Streaming\n// ============================================\n\n/**\n * Create a readable stream for a log file (handles .gz files)\n */\nasync function createLogFileStream(filePath: string): Promise<Readable> {\n if (filePath.endsWith('.gz')) {\n const compressed = await readFile(filePath);\n const decompressed = await gunzipAsync(compressed);\n return Readable.from(decompressed.toString('utf-8').split('\\n'));\n }\n \n return createReadStream(filePath, { encoding: 'utf-8' });\n}\n\n/**\n * Stream and filter log entries from a file\n */\nasync function* streamLogFile(\n filePath: string,\n query: LogQuery = {}\n): AsyncGenerator<ParsedLogEntry> {\n if (!existsSync(filePath)) return;\n\n const fileName = basename(filePath);\n let stream: Readable;\n \n try {\n stream = await createLogFileStream(filePath);\n } catch {\n return;\n }\n\n const rl = createInterface({ \n input: stream,\n crlfDelay: Infinity \n });\n\n let lineNumber = 0;\n\n try {\n for await (const line of rl) {\n lineNumber++;\n const entry = parseLogLine(line, fileName, lineNumber);\n if (!entry) continue;\n\n // Apply filters\n if (!matchesQuery(entry, query)) continue;\n\n yield entry;\n }\n } finally {\n rl.close();\n }\n}\n\n/**\n * Check if a log entry matches the query filters\n */\nfunction matchesQuery(entry: ParsedLogEntry, query: LogQuery): boolean {\n // Filter by levels\n if (query.levels?.length && !query.levels.includes(entry.level as LogLevel)) {\n return false;\n }\n\n // Filter by time range\n if (query.from) {\n const fromDate = new Date(query.from);\n const entryDate = new Date(entry.timestamp);\n if (entryDate < fromDate) return false;\n }\n if (query.to) {\n const toDate = new Date(query.to);\n const entryDate = new Date(entry.timestamp);\n if (entryDate > toDate) return false;\n }\n\n // Filter by keyword (search in message and context fields)\n if (query.q) {\n const keyword = query.q.toLowerCase();\n const searchable = [\n entry.message,\n entry.module,\n entry.prefix,\n entry.service,\n entry.extension,\n entry.requestId,\n entry.sessionId,\n ]\n .filter(Boolean)\n .map(String)\n .join(' ')\n .toLowerCase();\n \n if (!searchable.includes(keyword)) return false;\n }\n\n // Filter by specific fields\n if (query.module && entry.module !== query.module) return false;\n if (query.extension && entry.extension !== query.extension) return false;\n if (query.service && entry.service !== query.service) return false;\n if (query.requestId && entry.requestId !== query.requestId) return false;\n if (query.sessionId && entry.sessionId !== query.sessionId) return false;\n\n return true;\n}\n\n// ============================================\n// Query API\n// ============================================\n\n/** Cap matched rows before sort/slice so query stays bounded on huge log dirs. */\nconst QUERY_LOGS_MAX_MATCHED = 25_000;\n\n/**\n * Query logs across multiple files\n */\nexport async function queryLogs(query: LogQuery = {}): Promise<LogEntry[]> {\n ensureLogDir();\n\n const results: ParsedLogEntry[] = [];\n const files = getLogFiles();\n\n // Filter files by date range if specified\n let relevantFiles = files;\n if (query.from || query.to) {\n const fromDate = query.from ? new Date(query.from) : new Date(0);\n const toDate = query.to ? new Date(query.to) : new Date();\n relevantFiles = getLogFilesForRange(fromDate, toDate);\n }\n\n // Collect all matches (files are newest-first; lines in each file are time-asc).\n // Do not stop at `limit` while streaming — that only captured the oldest tail of\n // early files and made \"newest first\" wrong after sort.\n for (const file of relevantFiles) {\n if (results.length >= QUERY_LOGS_MAX_MATCHED) break;\n for await (const entry of streamLogFile(file.path, query)) {\n results.push(entry);\n if (results.length >= QUERY_LOGS_MAX_MATCHED) break;\n }\n }\n\n // Sort by timestamp\n const order = query.order || 'desc';\n results.sort((a, b) => {\n const diff = new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();\n return order === 'desc' ? diff : -diff;\n });\n\n // Apply offset/limit\n const offset = query.offset || 0;\n const limit = query.limit || 100;\n return results.slice(offset, offset + limit).map(({ _source, _lineNumber, ...rest }) => rest);\n}\n\n/**\n * Get recent logs (convenience method)\n */\nexport async function getRecentLogs(options?: {\n level?: LogLevel;\n limit?: number;\n module?: string;\n}): Promise<LogEntry[]> {\n return queryLogs({\n levels: options?.level ? [options.level] : undefined,\n limit: options?.limit || 50,\n module: options?.module,\n order: 'desc',\n });\n}\n\n/**\n * Search logs by keyword\n */\nexport async function searchLogs(\n keyword: string,\n options?: {\n from?: string;\n to?: string;\n limit?: number;\n }\n): Promise<LogEntry[]> {\n return queryLogs({\n q: keyword,\n ...options,\n limit: options?.limit || 100,\n });\n}\n\n/**\n * Get logs for a specific request/session\n */\nexport async function getLogsByContext(\n contextType: 'requestId' | 'sessionId',\n contextValue: string,\n limit: number = 100\n): Promise<LogEntry[]> {\n return queryLogs({\n [contextType]: contextValue,\n limit,\n order: 'asc',\n });\n}\n\n// ============================================\n// Statistics\n// ============================================\n\n/**\n * Get available log levels from actual log data\n */\nexport async function getLogLevelsFromData(): Promise<LogLevel[]> {\n const levels = new Set<LogLevel>();\n const files = getLogFiles().slice(0, 3); // Check last 3 files\n\n for (const file of files) {\n for await (const entry of streamLogFile(file.path, { limit: 500 })) {\n levels.add(entry.level as LogLevel);\n }\n }\n\n return Array.from(levels).sort();\n}\n\n/**\n * Get available modules from log data\n */\nexport async function getLogModules(): Promise<string[]> {\n const modules = new Set<string>();\n const files = getLogFiles().slice(0, 7);\n\n for (const file of files) {\n for await (const entry of streamLogFile(file.path, { limit: 1000 })) {\n if (entry.module) modules.add(entry.module);\n if (entry.prefix) modules.add(entry.prefix);\n }\n }\n\n return Array.from(modules).filter(Boolean).sort();\n}\n\n/**\n * Get log statistics by level (sampled from recent files)\n */\nexport async function getLogStats(): Promise<LogStats> {\n const files = getLogFiles();\n\n // Count by level (sample from recent files)\n const byLevel: Record<LogLevel, number> = {\n trace: 0,\n debug: 0,\n info: 0,\n warn: 0,\n error: 0,\n fatal: 0,\n silent: 0,\n };\n\n for (const file of files.slice(0, 7)) {\n for await (const entry of streamLogFile(file.path, { limit: 1000 })) {\n if (entry.level in byLevel) {\n byLevel[entry.level as LogLevel]++;\n }\n }\n }\n\n return { byLevel };\n}\n\n// ============================================\n// Cleanup\n// ============================================\n\n/**\n * Clean old logs (actually deletes files)\n */\nexport function cleanOldLogs(keepDays: number = 7): {\n deleted: number;\n freedBytes: number;\n errors: string[];\n} {\n const cutoff = new Date();\n cutoff.setDate(cutoff.getDate() - keepDays);\n\n let deleted = 0;\n let freedBytes = 0;\n const errors: string[] = [];\n\n const files = getLogFiles();\n for (const file of files) {\n const fileDate = new Date(file.modified);\n if (fileDate < cutoff) {\n try {\n const size = file.size;\n unlinkSync(file.path);\n deleted++;\n freedBytes += size;\n } catch (err) {\n errors.push(`Failed to delete ${file.name}: ${err}`);\n }\n }\n }\n\n return { deleted, freedBytes, errors };\n}\n\n/**\n * Clean logs by size (keep total under limit)\n */\nfunction _cleanBySize(maxTotalMB: number = 500): {\n deleted: number;\n freedBytes: number;\n errors: string[];\n} {\n const maxBytes = maxTotalMB * 1024 * 1024;\n const files = getLogFiles();\n \n let totalSize = files.reduce((sum, f) => sum + f.size, 0);\n let deleted = 0;\n let freedBytes = 0;\n const errors: string[] = [];\n\n // Delete oldest files until under limit\n for (const file of files.slice().reverse()) {\n if (totalSize <= maxBytes) break;\n\n try {\n unlinkSync(file.path);\n freedBytes += file.size;\n totalSize -= file.size;\n deleted++;\n } catch (err) {\n errors.push(`Failed to delete ${file.name}: ${err}`);\n }\n }\n\n return { deleted, freedBytes, errors };\n}\n\n// ============================================\n// Log Levels\n// ============================================\n\nconst LOG_LEVELS: LogLevel[] = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];\n\n/**\n * Get available log levels\n */\nexport function getLogLevels(): LogLevel[] {\n return [...LOG_LEVELS];\n}\n\n// ============================================\n// Exports\n// ============================================\n\nexport { LOG_DIR };\nexport type { LogEntry, LogQuery, LogFileMeta, LogStats } from './types.js';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"log-store.js","names":[],"sources":["../../../../src/utils/logger/log-store.ts"],"sourcesContent":["/**\n * Log Store - Enhanced File-based Log Storage\n * \n * Features:\n * - Query logs across multiple files with filtering\n * - Support for compressed (.gz) log files\n * - Pagination and sorting\n * - Statistics and analytics\n * - Safe log cleanup with actual deletion\n */\n\nimport { \n existsSync, \n mkdirSync, \n readdirSync, \n statSync, \n createReadStream,\n} from 'fs';\nimport { readFile } from 'fs/promises';\nimport { join, basename } from 'path';\nimport { createInterface } from 'readline';\nimport { gunzip } from 'zlib';\nimport { promisify } from 'util';\nimport { Readable } from 'stream';\nimport type { LogLevel, LogFileMeta, LogQuery, LogStats, LogEntry } from './types.js';\nimport { logEntrySearchText, pinoRecordToLogEntry } from './pino-record.js';\n\nconst gunzipAsync = promisify(gunzip);\n\n// ============================================\n// Types\n// ============================================\n\ninterface ParsedLogEntry extends LogEntry {\n _source?: string;\n _lineNumber?: number;\n}\n\n// ============================================\n// Configuration\n// ============================================\n\nconst LOG_DIR = process.env.XOPC_LOG_DIR || join(process.env.HOME || '.', '.xopc', 'logs');\n\nfunction ensureLogDir(): void {\n if (!existsSync(LOG_DIR)) {\n mkdirSync(LOG_DIR, { recursive: true });\n }\n}\n\n// ============================================\n// File Management\n// ============================================\n\n/**\n * Get all log files (including compressed)\n */\nexport function getLogFiles(): LogFileMeta[] {\n ensureLogDir();\n\n const files = readdirSync(LOG_DIR)\n .filter(f => f.endsWith('.log') || f.endsWith('.log.gz'))\n .map(f => {\n const filePath = join(LOG_DIR, f);\n const stats = statSync(filePath);\n \n let type: LogFileMeta['type'] = 'app';\n if (f.includes('error')) type = 'error';\n else if (f.includes('audit')) type = 'audit';\n else if (f.includes('access')) type = 'access';\n\n return {\n name: f,\n path: filePath,\n size: stats.size,\n created: stats.birthtime.toISOString(),\n modified: stats.mtime.toISOString(),\n type,\n };\n })\n .sort((a, b) => new Date(b.modified).getTime() - new Date(a.modified).getTime());\n\n return files;\n}\n\n/**\n * Get log file path for a specific date and type\n */\nexport function getLogPath(\n date: Date = new Date(), \n type: 'app' | 'error' | 'audit' | 'access' = 'app'\n): string {\n ensureLogDir();\n const dateStr = date.toISOString().split('T')[0];\n return join(LOG_DIR, `${type}-${dateStr}.log`);\n}\n\n/**\n * Get available log files for a date range\n */\nfunction getLogFilesForRange(from: Date, to: Date): LogFileMeta[] {\n const allFiles = getLogFiles();\n \n return allFiles.filter(f => {\n // Extract date from filename (e.g., app-2024-01-01.log)\n const match = f.name.match(/(\\d{4}-\\d{2}-\\d{2})/);\n if (!match) return false;\n \n const fileDate = new Date(match[1]);\n return fileDate >= from && fileDate <= to;\n });\n}\n\n// ============================================\n// Log Parsing\n// ============================================\n\n/**\n * Parse a single log line (JSON format from pino)\n */\nfunction parseLogLine(line: string, source?: string, lineNumber?: number): ParsedLogEntry | null {\n const trimmed = line.trim();\n if (!trimmed) return null;\n\n try {\n const parsed = JSON.parse(trimmed) as Record<string, unknown>;\n const entry = pinoRecordToLogEntry(parsed) as ParsedLogEntry;\n entry._source = source;\n entry._lineNumber = lineNumber;\n return entry;\n } catch {\n return null;\n }\n}\n\n// ============================================\n// Streaming\n// ============================================\n\n/**\n * Create a readable stream for a log file (handles .gz files)\n */\nasync function createLogFileStream(filePath: string): Promise<Readable> {\n if (filePath.endsWith('.gz')) {\n const compressed = await readFile(filePath);\n const decompressed = await gunzipAsync(compressed);\n return Readable.from(decompressed.toString('utf-8').split('\\n'));\n }\n \n return createReadStream(filePath, { encoding: 'utf-8' });\n}\n\n/**\n * Stream and filter log entries from a file\n */\nasync function* streamLogFile(\n filePath: string,\n query: LogQuery = {}\n): AsyncGenerator<ParsedLogEntry> {\n if (!existsSync(filePath)) return;\n\n const fileName = basename(filePath);\n let stream: Readable;\n \n try {\n stream = await createLogFileStream(filePath);\n } catch {\n return;\n }\n\n const rl = createInterface({ \n input: stream,\n crlfDelay: Infinity \n });\n\n let lineNumber = 0;\n\n try {\n for await (const line of rl) {\n lineNumber++;\n const entry = parseLogLine(line, fileName, lineNumber);\n if (!entry) continue;\n\n // Apply filters\n if (!matchesQuery(entry, query)) continue;\n\n yield entry;\n }\n } finally {\n rl.close();\n }\n}\n\n/**\n * Check if a log entry matches the query filters\n */\nfunction matchesQuery(entry: ParsedLogEntry, query: LogQuery): boolean {\n // Filter by levels\n if (query.levels?.length && !query.levels.includes(entry.level as LogLevel)) {\n return false;\n }\n\n // Filter by time range\n if (query.from) {\n const fromDate = new Date(query.from);\n const entryDate = new Date(entry.timestamp);\n if (entryDate < fromDate) return false;\n }\n if (query.to) {\n const toDate = new Date(query.to);\n const entryDate = new Date(entry.timestamp);\n if (entryDate > toDate) return false;\n }\n\n // Filter by keyword (search message, module, phase, err, structured fields)\n if (query.q) {\n const keyword = query.q.toLowerCase();\n if (!logEntrySearchText(entry).includes(keyword)) return false;\n }\n\n if (query.module && entry.module !== query.module) return false;\n if (query.extension && entry.extension !== query.extension) return false;\n if (query.service && entry.service !== query.service) return false;\n if (query.requestId && entry.requestId !== query.requestId) return false;\n if (query.sessionId && entry.sessionId !== query.sessionId) return false;\n\n return true;\n}\n\n// ============================================\n// Query API\n// ============================================\n\n/** Cap matched rows before sort/slice so query stays bounded on huge log dirs. */\nconst QUERY_LOGS_MAX_MATCHED = 25_000;\n\n/**\n * Query logs across multiple files\n */\nexport async function queryLogs(query: LogQuery = {}): Promise<LogEntry[]> {\n ensureLogDir();\n\n const results: ParsedLogEntry[] = [];\n const files = getLogFiles();\n\n // Filter files by date range if specified\n let relevantFiles = files;\n if (query.from || query.to) {\n const fromDate = query.from ? new Date(query.from) : new Date(0);\n const toDate = query.to ? new Date(query.to) : new Date();\n relevantFiles = getLogFilesForRange(fromDate, toDate);\n }\n\n // Collect all matches (files are newest-first; lines in each file are time-asc).\n // Do not stop at `limit` while streaming — that only captured the oldest tail of\n // early files and made \"newest first\" wrong after sort.\n for (const file of relevantFiles) {\n if (results.length >= QUERY_LOGS_MAX_MATCHED) break;\n for await (const entry of streamLogFile(file.path, query)) {\n results.push(entry);\n if (results.length >= QUERY_LOGS_MAX_MATCHED) break;\n }\n }\n\n // Sort by timestamp\n const order = query.order || 'desc';\n results.sort((a, b) => {\n const diff = new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();\n return order === 'desc' ? diff : -diff;\n });\n\n // Apply offset/limit\n const offset = query.offset || 0;\n const limit = query.limit || 100;\n return results.slice(offset, offset + limit).map(({ _source, _lineNumber, ...rest }) => rest);\n}\n\n// ============================================\n// Statistics\n// ============================================\nexport async function getLogModules(): Promise<string[]> {\n const modules = new Set<string>();\n const files = getLogFiles().slice(0, 7);\n\n for (const file of files) {\n for await (const entry of streamLogFile(file.path, { limit: 1000 })) {\n if (entry.module) modules.add(entry.module);\n }\n }\n\n return Array.from(modules).filter(Boolean).sort();\n}\n\n/**\n * Get log statistics by level (sampled from recent files)\n */\nexport async function getFileLogStats(): Promise<LogStats> {\n const files = getLogFiles();\n\n // Count by level (sample from recent files)\n const byLevel: Record<LogLevel, number> = {\n trace: 0,\n debug: 0,\n info: 0,\n warn: 0,\n error: 0,\n fatal: 0,\n silent: 0,\n };\n\n for (const file of files.slice(0, 7)) {\n for await (const entry of streamLogFile(file.path, { limit: 1000 })) {\n if (entry.level in byLevel) {\n byLevel[entry.level as LogLevel]++;\n }\n }\n }\n\n return { byLevel };\n}\n\n// ============================================\n// Error aggregation\n// ============================================\n\nexport interface LogErrorSummaryItem {\n key: string;\n errName: string;\n phase?: string;\n module?: string;\n count: number;\n lastSeen: string;\n sampleMessage: string;\n}\n\nfunction entryMeta(entry: ParsedLogEntry): Record<string, unknown> | undefined {\n if (!entry.meta || typeof entry.meta !== 'object') return undefined;\n return entry.meta as Record<string, unknown>;\n}\n\nfunction extractErrName(entry: ParsedLogEntry): string {\n const meta = entryMeta(entry);\n const raw = entry.err ?? meta?.err;\n if (raw && typeof raw === 'object' && raw !== null) {\n const name = (raw as Record<string, unknown>).name;\n if (typeof name === 'string' && name.trim()) return name;\n const message = (raw as Record<string, unknown>).message;\n if (typeof message === 'string' && message.trim()) return message.slice(0, 120);\n }\n if (typeof entry.errorMessage === 'string' && entry.errorMessage.trim()) {\n return entry.errorMessage.slice(0, 120);\n }\n return entry.message?.slice(0, 120) || 'Error';\n}\n\nfunction summaryKey(entry: ParsedLogEntry): string {\n const errName = extractErrName(entry);\n const meta = entryMeta(entry);\n const phase =\n typeof entry.phase === 'string'\n ? entry.phase\n : typeof meta?.phase === 'string'\n ? meta.phase\n : '';\n const module = entry.module || '';\n return `${errName}::${phase}::${module}`;\n}\n\n/**\n * Aggregate recent error/fatal logs by err name + phase + module.\n */\nexport async function getLogErrorSummary(options?: {\n from?: string;\n to?: string;\n limit?: number;\n}): Promise<LogErrorSummaryItem[]> {\n const limit = options?.limit ?? 20;\n const groups = new Map<string, LogErrorSummaryItem>();\n\n const entries = await queryLogs({\n levels: ['error', 'fatal'],\n from: options?.from,\n to: options?.to,\n limit: 5000,\n order: 'desc',\n });\n\n for (const entry of entries) {\n const key = summaryKey(entry as ParsedLogEntry);\n const parsed = entry as ParsedLogEntry;\n const existing = groups.get(key);\n if (existing) {\n existing.count += 1;\n if (entry.timestamp > existing.lastSeen) {\n existing.lastSeen = entry.timestamp;\n existing.sampleMessage = entry.message;\n }\n continue;\n }\n groups.set(key, {\n key,\n errName: extractErrName(parsed),\n phase:\n typeof parsed.phase === 'string'\n ? parsed.phase\n : typeof entryMeta(parsed)?.phase === 'string'\n ? String(entryMeta(parsed)?.phase)\n : undefined,\n module: parsed.module,\n count: 1,\n lastSeen: entry.timestamp,\n sampleMessage: entry.message,\n });\n }\n\n return Array.from(groups.values())\n .sort((a, b) => b.count - a.count || b.lastSeen.localeCompare(a.lastSeen))\n .slice(0, limit);\n}\n\n// ============================================\n// Log Levels\n// ============================================\n\nconst LOG_LEVELS: LogLevel[] = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];\n\n/**\n * Get available log levels\n */\nexport function getLogLevels(): LogLevel[] {\n return [...LOG_LEVELS];\n}\n\n// ============================================\n// Exports\n// ============================================\n\nexport { LOG_DIR };\nexport type { LogEntry, LogQuery, LogFileMeta, LogStats } from './types.js';\n"],"mappings":"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"}
|