@evalops/maestro 0.10.23 → 0.10.24
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/README.md +4 -4
- package/dist/agent/agent.d.ts +2 -2
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +1 -1
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/keys.d.ts +2 -5
- package/dist/agent/keys.d.ts.map +1 -1
- package/dist/agent/keys.js +4 -5
- package/dist/agent/keys.js.map +1 -1
- package/dist/agent/modes.d.ts +2 -2
- package/dist/agent/modes.d.ts.map +1 -1
- package/dist/agent/modes.js +6 -7
- package/dist/agent/modes.js.map +1 -1
- package/dist/agent/providers/anthropic.d.ts.map +1 -1
- package/dist/agent/providers/anthropic.js +4 -4
- package/dist/agent/providers/anthropic.js.map +1 -1
- package/dist/agent/providers/google.js +1 -1
- package/dist/agent/swarm/executor.d.ts +2 -0
- package/dist/agent/swarm/executor.d.ts.map +1 -1
- package/dist/agent/swarm/executor.js +208 -35
- package/dist/agent/swarm/executor.js.map +1 -1
- package/dist/agent/swarm/types.d.ts +2 -0
- package/dist/agent/swarm/types.d.ts.map +1 -1
- package/dist/agent/swarm/types.js.map +1 -1
- package/dist/agent/tool-capability-types.d.ts +36 -0
- package/dist/agent/tool-capability-types.d.ts.map +1 -0
- package/dist/agent/tool-capability-types.js +2 -0
- package/dist/agent/tool-capability-types.js.map +1 -0
- package/dist/agent/transport/tool-execution-bridge.d.ts +13 -0
- package/dist/agent/transport/tool-execution-bridge.d.ts.map +1 -1
- package/dist/agent/transport/tool-execution-bridge.js +28 -15
- package/dist/agent/transport/tool-execution-bridge.js.map +1 -1
- package/dist/agent/transport/transport-utils.d.ts.map +1 -1
- package/dist/agent/transport/transport-utils.js +2 -3
- package/dist/agent/transport/transport-utils.js.map +1 -1
- package/dist/agent/transport.js +1 -1
- package/dist/agent/transport.js.map +1 -1
- package/dist/agent/type-guards.d.ts +1 -1
- package/dist/agent/type-guards.js +1 -1
- package/dist/agent/type-guards.js.map +1 -1
- package/dist/agent/types.d.ts +5 -3
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js +2 -2
- package/dist/agent/types.js.map +1 -1
- package/dist/app-server/daemon-lifecycle-api.d.ts +31 -0
- package/dist/app-server/daemon-lifecycle-api.d.ts.map +1 -0
- package/dist/app-server/daemon-lifecycle-api.js +259 -0
- package/dist/app-server/daemon-lifecycle-api.js.map +1 -0
- package/dist/app-server/external-agent-import-api.d.ts +30 -0
- package/dist/app-server/external-agent-import-api.d.ts.map +1 -0
- package/dist/app-server/external-agent-import-api.js +553 -0
- package/dist/app-server/external-agent-import-api.js.map +1 -0
- package/dist/app-server/host-control-api.d.ts +30 -0
- package/dist/app-server/host-control-api.d.ts.map +1 -0
- package/dist/app-server/host-control-api.js +343 -0
- package/dist/app-server/host-control-api.js.map +1 -0
- package/dist/app-server/in-process-client.d.ts +2 -2
- package/dist/app-server/in-process-client.d.ts.map +1 -1
- package/dist/app-server/in-process-client.js.map +1 -1
- package/dist/app-server/network-governance-api.d.ts +26 -0
- package/dist/app-server/network-governance-api.d.ts.map +1 -0
- package/dist/app-server/network-governance-api.js +476 -0
- package/dist/app-server/network-governance-api.js.map +1 -0
- package/dist/app-server/plugin-bundle-api.d.ts +17 -0
- package/dist/app-server/plugin-bundle-api.d.ts.map +1 -0
- package/dist/app-server/plugin-bundle-api.js +219 -0
- package/dist/app-server/plugin-bundle-api.js.map +1 -0
- package/dist/app-server/policy-control-api.d.ts +14 -0
- package/dist/app-server/policy-control-api.d.ts.map +1 -0
- package/dist/app-server/policy-control-api.js +200 -0
- package/dist/app-server/policy-control-api.js.map +1 -0
- package/dist/app-server/protocol-modes-api.d.ts +22 -0
- package/dist/app-server/protocol-modes-api.d.ts.map +1 -0
- package/dist/app-server/protocol-modes-api.js +124 -0
- package/dist/app-server/protocol-modes-api.js.map +1 -0
- package/dist/app-server/sandbox-proof-api.d.ts +21 -0
- package/dist/app-server/sandbox-proof-api.d.ts.map +1 -0
- package/dist/app-server/sandbox-proof-api.js +127 -0
- package/dist/app-server/sandbox-proof-api.js.map +1 -0
- package/dist/app-server/session-api.d.ts +93 -3
- package/dist/app-server/session-api.d.ts.map +1 -1
- package/dist/app-server/session-api.js +706 -18
- package/dist/app-server/session-api.js.map +1 -1
- package/dist/audit/logger.d.ts +1 -1
- package/dist/bootstrap/auth-setup.d.ts.map +1 -1
- package/dist/bootstrap/auth-setup.js +17 -2
- package/dist/bootstrap/auth-setup.js.map +1 -1
- package/dist/bootstrap/model-resolution-setup.d.ts.map +1 -1
- package/dist/bootstrap/model-resolution-setup.js +19 -4
- package/dist/bootstrap/model-resolution-setup.js.map +1 -1
- package/dist/cli/args.d.ts +2 -2
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +2 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/commands/a2a.d.ts.map +1 -1
- package/dist/cli/commands/a2a.js +226 -14
- package/dist/cli/commands/a2a.js.map +1 -1
- package/dist/cli/commands/anthropic.d.ts +1 -1
- package/dist/cli/commands/anthropic.d.ts.map +1 -1
- package/dist/cli/commands/anthropic.js +3 -68
- package/dist/cli/commands/anthropic.js.map +1 -1
- package/dist/cli/commands/modes.js +1 -1
- package/dist/cli/commands/modes.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +5 -7
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/rpc-mode.d.ts +1 -1
- package/dist/cli/rpc-mode.js +1 -1
- package/dist/cli/rpc-mode.js.map +1 -1
- package/dist/cli/session.js +1 -1
- package/dist/cli/session.js.map +1 -1
- package/dist/cli-tui/commands/command-catalog.d.ts.map +1 -1
- package/dist/cli-tui/commands/command-catalog.js +7 -12
- package/dist/cli-tui/commands/command-catalog.js.map +1 -1
- package/dist/cli-tui/commands/subcommands/auth-commands.d.ts +1 -1
- package/dist/cli-tui/commands/subcommands/auth-commands.d.ts.map +1 -1
- package/dist/cli-tui/commands/subcommands/auth-commands.js +4 -12
- package/dist/cli-tui/commands/subcommands/auth-commands.js.map +1 -1
- package/dist/cli-tui/custom-editor.d.ts +1 -1
- package/dist/cli-tui/custom-editor.js +2 -2
- package/dist/cli-tui/custom-editor.js.map +1 -1
- package/dist/cli-tui/oauth/oauth-flow-controller.d.ts.map +1 -1
- package/dist/cli-tui/oauth/oauth-flow-controller.js +19 -23
- package/dist/cli-tui/oauth/oauth-flow-controller.js.map +1 -1
- package/dist/cli-tui/selectors/model-selector.d.ts.map +1 -1
- package/dist/cli-tui/selectors/model-selector.js +0 -1
- package/dist/cli-tui/selectors/model-selector.js.map +1 -1
- package/dist/cli-tui/selectors/oauth-selector-view.d.ts +2 -2
- package/dist/cli-tui/selectors/oauth-selector-view.d.ts.map +1 -1
- package/dist/cli-tui/selectors/oauth-selector-view.js.map +1 -1
- package/dist/cli-tui/selectors/oauth-selector.d.ts +2 -2
- package/dist/cli-tui/selectors/oauth-selector.d.ts.map +1 -1
- package/dist/cli-tui/selectors/oauth-selector.js +3 -4
- package/dist/cli-tui/selectors/oauth-selector.js.map +1 -1
- package/dist/cli-tui/tui-renderer/mcp-events-setup.js +1 -1
- package/dist/cli-tui/tui-renderer/mcp-events-setup.js.map +1 -1
- package/dist/cli-tui/tui-renderer.js +2 -2
- package/dist/cli-tui/tui-renderer.js.map +1 -1
- package/dist/cli-tui/utils/footer-utils.d.ts +1 -1
- package/dist/cli-tui/utils/footer-utils.js +1 -1
- package/dist/cli-tui/utils/footer-utils.js.map +1 -1
- package/dist/cli.js +15156 -14232
- package/dist/commands/prompts.d.ts +1 -1
- package/dist/commands/prompts.js +1 -1
- package/dist/commands/prompts.js.map +1 -1
- package/dist/config/constants.d.ts +2 -2
- package/dist/config/constants.d.ts.map +1 -1
- package/dist/config/constants.js +2 -2
- package/dist/config/constants.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/toml-config.js +3 -3
- package/dist/config/toml-config.js.map +1 -1
- package/dist/db/client.d.ts +2 -2
- package/dist/db/client.js +2 -2
- package/dist/db/client.js.map +1 -1
- package/dist/db/migrate.d.ts +1 -1
- package/dist/db/migrate.js +1 -1
- package/dist/db/migrate.js.map +1 -1
- package/dist/db/schema.d.ts +2 -2
- package/dist/db/schema.js +1 -1
- package/dist/db/schema.js.map +1 -1
- package/dist/errors/index.d.ts +2 -2
- package/dist/errors/index.js +2 -2
- package/dist/errors/index.js.map +1 -1
- package/dist/main.d.ts +4 -5
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +17 -14
- package/dist/main.js.map +1 -1
- package/dist/mcp/config.d.ts +15 -2
- package/dist/mcp/config.d.ts.map +1 -1
- package/dist/mcp/config.js +50 -26
- package/dist/mcp/config.js.map +1 -1
- package/dist/mcp/fathom-cua.d.ts.map +1 -1
- package/dist/mcp/fathom-cua.js +10 -0
- package/dist/mcp/fathom-cua.js.map +1 -1
- package/dist/mcp/manager.d.ts.map +1 -1
- package/dist/mcp/manager.js +32 -1
- package/dist/mcp/manager.js.map +1 -1
- package/dist/mcp/tool-bridge.d.ts.map +1 -1
- package/dist/mcp/tool-bridge.js +12 -3
- package/dist/mcp/tool-bridge.js.map +1 -1
- package/dist/mcp/tool-capabilities.d.ts +5 -0
- package/dist/mcp/tool-capabilities.d.ts.map +1 -0
- package/dist/mcp/tool-capabilities.js +271 -0
- package/dist/mcp/tool-capabilities.js.map +1 -0
- package/dist/mcp/types.d.ts +6 -1
- package/dist/mcp/types.d.ts.map +1 -1
- package/dist/mcp/types.js.map +1 -1
- package/dist/models/config-loader.d.ts +1 -1
- package/dist/models/factory-integration.d.ts +1 -1
- package/dist/models/factory-integration.js +1 -1
- package/dist/models/factory-integration.js.map +1 -1
- package/dist/node_modules/@evalops/contracts/dist/index.d.ts +3 -3
- package/dist/node_modules/@evalops/contracts/dist/index.js +2 -2
- package/dist/node_modules/@evalops/contracts/dist/index.js.map +1 -1
- package/dist/node_modules/@evalops/contracts/dist/maestro-app-server.d.ts +1182 -14
- package/dist/node_modules/@evalops/contracts/dist/maestro-app-server.d.ts.map +1 -1
- package/dist/node_modules/@evalops/contracts/dist/maestro-app-server.js +450 -1
- package/dist/node_modules/@evalops/contracts/dist/maestro-app-server.js.map +1 -1
- package/dist/node_modules/@evalops/contracts/package.json +1 -1
- package/dist/node_modules/@evalops/tui/package.json +1 -1
- package/dist/oauth/connectors.d.ts +2 -2
- package/dist/oauth/connectors.d.ts.map +1 -1
- package/dist/oauth/connectors.js +3 -5
- package/dist/oauth/connectors.js.map +1 -1
- package/dist/oauth/github-copilot.d.ts +1 -1
- package/dist/oauth/github-copilot.js +1 -1
- package/dist/oauth/github-copilot.js.map +1 -1
- package/dist/oauth/index.d.ts +10 -8
- package/dist/oauth/index.d.ts.map +1 -1
- package/dist/oauth/index.js +54 -31
- package/dist/oauth/index.js.map +1 -1
- package/dist/oauth/openai.d.ts +1 -1
- package/dist/oauth/openai.js +2 -2
- package/dist/oauth/openai.js.map +1 -1
- package/dist/platform/a2a-completion-audit.d.ts +41 -0
- package/dist/platform/a2a-completion-audit.d.ts.map +1 -0
- package/dist/platform/a2a-completion-audit.js +177 -0
- package/dist/platform/a2a-completion-audit.js.map +1 -0
- package/dist/platform/a2a-task-ledger.d.ts.map +1 -1
- package/dist/platform/a2a-task-ledger.js +17 -0
- package/dist/platform/a2a-task-ledger.js.map +1 -1
- package/dist/platform/a2a-telemetry-inspect.d.ts +58 -0
- package/dist/platform/a2a-telemetry-inspect.d.ts.map +1 -0
- package/dist/platform/a2a-telemetry-inspect.js +224 -0
- package/dist/platform/a2a-telemetry-inspect.js.map +1 -0
- package/dist/platform/agent-registry-client.d.ts +8 -0
- package/dist/platform/agent-registry-client.d.ts.map +1 -1
- package/dist/platform/agent-registry-client.js +9 -1
- package/dist/platform/agent-registry-client.js.map +1 -1
- package/dist/providers/api-keys.d.ts +2 -2
- package/dist/providers/api-keys.js +2 -2
- package/dist/providers/api-keys.js.map +1 -1
- package/dist/providers/auth.d.ts +3 -6
- package/dist/providers/auth.d.ts.map +1 -1
- package/dist/providers/auth.js +1 -40
- package/dist/providers/auth.js.map +1 -1
- package/dist/rpc/index.d.ts +2 -2
- package/dist/rpc/index.js +2 -2
- package/dist/rpc/index.js.map +1 -1
- package/dist/rpc/rpc-types.d.ts +1 -1
- package/dist/rpc/rpc-types.js +1 -1
- package/dist/rpc/rpc-types.js.map +1 -1
- package/dist/safety/nested-agent-guard.js +1 -1
- package/dist/safety/nested-agent-guard.js.map +1 -1
- package/dist/safety/policy.d.ts +2 -2
- package/dist/safety/policy.js +2 -2
- package/dist/safety/policy.js.map +1 -1
- package/dist/safety/validators/network-policy-validator.d.ts +8 -0
- package/dist/safety/validators/network-policy-validator.d.ts.map +1 -1
- package/dist/safety/validators/network-policy-validator.js +29 -4
- package/dist/safety/validators/network-policy-validator.js.map +1 -1
- package/dist/sandbox/local-sandbox.d.ts +1 -1
- package/dist/sandbox/local-sandbox.js +1 -1
- package/dist/sandbox/local-sandbox.js.map +1 -1
- package/dist/server/app-context.d.ts +2 -0
- package/dist/server/app-context.d.ts.map +1 -1
- package/dist/server/app-context.js.map +1 -1
- package/dist/server/handlers/attachments.d.ts.map +1 -1
- package/dist/server/handlers/attachments.js +1 -0
- package/dist/server/handlers/attachments.js.map +1 -1
- package/dist/server/handlers/models.d.ts.map +1 -1
- package/dist/server/handlers/models.js +3 -1
- package/dist/server/handlers/models.js.map +1 -1
- package/dist/server/handlers/session-attachments.d.ts.map +1 -1
- package/dist/server/handlers/session-attachments.js +2 -0
- package/dist/server/handlers/session-attachments.js.map +1 -1
- package/dist/server/handlers/sessions.js +1 -1
- package/dist/server/handlers/sessions.js.map +1 -1
- package/dist/server/handlers/status.d.ts +2 -0
- package/dist/server/handlers/status.d.ts.map +1 -1
- package/dist/server/handlers/status.js +3 -2
- package/dist/server/handlers/status.js.map +1 -1
- package/dist/server/headless-runtime-service.d.ts.map +1 -1
- package/dist/server/headless-runtime-service.js +32 -3
- package/dist/server/headless-runtime-service.js.map +1 -1
- package/dist/server/hosted-session-manager.d.ts +2 -0
- package/dist/server/hosted-session-manager.d.ts.map +1 -1
- package/dist/server/hosted-session-manager.js +9 -0
- package/dist/server/hosted-session-manager.js.map +1 -1
- package/dist/server/model-selection.d.ts.map +1 -1
- package/dist/server/model-selection.js +9 -1
- package/dist/server/model-selection.js.map +1 -1
- package/dist/server/rate-limiter.d.ts +1 -1
- package/dist/server/rate-limiter.js +1 -1
- package/dist/server/rate-limiter.js.map +1 -1
- package/dist/server/router.d.ts +2 -2
- package/dist/server/router.js.map +1 -1
- package/dist/server/session-serialization.d.ts +9 -9
- package/dist/server/session-serialization.js +15 -15
- package/dist/server/session-serialization.js.map +1 -1
- package/dist/session/manager.d.ts +5 -0
- package/dist/session/manager.d.ts.map +1 -1
- package/dist/session/manager.js +56 -8
- package/dist/session/manager.js.map +1 -1
- package/dist/session/session-catalog.d.ts +2 -0
- package/dist/session/session-catalog.d.ts.map +1 -1
- package/dist/session/session-catalog.js +6 -0
- package/dist/session/session-catalog.js.map +1 -1
- package/dist/session/session-context.d.ts +2 -0
- package/dist/session/session-context.d.ts.map +1 -1
- package/dist/session/session-context.js +15 -0
- package/dist/session/session-context.js.map +1 -1
- package/dist/session/types.d.ts +7 -0
- package/dist/session/types.d.ts.map +1 -1
- package/dist/session/types.js.map +1 -1
- package/dist/session/wire-format.generated.d.ts +1 -0
- package/dist/session/wire-format.generated.d.ts.map +1 -1
- package/dist/session/wire-format.generated.js +1 -0
- package/dist/session/wire-format.generated.js.map +1 -1
- package/dist/shared-memory/contract.d.ts +4 -4
- package/dist/shared-memory/contract.js +2 -2
- package/dist/shared-memory/contract.js.map +1 -1
- package/dist/telemetry/index.d.ts +2 -2
- package/dist/telemetry/index.d.ts.map +1 -1
- package/dist/telemetry/index.js +2 -2
- package/dist/telemetry/index.js.map +1 -1
- package/dist/telemetry/maestro-event-bus.d.ts +57 -0
- package/dist/telemetry/maestro-event-bus.d.ts.map +1 -1
- package/dist/telemetry/maestro-event-bus.js +59 -1
- package/dist/telemetry/maestro-event-bus.js.map +1 -1
- package/dist/telemetry/maestro-event-catalog.d.ts +17 -1
- package/dist/telemetry/maestro-event-catalog.d.ts.map +1 -1
- package/dist/telemetry/maestro-event-catalog.js +24 -0
- package/dist/telemetry/maestro-event-catalog.js.map +1 -1
- package/dist/telemetry/mcp-beacon.d.ts +2 -0
- package/dist/telemetry/mcp-beacon.d.ts.map +1 -1
- package/dist/telemetry/mcp-beacon.js +15 -0
- package/dist/telemetry/mcp-beacon.js.map +1 -1
- package/dist/telemetry/metrics.d.ts +62 -0
- package/dist/telemetry/metrics.d.ts.map +1 -1
- package/dist/telemetry/metrics.js +84 -0
- package/dist/telemetry/metrics.js.map +1 -1
- package/dist/telemetry.d.ts +44 -0
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +84 -2
- package/dist/telemetry.js.map +1 -1
- package/dist/theme/theme-loader.js +2 -2
- package/dist/theme/theme-loader.js.map +1 -1
- package/dist/theme/theme-schema.json +2 -2
- package/dist/theme/theme.d.ts +1 -1
- package/dist/theme/theme.js.map +1 -1
- package/dist/tools/background/task-types.d.ts +1 -1
- package/dist/tools/extract-document.d.ts +1 -0
- package/dist/tools/extract-document.d.ts.map +1 -1
- package/dist/tools/extract-document.js +2 -1
- package/dist/tools/extract-document.js.map +1 -1
- package/dist/tools/index.d.ts +55 -55
- package/dist/tools/index.js +1 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/tool-dsl.d.ts +1 -1
- package/dist/tools/tool-dsl.js +1 -1
- package/dist/tools/tool-dsl.js.map +1 -1
- package/dist/utils/document-extractor.d.ts +1 -0
- package/dist/utils/document-extractor.d.ts.map +1 -1
- package/dist/utils/document-extractor.js +320 -40
- package/dist/utils/document-extractor.js.map +1 -1
- package/dist/utils/logger.d.ts +1 -1
- package/dist/utils/logger.js +1 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/version.json +2 -2
- package/dist/web-server.d.ts +1 -1
- package/dist/web-server.d.ts.map +1 -1
- package/dist/web-server.js +12 -8
- package/dist/web-server.js.map +1 -1
- package/package.json +4 -2
- package/dist/oauth/anthropic.d.ts +0 -17
- package/dist/oauth/anthropic.d.ts.map +0 -1
- package/dist/oauth/anthropic.js +0 -81
- package/dist/oauth/anthropic.js.map +0 -1
- package/dist/providers/anthropic-auth.d.ts +0 -75
- package/dist/providers/anthropic-auth.d.ts.map +0 -1
- package/dist/providers/anthropic-auth.js +0 -221
- package/dist/providers/anthropic-auth.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attachments.js","sourceRoot":"","sources":["../../../src/server/handlers/attachments.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { extractDocumentText } from \"../../utils/document-extractor.js\";\nimport {\n\treadJsonBody,\n\trespondWithApiError,\n\tsendJson,\n} from \"../server-utils.js\";\n\ninterface ExtractAttachmentBody {\n\tfileName: string;\n\tmimeType?: string;\n\tcontentBase64: string;\n\tmaxChars?: number;\n}\n\nfunction normalizeBase64(input: string): string {\n\treturn input.replace(/\\s+/g, \"\");\n}\n\nfunction isValidBase64(input: string): boolean {\n\tif (!input) return false;\n\tconst mod = input.length % 4;\n\tif (mod === 1) return false;\n\treturn /^[A-Za-z0-9+/]*={0,2}$/.test(input);\n}\n\nexport async function handleAttachmentExtract(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tcors: Record<string, string>,\n) {\n\ttry {\n\t\tif (req.method !== \"POST\") {\n\t\t\tsendJson(res, 405, { error: \"Method not allowed\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\t// Allow large JSON payloads (base64 content); enforce actual byte limits in extractor.\n\t\tconst body = await readJsonBody<Partial<ExtractAttachmentBody>>(\n\t\t\treq,\n\t\t\t80 * 1024 * 1024,\n\t\t);\n\n\t\tconst fileName =\n\t\t\ttypeof body.fileName === \"string\" ? body.fileName.trim() : \"\";\n\t\tconst mimeType =\n\t\t\ttypeof body.mimeType === \"string\" ? body.mimeType.trim() : undefined;\n\t\tconst contentBase64 =\n\t\t\ttypeof body.contentBase64 === \"string\" ? body.contentBase64.trim() : \"\";\n\t\tconst maxChars =\n\t\t\ttypeof body.maxChars === \"number\" && Number.isFinite(body.maxChars)\n\t\t\t\t? body.maxChars\n\t\t\t\t: undefined;\n\n\t\tif (!fileName) {\n\t\t\tsendJson(res, 400, { error: \"fileName is required\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\t\tif (!contentBase64) {\n\t\t\tsendJson(res, 400, { error: \"contentBase64 is required\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst normalized = normalizeBase64(contentBase64);\n\t\tif (!isValidBase64(normalized)) {\n\t\t\tsendJson(res, 400, { error: \"Invalid base64 content\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst buffer = Buffer.from(normalized, \"base64\");\n\n\t\tconst extracted = await extractDocumentText({\n\t\t\tbuffer,\n\t\t\tfileName,\n\t\t\tmimeType,\n\t\t\tmaxChars,\n\t\t});\n\n\t\tif (!extracted.extractedText && extracted.format === \"unknown\") {\n\t\t\tsendJson(res, 400, { error: \"Unsupported document format\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tsendJson(\n\t\t\tres,\n\t\t\t200,\n\t\t\t{\n\t\t\t\tfileName,\n\t\t\t\tformat: extracted.format,\n\t\t\t\tsize: extracted.sizeBytes,\n\t\t\t\ttruncated: extracted.truncated,\n\t\t\t\textractedText: extracted.extractedText,\n\t\t\t},\n\t\t\tcors,\n\t\t\treq,\n\t\t);\n\t} catch (error) {\n\t\trespondWithApiError(res, error, 500, cors, req);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"attachments.js","sourceRoot":"","sources":["../../../src/server/handlers/attachments.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { extractDocumentText } from \"../../utils/document-extractor.js\";\nimport {\n\treadJsonBody,\n\trespondWithApiError,\n\tsendJson,\n} from \"../server-utils.js\";\n\ninterface ExtractAttachmentBody {\n\tfileName: string;\n\tmimeType?: string;\n\tcontentBase64: string;\n\tmaxChars?: number;\n}\n\nfunction normalizeBase64(input: string): string {\n\treturn input.replace(/\\s+/g, \"\");\n}\n\nfunction isValidBase64(input: string): boolean {\n\tif (!input) return false;\n\tconst mod = input.length % 4;\n\tif (mod === 1) return false;\n\treturn /^[A-Za-z0-9+/]*={0,2}$/.test(input);\n}\n\nexport async function handleAttachmentExtract(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tcors: Record<string, string>,\n) {\n\ttry {\n\t\tif (req.method !== \"POST\") {\n\t\t\tsendJson(res, 405, { error: \"Method not allowed\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\t// Allow large JSON payloads (base64 content); enforce actual byte limits in extractor.\n\t\tconst body = await readJsonBody<Partial<ExtractAttachmentBody>>(\n\t\t\treq,\n\t\t\t80 * 1024 * 1024,\n\t\t);\n\n\t\tconst fileName =\n\t\t\ttypeof body.fileName === \"string\" ? body.fileName.trim() : \"\";\n\t\tconst mimeType =\n\t\t\ttypeof body.mimeType === \"string\" ? body.mimeType.trim() : undefined;\n\t\tconst contentBase64 =\n\t\t\ttypeof body.contentBase64 === \"string\" ? body.contentBase64.trim() : \"\";\n\t\tconst maxChars =\n\t\t\ttypeof body.maxChars === \"number\" && Number.isFinite(body.maxChars)\n\t\t\t\t? body.maxChars\n\t\t\t\t: undefined;\n\n\t\tif (!fileName) {\n\t\t\tsendJson(res, 400, { error: \"fileName is required\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\t\tif (!contentBase64) {\n\t\t\tsendJson(res, 400, { error: \"contentBase64 is required\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst normalized = normalizeBase64(contentBase64);\n\t\tif (!isValidBase64(normalized)) {\n\t\t\tsendJson(res, 400, { error: \"Invalid base64 content\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst buffer = Buffer.from(normalized, \"base64\");\n\n\t\tconst extracted = await extractDocumentText({\n\t\t\tbuffer,\n\t\t\tfileName,\n\t\t\tmimeType,\n\t\t\tmaxChars,\n\t\t});\n\n\t\tif (!extracted.extractedText && extracted.format === \"unknown\") {\n\t\t\tsendJson(res, 400, { error: \"Unsupported document format\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tsendJson(\n\t\t\tres,\n\t\t\t200,\n\t\t\t{\n\t\t\t\tfileName,\n\t\t\t\tformat: extracted.format,\n\t\t\t\textractor: extracted.extractor,\n\t\t\t\tsize: extracted.sizeBytes,\n\t\t\t\ttruncated: extracted.truncated,\n\t\t\t\textractedText: extracted.extractedText,\n\t\t\t},\n\t\t\tcors,\n\t\t\treq,\n\t\t);\n\t} catch (error) {\n\t\trespondWithApiError(res, error, 500, cors, req);\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../../src/server/handlers/models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAIjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAa1D,wBAAsB,YAAY,CACjC,IAAI,EAAE,eAAe,EACrB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,iBAoB5B;AAgCD,wBAAsB,WAAW,CAChC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../../src/server/handlers/models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAIjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAa1D,wBAAsB,YAAY,CACjC,IAAI,EAAE,eAAe,EACrB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,iBAoB5B;AAgCD,wBAAsB,WAAW,CAChC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,gBAAgB,iBA0EzB"}
|
|
@@ -76,7 +76,9 @@ export async function handleModel(req, res, context) {
|
|
|
76
76
|
}, cors, req);
|
|
77
77
|
return;
|
|
78
78
|
}
|
|
79
|
-
|
|
79
|
+
if (registeredModel.api !== "openai-codex-app-server") {
|
|
80
|
+
await ensureCredential(registeredModel.provider);
|
|
81
|
+
}
|
|
80
82
|
if (onSelect)
|
|
81
83
|
onSelect(registeredModel);
|
|
82
84
|
respondWithModel(res, registeredModel, cors, req);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../../src/server/handlers/models.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../../src/server/handlers/models.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { getRegisteredModels } from \"../../models/registry.js\";\nimport type { RegisteredModel } from \"../../models/registry.js\";\nimport { evaluateModelPolicy } from \"../../services/workspace-config/policy.js\";\nimport type { WebServerContext } from \"../app-context.js\";\nimport {\n\tdetermineModelSelection,\n\tgetRegisteredModelOrThrow,\n} from \"../model-selection.js\";\nimport { getWorkspaceConfigContext } from \"../request-context.js\";\nimport { respondWithApiError, sendJson } from \"../server-utils.js\";\nimport {\n\ttype ModelSetInput,\n\tModelSetSchema,\n\tparseAndValidateJson,\n} from \"../validation.js\";\n\nexport async function handleModels(\n\t_req: IncomingMessage,\n\tres: ServerResponse,\n\tcors: Record<string, string>,\n) {\n\tconst models = getRegisteredModels();\n\tconst modelList = models.map((m) => ({\n\t\tid: m.id,\n\t\tprovider: m.provider,\n\t\tname: m.name || m.id,\n\t\tapi: m.api,\n\t\tcontextWindow: m.contextWindow,\n\t\tmaxTokens: m.maxTokens,\n\t\tcost: m.cost,\n\t\tcapabilities: {\n\t\t\tstreaming: true,\n\t\t\ttools: true,\n\t\t\tvision: m.input?.includes(\"image\") || false,\n\t\t\treasoning: m.reasoning || false,\n\t\t},\n\t}));\n\n\tsendJson(res, 200, { models: modelList }, cors, _req);\n}\n\nfunction respondWithModel(\n\tres: ServerResponse,\n\tmodel: RegisteredModel,\n\tcors: Record<string, string>,\n\treq?: IncomingMessage,\n) {\n\tsendJson(\n\t\tres,\n\t\t200,\n\t\t{\n\t\t\tid: model.id,\n\t\t\tprovider: model.provider,\n\t\t\tname: model.name || model.id,\n\t\t\tapi: model.api,\n\t\t\tcontextWindow: model.contextWindow,\n\t\t\tmaxTokens: model.maxTokens,\n\t\t\tcost: model.cost,\n\t\t\tcapabilities: {\n\t\t\t\tstreaming: true,\n\t\t\t\ttools: true,\n\t\t\t\tvision: model.input?.includes(\"image\") || false,\n\t\t\t\treasoning: model.reasoning || false,\n\t\t\t},\n\t\t\treasoning: model.reasoning,\n\t\t},\n\t\tcors,\n\t\treq,\n\t);\n}\n\nexport async function handleModel(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tcontext: WebServerContext,\n) {\n\tconst {\n\t\tcorsHeaders: cors,\n\t\tgetCurrentSelection,\n\t\tensureCredential,\n\t\tsetModelSelection: onSelect,\n\t} = context;\n\tconst defaults = getCurrentSelection();\n\n\tif (req.method === \"GET\") {\n\t\tconst models = getRegisteredModels();\n\t\tconst active =\n\t\t\tmodels.find(\n\t\t\t\t(entry) =>\n\t\t\t\t\tentry.provider === defaults.provider && entry.id === defaults.modelId,\n\t\t\t) ?? models[0];\n\t\tif (!active) {\n\t\t\tres.writeHead(404, {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t...cors,\n\t\t\t});\n\t\t\tres.end(JSON.stringify({ error: \"No models registered\" }));\n\t\t\treturn;\n\t\t}\n\t\trespondWithModel(res, active, cors, req);\n\t\treturn;\n\t}\n\n\tif (req.method === \"POST\") {\n\t\t// Remove try/catch, let router handle it\n\t\tconst payload = await parseAndValidateJson<ModelSetInput>(\n\t\t\treq,\n\t\t\tModelSetSchema,\n\t\t);\n\t\tconst modelInput = payload.model.trim();\n\n\t\tconst selection = determineModelSelection(\n\t\t\tmodelInput,\n\t\t\tdefaults.provider,\n\t\t\tdefaults.modelId,\n\t\t);\n\t\tconst registeredModel = getRegisteredModelOrThrow(selection);\n\t\tconst violation = evaluateModelPolicy(getWorkspaceConfigContext()?.config, {\n\t\t\tprovider: registeredModel.provider,\n\t\t\tmodelId: registeredModel.id,\n\t\t});\n\t\tif (violation) {\n\t\t\tsendJson(\n\t\t\t\tres,\n\t\t\t\t403,\n\t\t\t\t{\n\t\t\t\t\terror: violation.message,\n\t\t\t\t\tcode: violation.code,\n\t\t\t\t\tworkspaceId: violation.workspaceId,\n\t\t\t\t},\n\t\t\t\tcors,\n\t\t\t\treq,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tif (registeredModel.api !== \"openai-codex-app-server\") {\n\t\t\tawait ensureCredential(registeredModel.provider);\n\t\t}\n\t\tif (onSelect) onSelect(registeredModel);\n\t\trespondWithModel(res, registeredModel, cors, req);\n\t\treturn;\n\t}\n\n\tres.writeHead(405, {\n\t\t\"Content-Type\": \"application/json\",\n\t\t...cors,\n\t});\n\tres.end(JSON.stringify({ error: \"Method not allowed\" }));\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-attachments.d.ts","sourceRoot":"","sources":["../../../src/server/handlers/session-attachments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AA4DjE,wBAAsB,uBAAuB,CAC5C,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,EAC5C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,iBAuD5B;AAED,wBAAsB,8BAA8B,CACnD,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,EAC5C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"session-attachments.d.ts","sourceRoot":"","sources":["../../../src/server/handlers/session-attachments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AA4DjE,wBAAsB,uBAAuB,CAC5C,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,EAC5C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,iBAuD5B;AAED,wBAAsB,8BAA8B,CACnD,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,EAC5C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,iBA8F5B"}
|
|
@@ -120,6 +120,7 @@ export async function handleSessionAttachmentExtract(req, res, params, cors) {
|
|
|
120
120
|
sendJson(res, 200, {
|
|
121
121
|
fileName: attachment.fileName,
|
|
122
122
|
format: "unknown",
|
|
123
|
+
extractor: "native",
|
|
123
124
|
size: attachment.size ?? 0,
|
|
124
125
|
truncated: false,
|
|
125
126
|
extractedText: attachment.extractedText,
|
|
@@ -145,6 +146,7 @@ export async function handleSessionAttachmentExtract(req, res, params, cors) {
|
|
|
145
146
|
sendJson(res, 200, {
|
|
146
147
|
fileName: attachment.fileName,
|
|
147
148
|
format: extracted.format,
|
|
149
|
+
extractor: extracted.extractor,
|
|
148
150
|
size: extracted.sizeBytes,
|
|
149
151
|
truncated: extracted.truncated,
|
|
150
152
|
extractedText: extracted.extractedText,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-attachments.js","sourceRoot":"","sources":["../../../src/server/handlers/session-attachments.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { extractDocumentText } from \"../../utils/document-extractor.js\";\nimport {\n\tbuildContentDisposition,\n\trespondWithApiError,\n\tsendJson,\n} from \"../server-utils.js\";\nimport { createWebSessionManagerForRequest } from \"../session-scope.js\";\n\nconst sessionIdPattern = /^[a-zA-Z0-9._-]+$/;\nconst attachmentIdPattern = /^[a-zA-Z0-9._-]+$/;\n\nfunction findAttachmentInSession(\n\tsession: { messages?: Array<unknown> },\n\tattachmentId: string,\n): {\n\tfileName: string;\n\tmimeType: string;\n\tcontentBase64: string;\n\tsize?: number;\n\textractedText?: string;\n} | null {\n\tconst messages = Array.isArray(session.messages) ? session.messages : [];\n\tfor (const msg of messages) {\n\t\tif (!msg || typeof msg !== \"object\") continue;\n\t\tconst maybeAttachments = (msg as { attachments?: unknown }).attachments;\n\t\tif (!Array.isArray(maybeAttachments)) continue;\n\n\t\tfor (const att of maybeAttachments) {\n\t\t\tif (!att || typeof att !== \"object\") continue;\n\t\t\tconst a = att as {\n\t\t\t\tid?: unknown;\n\t\t\t\tfileName?: unknown;\n\t\t\t\tmimeType?: unknown;\n\t\t\t\tcontent?: unknown;\n\t\t\t\tsize?: unknown;\n\t\t\t\textractedText?: unknown;\n\t\t\t};\n\t\t\tif (a.id !== attachmentId) continue;\n\t\t\tif (typeof a.fileName !== \"string\" || !a.fileName) return null;\n\t\t\tif (typeof a.mimeType !== \"string\" || !a.mimeType) return null;\n\t\t\tif (typeof a.content !== \"string\" || !a.content) return null;\n\t\t\treturn {\n\t\t\t\tfileName: a.fileName,\n\t\t\t\tmimeType: a.mimeType,\n\t\t\t\tcontentBase64: a.content,\n\t\t\t\tsize:\n\t\t\t\t\ttypeof a.size === \"number\" && Number.isFinite(a.size)\n\t\t\t\t\t\t? a.size\n\t\t\t\t\t\t: undefined,\n\t\t\t\textractedText:\n\t\t\t\t\ttypeof a.extractedText === \"string\" && a.extractedText.length > 0\n\t\t\t\t\t\t? a.extractedText\n\t\t\t\t\t\t: undefined,\n\t\t\t};\n\t\t}\n\t}\n\treturn null;\n}\n\nexport async function handleSessionAttachment(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tparams: { id: string; attachmentId: string },\n\tcors: Record<string, string>,\n) {\n\tconst sessionManager = createWebSessionManagerForRequest(req, true);\n\n\ttry {\n\t\tif (req.method !== \"GET\") {\n\t\t\tsendJson(res, 405, { error: \"Method not allowed\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst sessionId = params.id;\n\t\tconst attachmentId = params.attachmentId;\n\n\t\tif (!sessionIdPattern.test(sessionId)) {\n\t\t\tsendJson(res, 400, { error: \"Invalid session id\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\t\tif (!attachmentIdPattern.test(attachmentId)) {\n\t\t\tsendJson(res, 400, { error: \"Invalid attachment id\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst session = await sessionManager.loadSession(sessionId);\n\t\tif (!session) {\n\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst attachment = findAttachmentInSession(session, attachmentId);\n\t\tif (!attachment) {\n\t\t\tsendJson(res, 404, { error: \"Attachment not found\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst bytes = Buffer.from(attachment.contentBase64, \"base64\");\n\n\t\tconst requestUrl = new URL(req.url || \"/\", \"http://localhost\");\n\t\tconst download = requestUrl.searchParams.get(\"download\");\n\t\tconst shouldDownload = download === \"1\" || download === \"true\";\n\n\t\tres.writeHead(200, {\n\t\t\t\"Content-Type\": attachment.mimeType,\n\t\t\t\"Content-Length\": bytes.length,\n\t\t\t\"Cache-Control\": \"private, max-age=3600\",\n\t\t\t...(shouldDownload\n\t\t\t\t? {\n\t\t\t\t\t\t\"Content-Disposition\": buildContentDisposition(attachment.fileName),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\t...cors,\n\t\t});\n\t\tres.end(bytes);\n\t} catch (error) {\n\t\trespondWithApiError(res, error, 500, cors, req);\n\t}\n}\n\nexport async function handleSessionAttachmentExtract(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tparams: { id: string; attachmentId: string },\n\tcors: Record<string, string>,\n) {\n\tconst sessionManager = createWebSessionManagerForRequest(req, true);\n\n\ttry {\n\t\tif (req.method !== \"POST\") {\n\t\t\tsendJson(res, 405, { error: \"Method not allowed\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst sessionId = params.id;\n\t\tconst attachmentId = params.attachmentId;\n\n\t\tif (!sessionIdPattern.test(sessionId)) {\n\t\t\tsendJson(res, 400, { error: \"Invalid session id\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\t\tif (!attachmentIdPattern.test(attachmentId)) {\n\t\t\tsendJson(res, 400, { error: \"Invalid attachment id\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst requestUrl = new URL(req.url || \"/\", \"http://localhost\");\n\t\tconst force = requestUrl.searchParams.get(\"force\");\n\t\tconst shouldForce = force === \"1\" || force === \"true\";\n\n\t\tconst session = await sessionManager.loadSession(sessionId);\n\t\tif (!session) {\n\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst attachment = findAttachmentInSession(session, attachmentId);\n\t\tif (!attachment) {\n\t\t\tsendJson(res, 404, { error: \"Attachment not found\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tif (attachment.extractedText && !shouldForce) {\n\t\t\tsendJson(\n\t\t\t\tres,\n\t\t\t\t200,\n\t\t\t\t{\n\t\t\t\t\tfileName: attachment.fileName,\n\t\t\t\t\tformat: \"unknown\",\n\t\t\t\t\tsize: attachment.size ?? 0,\n\t\t\t\t\ttruncated: false,\n\t\t\t\t\textractedText: attachment.extractedText,\n\t\t\t\t\tcached: true,\n\t\t\t\t},\n\t\t\t\tcors,\n\t\t\t\treq,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst bytes = Buffer.from(attachment.contentBase64, \"base64\");\n\t\tconst extracted = await extractDocumentText({\n\t\t\tbuffer: bytes,\n\t\t\tfileName: attachment.fileName,\n\t\t\tmimeType: attachment.mimeType,\n\t\t});\n\n\t\tconst sessionFile = sessionManager.getSessionFileById(sessionId);\n\t\tif (sessionFile && typeof extracted.extractedText === \"string\") {\n\t\t\ttry {\n\t\t\t\tsessionManager.saveAttachmentExtraction(\n\t\t\t\t\tsessionFile,\n\t\t\t\t\tattachmentId,\n\t\t\t\t\textracted.extractedText,\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\t// ignore persistence errors; extraction result still returned\n\t\t\t}\n\t\t}\n\n\t\tsendJson(\n\t\t\tres,\n\t\t\t200,\n\t\t\t{\n\t\t\t\tfileName: attachment.fileName,\n\t\t\t\tformat: extracted.format,\n\t\t\t\tsize: extracted.sizeBytes,\n\t\t\t\ttruncated: extracted.truncated,\n\t\t\t\textractedText: extracted.extractedText,\n\t\t\t},\n\t\t\tcors,\n\t\t\treq,\n\t\t);\n\t} catch (error) {\n\t\trespondWithApiError(res, error, 500, cors, req);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session-attachments.js","sourceRoot":"","sources":["../../../src/server/handlers/session-attachments.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { extractDocumentText } from \"../../utils/document-extractor.js\";\nimport {\n\tbuildContentDisposition,\n\trespondWithApiError,\n\tsendJson,\n} from \"../server-utils.js\";\nimport { createWebSessionManagerForRequest } from \"../session-scope.js\";\n\nconst sessionIdPattern = /^[a-zA-Z0-9._-]+$/;\nconst attachmentIdPattern = /^[a-zA-Z0-9._-]+$/;\n\nfunction findAttachmentInSession(\n\tsession: { messages?: Array<unknown> },\n\tattachmentId: string,\n): {\n\tfileName: string;\n\tmimeType: string;\n\tcontentBase64: string;\n\tsize?: number;\n\textractedText?: string;\n} | null {\n\tconst messages = Array.isArray(session.messages) ? session.messages : [];\n\tfor (const msg of messages) {\n\t\tif (!msg || typeof msg !== \"object\") continue;\n\t\tconst maybeAttachments = (msg as { attachments?: unknown }).attachments;\n\t\tif (!Array.isArray(maybeAttachments)) continue;\n\n\t\tfor (const att of maybeAttachments) {\n\t\t\tif (!att || typeof att !== \"object\") continue;\n\t\t\tconst a = att as {\n\t\t\t\tid?: unknown;\n\t\t\t\tfileName?: unknown;\n\t\t\t\tmimeType?: unknown;\n\t\t\t\tcontent?: unknown;\n\t\t\t\tsize?: unknown;\n\t\t\t\textractedText?: unknown;\n\t\t\t};\n\t\t\tif (a.id !== attachmentId) continue;\n\t\t\tif (typeof a.fileName !== \"string\" || !a.fileName) return null;\n\t\t\tif (typeof a.mimeType !== \"string\" || !a.mimeType) return null;\n\t\t\tif (typeof a.content !== \"string\" || !a.content) return null;\n\t\t\treturn {\n\t\t\t\tfileName: a.fileName,\n\t\t\t\tmimeType: a.mimeType,\n\t\t\t\tcontentBase64: a.content,\n\t\t\t\tsize:\n\t\t\t\t\ttypeof a.size === \"number\" && Number.isFinite(a.size)\n\t\t\t\t\t\t? a.size\n\t\t\t\t\t\t: undefined,\n\t\t\t\textractedText:\n\t\t\t\t\ttypeof a.extractedText === \"string\" && a.extractedText.length > 0\n\t\t\t\t\t\t? a.extractedText\n\t\t\t\t\t\t: undefined,\n\t\t\t};\n\t\t}\n\t}\n\treturn null;\n}\n\nexport async function handleSessionAttachment(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tparams: { id: string; attachmentId: string },\n\tcors: Record<string, string>,\n) {\n\tconst sessionManager = createWebSessionManagerForRequest(req, true);\n\n\ttry {\n\t\tif (req.method !== \"GET\") {\n\t\t\tsendJson(res, 405, { error: \"Method not allowed\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst sessionId = params.id;\n\t\tconst attachmentId = params.attachmentId;\n\n\t\tif (!sessionIdPattern.test(sessionId)) {\n\t\t\tsendJson(res, 400, { error: \"Invalid session id\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\t\tif (!attachmentIdPattern.test(attachmentId)) {\n\t\t\tsendJson(res, 400, { error: \"Invalid attachment id\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst session = await sessionManager.loadSession(sessionId);\n\t\tif (!session) {\n\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst attachment = findAttachmentInSession(session, attachmentId);\n\t\tif (!attachment) {\n\t\t\tsendJson(res, 404, { error: \"Attachment not found\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst bytes = Buffer.from(attachment.contentBase64, \"base64\");\n\n\t\tconst requestUrl = new URL(req.url || \"/\", \"http://localhost\");\n\t\tconst download = requestUrl.searchParams.get(\"download\");\n\t\tconst shouldDownload = download === \"1\" || download === \"true\";\n\n\t\tres.writeHead(200, {\n\t\t\t\"Content-Type\": attachment.mimeType,\n\t\t\t\"Content-Length\": bytes.length,\n\t\t\t\"Cache-Control\": \"private, max-age=3600\",\n\t\t\t...(shouldDownload\n\t\t\t\t? {\n\t\t\t\t\t\t\"Content-Disposition\": buildContentDisposition(attachment.fileName),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\t...cors,\n\t\t});\n\t\tres.end(bytes);\n\t} catch (error) {\n\t\trespondWithApiError(res, error, 500, cors, req);\n\t}\n}\n\nexport async function handleSessionAttachmentExtract(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tparams: { id: string; attachmentId: string },\n\tcors: Record<string, string>,\n) {\n\tconst sessionManager = createWebSessionManagerForRequest(req, true);\n\n\ttry {\n\t\tif (req.method !== \"POST\") {\n\t\t\tsendJson(res, 405, { error: \"Method not allowed\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst sessionId = params.id;\n\t\tconst attachmentId = params.attachmentId;\n\n\t\tif (!sessionIdPattern.test(sessionId)) {\n\t\t\tsendJson(res, 400, { error: \"Invalid session id\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\t\tif (!attachmentIdPattern.test(attachmentId)) {\n\t\t\tsendJson(res, 400, { error: \"Invalid attachment id\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst requestUrl = new URL(req.url || \"/\", \"http://localhost\");\n\t\tconst force = requestUrl.searchParams.get(\"force\");\n\t\tconst shouldForce = force === \"1\" || force === \"true\";\n\n\t\tconst session = await sessionManager.loadSession(sessionId);\n\t\tif (!session) {\n\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst attachment = findAttachmentInSession(session, attachmentId);\n\t\tif (!attachment) {\n\t\t\tsendJson(res, 404, { error: \"Attachment not found\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tif (attachment.extractedText && !shouldForce) {\n\t\t\tsendJson(\n\t\t\t\tres,\n\t\t\t\t200,\n\t\t\t\t{\n\t\t\t\t\tfileName: attachment.fileName,\n\t\t\t\t\tformat: \"unknown\",\n\t\t\t\t\textractor: \"native\",\n\t\t\t\t\tsize: attachment.size ?? 0,\n\t\t\t\t\ttruncated: false,\n\t\t\t\t\textractedText: attachment.extractedText,\n\t\t\t\t\tcached: true,\n\t\t\t\t},\n\t\t\t\tcors,\n\t\t\t\treq,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst bytes = Buffer.from(attachment.contentBase64, \"base64\");\n\t\tconst extracted = await extractDocumentText({\n\t\t\tbuffer: bytes,\n\t\t\tfileName: attachment.fileName,\n\t\t\tmimeType: attachment.mimeType,\n\t\t});\n\n\t\tconst sessionFile = sessionManager.getSessionFileById(sessionId);\n\t\tif (sessionFile && typeof extracted.extractedText === \"string\") {\n\t\t\ttry {\n\t\t\t\tsessionManager.saveAttachmentExtraction(\n\t\t\t\t\tsessionFile,\n\t\t\t\t\tattachmentId,\n\t\t\t\t\textracted.extractedText,\n\t\t\t\t);\n\t\t\t} catch {\n\t\t\t\t// ignore persistence errors; extraction result still returned\n\t\t\t}\n\t\t}\n\n\t\tsendJson(\n\t\t\tres,\n\t\t\t200,\n\t\t\t{\n\t\t\t\tfileName: attachment.fileName,\n\t\t\t\tformat: extracted.format,\n\t\t\t\textractor: extracted.extractor,\n\t\t\t\tsize: extracted.sizeBytes,\n\t\t\t\ttruncated: extracted.truncated,\n\t\t\t\textractedText: extracted.extractedText,\n\t\t\t},\n\t\t\tcors,\n\t\t\treq,\n\t\t);\n\t} catch (error) {\n\t\trespondWithApiError(res, error, 500, cors, req);\n\t}\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Session Management HTTP Handlers
|
|
3
3
|
*
|
|
4
|
-
* This module provides REST API endpoints for managing
|
|
4
|
+
* This module provides REST API endpoints for managing Maestro sessions,
|
|
5
5
|
* including CRUD operations, session sharing, and export functionality.
|
|
6
6
|
*
|
|
7
7
|
* ## Endpoints
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../../src/server/handlers/sessions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Session Management HTTP Handlers\n *\n * This module provides REST API endpoints for managing Composer sessions,\n * including CRUD operations, session sharing, and export functionality.\n *\n * ## Endpoints\n *\n * | Method | Path | Description |\n * |--------|------|-------------|\n * | GET | `/api/sessions` | List all sessions with pagination |\n * | GET | `/api/sessions/:id` | Get a specific session with messages |\n * | GET | `/api/sessions/:id/timeline` | Get a redacted run timeline |\n * | POST | `/api/sessions` | Create a new session |\n * | PATCH | `/api/sessions/:id` | Update session metadata (title, tags, favorite) |\n * | DELETE | `/api/sessions/:id` | Delete a session |\n * | POST | `/api/sessions/:id/share` | Generate a shareable link |\n * | GET | `/api/sessions/shared/:token` | Access a shared session |\n * | POST | `/api/sessions/:id/export` | Export session in various formats |\n *\n * ## Session Sharing\n *\n * Sessions can be shared via time-limited, access-limited tokens:\n * - Configurable expiration (default 24h, max 1 week)\n * - Optional access count limits\n * - Rate limiting to prevent brute-force attacks\n * - Atomic access counting to prevent race conditions\n *\n * ## Storage\n *\n * - Session data: JSONL files in `~/.maestro/agent/sessions/`\n * - Share tokens: PostgreSQL (with in-memory fallback)\n * - Rate limits: Redis (with in-memory fallback)\n *\n * @module web/handlers/sessions\n */\nimport crypto from \"node:crypto\";\nimport { readFileSync } from \"node:fs\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport type {\n\tComposerSession,\n\tComposerSessionSummary,\n} from \"@evalops/contracts\";\nimport {\n\tscanOutboundSensitiveContent,\n\tsummarizeOutboundSensitiveFindings,\n} from \"../../safety/outbound-secret-preflight.js\";\nimport { safeReadSessionEntries } from \"../../session/session-context.js\";\nimport type { SessionEntry, SessionMessagesView } from \"../../session/types.js\";\nimport { recordMaestroSessionEvent } from \"../../telemetry/maestro-event-bus.js\";\nimport { createLogger } from \"../../utils/logger.js\";\nimport { getAuthSubject } from \"../authz.js\";\nimport { isHostedSessionManager } from \"../hosted-session-manager.js\";\nimport { getPendingServerRequestPayload } from \"../pending-request-payload.js\";\nimport {\n\tbuildContentDisposition,\n\treadJsonBody,\n\trespondWithApiError,\n\tsendJson,\n} from \"../server-utils.js\";\nimport { webSessionEventEnv } from \"../session-event-env.js\";\nimport {\n\tcreateWebSessionManagerForRequest,\n\tcreateWebSessionManagerForScope,\n\tdecodeScopedSessionId,\n\tencodeScopedSessionId,\n\tresolveSessionScope,\n} from \"../session-scope.js\";\nimport { convertAppMessagesToComposer } from \"../session-serialization.js\";\nimport { exportToMarkdown, exportToText } from \"./session-export-formatters.js\";\nimport {\n\ttype SessionExportFormat,\n\ttype SessionShareOptions,\n\ttype SessionUpdateBody,\n\tcheckShareRateLimit,\n\tcreateSharedSessionInDb,\n\tdeleteExpiredShares,\n\tdeleteShareByToken,\n\tgetSharedSessionFromDb,\n\ttryAccessShare,\n} from \"./session-share-store.js\";\n\nexport {\n\tcheckShareRateLimit,\n\tresetShareRateLimit,\n\tstopShareRateLimiter,\n} from \"./session-share-store.js\";\nexport type {\n\tSessionExportFormat,\n\tSessionShareOptions,\n\tSessionUpdateBody,\n} from \"./session-share-store.js\";\n\nconst logger = createLogger(\"sessions-handler\");\nexport const sessionIdPattern = /^[a-zA-Z0-9._-]+$/;\nconst attachmentIdPattern = /^[a-zA-Z0-9._-]+$/;\n\nfunction parseSessionMessagesView(\n\tvalue: string | null,\n): SessionMessagesView | null {\n\tif (value === null || value === \"\" || value === \"full\") {\n\t\treturn \"full\";\n\t}\n\tif (value === \"summary\" || value === \"notLoaded\") {\n\t\treturn value;\n\t}\n\treturn null;\n}\n\nfunction sendSensitiveContentBlockedResponse(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tcors: Record<string, string>,\n\taction: \"export\" | \"share\",\n\tfindings: ReturnType<typeof summarizeOutboundSensitiveFindings>,\n): void {\n\tsendJson(\n\t\tres,\n\t\t409,\n\t\t{\n\t\t\terror: `Sensitive content detected. Review and confirm before continuing with this ${action}.`,\n\t\t\tcode: \"sensitive_content_detected\",\n\t\t\tdetails: findings,\n\t\t},\n\t\tcors,\n\t\treq,\n\t);\n}\n\n/**\n * Verify that the authenticated subject has access to the session.\n * Prevents IDOR attacks by checking session ownership fields.\n *\n * Returns true if access is allowed, false otherwise.\n */\nexport function verifySessionOwnership(\n\tsession: { owner?: unknown; subject?: unknown },\n\tsubject: string,\n): boolean {\n\t// Check explicit owner field\n\tif (typeof session.owner === \"string\" && session.owner) {\n\t\treturn session.owner === subject;\n\t}\n\n\t// Check subject field (for API-created sessions)\n\tif (typeof session.subject === \"string\" && session.subject) {\n\t\treturn session.subject === subject;\n\t}\n\n\t// For sessions without ownership info:\n\t// In strict mode (multi-user), deny access to prevent IDOR attacks.\n\t// Sessions created via CLI don't have ownership info, but API access\n\t// should be restricted in hosted environments.\n\tconst strictMode = process.env.MAESTRO_STRICT_SESSION_ACCESS !== \"false\";\n\treturn !strictMode;\n}\n\nfunction findAttachmentInSession(\n\tsession: { messages?: Array<unknown> },\n\tattachmentId: string,\n): {\n\tfileName: string;\n\tmimeType: string;\n\tcontentBase64: string;\n\tsize?: number;\n} | null {\n\tconst messages = Array.isArray(session.messages) ? session.messages : [];\n\tfor (const msg of messages) {\n\t\tif (!msg || typeof msg !== \"object\") continue;\n\t\tconst maybeAttachments = (msg as { attachments?: unknown }).attachments;\n\t\tif (!Array.isArray(maybeAttachments)) continue;\n\n\t\tfor (const att of maybeAttachments) {\n\t\t\tif (!att || typeof att !== \"object\") continue;\n\t\t\tconst a = att as {\n\t\t\t\tid?: unknown;\n\t\t\t\tfileName?: unknown;\n\t\t\t\tmimeType?: unknown;\n\t\t\t\tcontent?: unknown;\n\t\t\t\tsize?: unknown;\n\t\t\t};\n\t\t\tif (a.id !== attachmentId) continue;\n\t\t\tif (typeof a.fileName !== \"string\" || !a.fileName) return null;\n\t\t\tif (typeof a.mimeType !== \"string\" || !a.mimeType) return null;\n\t\t\tif (typeof a.content !== \"string\" || !a.content) return null;\n\t\t\treturn {\n\t\t\t\tfileName: a.fileName,\n\t\t\t\tmimeType: a.mimeType,\n\t\t\t\tcontentBase64: a.content,\n\t\t\t\tsize:\n\t\t\t\t\ttypeof a.size === \"number\" && Number.isFinite(a.size)\n\t\t\t\t\t\t? a.size\n\t\t\t\t\t\t: undefined,\n\t\t\t};\n\t\t}\n\t}\n\treturn null;\n}\n\nasync function resolveSharedSessionAccess(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tshareToken: string,\n\tcors: Record<string, string>,\n): Promise<string | null> {\n\tconst clientIp =\n\t\t(req.headers[\"x-forwarded-for\"] as string)?.split(\",\")[0]?.trim() ||\n\t\treq.socket.remoteAddress ||\n\t\t\"unknown\";\n\tconst rateLimit = await checkShareRateLimit(clientIp);\n\tif (!rateLimit.allowed) {\n\t\tres.setHeader(\"Retry-After\", String(rateLimit.retryAfterSeconds ?? 60));\n\t\tsendJson(\n\t\t\tres,\n\t\t\t429,\n\t\t\t{\n\t\t\t\terror: \"Too many requests\",\n\t\t\t\tretryAfter: rateLimit.retryAfterSeconds,\n\t\t\t},\n\t\t\tcors,\n\t\t\treq,\n\t\t);\n\t\treturn null;\n\t}\n\n\tconst accessResult = await tryAccessShare(shareToken);\n\tif (!accessResult.allowed) {\n\t\tswitch (accessResult.reason) {\n\t\t\tcase \"not_found\":\n\t\t\t\tsendJson(\n\t\t\t\t\tres,\n\t\t\t\t\t404,\n\t\t\t\t\t{ error: \"Share link not found or expired\" },\n\t\t\t\t\tcors,\n\t\t\t\t\treq,\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t\tcase \"expired\":\n\t\t\t\tawait deleteShareByToken(shareToken);\n\t\t\t\tsendJson(res, 410, { error: \"Share link has expired\" }, cors, req);\n\t\t\t\treturn null;\n\t\t\tcase \"max_accesses\":\n\t\t\t\tsendJson(\n\t\t\t\t\tres,\n\t\t\t\t\t410,\n\t\t\t\t\t{ error: \"Share link has reached maximum accesses\" },\n\t\t\t\t\tcors,\n\t\t\t\t\treq,\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t}\n\t}\n\n\treturn accessResult.sessionId ?? null;\n}\n\n// ============================================================================\n// SESSION HANDLERS\n// ============================================================================\n\n/**\n * Handle session list and CRUD operations\n */\nexport async function handleSessions(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tparams: { id?: string },\n\tcors: Record<string, string>,\n) {\n\tconst sessionManager = createWebSessionManagerForRequest(req, true);\n\tconst sessionId = params.id;\n\tconst url = new URL(req.url || \"/api/sessions\", \"http://localhost\");\n\tconst limitParam = url.searchParams.get(\"limit\");\n\tconst offsetParam = url.searchParams.get(\"offset\");\n\tconst parsedLimit = limitParam ? Number.parseInt(limitParam, 10) : undefined;\n\tconst parsedOffset = offsetParam\n\t\t? Number.parseInt(offsetParam, 10)\n\t\t: undefined;\n\tconst limit =\n\t\ttypeof parsedLimit === \"number\" && Number.isFinite(parsedLimit)\n\t\t\t? Math.max(1, parsedLimit)\n\t\t\t: undefined;\n\tconst offset =\n\t\ttypeof parsedOffset === \"number\" && Number.isFinite(parsedOffset)\n\t\t\t? Math.max(0, parsedOffset)\n\t\t\t: undefined;\n\n\ttry {\n\t\tif (req.method === \"GET\" && !sessionId) {\n\t\t\tconst subject = getAuthSubject(req);\n\t\t\tconst paginationRequested =\n\t\t\t\ttypeof limit === \"number\" || typeof offset === \"number\";\n\t\t\tconst sessions = (\n\t\t\t\tawait sessionManager.listSessions(\n\t\t\t\t\tpaginationRequested ? undefined : { limit, offset },\n\t\t\t\t)\n\t\t\t).filter((s) => verifySessionOwnership(s, subject));\n\t\t\tconst pagedSessions = paginationRequested\n\t\t\t\t? sessions.slice(\n\t\t\t\t\t\toffset ?? 0,\n\t\t\t\t\t\t(offset ?? 0) + (limit ?? sessions.length),\n\t\t\t\t\t)\n\t\t\t\t: sessions;\n\t\t\tconst sessionList: ComposerSessionSummary[] = pagedSessions.map((s) => ({\n\t\t\t\tid: s.id,\n\t\t\t\ttitle: s.title || `Session ${s.id.slice(0, 8)}`,\n\t\t\t\tresumeSummary: s.resumeSummary,\n\t\t\t\tcreatedAt: s.createdAt || new Date().toISOString(),\n\t\t\t\tupdatedAt: s.updatedAt || new Date().toISOString(),\n\t\t\t\tmessageCount: s.messageCount || 0,\n\t\t\t\tfavorite: s.favorite,\n\t\t\t\ttags: s.tags,\n\t\t\t}));\n\n\t\t\tsendJson(res, 200, { sessions: sessionList }, cors, req);\n\t\t} else if (req.method === \"GET\" && sessionId) {\n\t\t\tif (!sessionIdPattern.test(sessionId)) {\n\t\t\t\tsendJson(res, 400, { error: \"Invalid session id\" }, cors, req);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst messagesView = parseSessionMessagesView(\n\t\t\t\turl.searchParams.get(\"messagesView\") ??\n\t\t\t\t\turl.searchParams.get(\"itemsView\"),\n\t\t\t);\n\t\t\tif (!messagesView) {\n\t\t\t\tsendJson(\n\t\t\t\t\tres,\n\t\t\t\t\t400,\n\t\t\t\t\t{\n\t\t\t\t\t\terror:\n\t\t\t\t\t\t\t\"Invalid messagesView. Expected full, summary, or notLoaded.\",\n\t\t\t\t\t},\n\t\t\t\t\tcors,\n\t\t\t\t\treq,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst session = await sessionManager.loadSession(sessionId, {\n\t\t\t\tmessagesView,\n\t\t\t});\n\n\t\t\tif (!session) {\n\t\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Verify session ownership to prevent IDOR attacks\n\t\t\tconst subject = getAuthSubject(req);\n\t\t\tif (!verifySessionOwnership(session, subject)) {\n\t\t\t\tsendJson(\n\t\t\t\t\tres,\n\t\t\t\t\t403,\n\t\t\t\t\t{ error: \"Access denied: session belongs to another user\" },\n\t\t\t\t\tcors,\n\t\t\t\t\treq,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst responseBody: ComposerSession = {\n\t\t\t\tid: session.id,\n\t\t\t\ttitle: session.title,\n\t\t\t\tresumeSummary: session.resumeSummary,\n\t\t\t\tcreatedAt: session.createdAt,\n\t\t\t\tupdatedAt: session.updatedAt,\n\t\t\t\tmessageCount: session.messageCount,\n\t\t\t\tfavorite: session.favorite,\n\t\t\t\ttags: session.tags,\n\t\t\t\tmessagesView: session.messagesView,\n\t\t\t\tmessages: convertAppMessagesToComposer(session.messages || [], {\n\t\t\t\t\tincludeAttachmentContent: false,\n\t\t\t\t}),\n\t\t\t\t...getPendingServerRequestPayload(session.id),\n\t\t\t};\n\n\t\t\tsendJson(res, 200, responseBody, cors, req);\n\t\t} else if (req.method === \"POST\" && !sessionId) {\n\t\t\tconst { title } = await readJsonBody<{ title?: string }>(req);\n\t\t\tconst session = await sessionManager.createSession({ title });\n\t\t\tconst responseBody: ComposerSession = {\n\t\t\t\tid: session.id,\n\t\t\t\ttitle: session.title,\n\t\t\t\tresumeSummary: session.resumeSummary,\n\t\t\t\tcreatedAt: session.createdAt,\n\t\t\t\tupdatedAt: session.updatedAt,\n\t\t\t\tmessageCount: session.messageCount,\n\t\t\t\tfavorite: false,\n\t\t\t\ttags: undefined,\n\t\t\t\tmessages: convertAppMessagesToComposer(session.messages || [], {\n\t\t\t\t\tincludeAttachmentContent: false,\n\t\t\t\t}),\n\t\t\t};\n\n\t\t\tsendJson(res, 201, responseBody, cors, req);\n\t\t} else if (req.method === \"PATCH\" && sessionId) {\n\t\t\t// Update session metadata\n\t\t\tif (!sessionIdPattern.test(sessionId)) {\n\t\t\t\tsendJson(res, 400, { error: \"Invalid session id\" }, cors, req);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst updates = await readJsonBody<SessionUpdateBody>(req);\n\t\t\tconst session = await sessionManager.loadSession(sessionId);\n\n\t\t\tif (!session) {\n\t\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Verify session ownership to prevent IDOR attacks\n\t\t\tconst subject = getAuthSubject(req);\n\t\t\tif (!verifySessionOwnership(session, subject)) {\n\t\t\t\tsendJson(\n\t\t\t\t\tres,\n\t\t\t\t\t403,\n\t\t\t\t\t{ error: \"Access denied: session belongs to another user\" },\n\t\t\t\t\tcors,\n\t\t\t\t\treq,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (isHostedSessionManager(sessionManager)) {\n\t\t\t\tawait sessionManager.updateSessionMetadata(sessionId, updates);\n\t\t\t} else {\n\t\t\t\tconst sessionPath = sessionManager.getSessionFileById(sessionId);\n\t\t\t\tif (!sessionPath) {\n\t\t\t\t\tsendJson(res, 404, { error: \"Session file not found\" }, cors, req);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Apply updates - SessionManager supports favorite, title, and tags via meta entries\n\t\t\t\tif (updates.favorite !== undefined) {\n\t\t\t\t\tsessionManager.setSessionFavorite(sessionPath, updates.favorite);\n\t\t\t\t}\n\t\t\t\tif (updates.title !== undefined) {\n\t\t\t\t\tsessionManager.setSessionTitle(sessionPath, updates.title);\n\t\t\t\t}\n\t\t\t\tif (updates.tags !== undefined) {\n\t\t\t\t\tsessionManager.setSessionTags(sessionPath, updates.tags);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Return the updated values directly (writes are applied synchronously to the file)\n\t\t\tsendJson(\n\t\t\t\tres,\n\t\t\t\t200,\n\t\t\t\t{\n\t\t\t\t\tid: sessionId,\n\t\t\t\t\ttitle: updates.title ?? session.title,\n\t\t\t\t\tcreatedAt: session.createdAt,\n\t\t\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\t\t\tmessageCount: session.messageCount,\n\t\t\t\t\tfavorite: updates.favorite ?? session.favorite,\n\t\t\t\t\ttags: updates.tags ?? session.tags,\n\t\t\t\t},\n\t\t\t\tcors,\n\t\t\t\treq,\n\t\t\t);\n\t\t} else if (req.method === \"DELETE\" && sessionId) {\n\t\t\tif (!sessionIdPattern.test(sessionId)) {\n\t\t\t\tsendJson(res, 400, { error: \"Invalid session id\" }, cors, req);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Load session to verify ownership before deletion\n\t\t\tconst session = await sessionManager.loadSession(sessionId);\n\t\t\tif (!session) {\n\t\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Verify session ownership to prevent IDOR attacks\n\t\t\tconst subject = getAuthSubject(req);\n\t\t\tif (!verifySessionOwnership(session, subject)) {\n\t\t\t\tsendJson(\n\t\t\t\t\tres,\n\t\t\t\t\t403,\n\t\t\t\t\t{ error: \"Access denied: session belongs to another user\" },\n\t\t\t\t\tcors,\n\t\t\t\t\treq,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tawait sessionManager.deleteSession(sessionId);\n\t\t\trecordMaestroSessionEvent(\"MAESTRO_SESSION_STATE_CLOSED\", {\n\t\t\t\tsessionId,\n\t\t\t\tcloseReason: \"MAESTRO_CLOSE_REASON_USER_STOPPED\",\n\t\t\t\tcloseMessage: \"Web session deleted\",\n\t\t\t\tcorrelation: { principal_id: subject },\n\t\t\t\tenv: webSessionEventEnv(),\n\t\t\t\tmetadata: { operation: \"session.delete\" },\n\t\t\t});\n\n\t\t\tres.writeHead(204, cors);\n\t\t\tres.end();\n\t\t} else {\n\t\t\tres.writeHead(404, {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t...cors,\n\t\t\t});\n\t\t\tres.end(JSON.stringify({ error: \"Not found\" }));\n\t\t}\n\t} catch (error) {\n\t\trespondWithApiError(res, error, 500, cors, req);\n\t}\n}\n\n/**\n * Handle session sharing - generate a shareable link\n */\nexport async function handleSessionShare(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tparams: { id: string },\n\tcors: Record<string, string>,\n) {\n\tconst sessionManager = createWebSessionManagerForRequest(req, true);\n\tconst sessionId = params.id;\n\n\ttry {\n\t\tif (req.method !== \"POST\") {\n\t\t\tsendJson(res, 405, { error: \"Method not allowed\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!sessionIdPattern.test(sessionId)) {\n\t\t\tsendJson(res, 400, { error: \"Invalid session id\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst session = await sessionManager.loadSession(sessionId);\n\t\tif (!session) {\n\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\t// Verify session ownership to prevent sharing others' sessions\n\t\tconst subject = getAuthSubject(req);\n\t\tif (!verifySessionOwnership(session, subject)) {\n\t\t\tsendJson(\n\t\t\t\tres,\n\t\t\t\t403,\n\t\t\t\t{ error: \"Access denied: session belongs to another user\" },\n\t\t\t\tcors,\n\t\t\t\treq,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst options = await readJsonBody<\n\t\t\tSessionShareOptions & { allowSensitiveContent?: boolean }\n\t\t>(req);\n\t\tconst expiresInHours = Math.min(options.expiresInHours ?? 24, 168); // Max 1 week\n\t\tconst maxAccesses =\n\t\t\toptions.maxAccesses === null\n\t\t\t\t? null // explicit unlimited\n\t\t\t\t: Math.max(1, options.maxAccesses ?? 100); // default finite, clamp minimum\n\n\t\tif (!options.allowSensitiveContent) {\n\t\t\tconst scan = scanOutboundSensitiveContent({\n\t\t\t\ttitle: session.title,\n\t\t\t\tmessages: session.messages || [],\n\t\t\t});\n\t\t\tif (scan.blockingFindings.length > 0) {\n\t\t\t\tsendSensitiveContentBlockedResponse(\n\t\t\t\t\treq,\n\t\t\t\t\tres,\n\t\t\t\t\tcors,\n\t\t\t\t\t\"share\",\n\t\t\t\t\tsummarizeOutboundSensitiveFindings(scan.blockingFindings),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Generate a share token\n\t\tconst shareToken = crypto.randomBytes(32).toString(\"base64url\");\n\t\tconst expiresAt = new Date(Date.now() + expiresInHours * 60 * 60 * 1000);\n\n\t\tconst scope = resolveSessionScope(req);\n\t\tconst scopedSessionId = encodeScopedSessionId(scope, sessionId);\n\n\t\t// Store in database (or fallback to memory)\n\t\tawait createSharedSessionInDb(\n\t\t\tscopedSessionId,\n\t\t\tshareToken,\n\t\t\texpiresAt,\n\t\t\tmaxAccesses,\n\t\t);\n\n\t\t// Cleanup expired shares in background\n\t\tdeleteExpiredShares().catch((err) => {\n\t\t\tlogger.error(\"Failed to cleanup expired shares\", err);\n\t\t});\n\n\t\tsendJson(\n\t\t\tres,\n\t\t\t201,\n\t\t\t{\n\t\t\t\tshareToken,\n\t\t\t\tshareUrl: `/api/sessions/shared/${shareToken}`,\n\t\t\t\twebShareUrl: `/share/${shareToken}`,\n\t\t\t\texpiresAt: expiresAt.toISOString(),\n\t\t\t\tmaxAccesses,\n\t\t\t},\n\t\t\tcors,\n\t\t\treq,\n\t\t);\n\t} catch (error) {\n\t\trespondWithApiError(res, error, 500, cors, req);\n\t}\n}\n\n/**\n * Handle accessing a shared session\n */\nexport async function handleSharedSession(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tparams: { token: string },\n\tcors: Record<string, string>,\n) {\n\tconst shareToken = params.token;\n\n\ttry {\n\t\tif (req.method !== \"GET\") {\n\t\t\tsendJson(res, 405, { error: \"Method not allowed\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst resolvedSessionId = await resolveSharedSessionAccess(\n\t\t\treq,\n\t\t\tres,\n\t\t\tshareToken,\n\t\t\tcors,\n\t\t);\n\t\tif (!resolvedSessionId) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst { scope, sessionId } = decodeScopedSessionId(resolvedSessionId);\n\t\tconst sessionManager = createWebSessionManagerForScope(scope, true);\n\t\tconst session = await sessionManager.loadSession(sessionId);\n\t\tif (!session) {\n\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\t// Return a read-only view of the session\n\t\tconst responseBody: ComposerSession = {\n\t\t\t// Don't leak the underlying session id through share links; treat token as the public id.\n\t\t\tid: `shared:${shareToken}`,\n\t\t\ttitle: session.title,\n\t\t\tresumeSummary: session.resumeSummary,\n\t\t\tcreatedAt: session.createdAt,\n\t\t\tupdatedAt: session.updatedAt,\n\t\t\tmessageCount: session.messageCount,\n\t\t\tmessages: convertAppMessagesToComposer(session.messages || [], {\n\t\t\t\tincludeAttachmentContent: false,\n\t\t\t}),\n\t\t};\n\n\t\tsendJson(res, 200, { ...responseBody, isShared: true }, cors, req);\n\t} catch (error) {\n\t\trespondWithApiError(res, error, 500, cors, req);\n\t}\n}\n\n/**\n * Fetch a shared session attachment by token (for lazy-loaded attachment content).\n *\n * This does NOT increment accessCount (unlike handleSharedSession), so users\n * can fetch attachments after opening the shared session even when maxAccesses\n * is low (e.g., 1).\n */\nexport async function handleSharedSessionAttachment(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tparams: { token: string; attachmentId: string },\n\tcors: Record<string, string>,\n) {\n\tconst shareToken = params.token;\n\tconst attachmentId = params.attachmentId;\n\n\ttry {\n\t\tif (req.method !== \"GET\") {\n\t\t\tsendJson(res, 405, { error: \"Method not allowed\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!attachmentIdPattern.test(attachmentId)) {\n\t\t\tsendJson(res, 400, { error: \"Invalid attachment id\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst resolvedSessionId = await resolveSharedSessionAccess(\n\t\t\treq,\n\t\t\tres,\n\t\t\tshareToken,\n\t\t\tcors,\n\t\t);\n\t\tif (!resolvedSessionId) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst { scope, sessionId } = decodeScopedSessionId(resolvedSessionId);\n\t\tconst sessionManager = createWebSessionManagerForScope(scope, true);\n\t\tconst session = await sessionManager.loadSession(sessionId);\n\t\tif (!session) {\n\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst attachment = findAttachmentInSession(session, attachmentId);\n\t\tif (!attachment) {\n\t\t\tsendJson(res, 404, { error: \"Attachment not found\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst bytes = Buffer.from(attachment.contentBase64, \"base64\");\n\t\tconst requestUrl = new URL(req.url || \"/\", \"http://localhost\");\n\t\tconst download = requestUrl.searchParams.get(\"download\");\n\t\tconst shouldDownload = download === \"1\" || download === \"true\";\n\n\t\tres.writeHead(200, {\n\t\t\t\"Content-Type\": attachment.mimeType,\n\t\t\t\"Content-Length\": bytes.length,\n\t\t\t\"Cache-Control\": \"private, max-age=3600\",\n\t\t\t...(shouldDownload\n\t\t\t\t? {\n\t\t\t\t\t\t\"Content-Disposition\": buildContentDisposition(attachment.fileName),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\t...cors,\n\t\t});\n\t\tres.end(bytes);\n\t} catch (error) {\n\t\trespondWithApiError(res, error, 500, cors, req);\n\t}\n}\n\n/**\n * Handle session export\n */\nexport async function handleSessionExport(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tparams: { id: string },\n\tcors: Record<string, string>,\n) {\n\tconst sessionManager = createWebSessionManagerForRequest(req, true);\n\tconst sessionId = params.id;\n\n\ttry {\n\t\tif (req.method !== \"POST\") {\n\t\t\tsendJson(res, 405, { error: \"Method not allowed\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!sessionIdPattern.test(sessionId)) {\n\t\t\tsendJson(res, 400, { error: \"Invalid session id\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst session = await sessionManager.loadSession(sessionId);\n\t\tif (!session) {\n\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\t// Verify session ownership to prevent exporting others' sessions\n\t\tconst subject = getAuthSubject(req);\n\t\tif (!verifySessionOwnership(session, subject)) {\n\t\t\tsendJson(\n\t\t\t\tres,\n\t\t\t\t403,\n\t\t\t\t{ error: \"Access denied: session belongs to another user\" },\n\t\t\t\tcors,\n\t\t\t\treq,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst options = await readJsonBody<\n\t\t\tSessionExportFormat & { allowSensitiveContent?: boolean }\n\t\t>(req);\n\t\tconst format = options.format || \"json\";\n\t\tconst messages = session.messages || [];\n\t\tlet jsonlEntries: SessionEntry[] | null | undefined = undefined;\n\t\tlet jsonlSessionFile: string | null = null;\n\t\tif (format === \"jsonl\") {\n\t\t\tif (isHostedSessionManager(sessionManager)) {\n\t\t\t\tjsonlEntries = await sessionManager.loadEntries(sessionId);\n\t\t\t\tif (!jsonlEntries) {\n\t\t\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tjsonlSessionFile = sessionManager.getSessionFileById(sessionId);\n\t\t\t\tif (!jsonlSessionFile) {\n\t\t\t\t\tsendJson(res, 404, { error: \"Session file not found\" }, cors, req);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tjsonlEntries = safeReadSessionEntries(jsonlSessionFile);\n\t\t\t\tif (!jsonlEntries) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!options.allowSensitiveContent) {\n\t\t\tconst scan = scanOutboundSensitiveContent({\n\t\t\t\ttitle: session.title,\n\t\t\t\tmessages: format === \"jsonl\" ? (jsonlEntries ?? []) : messages,\n\t\t\t});\n\t\t\tif (scan.blockingFindings.length > 0) {\n\t\t\t\tsendSensitiveContentBlockedResponse(\n\t\t\t\t\treq,\n\t\t\t\t\tres,\n\t\t\t\t\tcors,\n\t\t\t\t\t\"export\",\n\t\t\t\t\tsummarizeOutboundSensitiveFindings(scan.blockingFindings),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tlet content: string;\n\t\tlet contentType: string;\n\t\tlet filename: string;\n\n\t\tswitch (format) {\n\t\t\tcase \"jsonl\": {\n\t\t\t\tif (isHostedSessionManager(sessionManager)) {\n\t\t\t\t\tcontent = `${(jsonlEntries ?? []).map((entry) => JSON.stringify(entry)).join(\"\\n\")}\\n`;\n\t\t\t\t} else {\n\t\t\t\t\tif (!jsonlSessionFile) {\n\t\t\t\t\t\tsendJson(res, 404, { error: \"Session file not found\" }, cors, req);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tcontent = readFileSync(jsonlSessionFile, \"utf8\");\n\t\t\t\t}\n\t\t\t\tcontentType = \"application/x-ndjson\";\n\t\t\t\tfilename = `session-${sessionId}.jsonl`;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"markdown\":\n\t\t\t\tcontent = exportToMarkdown(session, messages);\n\t\t\t\tcontentType = \"text/markdown\";\n\t\t\t\tfilename = `session-${sessionId}.md`;\n\t\t\t\tbreak;\n\t\t\tcase \"text\":\n\t\t\t\tcontent = exportToText(session, messages);\n\t\t\t\tcontentType = \"text/plain\";\n\t\t\t\tfilename = `session-${sessionId}.txt`;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tcontent = JSON.stringify(\n\t\t\t\t\t{\n\t\t\t\t\t\tid: session.id,\n\t\t\t\t\t\ttitle: session.title,\n\t\t\t\t\t\tcreatedAt: session.createdAt,\n\t\t\t\t\t\tupdatedAt: session.updatedAt,\n\t\t\t\t\t\tmessageCount: session.messageCount,\n\t\t\t\t\t\tmessages: convertAppMessagesToComposer(messages, {\n\t\t\t\t\t\t\tincludeAttachmentContent: true,\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tnull,\n\t\t\t\t\t2,\n\t\t\t\t);\n\t\t\t\tcontentType = \"application/json\";\n\t\t\t\tfilename = `session-${sessionId}.json`;\n\t\t\t\tbreak;\n\t\t}\n\n\t\tres.writeHead(200, {\n\t\t\t\"Content-Type\": contentType,\n\t\t\t\"Content-Disposition\": buildContentDisposition(filename),\n\t\t\t...cors,\n\t\t});\n\t\tres.end(content);\n\t} catch (error) {\n\t\trespondWithApiError(res, error, 500, cors, req);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../../src/server/handlers/sessions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Session Management HTTP Handlers\n *\n * This module provides REST API endpoints for managing Maestro sessions,\n * including CRUD operations, session sharing, and export functionality.\n *\n * ## Endpoints\n *\n * | Method | Path | Description |\n * |--------|------|-------------|\n * | GET | `/api/sessions` | List all sessions with pagination |\n * | GET | `/api/sessions/:id` | Get a specific session with messages |\n * | GET | `/api/sessions/:id/timeline` | Get a redacted run timeline |\n * | POST | `/api/sessions` | Create a new session |\n * | PATCH | `/api/sessions/:id` | Update session metadata (title, tags, favorite) |\n * | DELETE | `/api/sessions/:id` | Delete a session |\n * | POST | `/api/sessions/:id/share` | Generate a shareable link |\n * | GET | `/api/sessions/shared/:token` | Access a shared session |\n * | POST | `/api/sessions/:id/export` | Export session in various formats |\n *\n * ## Session Sharing\n *\n * Sessions can be shared via time-limited, access-limited tokens:\n * - Configurable expiration (default 24h, max 1 week)\n * - Optional access count limits\n * - Rate limiting to prevent brute-force attacks\n * - Atomic access counting to prevent race conditions\n *\n * ## Storage\n *\n * - Session data: JSONL files in `~/.maestro/agent/sessions/`\n * - Share tokens: PostgreSQL (with in-memory fallback)\n * - Rate limits: Redis (with in-memory fallback)\n *\n * @module web/handlers/sessions\n */\nimport crypto from \"node:crypto\";\nimport { readFileSync } from \"node:fs\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport type {\n\tComposerSession,\n\tComposerSessionSummary,\n} from \"@evalops/contracts\";\nimport {\n\tscanOutboundSensitiveContent,\n\tsummarizeOutboundSensitiveFindings,\n} from \"../../safety/outbound-secret-preflight.js\";\nimport { safeReadSessionEntries } from \"../../session/session-context.js\";\nimport type { SessionEntry, SessionMessagesView } from \"../../session/types.js\";\nimport { recordMaestroSessionEvent } from \"../../telemetry/maestro-event-bus.js\";\nimport { createLogger } from \"../../utils/logger.js\";\nimport { getAuthSubject } from \"../authz.js\";\nimport { isHostedSessionManager } from \"../hosted-session-manager.js\";\nimport { getPendingServerRequestPayload } from \"../pending-request-payload.js\";\nimport {\n\tbuildContentDisposition,\n\treadJsonBody,\n\trespondWithApiError,\n\tsendJson,\n} from \"../server-utils.js\";\nimport { webSessionEventEnv } from \"../session-event-env.js\";\nimport {\n\tcreateWebSessionManagerForRequest,\n\tcreateWebSessionManagerForScope,\n\tdecodeScopedSessionId,\n\tencodeScopedSessionId,\n\tresolveSessionScope,\n} from \"../session-scope.js\";\nimport { convertAppMessagesToComposer } from \"../session-serialization.js\";\nimport { exportToMarkdown, exportToText } from \"./session-export-formatters.js\";\nimport {\n\ttype SessionExportFormat,\n\ttype SessionShareOptions,\n\ttype SessionUpdateBody,\n\tcheckShareRateLimit,\n\tcreateSharedSessionInDb,\n\tdeleteExpiredShares,\n\tdeleteShareByToken,\n\tgetSharedSessionFromDb,\n\ttryAccessShare,\n} from \"./session-share-store.js\";\n\nexport {\n\tcheckShareRateLimit,\n\tresetShareRateLimit,\n\tstopShareRateLimiter,\n} from \"./session-share-store.js\";\nexport type {\n\tSessionExportFormat,\n\tSessionShareOptions,\n\tSessionUpdateBody,\n} from \"./session-share-store.js\";\n\nconst logger = createLogger(\"sessions-handler\");\nexport const sessionIdPattern = /^[a-zA-Z0-9._-]+$/;\nconst attachmentIdPattern = /^[a-zA-Z0-9._-]+$/;\n\nfunction parseSessionMessagesView(\n\tvalue: string | null,\n): SessionMessagesView | null {\n\tif (value === null || value === \"\" || value === \"full\") {\n\t\treturn \"full\";\n\t}\n\tif (value === \"summary\" || value === \"notLoaded\") {\n\t\treturn value;\n\t}\n\treturn null;\n}\n\nfunction sendSensitiveContentBlockedResponse(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tcors: Record<string, string>,\n\taction: \"export\" | \"share\",\n\tfindings: ReturnType<typeof summarizeOutboundSensitiveFindings>,\n): void {\n\tsendJson(\n\t\tres,\n\t\t409,\n\t\t{\n\t\t\terror: `Sensitive content detected. Review and confirm before continuing with this ${action}.`,\n\t\t\tcode: \"sensitive_content_detected\",\n\t\t\tdetails: findings,\n\t\t},\n\t\tcors,\n\t\treq,\n\t);\n}\n\n/**\n * Verify that the authenticated subject has access to the session.\n * Prevents IDOR attacks by checking session ownership fields.\n *\n * Returns true if access is allowed, false otherwise.\n */\nexport function verifySessionOwnership(\n\tsession: { owner?: unknown; subject?: unknown },\n\tsubject: string,\n): boolean {\n\t// Check explicit owner field\n\tif (typeof session.owner === \"string\" && session.owner) {\n\t\treturn session.owner === subject;\n\t}\n\n\t// Check subject field (for API-created sessions)\n\tif (typeof session.subject === \"string\" && session.subject) {\n\t\treturn session.subject === subject;\n\t}\n\n\t// For sessions without ownership info:\n\t// In strict mode (multi-user), deny access to prevent IDOR attacks.\n\t// Sessions created via CLI don't have ownership info, but API access\n\t// should be restricted in hosted environments.\n\tconst strictMode = process.env.MAESTRO_STRICT_SESSION_ACCESS !== \"false\";\n\treturn !strictMode;\n}\n\nfunction findAttachmentInSession(\n\tsession: { messages?: Array<unknown> },\n\tattachmentId: string,\n): {\n\tfileName: string;\n\tmimeType: string;\n\tcontentBase64: string;\n\tsize?: number;\n} | null {\n\tconst messages = Array.isArray(session.messages) ? session.messages : [];\n\tfor (const msg of messages) {\n\t\tif (!msg || typeof msg !== \"object\") continue;\n\t\tconst maybeAttachments = (msg as { attachments?: unknown }).attachments;\n\t\tif (!Array.isArray(maybeAttachments)) continue;\n\n\t\tfor (const att of maybeAttachments) {\n\t\t\tif (!att || typeof att !== \"object\") continue;\n\t\t\tconst a = att as {\n\t\t\t\tid?: unknown;\n\t\t\t\tfileName?: unknown;\n\t\t\t\tmimeType?: unknown;\n\t\t\t\tcontent?: unknown;\n\t\t\t\tsize?: unknown;\n\t\t\t};\n\t\t\tif (a.id !== attachmentId) continue;\n\t\t\tif (typeof a.fileName !== \"string\" || !a.fileName) return null;\n\t\t\tif (typeof a.mimeType !== \"string\" || !a.mimeType) return null;\n\t\t\tif (typeof a.content !== \"string\" || !a.content) return null;\n\t\t\treturn {\n\t\t\t\tfileName: a.fileName,\n\t\t\t\tmimeType: a.mimeType,\n\t\t\t\tcontentBase64: a.content,\n\t\t\t\tsize:\n\t\t\t\t\ttypeof a.size === \"number\" && Number.isFinite(a.size)\n\t\t\t\t\t\t? a.size\n\t\t\t\t\t\t: undefined,\n\t\t\t};\n\t\t}\n\t}\n\treturn null;\n}\n\nasync function resolveSharedSessionAccess(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tshareToken: string,\n\tcors: Record<string, string>,\n): Promise<string | null> {\n\tconst clientIp =\n\t\t(req.headers[\"x-forwarded-for\"] as string)?.split(\",\")[0]?.trim() ||\n\t\treq.socket.remoteAddress ||\n\t\t\"unknown\";\n\tconst rateLimit = await checkShareRateLimit(clientIp);\n\tif (!rateLimit.allowed) {\n\t\tres.setHeader(\"Retry-After\", String(rateLimit.retryAfterSeconds ?? 60));\n\t\tsendJson(\n\t\t\tres,\n\t\t\t429,\n\t\t\t{\n\t\t\t\terror: \"Too many requests\",\n\t\t\t\tretryAfter: rateLimit.retryAfterSeconds,\n\t\t\t},\n\t\t\tcors,\n\t\t\treq,\n\t\t);\n\t\treturn null;\n\t}\n\n\tconst accessResult = await tryAccessShare(shareToken);\n\tif (!accessResult.allowed) {\n\t\tswitch (accessResult.reason) {\n\t\t\tcase \"not_found\":\n\t\t\t\tsendJson(\n\t\t\t\t\tres,\n\t\t\t\t\t404,\n\t\t\t\t\t{ error: \"Share link not found or expired\" },\n\t\t\t\t\tcors,\n\t\t\t\t\treq,\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t\tcase \"expired\":\n\t\t\t\tawait deleteShareByToken(shareToken);\n\t\t\t\tsendJson(res, 410, { error: \"Share link has expired\" }, cors, req);\n\t\t\t\treturn null;\n\t\t\tcase \"max_accesses\":\n\t\t\t\tsendJson(\n\t\t\t\t\tres,\n\t\t\t\t\t410,\n\t\t\t\t\t{ error: \"Share link has reached maximum accesses\" },\n\t\t\t\t\tcors,\n\t\t\t\t\treq,\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t}\n\t}\n\n\treturn accessResult.sessionId ?? null;\n}\n\n// ============================================================================\n// SESSION HANDLERS\n// ============================================================================\n\n/**\n * Handle session list and CRUD operations\n */\nexport async function handleSessions(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tparams: { id?: string },\n\tcors: Record<string, string>,\n) {\n\tconst sessionManager = createWebSessionManagerForRequest(req, true);\n\tconst sessionId = params.id;\n\tconst url = new URL(req.url || \"/api/sessions\", \"http://localhost\");\n\tconst limitParam = url.searchParams.get(\"limit\");\n\tconst offsetParam = url.searchParams.get(\"offset\");\n\tconst parsedLimit = limitParam ? Number.parseInt(limitParam, 10) : undefined;\n\tconst parsedOffset = offsetParam\n\t\t? Number.parseInt(offsetParam, 10)\n\t\t: undefined;\n\tconst limit =\n\t\ttypeof parsedLimit === \"number\" && Number.isFinite(parsedLimit)\n\t\t\t? Math.max(1, parsedLimit)\n\t\t\t: undefined;\n\tconst offset =\n\t\ttypeof parsedOffset === \"number\" && Number.isFinite(parsedOffset)\n\t\t\t? Math.max(0, parsedOffset)\n\t\t\t: undefined;\n\n\ttry {\n\t\tif (req.method === \"GET\" && !sessionId) {\n\t\t\tconst subject = getAuthSubject(req);\n\t\t\tconst paginationRequested =\n\t\t\t\ttypeof limit === \"number\" || typeof offset === \"number\";\n\t\t\tconst sessions = (\n\t\t\t\tawait sessionManager.listSessions(\n\t\t\t\t\tpaginationRequested ? undefined : { limit, offset },\n\t\t\t\t)\n\t\t\t).filter((s) => verifySessionOwnership(s, subject));\n\t\t\tconst pagedSessions = paginationRequested\n\t\t\t\t? sessions.slice(\n\t\t\t\t\t\toffset ?? 0,\n\t\t\t\t\t\t(offset ?? 0) + (limit ?? sessions.length),\n\t\t\t\t\t)\n\t\t\t\t: sessions;\n\t\t\tconst sessionList: ComposerSessionSummary[] = pagedSessions.map((s) => ({\n\t\t\t\tid: s.id,\n\t\t\t\ttitle: s.title || `Session ${s.id.slice(0, 8)}`,\n\t\t\t\tresumeSummary: s.resumeSummary,\n\t\t\t\tcreatedAt: s.createdAt || new Date().toISOString(),\n\t\t\t\tupdatedAt: s.updatedAt || new Date().toISOString(),\n\t\t\t\tmessageCount: s.messageCount || 0,\n\t\t\t\tfavorite: s.favorite,\n\t\t\t\ttags: s.tags,\n\t\t\t}));\n\n\t\t\tsendJson(res, 200, { sessions: sessionList }, cors, req);\n\t\t} else if (req.method === \"GET\" && sessionId) {\n\t\t\tif (!sessionIdPattern.test(sessionId)) {\n\t\t\t\tsendJson(res, 400, { error: \"Invalid session id\" }, cors, req);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst messagesView = parseSessionMessagesView(\n\t\t\t\turl.searchParams.get(\"messagesView\") ??\n\t\t\t\t\turl.searchParams.get(\"itemsView\"),\n\t\t\t);\n\t\t\tif (!messagesView) {\n\t\t\t\tsendJson(\n\t\t\t\t\tres,\n\t\t\t\t\t400,\n\t\t\t\t\t{\n\t\t\t\t\t\terror:\n\t\t\t\t\t\t\t\"Invalid messagesView. Expected full, summary, or notLoaded.\",\n\t\t\t\t\t},\n\t\t\t\t\tcors,\n\t\t\t\t\treq,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst session = await sessionManager.loadSession(sessionId, {\n\t\t\t\tmessagesView,\n\t\t\t});\n\n\t\t\tif (!session) {\n\t\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Verify session ownership to prevent IDOR attacks\n\t\t\tconst subject = getAuthSubject(req);\n\t\t\tif (!verifySessionOwnership(session, subject)) {\n\t\t\t\tsendJson(\n\t\t\t\t\tres,\n\t\t\t\t\t403,\n\t\t\t\t\t{ error: \"Access denied: session belongs to another user\" },\n\t\t\t\t\tcors,\n\t\t\t\t\treq,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst responseBody: ComposerSession = {\n\t\t\t\tid: session.id,\n\t\t\t\ttitle: session.title,\n\t\t\t\tresumeSummary: session.resumeSummary,\n\t\t\t\tcreatedAt: session.createdAt,\n\t\t\t\tupdatedAt: session.updatedAt,\n\t\t\t\tmessageCount: session.messageCount,\n\t\t\t\tfavorite: session.favorite,\n\t\t\t\ttags: session.tags,\n\t\t\t\tmessagesView: session.messagesView,\n\t\t\t\tmessages: convertAppMessagesToComposer(session.messages || [], {\n\t\t\t\t\tincludeAttachmentContent: false,\n\t\t\t\t}),\n\t\t\t\t...getPendingServerRequestPayload(session.id),\n\t\t\t};\n\n\t\t\tsendJson(res, 200, responseBody, cors, req);\n\t\t} else if (req.method === \"POST\" && !sessionId) {\n\t\t\tconst { title } = await readJsonBody<{ title?: string }>(req);\n\t\t\tconst session = await sessionManager.createSession({ title });\n\t\t\tconst responseBody: ComposerSession = {\n\t\t\t\tid: session.id,\n\t\t\t\ttitle: session.title,\n\t\t\t\tresumeSummary: session.resumeSummary,\n\t\t\t\tcreatedAt: session.createdAt,\n\t\t\t\tupdatedAt: session.updatedAt,\n\t\t\t\tmessageCount: session.messageCount,\n\t\t\t\tfavorite: false,\n\t\t\t\ttags: undefined,\n\t\t\t\tmessages: convertAppMessagesToComposer(session.messages || [], {\n\t\t\t\t\tincludeAttachmentContent: false,\n\t\t\t\t}),\n\t\t\t};\n\n\t\t\tsendJson(res, 201, responseBody, cors, req);\n\t\t} else if (req.method === \"PATCH\" && sessionId) {\n\t\t\t// Update session metadata\n\t\t\tif (!sessionIdPattern.test(sessionId)) {\n\t\t\t\tsendJson(res, 400, { error: \"Invalid session id\" }, cors, req);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst updates = await readJsonBody<SessionUpdateBody>(req);\n\t\t\tconst session = await sessionManager.loadSession(sessionId);\n\n\t\t\tif (!session) {\n\t\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Verify session ownership to prevent IDOR attacks\n\t\t\tconst subject = getAuthSubject(req);\n\t\t\tif (!verifySessionOwnership(session, subject)) {\n\t\t\t\tsendJson(\n\t\t\t\t\tres,\n\t\t\t\t\t403,\n\t\t\t\t\t{ error: \"Access denied: session belongs to another user\" },\n\t\t\t\t\tcors,\n\t\t\t\t\treq,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (isHostedSessionManager(sessionManager)) {\n\t\t\t\tawait sessionManager.updateSessionMetadata(sessionId, updates);\n\t\t\t} else {\n\t\t\t\tconst sessionPath = sessionManager.getSessionFileById(sessionId);\n\t\t\t\tif (!sessionPath) {\n\t\t\t\t\tsendJson(res, 404, { error: \"Session file not found\" }, cors, req);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Apply updates - SessionManager supports favorite, title, and tags via meta entries\n\t\t\t\tif (updates.favorite !== undefined) {\n\t\t\t\t\tsessionManager.setSessionFavorite(sessionPath, updates.favorite);\n\t\t\t\t}\n\t\t\t\tif (updates.title !== undefined) {\n\t\t\t\t\tsessionManager.setSessionTitle(sessionPath, updates.title);\n\t\t\t\t}\n\t\t\t\tif (updates.tags !== undefined) {\n\t\t\t\t\tsessionManager.setSessionTags(sessionPath, updates.tags);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Return the updated values directly (writes are applied synchronously to the file)\n\t\t\tsendJson(\n\t\t\t\tres,\n\t\t\t\t200,\n\t\t\t\t{\n\t\t\t\t\tid: sessionId,\n\t\t\t\t\ttitle: updates.title ?? session.title,\n\t\t\t\t\tcreatedAt: session.createdAt,\n\t\t\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\t\t\tmessageCount: session.messageCount,\n\t\t\t\t\tfavorite: updates.favorite ?? session.favorite,\n\t\t\t\t\ttags: updates.tags ?? session.tags,\n\t\t\t\t},\n\t\t\t\tcors,\n\t\t\t\treq,\n\t\t\t);\n\t\t} else if (req.method === \"DELETE\" && sessionId) {\n\t\t\tif (!sessionIdPattern.test(sessionId)) {\n\t\t\t\tsendJson(res, 400, { error: \"Invalid session id\" }, cors, req);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Load session to verify ownership before deletion\n\t\t\tconst session = await sessionManager.loadSession(sessionId);\n\t\t\tif (!session) {\n\t\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Verify session ownership to prevent IDOR attacks\n\t\t\tconst subject = getAuthSubject(req);\n\t\t\tif (!verifySessionOwnership(session, subject)) {\n\t\t\t\tsendJson(\n\t\t\t\t\tres,\n\t\t\t\t\t403,\n\t\t\t\t\t{ error: \"Access denied: session belongs to another user\" },\n\t\t\t\t\tcors,\n\t\t\t\t\treq,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tawait sessionManager.deleteSession(sessionId);\n\t\t\trecordMaestroSessionEvent(\"MAESTRO_SESSION_STATE_CLOSED\", {\n\t\t\t\tsessionId,\n\t\t\t\tcloseReason: \"MAESTRO_CLOSE_REASON_USER_STOPPED\",\n\t\t\t\tcloseMessage: \"Web session deleted\",\n\t\t\t\tcorrelation: { principal_id: subject },\n\t\t\t\tenv: webSessionEventEnv(),\n\t\t\t\tmetadata: { operation: \"session.delete\" },\n\t\t\t});\n\n\t\t\tres.writeHead(204, cors);\n\t\t\tres.end();\n\t\t} else {\n\t\t\tres.writeHead(404, {\n\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t...cors,\n\t\t\t});\n\t\t\tres.end(JSON.stringify({ error: \"Not found\" }));\n\t\t}\n\t} catch (error) {\n\t\trespondWithApiError(res, error, 500, cors, req);\n\t}\n}\n\n/**\n * Handle session sharing - generate a shareable link\n */\nexport async function handleSessionShare(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tparams: { id: string },\n\tcors: Record<string, string>,\n) {\n\tconst sessionManager = createWebSessionManagerForRequest(req, true);\n\tconst sessionId = params.id;\n\n\ttry {\n\t\tif (req.method !== \"POST\") {\n\t\t\tsendJson(res, 405, { error: \"Method not allowed\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!sessionIdPattern.test(sessionId)) {\n\t\t\tsendJson(res, 400, { error: \"Invalid session id\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst session = await sessionManager.loadSession(sessionId);\n\t\tif (!session) {\n\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\t// Verify session ownership to prevent sharing others' sessions\n\t\tconst subject = getAuthSubject(req);\n\t\tif (!verifySessionOwnership(session, subject)) {\n\t\t\tsendJson(\n\t\t\t\tres,\n\t\t\t\t403,\n\t\t\t\t{ error: \"Access denied: session belongs to another user\" },\n\t\t\t\tcors,\n\t\t\t\treq,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst options = await readJsonBody<\n\t\t\tSessionShareOptions & { allowSensitiveContent?: boolean }\n\t\t>(req);\n\t\tconst expiresInHours = Math.min(options.expiresInHours ?? 24, 168); // Max 1 week\n\t\tconst maxAccesses =\n\t\t\toptions.maxAccesses === null\n\t\t\t\t? null // explicit unlimited\n\t\t\t\t: Math.max(1, options.maxAccesses ?? 100); // default finite, clamp minimum\n\n\t\tif (!options.allowSensitiveContent) {\n\t\t\tconst scan = scanOutboundSensitiveContent({\n\t\t\t\ttitle: session.title,\n\t\t\t\tmessages: session.messages || [],\n\t\t\t});\n\t\t\tif (scan.blockingFindings.length > 0) {\n\t\t\t\tsendSensitiveContentBlockedResponse(\n\t\t\t\t\treq,\n\t\t\t\t\tres,\n\t\t\t\t\tcors,\n\t\t\t\t\t\"share\",\n\t\t\t\t\tsummarizeOutboundSensitiveFindings(scan.blockingFindings),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// Generate a share token\n\t\tconst shareToken = crypto.randomBytes(32).toString(\"base64url\");\n\t\tconst expiresAt = new Date(Date.now() + expiresInHours * 60 * 60 * 1000);\n\n\t\tconst scope = resolveSessionScope(req);\n\t\tconst scopedSessionId = encodeScopedSessionId(scope, sessionId);\n\n\t\t// Store in database (or fallback to memory)\n\t\tawait createSharedSessionInDb(\n\t\t\tscopedSessionId,\n\t\t\tshareToken,\n\t\t\texpiresAt,\n\t\t\tmaxAccesses,\n\t\t);\n\n\t\t// Cleanup expired shares in background\n\t\tdeleteExpiredShares().catch((err) => {\n\t\t\tlogger.error(\"Failed to cleanup expired shares\", err);\n\t\t});\n\n\t\tsendJson(\n\t\t\tres,\n\t\t\t201,\n\t\t\t{\n\t\t\t\tshareToken,\n\t\t\t\tshareUrl: `/api/sessions/shared/${shareToken}`,\n\t\t\t\twebShareUrl: `/share/${shareToken}`,\n\t\t\t\texpiresAt: expiresAt.toISOString(),\n\t\t\t\tmaxAccesses,\n\t\t\t},\n\t\t\tcors,\n\t\t\treq,\n\t\t);\n\t} catch (error) {\n\t\trespondWithApiError(res, error, 500, cors, req);\n\t}\n}\n\n/**\n * Handle accessing a shared session\n */\nexport async function handleSharedSession(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tparams: { token: string },\n\tcors: Record<string, string>,\n) {\n\tconst shareToken = params.token;\n\n\ttry {\n\t\tif (req.method !== \"GET\") {\n\t\t\tsendJson(res, 405, { error: \"Method not allowed\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst resolvedSessionId = await resolveSharedSessionAccess(\n\t\t\treq,\n\t\t\tres,\n\t\t\tshareToken,\n\t\t\tcors,\n\t\t);\n\t\tif (!resolvedSessionId) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst { scope, sessionId } = decodeScopedSessionId(resolvedSessionId);\n\t\tconst sessionManager = createWebSessionManagerForScope(scope, true);\n\t\tconst session = await sessionManager.loadSession(sessionId);\n\t\tif (!session) {\n\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\t// Return a read-only view of the session\n\t\tconst responseBody: ComposerSession = {\n\t\t\t// Don't leak the underlying session id through share links; treat token as the public id.\n\t\t\tid: `shared:${shareToken}`,\n\t\t\ttitle: session.title,\n\t\t\tresumeSummary: session.resumeSummary,\n\t\t\tcreatedAt: session.createdAt,\n\t\t\tupdatedAt: session.updatedAt,\n\t\t\tmessageCount: session.messageCount,\n\t\t\tmessages: convertAppMessagesToComposer(session.messages || [], {\n\t\t\t\tincludeAttachmentContent: false,\n\t\t\t}),\n\t\t};\n\n\t\tsendJson(res, 200, { ...responseBody, isShared: true }, cors, req);\n\t} catch (error) {\n\t\trespondWithApiError(res, error, 500, cors, req);\n\t}\n}\n\n/**\n * Fetch a shared session attachment by token (for lazy-loaded attachment content).\n *\n * This does NOT increment accessCount (unlike handleSharedSession), so users\n * can fetch attachments after opening the shared session even when maxAccesses\n * is low (e.g., 1).\n */\nexport async function handleSharedSessionAttachment(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tparams: { token: string; attachmentId: string },\n\tcors: Record<string, string>,\n) {\n\tconst shareToken = params.token;\n\tconst attachmentId = params.attachmentId;\n\n\ttry {\n\t\tif (req.method !== \"GET\") {\n\t\t\tsendJson(res, 405, { error: \"Method not allowed\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!attachmentIdPattern.test(attachmentId)) {\n\t\t\tsendJson(res, 400, { error: \"Invalid attachment id\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst resolvedSessionId = await resolveSharedSessionAccess(\n\t\t\treq,\n\t\t\tres,\n\t\t\tshareToken,\n\t\t\tcors,\n\t\t);\n\t\tif (!resolvedSessionId) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst { scope, sessionId } = decodeScopedSessionId(resolvedSessionId);\n\t\tconst sessionManager = createWebSessionManagerForScope(scope, true);\n\t\tconst session = await sessionManager.loadSession(sessionId);\n\t\tif (!session) {\n\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst attachment = findAttachmentInSession(session, attachmentId);\n\t\tif (!attachment) {\n\t\t\tsendJson(res, 404, { error: \"Attachment not found\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst bytes = Buffer.from(attachment.contentBase64, \"base64\");\n\t\tconst requestUrl = new URL(req.url || \"/\", \"http://localhost\");\n\t\tconst download = requestUrl.searchParams.get(\"download\");\n\t\tconst shouldDownload = download === \"1\" || download === \"true\";\n\n\t\tres.writeHead(200, {\n\t\t\t\"Content-Type\": attachment.mimeType,\n\t\t\t\"Content-Length\": bytes.length,\n\t\t\t\"Cache-Control\": \"private, max-age=3600\",\n\t\t\t...(shouldDownload\n\t\t\t\t? {\n\t\t\t\t\t\t\"Content-Disposition\": buildContentDisposition(attachment.fileName),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\t...cors,\n\t\t});\n\t\tres.end(bytes);\n\t} catch (error) {\n\t\trespondWithApiError(res, error, 500, cors, req);\n\t}\n}\n\n/**\n * Handle session export\n */\nexport async function handleSessionExport(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tparams: { id: string },\n\tcors: Record<string, string>,\n) {\n\tconst sessionManager = createWebSessionManagerForRequest(req, true);\n\tconst sessionId = params.id;\n\n\ttry {\n\t\tif (req.method !== \"POST\") {\n\t\t\tsendJson(res, 405, { error: \"Method not allowed\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!sessionIdPattern.test(sessionId)) {\n\t\t\tsendJson(res, 400, { error: \"Invalid session id\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst session = await sessionManager.loadSession(sessionId);\n\t\tif (!session) {\n\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\t// Verify session ownership to prevent exporting others' sessions\n\t\tconst subject = getAuthSubject(req);\n\t\tif (!verifySessionOwnership(session, subject)) {\n\t\t\tsendJson(\n\t\t\t\tres,\n\t\t\t\t403,\n\t\t\t\t{ error: \"Access denied: session belongs to another user\" },\n\t\t\t\tcors,\n\t\t\t\treq,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tconst options = await readJsonBody<\n\t\t\tSessionExportFormat & { allowSensitiveContent?: boolean }\n\t\t>(req);\n\t\tconst format = options.format || \"json\";\n\t\tconst messages = session.messages || [];\n\t\tlet jsonlEntries: SessionEntry[] | null | undefined = undefined;\n\t\tlet jsonlSessionFile: string | null = null;\n\t\tif (format === \"jsonl\") {\n\t\t\tif (isHostedSessionManager(sessionManager)) {\n\t\t\t\tjsonlEntries = await sessionManager.loadEntries(sessionId);\n\t\t\t\tif (!jsonlEntries) {\n\t\t\t\t\tsendJson(res, 404, { error: \"Session not found\" }, cors, req);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tjsonlSessionFile = sessionManager.getSessionFileById(sessionId);\n\t\t\t\tif (!jsonlSessionFile) {\n\t\t\t\t\tsendJson(res, 404, { error: \"Session file not found\" }, cors, req);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tjsonlEntries = safeReadSessionEntries(jsonlSessionFile);\n\t\t\t\tif (!jsonlEntries) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (!options.allowSensitiveContent) {\n\t\t\tconst scan = scanOutboundSensitiveContent({\n\t\t\t\ttitle: session.title,\n\t\t\t\tmessages: format === \"jsonl\" ? (jsonlEntries ?? []) : messages,\n\t\t\t});\n\t\t\tif (scan.blockingFindings.length > 0) {\n\t\t\t\tsendSensitiveContentBlockedResponse(\n\t\t\t\t\treq,\n\t\t\t\t\tres,\n\t\t\t\t\tcors,\n\t\t\t\t\t\"export\",\n\t\t\t\t\tsummarizeOutboundSensitiveFindings(scan.blockingFindings),\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tlet content: string;\n\t\tlet contentType: string;\n\t\tlet filename: string;\n\n\t\tswitch (format) {\n\t\t\tcase \"jsonl\": {\n\t\t\t\tif (isHostedSessionManager(sessionManager)) {\n\t\t\t\t\tcontent = `${(jsonlEntries ?? []).map((entry) => JSON.stringify(entry)).join(\"\\n\")}\\n`;\n\t\t\t\t} else {\n\t\t\t\t\tif (!jsonlSessionFile) {\n\t\t\t\t\t\tsendJson(res, 404, { error: \"Session file not found\" }, cors, req);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tcontent = readFileSync(jsonlSessionFile, \"utf8\");\n\t\t\t\t}\n\t\t\t\tcontentType = \"application/x-ndjson\";\n\t\t\t\tfilename = `session-${sessionId}.jsonl`;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"markdown\":\n\t\t\t\tcontent = exportToMarkdown(session, messages);\n\t\t\t\tcontentType = \"text/markdown\";\n\t\t\t\tfilename = `session-${sessionId}.md`;\n\t\t\t\tbreak;\n\t\t\tcase \"text\":\n\t\t\t\tcontent = exportToText(session, messages);\n\t\t\t\tcontentType = \"text/plain\";\n\t\t\t\tfilename = `session-${sessionId}.txt`;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tcontent = JSON.stringify(\n\t\t\t\t\t{\n\t\t\t\t\t\tid: session.id,\n\t\t\t\t\t\ttitle: session.title,\n\t\t\t\t\t\tcreatedAt: session.createdAt,\n\t\t\t\t\t\tupdatedAt: session.updatedAt,\n\t\t\t\t\t\tmessageCount: session.messageCount,\n\t\t\t\t\t\tmessages: convertAppMessagesToComposer(messages, {\n\t\t\t\t\t\t\tincludeAttachmentContent: true,\n\t\t\t\t\t\t}),\n\t\t\t\t\t},\n\t\t\t\t\tnull,\n\t\t\t\t\t2,\n\t\t\t\t);\n\t\t\t\tcontentType = \"application/json\";\n\t\t\t\tfilename = `session-${sessionId}.json`;\n\t\t\t\tbreak;\n\t\t}\n\n\t\tres.writeHead(200, {\n\t\t\t\"Content-Type\": contentType,\n\t\t\t\"Content-Disposition\": buildContentDisposition(filename),\n\t\t\t...cors,\n\t\t});\n\t\tres.end(content);\n\t} catch (error) {\n\t\trespondWithApiError(res, error, 500, cors, req);\n\t}\n}\n"]}
|
|
@@ -41,6 +41,7 @@ export interface RunHealthInput {
|
|
|
41
41
|
export declare function buildRunHealthSnapshot(input: RunHealthInput): RunHealthSnapshot;
|
|
42
42
|
export declare function getStatusSnapshot(options?: {
|
|
43
43
|
staticCacheMaxAge?: number;
|
|
44
|
+
cwd?: string;
|
|
44
45
|
}): Promise<{
|
|
45
46
|
cwd: string;
|
|
46
47
|
git: {
|
|
@@ -82,5 +83,6 @@ export declare function getStatusSnapshot(options?: {
|
|
|
82
83
|
}>;
|
|
83
84
|
export declare function handleStatus(req: IncomingMessage, res: ServerResponse, cors: Record<string, string>, options?: {
|
|
84
85
|
staticCacheMaxAge?: number;
|
|
86
|
+
cwd?: string;
|
|
85
87
|
}): Promise<void>;
|
|
86
88
|
//# sourceMappingURL=status.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/server/handlers/status.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AA4GjE,wBAAgB,sCAAsC,IAAI,IAAI,CAK7D;AAED,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;AAElE,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,YAAY,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,IAAI,CAAC;IACT,QAAQ,EAAE;QACT,UAAU,EAAE,OAAO,CAAC;QACpB,SAAS,EAAE,OAAO,CAAC;QACnB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,SAAS,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;IACF,KAAK,EAAE;QACN,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE;YACZ,GAAG,EAAE,MAAM,CAAC;YACZ,MAAM,EAAE,MAAM,CAAC;YACf,MAAM,EAAE,MAAM,CAAC;SACf,CAAC;KACF,CAAC;IACF,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAqHD,wBAAgB,sBAAsB,CACrC,KAAK,EAAE,cAAc,GACnB,iBAAiB,CAgBnB;AAED,wBAAsB,iBAAiB,CACtC,OAAO,GAAE;IAAE,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAO;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/server/handlers/status.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AA4GjE,wBAAgB,sCAAsC,IAAI,IAAI,CAK7D;AAED,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;AAElE,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,EAAE,YAAY,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,IAAI,CAAC;IACT,QAAQ,EAAE;QACT,UAAU,EAAE,OAAO,CAAC;QACpB,SAAS,EAAE,OAAO,CAAC;QACnB,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,SAAS,CAAC,EAAE,OAAO,CAAC;KACpB,CAAC;IACF,KAAK,EAAE;QACN,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE;YACZ,GAAG,EAAE,MAAM,CAAC;YACZ,MAAM,EAAE,MAAM,CAAC;YACf,MAAM,EAAE,MAAM,CAAC;SACf,CAAC;KACF,CAAC;IACF,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAqHD,wBAAgB,sBAAsB,CACrC,KAAK,EAAE,cAAc,GACnB,iBAAiB,CAgBnB;AAED,wBAAsB,iBAAiB,CACtC,OAAO,GAAE;IAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO;;;;;;;;;;;;;;;;;;;;;;;oBAhN9C,OAAO;mBACR,OAAO;;;;;;;;;;;;;;GAoRlB;AAED,wBAAsB,YAAY,CACjC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,OAAO,GAAE;IAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GACxD,OAAO,CAAC,IAAI,CAAC,CAkBf"}
|
|
@@ -209,7 +209,7 @@ export function buildRunHealthSnapshot(input) {
|
|
|
209
209
|
}
|
|
210
210
|
export async function getStatusSnapshot(options = {}) {
|
|
211
211
|
const startedAt = Date.now();
|
|
212
|
-
const cwd = process.cwd();
|
|
212
|
+
const cwd = options.cwd ?? process.cwd();
|
|
213
213
|
const database = await getDatabaseHealthSnapshot();
|
|
214
214
|
let gitBranch = null;
|
|
215
215
|
let gitStatus = null;
|
|
@@ -278,8 +278,9 @@ export async function handleStatus(req, res, cors, options = {}) {
|
|
|
278
278
|
const method = (req.method ?? "GET").toUpperCase();
|
|
279
279
|
const url = new URL(req.url ?? "/api/status", "http://localhost");
|
|
280
280
|
const action = url.searchParams.get("action");
|
|
281
|
+
const cwd = options.cwd ?? process.cwd();
|
|
281
282
|
if (method === "POST" && action === "mark-onboarding-seen") {
|
|
282
|
-
markProjectOnboardingSeen(
|
|
283
|
+
markProjectOnboardingSeen(cwd);
|
|
283
284
|
sendJson(res, 200, { success: true }, cors, req);
|
|
284
285
|
return;
|
|
285
286
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/server/handlers/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EACN,iBAAiB,EACjB,0BAA0B,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACN,yBAAyB,EACzB,yBAAyB,GACzB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnE,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAC3C,MAAM,gCAAgC,GAAG,GAAG,CAAC;AAC7C,MAAM,oCAAoC,GAAG,MAAM,CAAC;AAEpD,IAAI,mBAAmB,GACtB,IAAI,CAAC;AACN,IAAI,mBAAmB,GAA4B,IAAI,CAAC;AACxD,IAAI,4BAA4B,GAAG,CAAC,CAAC;AACrC,IAAI,6BAA6B,GAAG,CAAC,CAAC;AAEtC,SAAS,wBAAwB;IAChC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,IACC,mBAAmB;QACnB,GAAG,GAAG,4BAA4B,GAAG,oCAAoC,EACxE,CAAC;QACF,OAAO,mBAAmB,CAAC;IAC5B,CAAC;IAED,MAAM,UAAU,GAAG,6BAA6B,GAAG,CAAC,CAAC;IACrD,6BAA6B,GAAG,UAAU,CAAC;IAC3C,4BAA4B,GAAG,GAAG,CAAC;IACnC,MAAM,KAAK,GAAG,cAAc,EAAE;SAC5B,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE;QACnB,IAAI,6BAA6B,KAAK,UAAU,EAAE,CAAC;YAClD,mBAAmB,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QAC5D,CAAC;QACD,OAAO,SAAS,CAAC;IAClB,CAAC,CAAC;SACD,KAAK,CAAC,GAAG,EAAE;QACX,IAAI,6BAA6B,KAAK,UAAU,EAAE,CAAC;YAClD,mBAAmB,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QACnE,CAAC;QACD,OAAO,KAAK,CAAC;IACd,CAAC,CAAC;SACD,OAAO,CAAC,GAAG,EAAE;QACb,IAAI,mBAAmB,KAAK,KAAK,EAAE,CAAC;YACnC,mBAAmB,GAAG,IAAI,CAAC;YAC3B,4BAA4B,GAAG,CAAC,CAAC;QAClC,CAAC;IACF,CAAC,CAAC,CAAC;IAEJ,mBAAmB,GAAG,KAAK,CAAC;IAC5B,OAAO,KAAK,CAAC;AACd,CAAC;AAED,KAAK,UAAU,oBAAoB,CAClC,KAAuB,EACvB,QAAiB;IAEjB,IAAI,OAAkD,CAAC;IACvD,IAAI,CAAC;QACJ,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC;YACzB,KAAK;YACL,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE;gBAChC,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;oBACzB,OAAO,CAAC,QAAQ,CAAC,CAAC;gBACnB,CAAC,EAAE,gCAAgC,CAAC,CAAC;gBACrC,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,IAAI,OAAO,EAAE,CAAC;oBACvD,OAAO,CAAC,KAAK,EAAE,CAAC;gBACjB,CAAC;YACF,CAAC,CAAC;SACF,CAAC,CAAC;IACJ,CAAC;YAAS,CAAC;QACV,IAAI,OAAO,EAAE,CAAC;YACb,YAAY,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;IACF,CAAC;AACF,CAAC;AAED,KAAK,UAAU,yBAAyB;IAIvC,MAAM,UAAU,GAAG,oBAAoB,EAAE,CAAC;IAC1C,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAChD,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,IACC,mBAAmB;QACnB,GAAG,GAAG,mBAAmB,CAAC,SAAS,IAAI,4BAA4B,EAClE,CAAC;QACF,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,mBAAmB,CAAC,SAAS,EAAE,CAAC;IACvE,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,oBAAoB,CAC3C,wBAAwB,EAAE,EAC1B,KAAK,CACL,CAAC;IACF,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,sCAAsC;IACrD,mBAAmB,GAAG,IAAI,CAAC;IAC3B,mBAAmB,GAAG,IAAI,CAAC;IAC3B,4BAA4B,GAAG,CAAC,CAAC;IACjC,6BAA6B,GAAG,CAAC,CAAC;AACnC,CAAC;AA4CD,SAAS,cAAc,CAAC,MAAwB;IAC/C,IAAI,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC;QAAE,OAAO,WAAW,CAAC;IACrD,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;QAAE,OAAO,UAAU,CAAC;IACnD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,SAAS,UAAU,CAAC,YAAoB;IACvC,MAAM,MAAM,GACX,YAAY,GAAG,IAAI;QAClB,CAAC,CAAC,WAAW;QACb,CAAC,CAAC,YAAY,GAAG,IAAI;YACpB,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,SAAS,CAAC;IACf,OAAO;QACN,EAAE,EAAE,aAAa;QACjB,KAAK,EAAE,aAAa;QACpB,MAAM;QACN,MAAM,EAAE,wBAAwB;QAChC,QAAQ,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,IAAI;QACtD,GAAG,CAAC,MAAM,KAAK,SAAS;YACvB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,MAAM,EAAE,0DAA0D,EAAE,CAAC;KAC1E,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,QAAoC;IACxD,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;QAC1B,OAAO;YACN,EAAE,EAAE,UAAU;YACd,KAAK,EAAE,UAAU;YACjB,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,2BAA2B;YACnC,QAAQ,EAAE,cAAc;YACxB,MAAM,EAAE,oDAAoD;SAC5D,CAAC;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,SAAS,KAAK,KAAK,EAAE,CAAC;QAClC,OAAO;YACN,EAAE,EAAE,UAAU;YACd,KAAK,EAAE,UAAU;YACjB,MAAM,EAAE,WAAW;YACnB,MAAM,EAAE,2BAA2B;YACnC,QAAQ,EAAE,aAAa;YACvB,MAAM,EAAE,4DAA4D;SACpE,CAAC;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,SAAS,IAAI,QAAQ,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;QACvD,OAAO;YACN,EAAE,EAAE,UAAU;YACd,KAAK,EAAE,UAAU;YACjB,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,2BAA2B;YACnC,QAAQ,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW;SACxD,CAAC;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,WAAW,KAAK,KAAK,EAAE,CAAC;QACpC,OAAO;YACN,EAAE,EAAE,UAAU;YACd,KAAK,EAAE,UAAU;YACjB,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,2BAA2B;YACnC,QAAQ,EAAE,kBAAkB;YAC5B,MAAM,EACL,2EAA2E;SAC5E,CAAC;IACH,CAAC;IACD,OAAO;QACN,EAAE,EAAE,UAAU;QACd,KAAK,EAAE,UAAU;QACjB,MAAM,EAAE,WAAW;QACnB,MAAM,EAAE,2BAA2B;QACnC,QAAQ,EAAE,cAAc;QACxB,MAAM,EAAE,iDAAiD;KACzD,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CACzB,eAAkD;IAElD,MAAM,MAAM,GAAG,eAAe,EAAE,MAAM,IAAI,CAAC,CAAC;IAC5C,MAAM,UAAU,GAAG,eAAe,EAAE,UAAU,IAAI,CAAC,CAAC;IACpD,MAAM,MAAM,GACX,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IACpE,OAAO;QACN,EAAE,EAAE,kBAAkB;QACtB,KAAK,EAAE,kBAAkB;QACzB,MAAM;QACN,MAAM,EAAE,wBAAwB;QAChC,QAAQ,EAAE,GAAG,eAAe,EAAE,OAAO,IAAI,CAAC,aAAa,MAAM,YAAY,UAAU,aAAa;QAChG,GAAG,CAAC,MAAM,KAAK,SAAS;YACvB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,MAAM,EAAE,gDAAgD,EAAE,CAAC;KAChE,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,KAA8B;IACnD,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC;IACxC,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC;IACxC,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC;IAClC,MAAM,MAAM,GACX,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAClE,OAAO;QACN,EAAE,EAAE,YAAY;QAChB,KAAK,EAAE,YAAY;QACnB,MAAM;QACN,MAAM,EAAE,gBAAgB;QACxB,QAAQ,EAAE,GAAG,MAAM,IAAI,GAAG,YAAY,MAAM,YAAY,KAAK,CAAC,aAAa,QAAQ;QACnF,GAAG,CAAC,MAAM,KAAK,SAAS;YACvB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACA,MAAM,EAAE,2DAA2D;aACnE,CAAC;KACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,sBAAsB,CACrC,KAAqB;IAErB,MAAM,IAAI,GAAG;QACZ,UAAU,CAAC,KAAK,CAAC,YAAY,CAAC;QAC9B,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC;QAC3B,iBAAiB,CAAC,KAAK,CAAC,eAAe,CAAC;QACxC,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC;KACzB,CAAC;IACF,MAAM,WAAW,GAAG,IAAI;SACtB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC;SACzC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IAChD,OAAO;QACN,MAAM,EAAE,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACrD,IAAI;QACJ,WAAW;QACX,WAAW,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE;KACpE,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,UAA0C,EAAE;IAE5C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,QAAQ,GAAG,MAAM,yBAAyB,EAAE,CAAC;IAEnD,IAAI,SAAS,GAAG,IAAI,CAAC;IACrB,IAAI,SAAS,GAAG,IAAI,CAAC;IACrB,IAAI,CAAC;QACJ,SAAS,GAAG,QAAQ,CAAC,iCAAiC,EAAE;YACvD,GAAG;YACH,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC;SACjC,CAAC,CAAC,IAAI,EAAE,CAAC;QAEV,MAAM,MAAM,GAAG,QAAQ,CAAC,wBAAwB,EAAE;YACjD,GAAG;YACH,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC;SACjC,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACxD,SAAS,GAAG;YACX,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;YAChE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;YAC7D,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;YAC/D,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;YACjE,KAAK,EAAE,KAAK,CAAC,MAAM;SACnB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACR,4CAA4C;IAC7C,CAAC;IAED,MAAM,eAAe,GAAG,qBAAqB,CAAC,iBAAiB,CAAC;QAC/D,UAAU,EAAE,CAAC;QACb,QAAQ,EAAE,CAAC;KACX,CAAC,CAAC;IACH,MAAM,KAAK,GAAG;QACb,aAAa,EAAE,iBAAiB,EAAE;QAClC,WAAW,EAAE,0BAA0B,EAAE;KACzC,CAAC;IACF,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC/B,MAAM,aAAa,GAAG,WAAW,GAAG,SAAS,CAAC;IAE9C,OAAO;QACN,GAAG;QACH,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI;QAChE,OAAO,EAAE;YACR,OAAO,EAAE,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;YAC1C,QAAQ,EAAE,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;SAC5C;QACD,UAAU,EAAE,yBAAyB,CAAC,GAAG,CAAC;QAC1C,MAAM,EAAE;YACP,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE;YACxB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,wBAAwB,EAAE,OAAO,CAAC,iBAAiB;SACnD;QACD,QAAQ;QACR,eAAe;QACf,KAAK;QACL,SAAS,EAAE,sBAAsB,CAAC;YACjC,YAAY,EAAE,aAAa;YAC3B,eAAe;YACf,QAAQ;YACR,KAAK;YACL,WAAW,EAAE,WAAW;SACxB,CAAC;QACF,WAAW;QACX,aAAa;KACb,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CACjC,GAAoB,EACpB,GAAmB,EACnB,IAA4B,EAC5B,UAA0C,EAAE;IAE5C,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;QACnD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,aAAa,EAAE,kBAAkB,CAAC,CAAC;QAClE,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAE9C,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,sBAAsB,EAAE,CAAC;YAC5D,yBAAyB,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;YACzC,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;YACjD,OAAO;QACR,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAChD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;IACvC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,mBAAmB,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;IACjD,CAAC;AACF,CAAC","sourcesContent":["import { execSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { join } from \"node:path\";\nimport { isDatabaseConfigured, testConnection } from \"../../db/client.js\";\nimport {\n\tgetAsyncHookCount,\n\tgetHookConcurrencySnapshot,\n} from \"../../hooks/index.js\";\nimport {\n\tgetProjectOnboardingState,\n\tmarkProjectOnboardingSeen,\n} from \"../../onboarding/project-onboarding.js\";\nimport { backgroundTaskManager } from \"../../tools/background-tasks.js\";\nimport { respondWithApiError, sendJson } from \"../server-utils.js\";\n\nconst DATABASE_STATUS_CACHE_TTL_MS = 5_000;\nconst DATABASE_STATUS_PROBE_TIMEOUT_MS = 500;\nconst DATABASE_STATUS_PROBE_RETRY_AFTER_MS = 30_000;\n\nlet databaseHealthCache: { connected: boolean; checkedAt: number } | null =\n\tnull;\nlet databaseHealthProbe: Promise<boolean> | null = null;\nlet databaseHealthProbeStartedAt = 0;\nlet databaseHealthProbeGeneration = 0;\n\nfunction startDatabaseHealthProbe(): Promise<boolean> {\n\tconst now = Date.now();\n\tif (\n\t\tdatabaseHealthProbe &&\n\t\tnow - databaseHealthProbeStartedAt < DATABASE_STATUS_PROBE_RETRY_AFTER_MS\n\t) {\n\t\treturn databaseHealthProbe;\n\t}\n\n\tconst generation = databaseHealthProbeGeneration + 1;\n\tdatabaseHealthProbeGeneration = generation;\n\tdatabaseHealthProbeStartedAt = now;\n\tconst probe = testConnection()\n\t\t.then((connected) => {\n\t\t\tif (databaseHealthProbeGeneration === generation) {\n\t\t\t\tdatabaseHealthCache = { connected, checkedAt: Date.now() };\n\t\t\t}\n\t\t\treturn connected;\n\t\t})\n\t\t.catch(() => {\n\t\t\tif (databaseHealthProbeGeneration === generation) {\n\t\t\t\tdatabaseHealthCache = { connected: false, checkedAt: Date.now() };\n\t\t\t}\n\t\t\treturn false;\n\t\t})\n\t\t.finally(() => {\n\t\t\tif (databaseHealthProbe === probe) {\n\t\t\t\tdatabaseHealthProbe = null;\n\t\t\t\tdatabaseHealthProbeStartedAt = 0;\n\t\t\t}\n\t\t});\n\n\tdatabaseHealthProbe = probe;\n\treturn probe;\n}\n\nasync function waitForDatabaseProbe(\n\tprobe: Promise<boolean>,\n\tfallback: boolean,\n): Promise<boolean> {\n\tlet timeout: ReturnType<typeof setTimeout> | undefined;\n\ttry {\n\t\treturn await Promise.race([\n\t\t\tprobe,\n\t\t\tnew Promise<boolean>((resolve) => {\n\t\t\t\ttimeout = setTimeout(() => {\n\t\t\t\t\tresolve(fallback);\n\t\t\t\t}, DATABASE_STATUS_PROBE_TIMEOUT_MS);\n\t\t\t\tif (typeof timeout === \"object\" && \"unref\" in timeout) {\n\t\t\t\t\ttimeout.unref();\n\t\t\t\t}\n\t\t\t}),\n\t\t]);\n\t} finally {\n\t\tif (timeout) {\n\t\t\tclearTimeout(timeout);\n\t\t}\n\t}\n}\n\nasync function getDatabaseHealthSnapshot(): Promise<{\n\tconfigured: boolean;\n\tconnected: boolean;\n}> {\n\tconst configured = isDatabaseConfigured();\n\tif (!configured) {\n\t\treturn { configured: false, connected: false };\n\t}\n\n\tconst now = Date.now();\n\tif (\n\t\tdatabaseHealthCache &&\n\t\tnow - databaseHealthCache.checkedAt <= DATABASE_STATUS_CACHE_TTL_MS\n\t) {\n\t\treturn { configured: true, connected: databaseHealthCache.connected };\n\t}\n\n\tconst connected = await waitForDatabaseProbe(\n\t\tstartDatabaseHealthProbe(),\n\t\tfalse,\n\t);\n\treturn { configured: true, connected };\n}\n\nexport function resetStatusDatabaseHealthCacheForTests(): void {\n\tdatabaseHealthCache = null;\n\tdatabaseHealthProbe = null;\n\tdatabaseHealthProbeStartedAt = 0;\n\tdatabaseHealthProbeGeneration = 0;\n}\n\nexport type RunHealthLevel = \"healthy\" | \"degraded\" | \"unhealthy\";\n\nexport interface RunHealthSlo {\n\tid: string;\n\tlabel: string;\n\tstatus: RunHealthLevel;\n\ttarget: string;\n\tobserved: string;\n\tdetail?: string;\n}\n\nexport interface RunHealthSnapshot {\n\tstatus: RunHealthLevel;\n\tslos: RunHealthSlo[];\n\tdiagnostics: string[];\n\tgeneratedAt: string;\n}\n\nexport interface RunHealthInput {\n\tapiLatencyMs: number;\n\tbackgroundTasks: {\n\t\trunning?: number;\n\t\tfailed?: number;\n\t\trestarting?: number;\n\t} | null;\n\tdatabase: {\n\t\tconfigured: boolean;\n\t\tconnected: boolean;\n\t\tinitialized?: boolean;\n\t\treachable?: boolean;\n\t};\n\thooks: {\n\t\tasyncInFlight: number;\n\t\tconcurrency: {\n\t\t\tmax: number;\n\t\t\tactive: number;\n\t\t\tqueued: number;\n\t\t};\n\t};\n\tgeneratedAt?: number;\n}\n\nfunction maxHealthLevel(levels: RunHealthLevel[]): RunHealthLevel {\n\tif (levels.includes(\"unhealthy\")) return \"unhealthy\";\n\tif (levels.includes(\"degraded\")) return \"degraded\";\n\treturn \"healthy\";\n}\n\nfunction latencySlo(apiLatencyMs: number): RunHealthSlo {\n\tconst status =\n\t\tapiLatencyMs > 3000\n\t\t\t? \"unhealthy\"\n\t\t\t: apiLatencyMs > 1000\n\t\t\t\t? \"degraded\"\n\t\t\t\t: \"healthy\";\n\treturn {\n\t\tid: \"api_latency\",\n\t\tlabel: \"API latency\",\n\t\tstatus,\n\t\ttarget: \"p50 snapshot <= 1000ms\",\n\t\tobserved: `${Math.max(0, Math.round(apiLatencyMs))}ms`,\n\t\t...(status === \"healthy\"\n\t\t\t? {}\n\t\t\t: { detail: \"Status endpoint latency exceeded the local operator SLO.\" }),\n\t};\n}\n\nfunction databaseSlo(database: RunHealthInput[\"database\"]): RunHealthSlo {\n\tif (!database.configured) {\n\t\treturn {\n\t\t\tid: \"database\",\n\t\t\tlabel: \"Database\",\n\t\t\tstatus: \"healthy\",\n\t\t\ttarget: \"connected when configured\",\n\t\t\tobserved: \"unconfigured\",\n\t\t\tdetail: \"Local mode does not require a database connection.\",\n\t\t};\n\t}\n\tif (database.reachable === false) {\n\t\treturn {\n\t\t\tid: \"database\",\n\t\t\tlabel: \"Database\",\n\t\t\tstatus: \"unhealthy\",\n\t\t\ttarget: \"reachable when configured\",\n\t\t\tobserved: \"unreachable\",\n\t\t\tdetail: \"Configured database failed an explicit reachability check.\",\n\t\t};\n\t}\n\tif (database.connected || database.reachable === true) {\n\t\treturn {\n\t\t\tid: \"database\",\n\t\t\tlabel: \"Database\",\n\t\t\tstatus: \"healthy\",\n\t\t\ttarget: \"reachable when configured\",\n\t\t\tobserved: database.connected ? \"connected\" : \"reachable\",\n\t\t};\n\t}\n\tif (database.initialized === false) {\n\t\treturn {\n\t\t\tid: \"database\",\n\t\t\tlabel: \"Database\",\n\t\t\tstatus: \"healthy\",\n\t\t\ttarget: \"reachable when configured\",\n\t\t\tobserved: \"configured, idle\",\n\t\t\tdetail:\n\t\t\t\t\"Database is configured, but no query has initialized the lazy client yet.\",\n\t\t};\n\t}\n\treturn {\n\t\tid: \"database\",\n\t\tlabel: \"Database\",\n\t\tstatus: \"unhealthy\",\n\t\ttarget: \"reachable when configured\",\n\t\tobserved: \"disconnected\",\n\t\tdetail: \"Configured database is not currently reachable.\",\n\t};\n}\n\nfunction backgroundTaskSlo(\n\tbackgroundTasks: RunHealthInput[\"backgroundTasks\"],\n): RunHealthSlo {\n\tconst failed = backgroundTasks?.failed ?? 0;\n\tconst restarting = backgroundTasks?.restarting ?? 0;\n\tconst status =\n\t\tfailed > 0 ? \"unhealthy\" : restarting > 0 ? \"degraded\" : \"healthy\";\n\treturn {\n\t\tid: \"background_tasks\",\n\t\tlabel: \"Background tasks\",\n\t\tstatus,\n\t\ttarget: \"0 failed, 0 restarting\",\n\t\tobserved: `${backgroundTasks?.running ?? 0} running, ${failed} failed, ${restarting} restarting`,\n\t\t...(status === \"healthy\"\n\t\t\t? {}\n\t\t\t: { detail: \"Background task supervisor has unhealthy work.\" }),\n\t};\n}\n\nfunction hookQueueSlo(hooks: RunHealthInput[\"hooks\"]): RunHealthSlo {\n\tconst queued = hooks.concurrency.queued;\n\tconst active = hooks.concurrency.active;\n\tconst max = hooks.concurrency.max;\n\tconst status =\n\t\tqueued > max ? \"unhealthy\" : queued > 0 ? \"degraded\" : \"healthy\";\n\treturn {\n\t\tid: \"hook_queue\",\n\t\tlabel: \"Hook queue\",\n\t\tstatus,\n\t\ttarget: \"0 queued hooks\",\n\t\tobserved: `${active}/${max} active, ${queued} queued, ${hooks.asyncInFlight} async`,\n\t\t...(status === \"healthy\"\n\t\t\t? {}\n\t\t\t: {\n\t\t\t\t\tdetail: \"Hook execution is backing up behind the concurrency gate.\",\n\t\t\t\t}),\n\t};\n}\n\nexport function buildRunHealthSnapshot(\n\tinput: RunHealthInput,\n): RunHealthSnapshot {\n\tconst slos = [\n\t\tlatencySlo(input.apiLatencyMs),\n\t\tdatabaseSlo(input.database),\n\t\tbackgroundTaskSlo(input.backgroundTasks),\n\t\thookQueueSlo(input.hooks),\n\t];\n\tconst diagnostics = slos\n\t\t.filter((slo) => slo.status !== \"healthy\")\n\t\t.map((slo) => `${slo.label}: ${slo.observed}`);\n\treturn {\n\t\tstatus: maxHealthLevel(slos.map((slo) => slo.status)),\n\t\tslos,\n\t\tdiagnostics,\n\t\tgeneratedAt: new Date(input.generatedAt ?? Date.now()).toISOString(),\n\t};\n}\n\nexport async function getStatusSnapshot(\n\toptions: { staticCacheMaxAge?: number } = {},\n) {\n\tconst startedAt = Date.now();\n\tconst cwd = process.cwd();\n\tconst database = await getDatabaseHealthSnapshot();\n\n\tlet gitBranch = null;\n\tlet gitStatus = null;\n\ttry {\n\t\tgitBranch = execSync(\"git rev-parse --abbrev-ref HEAD\", {\n\t\t\tcwd,\n\t\t\tencoding: \"utf-8\",\n\t\t\tstdio: [\"pipe\", \"pipe\", \"ignore\"],\n\t\t}).trim();\n\n\t\tconst status = execSync(\"git status --porcelain\", {\n\t\t\tcwd,\n\t\t\tencoding: \"utf-8\",\n\t\t\tstdio: [\"pipe\", \"pipe\", \"ignore\"],\n\t\t});\n\t\tconst lines = status.trim().split(\"\\n\").filter(Boolean);\n\t\tgitStatus = {\n\t\t\tmodified: lines.filter((l: string) => l.startsWith(\" M\")).length,\n\t\t\tadded: lines.filter((l: string) => l.startsWith(\"A \")).length,\n\t\t\tdeleted: lines.filter((l: string) => l.startsWith(\" D\")).length,\n\t\t\tuntracked: lines.filter((l: string) => l.startsWith(\"??\")).length,\n\t\t\ttotal: lines.length,\n\t\t};\n\t} catch {\n\t\t// Not a git repository or git not available\n\t}\n\n\tconst backgroundTasks = backgroundTaskManager.getHealthSnapshot({\n\t\tmaxEntries: 5,\n\t\tlogLines: 2,\n\t});\n\tconst hooks = {\n\t\tasyncInFlight: getAsyncHookCount(),\n\t\tconcurrency: getHookConcurrencySnapshot(),\n\t};\n\tconst lastUpdated = Date.now();\n\tconst lastLatencyMs = lastUpdated - startedAt;\n\n\treturn {\n\t\tcwd,\n\t\tgit: gitBranch ? { branch: gitBranch, status: gitStatus } : null,\n\t\tcontext: {\n\t\t\tagentMd: existsSync(join(cwd, \"AGENT.md\")),\n\t\t\tclaudeMd: existsSync(join(cwd, \"CLAUDE.md\")),\n\t\t},\n\t\tonboarding: getProjectOnboardingState(cwd),\n\t\tserver: {\n\t\t\tuptime: process.uptime(),\n\t\t\tversion: process.version,\n\t\t\tstaticCacheMaxAgeSeconds: options.staticCacheMaxAge,\n\t\t},\n\t\tdatabase,\n\t\tbackgroundTasks,\n\t\thooks,\n\t\trunHealth: buildRunHealthSnapshot({\n\t\t\tapiLatencyMs: lastLatencyMs,\n\t\t\tbackgroundTasks,\n\t\t\tdatabase,\n\t\t\thooks,\n\t\t\tgeneratedAt: lastUpdated,\n\t\t}),\n\t\tlastUpdated,\n\t\tlastLatencyMs,\n\t};\n}\n\nexport async function handleStatus(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tcors: Record<string, string>,\n\toptions: { staticCacheMaxAge?: number } = {},\n): Promise<void> {\n\ttry {\n\t\tconst method = (req.method ?? \"GET\").toUpperCase();\n\t\tconst url = new URL(req.url ?? \"/api/status\", \"http://localhost\");\n\t\tconst action = url.searchParams.get(\"action\");\n\n\t\tif (method === \"POST\" && action === \"mark-onboarding-seen\") {\n\t\t\tmarkProjectOnboardingSeen(process.cwd());\n\t\t\tsendJson(res, 200, { success: true }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst status = await getStatusSnapshot(options);\n\t\tsendJson(res, 200, status, cors, req);\n\t} catch (error) {\n\t\trespondWithApiError(res, error, 500, cors, req);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/server/handlers/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EACN,iBAAiB,EACjB,0BAA0B,GAC1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACN,yBAAyB,EACzB,yBAAyB,GACzB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnE,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAC3C,MAAM,gCAAgC,GAAG,GAAG,CAAC;AAC7C,MAAM,oCAAoC,GAAG,MAAM,CAAC;AAEpD,IAAI,mBAAmB,GACtB,IAAI,CAAC;AACN,IAAI,mBAAmB,GAA4B,IAAI,CAAC;AACxD,IAAI,4BAA4B,GAAG,CAAC,CAAC;AACrC,IAAI,6BAA6B,GAAG,CAAC,CAAC;AAEtC,SAAS,wBAAwB;IAChC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,IACC,mBAAmB;QACnB,GAAG,GAAG,4BAA4B,GAAG,oCAAoC,EACxE,CAAC;QACF,OAAO,mBAAmB,CAAC;IAC5B,CAAC;IAED,MAAM,UAAU,GAAG,6BAA6B,GAAG,CAAC,CAAC;IACrD,6BAA6B,GAAG,UAAU,CAAC;IAC3C,4BAA4B,GAAG,GAAG,CAAC;IACnC,MAAM,KAAK,GAAG,cAAc,EAAE;SAC5B,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE;QACnB,IAAI,6BAA6B,KAAK,UAAU,EAAE,CAAC;YAClD,mBAAmB,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QAC5D,CAAC;QACD,OAAO,SAAS,CAAC;IAClB,CAAC,CAAC;SACD,KAAK,CAAC,GAAG,EAAE;QACX,IAAI,6BAA6B,KAAK,UAAU,EAAE,CAAC;YAClD,mBAAmB,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QACnE,CAAC;QACD,OAAO,KAAK,CAAC;IACd,CAAC,CAAC;SACD,OAAO,CAAC,GAAG,EAAE;QACb,IAAI,mBAAmB,KAAK,KAAK,EAAE,CAAC;YACnC,mBAAmB,GAAG,IAAI,CAAC;YAC3B,4BAA4B,GAAG,CAAC,CAAC;QAClC,CAAC;IACF,CAAC,CAAC,CAAC;IAEJ,mBAAmB,GAAG,KAAK,CAAC;IAC5B,OAAO,KAAK,CAAC;AACd,CAAC;AAED,KAAK,UAAU,oBAAoB,CAClC,KAAuB,EACvB,QAAiB;IAEjB,IAAI,OAAkD,CAAC;IACvD,IAAI,CAAC;QACJ,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC;YACzB,KAAK;YACL,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE;gBAChC,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;oBACzB,OAAO,CAAC,QAAQ,CAAC,CAAC;gBACnB,CAAC,EAAE,gCAAgC,CAAC,CAAC;gBACrC,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,IAAI,OAAO,EAAE,CAAC;oBACvD,OAAO,CAAC,KAAK,EAAE,CAAC;gBACjB,CAAC;YACF,CAAC,CAAC;SACF,CAAC,CAAC;IACJ,CAAC;YAAS,CAAC;QACV,IAAI,OAAO,EAAE,CAAC;YACb,YAAY,CAAC,OAAO,CAAC,CAAC;QACvB,CAAC;IACF,CAAC;AACF,CAAC;AAED,KAAK,UAAU,yBAAyB;IAIvC,MAAM,UAAU,GAAG,oBAAoB,EAAE,CAAC;IAC1C,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAChD,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,IACC,mBAAmB;QACnB,GAAG,GAAG,mBAAmB,CAAC,SAAS,IAAI,4BAA4B,EAClE,CAAC;QACF,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,mBAAmB,CAAC,SAAS,EAAE,CAAC;IACvE,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,oBAAoB,CAC3C,wBAAwB,EAAE,EAC1B,KAAK,CACL,CAAC;IACF,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,sCAAsC;IACrD,mBAAmB,GAAG,IAAI,CAAC;IAC3B,mBAAmB,GAAG,IAAI,CAAC;IAC3B,4BAA4B,GAAG,CAAC,CAAC;IACjC,6BAA6B,GAAG,CAAC,CAAC;AACnC,CAAC;AA4CD,SAAS,cAAc,CAAC,MAAwB;IAC/C,IAAI,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC;QAAE,OAAO,WAAW,CAAC;IACrD,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;QAAE,OAAO,UAAU,CAAC;IACnD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,SAAS,UAAU,CAAC,YAAoB;IACvC,MAAM,MAAM,GACX,YAAY,GAAG,IAAI;QAClB,CAAC,CAAC,WAAW;QACb,CAAC,CAAC,YAAY,GAAG,IAAI;YACpB,CAAC,CAAC,UAAU;YACZ,CAAC,CAAC,SAAS,CAAC;IACf,OAAO;QACN,EAAE,EAAE,aAAa;QACjB,KAAK,EAAE,aAAa;QACpB,MAAM;QACN,MAAM,EAAE,wBAAwB;QAChC,QAAQ,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,IAAI;QACtD,GAAG,CAAC,MAAM,KAAK,SAAS;YACvB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,MAAM,EAAE,0DAA0D,EAAE,CAAC;KAC1E,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,QAAoC;IACxD,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;QAC1B,OAAO;YACN,EAAE,EAAE,UAAU;YACd,KAAK,EAAE,UAAU;YACjB,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,2BAA2B;YACnC,QAAQ,EAAE,cAAc;YACxB,MAAM,EAAE,oDAAoD;SAC5D,CAAC;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,SAAS,KAAK,KAAK,EAAE,CAAC;QAClC,OAAO;YACN,EAAE,EAAE,UAAU;YACd,KAAK,EAAE,UAAU;YACjB,MAAM,EAAE,WAAW;YACnB,MAAM,EAAE,2BAA2B;YACnC,QAAQ,EAAE,aAAa;YACvB,MAAM,EAAE,4DAA4D;SACpE,CAAC;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,SAAS,IAAI,QAAQ,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;QACvD,OAAO;YACN,EAAE,EAAE,UAAU;YACd,KAAK,EAAE,UAAU;YACjB,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,2BAA2B;YACnC,QAAQ,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW;SACxD,CAAC;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,WAAW,KAAK,KAAK,EAAE,CAAC;QACpC,OAAO;YACN,EAAE,EAAE,UAAU;YACd,KAAK,EAAE,UAAU;YACjB,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,2BAA2B;YACnC,QAAQ,EAAE,kBAAkB;YAC5B,MAAM,EACL,2EAA2E;SAC5E,CAAC;IACH,CAAC;IACD,OAAO;QACN,EAAE,EAAE,UAAU;QACd,KAAK,EAAE,UAAU;QACjB,MAAM,EAAE,WAAW;QACnB,MAAM,EAAE,2BAA2B;QACnC,QAAQ,EAAE,cAAc;QACxB,MAAM,EAAE,iDAAiD;KACzD,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CACzB,eAAkD;IAElD,MAAM,MAAM,GAAG,eAAe,EAAE,MAAM,IAAI,CAAC,CAAC;IAC5C,MAAM,UAAU,GAAG,eAAe,EAAE,UAAU,IAAI,CAAC,CAAC;IACpD,MAAM,MAAM,GACX,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IACpE,OAAO;QACN,EAAE,EAAE,kBAAkB;QACtB,KAAK,EAAE,kBAAkB;QACzB,MAAM;QACN,MAAM,EAAE,wBAAwB;QAChC,QAAQ,EAAE,GAAG,eAAe,EAAE,OAAO,IAAI,CAAC,aAAa,MAAM,YAAY,UAAU,aAAa;QAChG,GAAG,CAAC,MAAM,KAAK,SAAS;YACvB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,MAAM,EAAE,gDAAgD,EAAE,CAAC;KAChE,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,KAA8B;IACnD,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC;IACxC,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC;IACxC,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC;IAClC,MAAM,MAAM,GACX,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAClE,OAAO;QACN,EAAE,EAAE,YAAY;QAChB,KAAK,EAAE,YAAY;QACnB,MAAM;QACN,MAAM,EAAE,gBAAgB;QACxB,QAAQ,EAAE,GAAG,MAAM,IAAI,GAAG,YAAY,MAAM,YAAY,KAAK,CAAC,aAAa,QAAQ;QACnF,GAAG,CAAC,MAAM,KAAK,SAAS;YACvB,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACA,MAAM,EAAE,2DAA2D;aACnE,CAAC;KACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,sBAAsB,CACrC,KAAqB;IAErB,MAAM,IAAI,GAAG;QACZ,UAAU,CAAC,KAAK,CAAC,YAAY,CAAC;QAC9B,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC;QAC3B,iBAAiB,CAAC,KAAK,CAAC,eAAe,CAAC;QACxC,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC;KACzB,CAAC;IACF,MAAM,WAAW,GAAG,IAAI;SACtB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC;SACzC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IAChD,OAAO;QACN,MAAM,EAAE,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACrD,IAAI;QACJ,WAAW;QACX,WAAW,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE;KACpE,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,UAAwD,EAAE;IAE1D,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,MAAM,QAAQ,GAAG,MAAM,yBAAyB,EAAE,CAAC;IAEnD,IAAI,SAAS,GAAG,IAAI,CAAC;IACrB,IAAI,SAAS,GAAG,IAAI,CAAC;IACrB,IAAI,CAAC;QACJ,SAAS,GAAG,QAAQ,CAAC,iCAAiC,EAAE;YACvD,GAAG;YACH,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC;SACjC,CAAC,CAAC,IAAI,EAAE,CAAC;QAEV,MAAM,MAAM,GAAG,QAAQ,CAAC,wBAAwB,EAAE;YACjD,GAAG;YACH,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC;SACjC,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACxD,SAAS,GAAG;YACX,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;YAChE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;YAC7D,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;YAC/D,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM;YACjE,KAAK,EAAE,KAAK,CAAC,MAAM;SACnB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACR,4CAA4C;IAC7C,CAAC;IAED,MAAM,eAAe,GAAG,qBAAqB,CAAC,iBAAiB,CAAC;QAC/D,UAAU,EAAE,CAAC;QACb,QAAQ,EAAE,CAAC;KACX,CAAC,CAAC;IACH,MAAM,KAAK,GAAG;QACb,aAAa,EAAE,iBAAiB,EAAE;QAClC,WAAW,EAAE,0BAA0B,EAAE;KACzC,CAAC;IACF,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC/B,MAAM,aAAa,GAAG,WAAW,GAAG,SAAS,CAAC;IAE9C,OAAO;QACN,GAAG;QACH,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,IAAI;QAChE,OAAO,EAAE;YACR,OAAO,EAAE,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;YAC1C,QAAQ,EAAE,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;SAC5C;QACD,UAAU,EAAE,yBAAyB,CAAC,GAAG,CAAC;QAC1C,MAAM,EAAE;YACP,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE;YACxB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,wBAAwB,EAAE,OAAO,CAAC,iBAAiB;SACnD;QACD,QAAQ;QACR,eAAe;QACf,KAAK;QACL,SAAS,EAAE,sBAAsB,CAAC;YACjC,YAAY,EAAE,aAAa;YAC3B,eAAe;YACf,QAAQ;YACR,KAAK;YACL,WAAW,EAAE,WAAW;SACxB,CAAC;QACF,WAAW;QACX,aAAa;KACb,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CACjC,GAAoB,EACpB,GAAmB,EACnB,IAA4B,EAC5B,UAAwD,EAAE;IAE1D,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,CAAC,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;QACnD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,aAAa,EAAE,kBAAkB,CAAC,CAAC;QAClE,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAEzC,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,sBAAsB,EAAE,CAAC;YAC5D,yBAAyB,CAAC,GAAG,CAAC,CAAC;YAC/B,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;YACjD,OAAO;QACR,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAChD,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;IACvC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,mBAAmB,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;IACjD,CAAC;AACF,CAAC","sourcesContent":["import { execSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport { join } from \"node:path\";\nimport { isDatabaseConfigured, testConnection } from \"../../db/client.js\";\nimport {\n\tgetAsyncHookCount,\n\tgetHookConcurrencySnapshot,\n} from \"../../hooks/index.js\";\nimport {\n\tgetProjectOnboardingState,\n\tmarkProjectOnboardingSeen,\n} from \"../../onboarding/project-onboarding.js\";\nimport { backgroundTaskManager } from \"../../tools/background-tasks.js\";\nimport { respondWithApiError, sendJson } from \"../server-utils.js\";\n\nconst DATABASE_STATUS_CACHE_TTL_MS = 5_000;\nconst DATABASE_STATUS_PROBE_TIMEOUT_MS = 500;\nconst DATABASE_STATUS_PROBE_RETRY_AFTER_MS = 30_000;\n\nlet databaseHealthCache: { connected: boolean; checkedAt: number } | null =\n\tnull;\nlet databaseHealthProbe: Promise<boolean> | null = null;\nlet databaseHealthProbeStartedAt = 0;\nlet databaseHealthProbeGeneration = 0;\n\nfunction startDatabaseHealthProbe(): Promise<boolean> {\n\tconst now = Date.now();\n\tif (\n\t\tdatabaseHealthProbe &&\n\t\tnow - databaseHealthProbeStartedAt < DATABASE_STATUS_PROBE_RETRY_AFTER_MS\n\t) {\n\t\treturn databaseHealthProbe;\n\t}\n\n\tconst generation = databaseHealthProbeGeneration + 1;\n\tdatabaseHealthProbeGeneration = generation;\n\tdatabaseHealthProbeStartedAt = now;\n\tconst probe = testConnection()\n\t\t.then((connected) => {\n\t\t\tif (databaseHealthProbeGeneration === generation) {\n\t\t\t\tdatabaseHealthCache = { connected, checkedAt: Date.now() };\n\t\t\t}\n\t\t\treturn connected;\n\t\t})\n\t\t.catch(() => {\n\t\t\tif (databaseHealthProbeGeneration === generation) {\n\t\t\t\tdatabaseHealthCache = { connected: false, checkedAt: Date.now() };\n\t\t\t}\n\t\t\treturn false;\n\t\t})\n\t\t.finally(() => {\n\t\t\tif (databaseHealthProbe === probe) {\n\t\t\t\tdatabaseHealthProbe = null;\n\t\t\t\tdatabaseHealthProbeStartedAt = 0;\n\t\t\t}\n\t\t});\n\n\tdatabaseHealthProbe = probe;\n\treturn probe;\n}\n\nasync function waitForDatabaseProbe(\n\tprobe: Promise<boolean>,\n\tfallback: boolean,\n): Promise<boolean> {\n\tlet timeout: ReturnType<typeof setTimeout> | undefined;\n\ttry {\n\t\treturn await Promise.race([\n\t\t\tprobe,\n\t\t\tnew Promise<boolean>((resolve) => {\n\t\t\t\ttimeout = setTimeout(() => {\n\t\t\t\t\tresolve(fallback);\n\t\t\t\t}, DATABASE_STATUS_PROBE_TIMEOUT_MS);\n\t\t\t\tif (typeof timeout === \"object\" && \"unref\" in timeout) {\n\t\t\t\t\ttimeout.unref();\n\t\t\t\t}\n\t\t\t}),\n\t\t]);\n\t} finally {\n\t\tif (timeout) {\n\t\t\tclearTimeout(timeout);\n\t\t}\n\t}\n}\n\nasync function getDatabaseHealthSnapshot(): Promise<{\n\tconfigured: boolean;\n\tconnected: boolean;\n}> {\n\tconst configured = isDatabaseConfigured();\n\tif (!configured) {\n\t\treturn { configured: false, connected: false };\n\t}\n\n\tconst now = Date.now();\n\tif (\n\t\tdatabaseHealthCache &&\n\t\tnow - databaseHealthCache.checkedAt <= DATABASE_STATUS_CACHE_TTL_MS\n\t) {\n\t\treturn { configured: true, connected: databaseHealthCache.connected };\n\t}\n\n\tconst connected = await waitForDatabaseProbe(\n\t\tstartDatabaseHealthProbe(),\n\t\tfalse,\n\t);\n\treturn { configured: true, connected };\n}\n\nexport function resetStatusDatabaseHealthCacheForTests(): void {\n\tdatabaseHealthCache = null;\n\tdatabaseHealthProbe = null;\n\tdatabaseHealthProbeStartedAt = 0;\n\tdatabaseHealthProbeGeneration = 0;\n}\n\nexport type RunHealthLevel = \"healthy\" | \"degraded\" | \"unhealthy\";\n\nexport interface RunHealthSlo {\n\tid: string;\n\tlabel: string;\n\tstatus: RunHealthLevel;\n\ttarget: string;\n\tobserved: string;\n\tdetail?: string;\n}\n\nexport interface RunHealthSnapshot {\n\tstatus: RunHealthLevel;\n\tslos: RunHealthSlo[];\n\tdiagnostics: string[];\n\tgeneratedAt: string;\n}\n\nexport interface RunHealthInput {\n\tapiLatencyMs: number;\n\tbackgroundTasks: {\n\t\trunning?: number;\n\t\tfailed?: number;\n\t\trestarting?: number;\n\t} | null;\n\tdatabase: {\n\t\tconfigured: boolean;\n\t\tconnected: boolean;\n\t\tinitialized?: boolean;\n\t\treachable?: boolean;\n\t};\n\thooks: {\n\t\tasyncInFlight: number;\n\t\tconcurrency: {\n\t\t\tmax: number;\n\t\t\tactive: number;\n\t\t\tqueued: number;\n\t\t};\n\t};\n\tgeneratedAt?: number;\n}\n\nfunction maxHealthLevel(levels: RunHealthLevel[]): RunHealthLevel {\n\tif (levels.includes(\"unhealthy\")) return \"unhealthy\";\n\tif (levels.includes(\"degraded\")) return \"degraded\";\n\treturn \"healthy\";\n}\n\nfunction latencySlo(apiLatencyMs: number): RunHealthSlo {\n\tconst status =\n\t\tapiLatencyMs > 3000\n\t\t\t? \"unhealthy\"\n\t\t\t: apiLatencyMs > 1000\n\t\t\t\t? \"degraded\"\n\t\t\t\t: \"healthy\";\n\treturn {\n\t\tid: \"api_latency\",\n\t\tlabel: \"API latency\",\n\t\tstatus,\n\t\ttarget: \"p50 snapshot <= 1000ms\",\n\t\tobserved: `${Math.max(0, Math.round(apiLatencyMs))}ms`,\n\t\t...(status === \"healthy\"\n\t\t\t? {}\n\t\t\t: { detail: \"Status endpoint latency exceeded the local operator SLO.\" }),\n\t};\n}\n\nfunction databaseSlo(database: RunHealthInput[\"database\"]): RunHealthSlo {\n\tif (!database.configured) {\n\t\treturn {\n\t\t\tid: \"database\",\n\t\t\tlabel: \"Database\",\n\t\t\tstatus: \"healthy\",\n\t\t\ttarget: \"connected when configured\",\n\t\t\tobserved: \"unconfigured\",\n\t\t\tdetail: \"Local mode does not require a database connection.\",\n\t\t};\n\t}\n\tif (database.reachable === false) {\n\t\treturn {\n\t\t\tid: \"database\",\n\t\t\tlabel: \"Database\",\n\t\t\tstatus: \"unhealthy\",\n\t\t\ttarget: \"reachable when configured\",\n\t\t\tobserved: \"unreachable\",\n\t\t\tdetail: \"Configured database failed an explicit reachability check.\",\n\t\t};\n\t}\n\tif (database.connected || database.reachable === true) {\n\t\treturn {\n\t\t\tid: \"database\",\n\t\t\tlabel: \"Database\",\n\t\t\tstatus: \"healthy\",\n\t\t\ttarget: \"reachable when configured\",\n\t\t\tobserved: database.connected ? \"connected\" : \"reachable\",\n\t\t};\n\t}\n\tif (database.initialized === false) {\n\t\treturn {\n\t\t\tid: \"database\",\n\t\t\tlabel: \"Database\",\n\t\t\tstatus: \"healthy\",\n\t\t\ttarget: \"reachable when configured\",\n\t\t\tobserved: \"configured, idle\",\n\t\t\tdetail:\n\t\t\t\t\"Database is configured, but no query has initialized the lazy client yet.\",\n\t\t};\n\t}\n\treturn {\n\t\tid: \"database\",\n\t\tlabel: \"Database\",\n\t\tstatus: \"unhealthy\",\n\t\ttarget: \"reachable when configured\",\n\t\tobserved: \"disconnected\",\n\t\tdetail: \"Configured database is not currently reachable.\",\n\t};\n}\n\nfunction backgroundTaskSlo(\n\tbackgroundTasks: RunHealthInput[\"backgroundTasks\"],\n): RunHealthSlo {\n\tconst failed = backgroundTasks?.failed ?? 0;\n\tconst restarting = backgroundTasks?.restarting ?? 0;\n\tconst status =\n\t\tfailed > 0 ? \"unhealthy\" : restarting > 0 ? \"degraded\" : \"healthy\";\n\treturn {\n\t\tid: \"background_tasks\",\n\t\tlabel: \"Background tasks\",\n\t\tstatus,\n\t\ttarget: \"0 failed, 0 restarting\",\n\t\tobserved: `${backgroundTasks?.running ?? 0} running, ${failed} failed, ${restarting} restarting`,\n\t\t...(status === \"healthy\"\n\t\t\t? {}\n\t\t\t: { detail: \"Background task supervisor has unhealthy work.\" }),\n\t};\n}\n\nfunction hookQueueSlo(hooks: RunHealthInput[\"hooks\"]): RunHealthSlo {\n\tconst queued = hooks.concurrency.queued;\n\tconst active = hooks.concurrency.active;\n\tconst max = hooks.concurrency.max;\n\tconst status =\n\t\tqueued > max ? \"unhealthy\" : queued > 0 ? \"degraded\" : \"healthy\";\n\treturn {\n\t\tid: \"hook_queue\",\n\t\tlabel: \"Hook queue\",\n\t\tstatus,\n\t\ttarget: \"0 queued hooks\",\n\t\tobserved: `${active}/${max} active, ${queued} queued, ${hooks.asyncInFlight} async`,\n\t\t...(status === \"healthy\"\n\t\t\t? {}\n\t\t\t: {\n\t\t\t\t\tdetail: \"Hook execution is backing up behind the concurrency gate.\",\n\t\t\t\t}),\n\t};\n}\n\nexport function buildRunHealthSnapshot(\n\tinput: RunHealthInput,\n): RunHealthSnapshot {\n\tconst slos = [\n\t\tlatencySlo(input.apiLatencyMs),\n\t\tdatabaseSlo(input.database),\n\t\tbackgroundTaskSlo(input.backgroundTasks),\n\t\thookQueueSlo(input.hooks),\n\t];\n\tconst diagnostics = slos\n\t\t.filter((slo) => slo.status !== \"healthy\")\n\t\t.map((slo) => `${slo.label}: ${slo.observed}`);\n\treturn {\n\t\tstatus: maxHealthLevel(slos.map((slo) => slo.status)),\n\t\tslos,\n\t\tdiagnostics,\n\t\tgeneratedAt: new Date(input.generatedAt ?? Date.now()).toISOString(),\n\t};\n}\n\nexport async function getStatusSnapshot(\n\toptions: { staticCacheMaxAge?: number; cwd?: string } = {},\n) {\n\tconst startedAt = Date.now();\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst database = await getDatabaseHealthSnapshot();\n\n\tlet gitBranch = null;\n\tlet gitStatus = null;\n\ttry {\n\t\tgitBranch = execSync(\"git rev-parse --abbrev-ref HEAD\", {\n\t\t\tcwd,\n\t\t\tencoding: \"utf-8\",\n\t\t\tstdio: [\"pipe\", \"pipe\", \"ignore\"],\n\t\t}).trim();\n\n\t\tconst status = execSync(\"git status --porcelain\", {\n\t\t\tcwd,\n\t\t\tencoding: \"utf-8\",\n\t\t\tstdio: [\"pipe\", \"pipe\", \"ignore\"],\n\t\t});\n\t\tconst lines = status.trim().split(\"\\n\").filter(Boolean);\n\t\tgitStatus = {\n\t\t\tmodified: lines.filter((l: string) => l.startsWith(\" M\")).length,\n\t\t\tadded: lines.filter((l: string) => l.startsWith(\"A \")).length,\n\t\t\tdeleted: lines.filter((l: string) => l.startsWith(\" D\")).length,\n\t\t\tuntracked: lines.filter((l: string) => l.startsWith(\"??\")).length,\n\t\t\ttotal: lines.length,\n\t\t};\n\t} catch {\n\t\t// Not a git repository or git not available\n\t}\n\n\tconst backgroundTasks = backgroundTaskManager.getHealthSnapshot({\n\t\tmaxEntries: 5,\n\t\tlogLines: 2,\n\t});\n\tconst hooks = {\n\t\tasyncInFlight: getAsyncHookCount(),\n\t\tconcurrency: getHookConcurrencySnapshot(),\n\t};\n\tconst lastUpdated = Date.now();\n\tconst lastLatencyMs = lastUpdated - startedAt;\n\n\treturn {\n\t\tcwd,\n\t\tgit: gitBranch ? { branch: gitBranch, status: gitStatus } : null,\n\t\tcontext: {\n\t\t\tagentMd: existsSync(join(cwd, \"AGENT.md\")),\n\t\t\tclaudeMd: existsSync(join(cwd, \"CLAUDE.md\")),\n\t\t},\n\t\tonboarding: getProjectOnboardingState(cwd),\n\t\tserver: {\n\t\t\tuptime: process.uptime(),\n\t\t\tversion: process.version,\n\t\t\tstaticCacheMaxAgeSeconds: options.staticCacheMaxAge,\n\t\t},\n\t\tdatabase,\n\t\tbackgroundTasks,\n\t\thooks,\n\t\trunHealth: buildRunHealthSnapshot({\n\t\t\tapiLatencyMs: lastLatencyMs,\n\t\t\tbackgroundTasks,\n\t\t\tdatabase,\n\t\t\thooks,\n\t\t\tgeneratedAt: lastUpdated,\n\t\t}),\n\t\tlastUpdated,\n\t\tlastLatencyMs,\n\t};\n}\n\nexport async function handleStatus(\n\treq: IncomingMessage,\n\tres: ServerResponse,\n\tcors: Record<string, string>,\n\toptions: { staticCacheMaxAge?: number; cwd?: string } = {},\n): Promise<void> {\n\ttry {\n\t\tconst method = (req.method ?? \"GET\").toUpperCase();\n\t\tconst url = new URL(req.url ?? \"/api/status\", \"http://localhost\");\n\t\tconst action = url.searchParams.get(\"action\");\n\t\tconst cwd = options.cwd ?? process.cwd();\n\n\t\tif (method === \"POST\" && action === \"mark-onboarding-seen\") {\n\t\t\tmarkProjectOnboardingSeen(cwd);\n\t\t\tsendJson(res, 200, { success: true }, cors, req);\n\t\t\treturn;\n\t\t}\n\n\t\tconst status = await getStatusSnapshot(options);\n\t\tsendJson(res, 200, status, cors, req);\n\t} catch (error) {\n\t\trespondWithApiError(res, error, 500, cors, req);\n\t}\n}\n"]}
|