@iislee/opencodex 2.11.0 → 2.31.0
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/AGENTS_INSTALL.md +109 -0
- package/README.md +114 -19
- package/bin/ocx.mjs +164 -36
- package/bin/package-main.mjs +1 -1
- package/gui/dist/assets/index-BF38heuV.js +104 -0
- package/gui/dist/assets/index-DMiI18Kv.css +1 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/alibaba-color.svg +1 -1
- package/gui/dist/provider-icons/antigravity-color.svg +1 -1
- package/gui/dist/provider-icons/claude-color.svg +1 -1
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -1
- package/gui/dist/provider-icons/cursor-color.svg +1 -1
- package/gui/dist/provider-icons/deepseek-color.svg +1 -1
- package/gui/dist/provider-icons/firepass-color.svg +1 -1
- package/gui/dist/provider-icons/fireworks-color.svg +1 -1
- package/gui/dist/provider-icons/gemini-color.svg +1 -1
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
- package/gui/dist/provider-icons/grok.svg +1 -1
- package/gui/dist/provider-icons/groq-color.svg +1 -1
- package/gui/dist/provider-icons/huggingface-color.svg +1 -1
- package/gui/dist/provider-icons/kimi-color.svg +1 -1
- package/gui/dist/provider-icons/kiro-color.svg +2 -2
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
- package/gui/dist/provider-icons/mistral-color.svg +1 -1
- package/gui/dist/provider-icons/moonshot-color.svg +1 -1
- package/gui/dist/provider-icons/nvidia-color.svg +1 -1
- package/gui/dist/provider-icons/ollama-color.svg +1 -1
- package/gui/dist/provider-icons/openai.svg +1 -1
- package/gui/dist/provider-icons/opencode.svg +2 -1
- package/gui/dist/provider-icons/openrouter-color.svg +1 -1
- package/gui/dist/provider-icons/pi.svg +2 -2
- package/gui/dist/provider-icons/qianfan-color.svg +1 -1
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/vllm-color.svg +1 -1
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
- package/package.json +19 -10
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +376 -52
- package/src/adapters/base.ts +54 -7
- package/src/adapters/client-fingerprint.ts +18 -12
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +601 -0
- package/src/adapters/cursor/checkpoint-store.ts +303 -0
- package/src/adapters/cursor/cursor-errors.ts +129 -6
- package/src/adapters/cursor/discovery.ts +61 -4
- package/src/adapters/cursor/effort-map.ts +27 -3
- package/src/adapters/cursor/framing.ts +39 -0
- package/src/adapters/cursor/h2-pool.ts +123 -0
- package/src/adapters/cursor/http1-bidi.ts +361 -0
- package/src/adapters/cursor/images.ts +704 -0
- package/src/adapters/cursor/live-models.ts +174 -58
- package/src/adapters/cursor/live-transport.ts +609 -170
- package/src/adapters/cursor/native-exec-common.ts +23 -2
- package/src/adapters/cursor/native-exec-fs.ts +10 -7
- package/src/adapters/cursor/native-exec-network.ts +1 -1
- package/src/adapters/cursor/native-exec-shell.ts +0 -1
- package/src/adapters/cursor/native-exec.ts +101 -14
- package/src/adapters/cursor/protobuf-events.ts +829 -11
- package/src/adapters/cursor/protobuf-request.ts +383 -65
- package/src/adapters/cursor/request-builder.ts +237 -31
- package/src/adapters/cursor/tool-definitions.ts +242 -12
- package/src/adapters/cursor/tool-result-normalize.ts +92 -0
- package/src/adapters/cursor/transport.ts +22 -0
- package/src/adapters/cursor/types.ts +28 -1
- package/src/adapters/cursor.ts +133 -7
- package/src/adapters/google-antigravity-replay.ts +558 -34
- package/src/adapters/google-antigravity-wire.ts +43 -10
- package/src/adapters/google-http.ts +38 -10
- package/src/adapters/google-tool-schema.ts +80 -15
- package/src/adapters/google-truncation.ts +11 -0
- package/src/adapters/google.ts +618 -74
- package/src/adapters/identity.ts +39 -6
- package/src/adapters/kiro-errors.ts +11 -0
- package/src/adapters/kiro-events.ts +19 -1
- package/src/adapters/kiro-thinking.ts +18 -2
- package/src/adapters/kiro-tools.ts +10 -1
- package/src/adapters/kiro.ts +92 -54
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1311 -336
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +830 -56
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +104 -21
- package/src/adapters/xai-web-search.ts +185 -0
- package/src/bridge.ts +428 -61
- package/src/chat/inbound.ts +43 -19
- package/src/chat/outbound.ts +82 -26
- package/src/claude/agents-inject.ts +32 -9
- package/src/claude/context-windows.ts +21 -5
- package/src/claude/desktop-3p.ts +243 -9
- package/src/claude/gateway-cache.ts +41 -4
- package/src/claude/inbound.ts +72 -3
- package/src/claude/model-info.ts +38 -15
- package/src/claude/outbound.ts +70 -16
- package/src/cli/account-api.ts +35 -1
- package/src/cli/account-auth.ts +33 -6
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +389 -2
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +30 -6
- package/src/cli/agent.ts +45 -1
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +29 -4
- package/src/cli/claude.ts +103 -34
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +2 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +8 -0
- package/src/cli/config-command.ts +74 -10
- package/src/cli/dispatch.ts +593 -0
- package/src/cli/doctor.ts +315 -43
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +46 -20
- package/src/cli/help.ts +20 -271
- package/src/cli/index.ts +378 -519
- package/src/cli/init.ts +4 -17
- package/src/cli/integrations.ts +120 -2
- package/src/cli/lab.ts +607 -0
- package/src/cli/launcher-context.ts +77 -0
- package/src/cli/minimax.ts +497 -0
- package/src/cli/models-runtime.ts +35 -2
- package/src/cli/models.ts +100 -14
- package/src/cli/observe.ts +92 -3
- package/src/cli/opencode.ts +4 -2
- package/src/cli/provider-runtime.ts +18 -1
- package/src/cli/provider.ts +24 -3
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +437 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +6 -3
- package/src/cli/star-prompt.ts +71 -15
- package/src/cli/status.ts +10 -3
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +153 -6
- package/src/cli/v2.ts +105 -10
- package/src/cli.ts +1 -1
- package/src/clients/config-export.ts +1358 -21
- package/src/codex/account-label.ts +14 -1
- package/src/codex/account-lifecycle.ts +130 -13
- package/src/codex/account-namespaces.ts +49 -3
- package/src/codex/account-priority.ts +83 -0
- package/src/codex/account-store.ts +29 -2
- package/src/codex/account-usability.ts +25 -2
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/app-server-processes.ts +493 -106
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +849 -242
- package/src/codex/auth-collision.ts +5 -3
- package/src/codex/auth-context.ts +345 -32
- package/src/codex/autostart-health.ts +8 -1
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +68 -10
- package/src/codex/catalog/bundled.ts +331 -33
- package/src/codex/catalog/effort.ts +121 -30
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +529 -45
- package/src/codex/catalog/native-models.ts +72 -0
- package/src/codex/catalog/parsing.ts +224 -30
- package/src/codex/catalog/provider-fetch.ts +1460 -134
- package/src/codex/catalog/sync.ts +1449 -186
- package/src/codex/catalog-admission.ts +199 -0
- package/src/codex/catalog-refresh-status.ts +105 -0
- package/src/codex/catalog-write-serialization.ts +242 -0
- package/src/codex/catalog.ts +6 -3
- package/src/codex/codex-write-lock.ts +384 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +651 -0
- package/src/codex/coordinator-doctor.ts +332 -0
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/features.ts +636 -39
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +407 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-migration-guardian.ts +26 -20
- package/src/codex/history-provider.ts +231 -28
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +220 -0
- package/src/codex/inject-coordination.ts +290 -0
- package/src/codex/inject.ts +1073 -152
- package/src/codex/injected-marker.ts +37 -3
- package/src/codex/integration-record.ts +266 -0
- package/src/codex/internal/catalog-writer.ts +203 -0
- package/src/codex/internal/history-writer.ts +80 -0
- package/src/codex/journal.ts +66 -4
- package/src/codex/log-guard/inspect.ts +506 -0
- package/src/codex/log-guard/lock.ts +150 -0
- package/src/codex/log-guard/maintenance.ts +403 -0
- package/src/codex/log-guard/path-safety.ts +88 -0
- package/src/codex/log-guard/policy.ts +44 -0
- package/src/codex/log-guard/processes.ts +205 -0
- package/src/codex/log-guard/protection.ts +489 -0
- package/src/codex/log-guard/sqlite-errors.ts +9 -0
- package/src/codex/main-account-cache.ts +24 -0
- package/src/codex/main-account.ts +29 -1
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +56 -10
- package/src/codex/model-entitlements.ts +353 -0
- package/src/codex/native-main-admission.ts +47 -0
- package/src/codex/native-main-auth-temp.ts +187 -0
- package/src/codex/native-main-claim.ts +178 -0
- package/src/codex/native-main-lock-file.ts +162 -0
- package/src/codex/native-main-owner.ts +329 -0
- package/src/codex/native-profile-api.ts +247 -0
- package/src/codex/native-profile-manager.ts +1531 -0
- package/src/codex/native-profile-processes.ts +121 -0
- package/src/codex/native-profile-recovery.ts +99 -0
- package/src/codex/native-profile-stage-store.ts +387 -0
- package/src/codex/native-profile-startup.ts +492 -0
- package/src/codex/native-profile-store.ts +855 -0
- package/src/codex/native-profile-types.ts +120 -0
- package/src/codex/native-residue.ts +682 -0
- package/src/codex/paths.ts +80 -1
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +1 -1
- package/src/codex/pool-rotation.ts +74 -4
- package/src/codex/project-config-warnings.ts +20 -6
- package/src/codex/prompt-journal.ts +352 -0
- package/src/codex/prompt-layers.ts +967 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +175 -13
- package/src/codex/refresh.ts +11 -2
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +505 -94
- package/src/codex/runtime.ts +159 -38
- package/src/codex/shim.ts +1009 -28
- package/src/codex/subagent-model-fallback.ts +350 -35
- package/src/codex/sync.ts +191 -2
- package/src/codex/transition-state.ts +612 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +557 -0
- package/src/codex/warmup.ts +187 -81
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +20 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/request.ts +32 -0
- package/src/combos/types.ts +81 -9
- package/src/config/provider-name.ts +24 -0
- package/src/config.ts +1762 -140
- package/src/generated/compatibility-version.json +3164 -0
- package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
- package/src/grok/inject.ts +16 -5
- package/src/grok/inspect.ts +45 -0
- package/src/grok/sync.ts +2 -2
- package/src/images/loop.ts +152 -29
- package/src/images/plan.ts +23 -13
- package/src/integrations/config-io.ts +269 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/mutation-flight.ts +71 -0
- package/src/integrations/native/ownership-preflight.ts +202 -0
- package/src/integrations/omp-yaml-source.ts +358 -0
- package/src/integrations/owned-refresh.ts +74 -0
- package/src/integrations/ownership.ts +111 -0
- package/src/integrations/registry.ts +159 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +361 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +691 -0
- package/src/lab/artifacts/sanitize.ts +586 -0
- package/src/lab/artifacts/secure-fs.ts +475 -0
- package/src/lab/artifacts/store.ts +310 -0
- package/src/lab/automation/budgets.ts +78 -0
- package/src/lab/automation/config-persistence.ts +256 -0
- package/src/lab/automation/constants.ts +39 -0
- package/src/lab/automation/cooldown.ts +103 -0
- package/src/lab/automation/dispatch.ts +211 -0
- package/src/lab/automation/index.ts +13 -0
- package/src/lab/automation/orchestrator.ts +499 -0
- package/src/lab/automation/persistence.ts +512 -0
- package/src/lab/automation/planner.ts +371 -0
- package/src/lab/automation/policy.ts +136 -0
- package/src/lab/automation/queue.ts +191 -0
- package/src/lab/automation/recovery.ts +24 -0
- package/src/lab/automation/route-context.ts +21 -0
- package/src/lab/automation/run-key.ts +44 -0
- package/src/lab/automation/runs-query.ts +34 -0
- package/src/lab/automation/types.ts +160 -0
- package/src/lab/conformance/assertion.ts +325 -0
- package/src/lab/conformance/digest.ts +22 -0
- package/src/lab/conformance/executor.ts +741 -0
- package/src/lab/conformance/fixture-provider.ts +27 -0
- package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
- package/src/lab/conformance/harness-budget.ts +47 -0
- package/src/lab/conformance/index.ts +5 -0
- package/src/lab/conformance/jcs.ts +64 -0
- package/src/lab/conformance/json-pointer.ts +39 -0
- package/src/lab/conformance/manifest.ts +180 -0
- package/src/lab/conformance/mcp-stub.ts +179 -0
- package/src/lab/conformance/negative-controls.ts +164 -0
- package/src/lab/conformance/observation.ts +355 -0
- package/src/lab/conformance/runner.ts +68 -0
- package/src/lab/conformance/sse-normalize.ts +59 -0
- package/src/lab/conformance/suite-manifest.ts +78 -0
- package/src/lab/conformance/types.ts +214 -0
- package/src/lab/constants.ts +126 -0
- package/src/lab/digest.ts +64 -0
- package/src/lab/events/errors.ts +9 -0
- package/src/lab/events/limits.ts +117 -0
- package/src/lab/events/types.ts +229 -0
- package/src/lab/events/validate.ts +781 -0
- package/src/lab/fabric/constants.ts +40 -0
- package/src/lab/fabric/executor.ts +492 -0
- package/src/lab/fabric/index.ts +80 -0
- package/src/lab/fabric/manifest.ts +222 -0
- package/src/lab/fabric/observe.ts +489 -0
- package/src/lab/fabric/patch.ts +79 -0
- package/src/lab/fabric/producer-child.ts +139 -0
- package/src/lab/fabric/producer-isolate.ts +276 -0
- package/src/lab/fabric/producer-protocol.ts +61 -0
- package/src/lab/fabric/scratch.ts +439 -0
- package/src/lab/fabric/subject.ts +106 -0
- package/src/lab/fabric/types.ts +134 -0
- package/src/lab/fabric/verifier.ts +98 -0
- package/src/lab/index.ts +54 -0
- package/src/lab/ledger/artifact-refs.ts +127 -0
- package/src/lab/ledger/invalidation.ts +136 -0
- package/src/lab/ledger/purge.ts +310 -0
- package/src/lab/ledger/store.ts +532 -0
- package/src/lab/live/credential-lease.ts +53 -0
- package/src/lab/live/destination.ts +155 -0
- package/src/lab/live/executor.ts +336 -0
- package/src/lab/live/inert-tools.ts +56 -0
- package/src/lab/live/manifest.ts +85 -0
- package/src/lab/live/mcp-loopback.ts +57 -0
- package/src/lab/live/runner.ts +19 -0
- package/src/lab/live/sandbox.ts +61 -0
- package/src/lab/live/suite-manifest.ts +41 -0
- package/src/lab/live/transport.ts +118 -0
- package/src/lab/live/types.ts +197 -0
- package/src/lab/observe/from-conformance.ts +301 -0
- package/src/lab/observe/from-live.ts +117 -0
- package/src/lab/paths.ts +153 -0
- package/src/lab/projection/rebuild.ts +495 -0
- package/src/lab/projection/schema.ts +135 -0
- package/src/lab/projection/verdicts.ts +474 -0
- package/src/lab/projection/verification.ts +412 -0
- package/src/lab/public/bundle.ts +217 -0
- package/src/lab/public/community-authority.ts +175 -0
- package/src/lab/public/community-files.ts +29 -0
- package/src/lab/public/community.ts +479 -0
- package/src/lab/public/file-safety.ts +155 -0
- package/src/lab/public/ids.ts +26 -0
- package/src/lab/public/index.ts +16 -0
- package/src/lab/public/mutation-lock.ts +424 -0
- package/src/lab/public/operator.ts +353 -0
- package/src/lab/public/origin-purge.ts +79 -0
- package/src/lab/public/origin.ts +203 -0
- package/src/lab/public/privacy.ts +143 -0
- package/src/lab/public/private-file.ts +261 -0
- package/src/lab/public/project.ts +124 -0
- package/src/lab/public/purge-test-fault.ts +21 -0
- package/src/lab/public/purge.ts +223 -0
- package/src/lab/public/registry.ts +44 -0
- package/src/lab/public/revocation.ts +252 -0
- package/src/lab/public/signature.ts +243 -0
- package/src/lab/public/storage.ts +105 -0
- package/src/lab/public/strict-json.ts +206 -0
- package/src/lab/public/time.ts +26 -0
- package/src/lab/public/types.ts +172 -0
- package/src/lab/public/validate.ts +391 -0
- package/src/lab/query/catalog.ts +101 -0
- package/src/lab/query/connection.ts +107 -0
- package/src/lab/query/constants.ts +4 -0
- package/src/lab/query/cursor.ts +132 -0
- package/src/lab/query/dto-map.ts +277 -0
- package/src/lab/query/errors.ts +22 -0
- package/src/lab/query/freshness.ts +53 -0
- package/src/lab/query/index.ts +45 -0
- package/src/lab/query/latest-observation.ts +59 -0
- package/src/lab/query/passive-production.ts +159 -0
- package/src/lab/query/queries.ts +444 -0
- package/src/lab/query/types.ts +266 -0
- package/src/lab/subject/behavior-fingerprint.ts +77 -0
- package/src/lab/subject/installation-salt.ts +112 -0
- package/src/lab/subject/protocol-subject.ts +80 -0
- package/src/lab/subject/route-subject.ts +74 -0
- package/src/lib/app-owned-memory-stores.ts +22 -0
- package/src/lib/bounded-body.ts +153 -9
- package/src/lib/bun-runtime.ts +125 -12
- package/src/lib/bun-stream-caps.ts +13 -9
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +6 -2
- package/src/lib/destination-policy.ts +65 -1
- package/src/lib/errors.ts +44 -2
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/lab-activation.ts +223 -0
- package/src/lib/lab-live-execution-authority.ts +13 -0
- package/src/lib/lab-live-host.ts +30 -0
- package/src/lib/lab-live-pinned-sender.ts +56 -0
- package/src/lib/lab-live-route-production.ts +130 -0
- package/src/lib/lab-passive-linker-registration.ts +26 -0
- package/src/lib/local-management-attestation.ts +51 -0
- package/src/lib/local-management-capability.ts +100 -0
- package/src/lib/local-provider-reload-contract.ts +100 -0
- package/src/lib/optional-shutdown-hooks.ts +57 -0
- package/src/lib/pinned-http.ts +145 -26
- package/src/lib/process-control.ts +4 -1
- package/src/lib/provider-outbound.ts +49 -9
- package/src/lib/redact.ts +419 -3
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/shadow-call.ts +35 -4
- package/src/lib/sse-decoder.ts +41 -0
- package/src/lib/state-store-registrations.ts +10 -2
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/token-estimate.ts +19 -2
- package/src/lib/tool-argument-integers.ts +202 -0
- package/src/lib/translator-budget.ts +44 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +156 -3
- package/src/lib/windows-atomic-replace.ts +155 -0
- package/src/lib/windows-elevation.ts +70 -2
- package/src/lib/windows-secret-acl.ts +409 -69
- package/src/lib/windows-service-wrappers.ts +72 -0
- package/src/lib/windows-text.ts +106 -0
- package/src/lib/windows-user-principal.ts +341 -0
- package/src/lib/winsw.ts +33 -5
- package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
- package/src/oauth/account-import/index.ts +15 -0
- package/src/oauth/account-import/parser.ts +83 -0
- package/src/oauth/account-import/registry.ts +18 -0
- package/src/oauth/account-import/service.ts +75 -0
- package/src/oauth/account-import/types.ts +91 -0
- package/src/oauth/anthropic.ts +12 -1
- package/src/oauth/callback-server.ts +8 -2
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +46 -5
- package/src/oauth/google-antigravity.ts +35 -3
- package/src/oauth/health.ts +20 -12
- package/src/oauth/index.ts +398 -66
- package/src/oauth/key-providers.ts +16 -0
- package/src/oauth/kimi.ts +16 -2
- package/src/oauth/kiro.ts +50 -6
- package/src/oauth/local-token-detect.ts +11 -2
- package/src/oauth/log.ts +3 -1
- package/src/oauth/login-cli.ts +88 -28
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/store.ts +119 -21
- package/src/oauth/token-guardian.ts +9 -3
- package/src/pi/models.ts +2 -2
- package/src/providers/alibaba-region-migration.ts +1 -1
- package/src/providers/antigravity-models.ts +521 -31
- package/src/providers/base-url-choices.ts +10 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +144 -0
- package/src/providers/context-cap.ts +22 -5
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/derive.ts +253 -6
- package/src/providers/fastwire.ts +501 -0
- package/src/providers/free-directory.ts +10 -7
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +71 -3
- package/src/providers/label.ts +1 -1
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +115 -22
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/openai-sidecar.ts +72 -4
- package/src/providers/openai-tier-startup.ts +31 -2
- package/src/providers/openai-tiers.ts +119 -4
- package/src/providers/openai-virtual-models.ts +1 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +29 -0
- package/src/providers/quota.ts +1319 -38
- package/src/providers/registry.ts +1429 -111
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +42 -6
- package/src/providers/static-model-discovery.ts +86 -0
- package/src/providers/xai-responses-opt-in.ts +15 -0
- package/src/providers/xai-transport.ts +11 -4
- package/src/reasoning-effort.ts +49 -1
- package/src/responses/compaction.ts +26 -1
- package/src/responses/custom-tool-compat.ts +266 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +355 -0
- package/src/responses/parser.ts +220 -38
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +9 -1
- package/src/responses/reasoning-replay-cache.ts +426 -0
- package/src/responses/schema.ts +7 -1
- package/src/responses/spill-store.ts +75 -10
- package/src/responses/state.ts +565 -27
- package/src/responses/thought-signature-replay.ts +347 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/router.ts +366 -30
- package/src/routing/analytics.ts +378 -0
- package/src/routing/capability.ts +244 -0
- package/src/routing/compatibility/assemble.ts +73 -0
- package/src/routing/compatibility/behavior.ts +278 -0
- package/src/routing/compatibility/catalog.ts +99 -0
- package/src/routing/compatibility/endpoint.ts +52 -0
- package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
- package/src/routing/compatibility/policy.ts +181 -0
- package/src/routing/compatibility/provider-slot.ts +56 -0
- package/src/routing/compatibility/reader.ts +110 -0
- package/src/routing/compatibility/subject.ts +191 -0
- package/src/routing/compatibility/types.ts +64 -0
- package/src/routing/compatibility/version.ts +104 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +495 -0
- package/src/routing/health.ts +412 -0
- package/src/routing/history/cursor.ts +43 -0
- package/src/routing/history/indexer.ts +605 -0
- package/src/routing/history/schema.ts +72 -0
- package/src/routing/profile-namespace.ts +15 -0
- package/src/routing/profile.ts +547 -0
- package/src/routing/quota.ts +145 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +776 -0
- package/src/server/adapter-resolve.ts +2 -29
- package/src/server/auth-cors.ts +267 -46
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +130 -56
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +426 -0
- package/src/server/claude-messages.ts +159 -43
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +18 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +39 -10
- package/src/server/images.ts +94 -12
- package/src/server/index.ts +865 -181
- package/src/server/lifecycle.ts +284 -13
- package/src/server/live.ts +136 -17
- package/src/server/local-management-read-client.ts +90 -0
- package/src/server/local-provider-reload-client.ts +137 -0
- package/src/server/management/agent-settings-routes.ts +398 -103
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/body.ts +6 -0
- package/src/server/management/combo-routes.ts +62 -24
- package/src/server/management/config-routes.ts +464 -51
- package/src/server/management/context.ts +80 -2
- package/src/server/management/integration-routes.ts +498 -0
- package/src/server/management/lab-automation-routes.ts +206 -0
- package/src/server/management/lab-routes.ts +563 -0
- package/src/server/management/logs-usage-routes.ts +101 -32
- package/src/server/management/model-routes.ts +189 -131
- package/src/server/management/model-rows.ts +163 -0
- package/src/server/management/native-integration-routes.ts +769 -0
- package/src/server/management/oauth-account-routes.ts +80 -4
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +764 -157
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +380 -0
- package/src/server/management/shared.ts +27 -11
- package/src/server/management/sidebar-routes.ts +47 -31
- package/src/server/management/storage-log-guard-routes.ts +186 -0
- package/src/server/management/sync-response.ts +69 -0
- package/src/server/management/system-restart.ts +288 -32
- package/src/server/management/system-routes.ts +77 -0
- package/src/server/management/usage-summary-cache.ts +9 -1
- package/src/server/management/vision-sidecar-options.ts +167 -0
- package/src/server/management/web-search-sidecar-options.ts +120 -0
- package/src/server/management-api.ts +115 -14
- package/src/server/management-auth.ts +220 -5
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/ports.ts +41 -1
- package/src/server/proxy-liveness.ts +132 -5
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +82 -42
- package/src/server/relay.ts +236 -76
- package/src/server/request-decompress.ts +113 -6
- package/src/server/request-log.ts +235 -22
- package/src/server/responses/agent-task-recovery-cache.ts +143 -0
- package/src/server/responses/agent-task-recovery.ts +465 -0
- package/src/server/responses/collaboration.ts +204 -35
- package/src/server/responses/compact.ts +442 -55
- package/src/server/responses/core.ts +2872 -331
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/encrypted-payload.ts +62 -39
- package/src/server/responses/fetch-helpers.ts +79 -4
- package/src/server/responses/input-admission.ts +185 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +251 -0
- package/src/server/responses/terminal-guard.ts +26 -5
- package/src/server/responses/upstream-error.ts +5 -0
- package/src/server/responses/ws-upstream.ts +308 -0
- package/src/server/responses-custom-tool-repair.ts +282 -0
- package/src/server/responses-item-id-repair.ts +54 -6
- package/src/server/responses-json-events.ts +90 -0
- package/src/server/responses-model-rewrite.ts +29 -0
- package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/responses-terminal-repair.ts +342 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +153 -0
- package/src/server/responses.ts +18 -2
- package/src/server/search.ts +78 -13
- package/src/server/sse-frame-buffer.ts +292 -0
- package/src/server/sse-payload-rewrite.ts +110 -22
- package/src/server/startup-action-control.ts +8 -1
- package/src/server/startup-health-cache.ts +19 -1
- package/src/server/system-env.ts +80 -9
- package/src/server/ws-bridge.ts +39 -38
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +1111 -90
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/storage/cleanup.ts +2 -2
- package/src/storage/scanner.ts +1 -1
- package/src/storage/worker-lifecycle.ts +14 -14
- package/src/tray/windows-tray.ps1 +83 -9
- package/src/tray/windows.ts +43 -16
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +845 -0
- package/src/types/provider.ts +545 -0
- package/src/types/request.ts +384 -0
- package/src/types/tools.ts +131 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +104 -1236
- package/src/update/index.ts +32 -19
- package/src/update/job.ts +442 -67
- package/src/update/notify.ts +12 -6
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +259 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +268 -16
- package/src/usage/log.ts +606 -41
- package/src/usage/summary.ts +177 -9
- package/src/usage/user-cost-overlay-reconciler.ts +313 -0
- package/src/usage/user-cost-overlays.ts +314 -0
- package/src/vision/anthropic-describe.ts +10 -6
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +9 -3
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +238 -24
- package/src/vision/reasoning.ts +55 -0
- package/src/vision/routed-describe.ts +175 -0
- package/src/vision/timeout-bounds.ts +9 -0
- package/src/web-search/anthropic-executor.ts +13 -7
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +88 -0
- package/src/web-search/executor.ts +11 -3
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +150 -15
- package/src/web-search/loop.ts +279 -50
- package/src/web-search/parse.ts +125 -30
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/xai-executor.ts +219 -0
- package/gui/dist/assets/index-DTpMHS4F.js +0 -67
- package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
- package/gui/dist/provider-icons/antigravity.svg +0 -1
- package/gui/dist/provider-icons/claude.svg +0 -1
- package/gui/dist/provider-icons/copilot.svg +0 -1
- package/gui/dist/provider-icons/cursor.svg +0 -2
- package/gui/dist/provider-icons/gemini.svg +0 -1
- package/gui/dist/provider-icons/grok-color.svg +0 -1
- package/gui/dist/provider-icons/kiro.svg +0 -14
- package/src/cli/internal-dispatch.ts +0 -20
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deny-by-default sanitization before artifact hashing/writing.
|
|
3
|
+
* Never persists prompts, secrets, paths, account IDs, raw URLs, or provider bodies.
|
|
4
|
+
*/
|
|
5
|
+
import type { ArtifactClass } from "../constants";
|
|
6
|
+
import { MAX_SANITIZED_STRING_FIELD } from "../constants";
|
|
7
|
+
import { jcsStringify } from "../digest";
|
|
8
|
+
import { redactSecretString } from "../../lib/redact";
|
|
9
|
+
|
|
10
|
+
const FORBIDDEN_KEY = /^(?:authorization|proxy-authorization|cookie|set-cookie|api[-_]?key|x-api-key|token|secret|password|email|prompt|messages|content|body|url|hostname|baseUrl|path|account|alias)$/i;
|
|
11
|
+
const SECRETISH = /sk-[a-z0-9]{10,}|credential-canary-[a-z0-9]{10,}|Bearer\s+[A-Za-z0-9._\-]+|ghp_[A-Za-z0-9]{20,}|xox[baprs]-[A-Za-z0-9-]{10,}/i;
|
|
12
|
+
const SECRETISH_GLOBAL = new RegExp(SECRETISH.source, "gi");
|
|
13
|
+
|
|
14
|
+
export function redactForArtifact(artifactClass: ArtifactClass, payload: unknown): unknown {
|
|
15
|
+
if (
|
|
16
|
+
artifactClass === "fixture" ||
|
|
17
|
+
artifactClass === "scenario_manifest" ||
|
|
18
|
+
artifactClass === "suite_manifest" ||
|
|
19
|
+
artifactClass === "claim_source_manifest"
|
|
20
|
+
) {
|
|
21
|
+
// Contract artifacts are already synthetic/canonical. Mutating them would
|
|
22
|
+
// invalidate content-addressed digests; reject secret-shaped material instead.
|
|
23
|
+
assertNoSecretMaterial(payload, 0);
|
|
24
|
+
return payload;
|
|
25
|
+
}
|
|
26
|
+
return scrubValue(payload, 0);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const FORBIDDEN_CONTRACT_KEYS = /^(?:authorization|proxy-authorization|cookie|set-cookie|api[-_]?key|x-api-key|token|secret|password|email|prompt|messages|baseUrl|hostname|account|alias)$/i;
|
|
30
|
+
|
|
31
|
+
function assertNoSecretMaterial(value: unknown, depth: number): void {
|
|
32
|
+
if (depth > 8) {
|
|
33
|
+
throw new Error("contract artifact exceeds sanitization inspection depth");
|
|
34
|
+
}
|
|
35
|
+
if (typeof value === "string") {
|
|
36
|
+
if (SECRETISH.test(value)) {
|
|
37
|
+
throw new Error("contract artifact contains forbidden secret-shaped material");
|
|
38
|
+
}
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (Array.isArray(value)) {
|
|
42
|
+
for (const item of value) assertNoSecretMaterial(item, depth + 1);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (value && typeof value === "object") {
|
|
46
|
+
for (const [key, child] of Object.entries(value as object)) {
|
|
47
|
+
if (FORBIDDEN_CONTRACT_KEYS.test(key)) {
|
|
48
|
+
throw new Error(`contract artifact forbids key ${key}`);
|
|
49
|
+
}
|
|
50
|
+
assertNoSecretMaterial(child, depth + 1);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function scrubValue(value: unknown, depth: number): unknown {
|
|
56
|
+
if (depth > 8) return "[truncated_depth]";
|
|
57
|
+
if (value === null || typeof value === "boolean" || typeof value === "number") return value;
|
|
58
|
+
if (typeof value === "string") return scrubString(value);
|
|
59
|
+
if (value instanceof Uint8Array) {
|
|
60
|
+
const text = new TextDecoder().decode(value);
|
|
61
|
+
return new TextEncoder().encode(scrubString(text));
|
|
62
|
+
}
|
|
63
|
+
if (Array.isArray(value)) {
|
|
64
|
+
if (value.length > 256) return value.slice(0, 256).map((v) => scrubValue(v, depth + 1));
|
|
65
|
+
return value.map((v) => scrubValue(v, depth + 1));
|
|
66
|
+
}
|
|
67
|
+
if (typeof value === "object") {
|
|
68
|
+
const out: Record<string, unknown> = {};
|
|
69
|
+
const keys = Object.keys(value as object).slice(0, 64);
|
|
70
|
+
for (const key of keys) {
|
|
71
|
+
if (FORBIDDEN_KEY.test(key)) {
|
|
72
|
+
out[key] = "[redacted]";
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
out[key] = scrubValue((value as Record<string, unknown>)[key], depth + 1);
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
return "[unsupported]";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Provider-controlled text can carry network and account identifiers that the
|
|
84
|
+
* Compatibility Lab privacy contract forbids persisting. The rules below run in
|
|
85
|
+
* a fixed total order; the order is load-bearing and documented in
|
|
86
|
+
* `devlog/_fin/260810_release_train_and_triage/040_sec02_remediation.md`:
|
|
87
|
+
*
|
|
88
|
+
* email before hostname - else the hostname rule eats `example.com` in an address
|
|
89
|
+
* MAC before IPv6 - else `01:23:45:67:89:ab` matches as a mangled address
|
|
90
|
+
* IPv6 before IPv4 - else `::ffff:192.0.2.128` degrades to `::ffff:[ip]`
|
|
91
|
+
* HTTP before other URIs - and the URI rule refuses http(s) so it cannot
|
|
92
|
+
* re-match this rule's own `https://[host]/path` output
|
|
93
|
+
*
|
|
94
|
+
* Every rule replaces a value whole or not at all. A prefix replacement is
|
|
95
|
+
* worse than no rule, because the output looks redacted while the tail leaks.
|
|
96
|
+
*/
|
|
97
|
+
|
|
98
|
+
/** Bounded, non-nested. */
|
|
99
|
+
const JWT_RE = /\beyJ[A-Za-z0-9_-]{8,512}\.[A-Za-z0-9_-]{8,512}\.[A-Za-z0-9_-]{8,512}\b/g;
|
|
100
|
+
// Both patterns end with a lookahead rather than `\b`, because `\b` fires
|
|
101
|
+
// between a letter and `-`: `ops@…xn--p1ai` matched only up to the first
|
|
102
|
+
// hyphen and left `[email]--p1ai`, and `acct_abcdef-prod` left `[account]-prod`.
|
|
103
|
+
// A prefix replacement looks redacted while the tail leaks, which is the one
|
|
104
|
+
// thing this module must never do.
|
|
105
|
+
// The local part accepts more than an ASCII dot-atom: an internationalized or
|
|
106
|
+
// quoted local part is still the account-identifying half of the address, and
|
|
107
|
+
// matching only the domain left `用户@[host]` and `"ops"@[host]` behind.
|
|
108
|
+
const EMAIL_RE =
|
|
109
|
+
/(?:"[^"\n]{1,64}"|[^\s@<>()[\],;:"]{1,64})@[^\s@<>()[\],;:"]{1,255}\.(?:xn--[a-z0-9-]{1,55}|\p{L}{2,24})(?![\w.-])/giu;
|
|
110
|
+
// The value run includes hyphens so `acct_abcdef-prod` is consumed whole. A
|
|
111
|
+
// `\b`-terminated form matched only `acct_abcdef` and left `-prod` dangling
|
|
112
|
+
// after the token, which reads as redacted while leaking the remainder.
|
|
113
|
+
const PREFIXED_ACCOUNT_RE = /\b(?:acct|cus|sub|org)[_-][A-Za-z0-9][A-Za-z0-9-]{5,63}(?![\w-])/g;
|
|
114
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
115
|
+
// `\b` treats `_` as a word character, so it does not fire between `_` and a
|
|
116
|
+
// digit: `backend_203.0.113.7_timeout` and `iface_01:23:45:67:89:ab_down` are
|
|
117
|
+
// ordinary machine-generated diagnostics that slipped past a `\b` form. These
|
|
118
|
+
// use explicit non-identifier boundaries on both sides instead.
|
|
119
|
+
// Colon notation is usual on POSIX, hyphen notation on Windows; separators
|
|
120
|
+
// must be consistent within one address.
|
|
121
|
+
const MAC_RE = /(?<![0-9A-Za-z:-])(?:(?:[0-9A-Fa-f]{2}:){5}|(?:[0-9A-Fa-f]{2}-){5})[0-9A-Fa-f]{2}(?![0-9A-Za-z:-])/g;
|
|
122
|
+
const IPV4_RE = /(?<![0-9A-Za-z.])(?:\d{1,3}\.){3}\d{1,3}(?![0-9A-Za-z.])/g;
|
|
123
|
+
// The final label is either an ordinary alphabetic TLD or a punycode one.
|
|
124
|
+
// Allowing hyphens and digits in every final label was too broad: it ate
|
|
125
|
+
// ordinary diagnostic tokens such as `foo.bar-baz` and `metric.p95`, which
|
|
126
|
+
// costs exactly the evidence quality the Lab exists to capture.
|
|
127
|
+
// A trailing `-` or word character after the TLD means this was never a
|
|
128
|
+
// hostname (`foo.bar-baz`), so the lookahead refuses the match. A trailing
|
|
129
|
+
// `.` is allowed and consumed: it is FQDN root notation and equally ordinary
|
|
130
|
+
// sentence punctuation, and forbidding it let `api.example.com.` through
|
|
131
|
+
// untouched.
|
|
132
|
+
//
|
|
133
|
+
// The leading `(?<![\w.-])` matters just as much: without it the engine
|
|
134
|
+
// backtracks to a later starting label and redacts a prefix, turning
|
|
135
|
+
// `provider.metric.p95` into `[host].p95` — evidence destruction dressed up
|
|
136
|
+
// as a redaction.
|
|
137
|
+
// An OPTIONAL trailing dot does not work: the engine simply skips it and takes
|
|
138
|
+
// a shorter match, so `provider.metric.p95` matched `provider.metric` and left
|
|
139
|
+
// `[host].p95`. The terminator is therefore mandatory and expressed as a
|
|
140
|
+
// single alternation — either the token ends cleanly, or it ends with an FQDN
|
|
141
|
+
// root dot that is itself followed by nothing label-shaped.
|
|
142
|
+
const HOSTNAME_RE = /(?<![\w.-])(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.){1,8}(?:xn--[a-z0-9-]{1,55}|[a-z]{2,24})(?:\.(?![\w-])|(?![\w.-]))/gi;
|
|
143
|
+
// The value is scanned to its delimiter, not to a word boundary: `\b` stops at
|
|
144
|
+
// the first `.` in `db_prod.internal`, which left `[host].internal` — a partial
|
|
145
|
+
// redaction that still names the host.
|
|
146
|
+
// `db.prod-1` and `api.us-east-1` are indistinguishable from ordinary dotted
|
|
147
|
+
// diagnostics by shape alone — a numeric or hyphenated final label is both a
|
|
148
|
+
// legitimate internal hostname and a legitimate metric or version namespace.
|
|
149
|
+
// Shape cannot decide it, so context does: these forms are redacted only when
|
|
150
|
+
// an unambiguous network marker introduces them. Outside such a marker they
|
|
151
|
+
// survive, and that is a recorded limit rather than an oversight.
|
|
152
|
+
//
|
|
153
|
+
// The marker grammar accepts the separators these messages actually use —
|
|
154
|
+
// whitespace, `=`, `:`, and JSON quoting — and the CANDIDATE IS VALIDATED
|
|
155
|
+
// before replacement. Accepting any following token turned
|
|
156
|
+
// `ETIMEDOUT after 30 seconds` into `ETIMEDOUT [host] 30 seconds`: a redaction
|
|
157
|
+
// that destroys the diagnostic and hides nothing.
|
|
158
|
+
// Markers differ in confidence, and treating them alike cost accuracy both
|
|
159
|
+
// ways. STRONG markers are resolver/socket errors and `host=`: whatever
|
|
160
|
+
// follows is a host by construction, so a bare `redis` or `localhost` counts.
|
|
161
|
+
// WEAK markers appear in ordinary prose (`upstream provider.metric.p95
|
|
162
|
+
// exceeded`), so they only redact a candidate that is already host-shaped.
|
|
163
|
+
const STRONG_HOST_CONTEXT_RE =
|
|
164
|
+
/\b(?:ENOTFOUND|EAI_AGAIN|ECONNREFUSED|ETIMEDOUT|EHOSTUNREACH|dial\s+(?:tcp|udp)|connect(?:ing)?\s+to|host)["']?\s*[\s=:]\s*["']?((?:[A-Za-z0-9_.-]{1,255}[\s:]{1,3}){0,3}[A-Za-z0-9_.-]{1,255})/gi;
|
|
165
|
+
const WEAK_HOST_CONTEXT_RE =
|
|
166
|
+
/\b(?:upstream)["']?\s*[\s=:]\s*["']?([A-Za-z0-9_.-]{1,255})/gi;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* A dotted run of plain alphabetic words with no digits or hyphens in any
|
|
170
|
+
* label — `provider.metric.p95` reads as a namespace, not a host. Under a
|
|
171
|
+
* WEAK marker this is left alone; under a STRONG one the marker decides.
|
|
172
|
+
*/
|
|
173
|
+
const DOTTED_NAMESPACE_RE = /^[a-z]+(?:\.[a-z]+)*\.[a-z]+[0-9]*$/i;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Does this token look like a host rather than an English word?
|
|
177
|
+
*
|
|
178
|
+
* Even a strong marker does not guarantee the very next token is the
|
|
179
|
+
* destination: Go emits `dial tcp: lookup db.prod-1: no such host`, and errno
|
|
180
|
+
* codes are routinely followed by prose (`ETIMEDOUT request after 30s`).
|
|
181
|
+
* Replacing whatever follows destroyed the helper word and left the real host
|
|
182
|
+
* in place — the worst of both outcomes.
|
|
183
|
+
*
|
|
184
|
+
* A stopword list would repeat the delimiter-enumeration mistake, so the
|
|
185
|
+
* candidate is validated instead. A token qualifies when it carries host
|
|
186
|
+
* punctuation (dot, hyphen, underscore, digit) or is a reserved name; a bare
|
|
187
|
+
* English word does not — EXCEPT directly after a resolver marker, where the
|
|
188
|
+
* argument is a name by construction and `ENOTFOUND redis` must still redact.
|
|
189
|
+
*/
|
|
190
|
+
const RESERVED_HOST_NAMES = new Set(["localhost", "broadcasthost"]);
|
|
191
|
+
const PROSE_AFTER_MARKER = new Set([
|
|
192
|
+
"lookup", "request", "connection", "attempt", "failed", "error", "timeout",
|
|
193
|
+
"after", "to", "for", "at", "no", "such", "the", "a", "an", "while", "during",
|
|
194
|
+
]);
|
|
195
|
+
function isHostCandidate(value: string, bareWordAllowed = false): boolean {
|
|
196
|
+
const lower = value.toLowerCase();
|
|
197
|
+
if (RESERVED_HOST_NAMES.has(lower)) return true;
|
|
198
|
+
if (/[.\-_0-9]/.test(value)) {
|
|
199
|
+
return AMBIGUOUS_HOST_RE.test(value) || CONTEXTUAL_HOST_TOKEN_RE.test(value);
|
|
200
|
+
}
|
|
201
|
+
// A bare word: only a resolver marker makes it a host, and only when it is
|
|
202
|
+
// not one of the connective words those messages actually use.
|
|
203
|
+
return bareWordAllowed && !PROSE_AFTER_MARKER.has(lower);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* A dotted label run whose final label may be numeric or hyphenated — the
|
|
208
|
+
* shape that is ambiguous between an internal hostname and a metric namespace.
|
|
209
|
+
* A single-label token is NOT a host: accepting one turned
|
|
210
|
+
* `upstream request failed` into `upstream [host] failed`.
|
|
211
|
+
*/
|
|
212
|
+
const AMBIGUOUS_HOST_RE = /^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.){1,8}[a-z0-9][a-z0-9-]{0,61}$/i;
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* A single-label internal name (`db-primary`, `db_prod.internal`'s owner). A
|
|
216
|
+
* bare word is only treated as a host inside an explicit network context, and
|
|
217
|
+
* it must still look like a name rather than an English word carrying the
|
|
218
|
+
* message — `request` and `after` are rejected by requiring a digit, hyphen,
|
|
219
|
+
* underscore, or dot somewhere in the token.
|
|
220
|
+
*/
|
|
221
|
+
const CONTEXTUAL_HOST_TOKEN_RE = /^[a-z0-9]+(?:[._-][a-z0-9]+)+$|^[a-z]+[0-9][a-z0-9]*$/i;
|
|
222
|
+
// `userID` is at least as common as `userId` in provider payloads, so the id
|
|
223
|
+
// suffix is matched case-insensitively.
|
|
224
|
+
// Case-insensitive throughout: `UserID`, `USER_ID`, and `AccountId` are all
|
|
225
|
+
// realistic field spellings, and matching only the lowercase base label left
|
|
226
|
+
// them intact.
|
|
227
|
+
// The id suffix may be joined (`userID`), underscored (`user_id`) or spaced
|
|
228
|
+
// (`User ID:`) — all three are ordinary provider phrasing.
|
|
229
|
+
const ACCOUNT_LABEL_RE = /\b(?:user|account|organization|org|tenant|workspace|project|customer)([\s_-]?id)?\b/gi;
|
|
230
|
+
const IDENTIFIER_ONLY_RE = /^[A-Za-z0-9_-]+$/;
|
|
231
|
+
const UNQUOTED_TERMINATOR = /[\s,;)\]}]/;
|
|
232
|
+
|
|
233
|
+
function isPrefixedAccount(value: string): boolean {
|
|
234
|
+
PREFIXED_ACCOUNT_RE.lastIndex = 0;
|
|
235
|
+
const m = PREFIXED_ACCOUNT_RE.exec(value);
|
|
236
|
+
PREFIXED_ACCOUNT_RE.lastIndex = 0;
|
|
237
|
+
return m?.[0] === value;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* A retained URL path keeps an error message diagnosable, but its segments are
|
|
242
|
+
* provider-controlled: `/users/<uuid>` or `/orgs/acct_…` carries an account
|
|
243
|
+
* identifier past the host rewrite. Replace whole segments that are an
|
|
244
|
+
* identifier shape; leave route names alone.
|
|
245
|
+
*/
|
|
246
|
+
function scrubUrlPath(pathname: string): string {
|
|
247
|
+
return pathname
|
|
248
|
+
.split("/")
|
|
249
|
+
.map((segment) => {
|
|
250
|
+
if (!segment) return segment;
|
|
251
|
+
// Decode first, then re-split: an encoded slash (`%2F`) means the
|
|
252
|
+
// segment is itself a path, and matching only whole segments let
|
|
253
|
+
// `/u/%2F<uuid>` through. Decoding repeats to a fixed point because a
|
|
254
|
+
// single pass left `%252D` reversible.
|
|
255
|
+
const decoded = decodeToFixedPoint(segment);
|
|
256
|
+
if (isIdentifierShape(decoded)) return "[account]";
|
|
257
|
+
// Do NOT enumerate delimiters. Chasing them cost four rounds of review:
|
|
258
|
+
// first `/`, then `?#&=`, then `:` action suffixes and `;` matrix
|
|
259
|
+
// parameters would have been next. Instead, find identifier shapes
|
|
260
|
+
// wherever they appear in the decoded text; whatever separates them is
|
|
261
|
+
// irrelevant.
|
|
262
|
+
const replaced = redactIdentifiersInText(decoded);
|
|
263
|
+
return replaced === decoded ? segment : replaced;
|
|
264
|
+
})
|
|
265
|
+
.join("/");
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function isIdentifierShape(value: string): boolean {
|
|
269
|
+
return UUID_RE.test(value) || isPrefixedAccount(value);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const UUID_ANYWHERE_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/gi;
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Replace identifier shapes anywhere in a decoded path, independent of the
|
|
276
|
+
* punctuation around them. Delimiter enumeration is a losing game: colon
|
|
277
|
+
* action suffixes (`/ops/<uuid>:cancel`) and matrix parameters
|
|
278
|
+
* (`<uuid>;retry=1`) are both ordinary provider API syntax, and the next
|
|
279
|
+
* unlisted separator would leak again.
|
|
280
|
+
*/
|
|
281
|
+
function redactIdentifiersInText(value: string): string {
|
|
282
|
+
return value.replace(UUID_ANYWHERE_RE, "[account]").replace(PREFIXED_ACCOUNT_RE, "[account]");
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** Percent-decode until the value stops changing, bounded against a decode bomb. */
|
|
286
|
+
function decodeToFixedPoint(value: string): string {
|
|
287
|
+
let current = value;
|
|
288
|
+
for (let pass = 0; pass < 6 && current.includes("%"); pass += 1) {
|
|
289
|
+
let next: string;
|
|
290
|
+
try {
|
|
291
|
+
next = decodeURIComponent(current);
|
|
292
|
+
} catch {
|
|
293
|
+
return current; // malformed escapes: treat what we have as opaque
|
|
294
|
+
}
|
|
295
|
+
if (next === current) break;
|
|
296
|
+
current = next;
|
|
297
|
+
}
|
|
298
|
+
return current;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** Valid IPv4 dotted quad: every octet in range and canonically written. */
|
|
302
|
+
function isIpv4(value: string): boolean {
|
|
303
|
+
const parts = value.split(".");
|
|
304
|
+
if (parts.length !== 4) return false;
|
|
305
|
+
return parts.every((p) => p.length >= 1 && p.length <= 3 && /^\d+$/.test(p) && Number(p) <= 255);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** Full IPv6 validation, including `::` compression and a trailing mapped IPv4. */
|
|
309
|
+
function isIpv6(value: string): boolean {
|
|
310
|
+
if (!value.includes(":")) return false;
|
|
311
|
+
let head = value;
|
|
312
|
+
let tailGroups = 0;
|
|
313
|
+
const lastColon = head.lastIndexOf(":");
|
|
314
|
+
const afterLastColon = head.slice(lastColon + 1);
|
|
315
|
+
if (afterLastColon.includes(".")) {
|
|
316
|
+
if (!isIpv4(afterLastColon)) return false;
|
|
317
|
+
head = head.slice(0, lastColon + 1);
|
|
318
|
+
tailGroups = 2;
|
|
319
|
+
} else if (head.includes(".")) {
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
const doubleColons = head.split("::").length - 1;
|
|
323
|
+
if (doubleColons > 1) return false;
|
|
324
|
+
const compressed = doubleColons === 1;
|
|
325
|
+
const [leftRaw = "", rightRaw = ""] = compressed ? head.split("::") : [head, ""];
|
|
326
|
+
const splitGroups = (part: string): string[] | null => {
|
|
327
|
+
if (part === "" || part === ":") return [];
|
|
328
|
+
const trimmed = part.replace(/^:|:$/g, "");
|
|
329
|
+
if (trimmed === "") return [];
|
|
330
|
+
const groups = trimmed.split(":");
|
|
331
|
+
return groups.every((g) => /^[0-9A-Fa-f]{1,4}$/.test(g)) ? groups : null;
|
|
332
|
+
};
|
|
333
|
+
const left = splitGroups(leftRaw);
|
|
334
|
+
const right = compressed ? splitGroups(rightRaw) : [];
|
|
335
|
+
if (left === null || right === null) return false;
|
|
336
|
+
const total = left.length + right.length + tailGroups;
|
|
337
|
+
return compressed ? total <= 7 : total === 8;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Two-stage IPv6 replacement: scan the address alphabet, validate the base,
|
|
342
|
+
* then extend the replaced span across an optional `%zone`. A single-alphabet
|
|
343
|
+
* scan would leave `fe80::1%en0` as `[ip]%en0`.
|
|
344
|
+
*/
|
|
345
|
+
function redactIpv6(value: string): string {
|
|
346
|
+
let out = "";
|
|
347
|
+
let i = 0;
|
|
348
|
+
while (i < value.length) {
|
|
349
|
+
const ch = value[i]!;
|
|
350
|
+
if (!/[0-9A-Fa-f:.]/.test(ch)) {
|
|
351
|
+
out += ch;
|
|
352
|
+
i += 1;
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
let end = i;
|
|
356
|
+
while (end < value.length && /[0-9A-Fa-f:.]/.test(value[end]!) && end - i < 45) end += 1;
|
|
357
|
+
// The candidate alphabet includes `.` for IPv4-mapped forms, so a trailing
|
|
358
|
+
// sentence period is swallowed too and `2001:db8::1.` failed validation as
|
|
359
|
+
// a whole. Retry on progressively shorter prefixes so ordinary punctuation
|
|
360
|
+
// after an address cannot defeat the rule.
|
|
361
|
+
let matched = 0;
|
|
362
|
+
for (let stop = end; stop > i; stop -= 1) {
|
|
363
|
+
// A candidate may not end on `.` — that is sentence punctuation after an
|
|
364
|
+
// address. It MAY end on `:`, because `2001:db8::` and `fe80::` are valid
|
|
365
|
+
// compressed forms; rejecting every trailing colon skipped them entirely.
|
|
366
|
+
// `isIpv6` is the arbiter, so a stray single trailing colon simply fails
|
|
367
|
+
// validation and the loop backs off one more character.
|
|
368
|
+
if (value[stop - 1] === ".") continue;
|
|
369
|
+
if (isIpv6(value.slice(i, stop))) {
|
|
370
|
+
matched = stop;
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (matched > i) {
|
|
375
|
+
let zoneEnd = matched;
|
|
376
|
+
if (value[zoneEnd] === "%") {
|
|
377
|
+
let z = zoneEnd + 1;
|
|
378
|
+
while (z < value.length && /[A-Za-z0-9_.-]/.test(value[z]!) && z - zoneEnd <= 64) z += 1;
|
|
379
|
+
if (z > zoneEnd + 1) zoneEnd = z;
|
|
380
|
+
}
|
|
381
|
+
out += "[ip]";
|
|
382
|
+
i = zoneEnd;
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
out += value.slice(i, end);
|
|
386
|
+
i = end;
|
|
387
|
+
}
|
|
388
|
+
return out;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/** Scan a delimited span (UNC path, non-HTTP URI) and replace it whole. */
|
|
392
|
+
function redactScannedSpans(value: string, start: RegExp, token: string, accept?: (span: string) => boolean): string {
|
|
393
|
+
let out = "";
|
|
394
|
+
let i = 0;
|
|
395
|
+
while (i < value.length) {
|
|
396
|
+
start.lastIndex = i;
|
|
397
|
+
const m = start.exec(value);
|
|
398
|
+
if (!m) {
|
|
399
|
+
out += value.slice(i);
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
let end = m.index + m[0].length;
|
|
403
|
+
while (end < value.length && !/[\s"']/.test(value[end]!)) end += 1;
|
|
404
|
+
const span = value.slice(m.index, end);
|
|
405
|
+
out += value.slice(i, m.index);
|
|
406
|
+
out += accept && !accept(span) ? span : token;
|
|
407
|
+
i = end;
|
|
408
|
+
}
|
|
409
|
+
return out;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Account identifiers under a label. The value span is resolved from its
|
|
414
|
+
* syntax (quote pair, or run to a terminator) BEFORE it is validated, so an
|
|
415
|
+
* out-of-grammar character or a missing closing quote leaves the text
|
|
416
|
+
* untouched instead of producing a partially redacted prefix.
|
|
417
|
+
*
|
|
418
|
+
* ID-bearing labels (`user_id`, `accountId`, ...) redact any value of six or
|
|
419
|
+
* more characters. Bare labels (`user`, `account`, ...) redact only an
|
|
420
|
+
* unambiguous account shape, because `org: engineering` is ordinary prose.
|
|
421
|
+
*/
|
|
422
|
+
function redactContextualAccounts(value: string): string {
|
|
423
|
+
let out = "";
|
|
424
|
+
let cursor = 0;
|
|
425
|
+
ACCOUNT_LABEL_RE.lastIndex = 0;
|
|
426
|
+
let label: RegExpExecArray | null;
|
|
427
|
+
while ((label = ACCOUNT_LABEL_RE.exec(value)) !== null) {
|
|
428
|
+
if (label.index < cursor) continue;
|
|
429
|
+
const idBearing = Boolean(label[1]);
|
|
430
|
+
let p = label.index + label[0].length;
|
|
431
|
+
// A JSON field name is quoted: `"userId": "v"`. Step over the label's own
|
|
432
|
+
// closing quote before looking for the separator.
|
|
433
|
+
if (value[p] === '"' || value[p] === "'") p += 1;
|
|
434
|
+
while (p < value.length && /\s/.test(value[p]!)) p += 1;
|
|
435
|
+
if (value[p] !== "=" && value[p] !== ":") continue;
|
|
436
|
+
p += 1;
|
|
437
|
+
while (p < value.length && /\s/.test(value[p]!)) p += 1;
|
|
438
|
+
const quote = value[p] === '"' || value[p] === "'" ? value[p]! : "";
|
|
439
|
+
let spanStart = quote ? p + 1 : p;
|
|
440
|
+
let spanEnd: number;
|
|
441
|
+
if (quote) {
|
|
442
|
+
const close = value.indexOf(quote, spanStart);
|
|
443
|
+
if (close === -1) continue;
|
|
444
|
+
spanEnd = close;
|
|
445
|
+
} else {
|
|
446
|
+
spanEnd = spanStart;
|
|
447
|
+
while (spanEnd < value.length && !UNQUOTED_TERMINATOR.test(value[spanEnd]!)) spanEnd += 1;
|
|
448
|
+
}
|
|
449
|
+
const span = value.slice(spanStart, spanEnd);
|
|
450
|
+
if (!span || !IDENTIFIER_ONLY_RE.test(span)) continue;
|
|
451
|
+
const redact = idBearing ? span.length >= 6 : isPrefixedAccount(span) || UUID_RE.test(span);
|
|
452
|
+
if (!redact) continue;
|
|
453
|
+
out += value.slice(cursor, spanStart) + "[account]";
|
|
454
|
+
cursor = spanEnd;
|
|
455
|
+
ACCOUNT_LABEL_RE.lastIndex = spanEnd;
|
|
456
|
+
}
|
|
457
|
+
out += value.slice(cursor);
|
|
458
|
+
return out;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function scrubString(value: string): string {
|
|
462
|
+
// 1. Existing secret redaction.
|
|
463
|
+
let s = redactSecretString(value);
|
|
464
|
+
s = s.replace(SECRETISH_GLOBAL, "[REDACTED]");
|
|
465
|
+
// 2. Filesystem paths, including UNC shares.
|
|
466
|
+
s = s.replace(/(?:[A-Za-z]:\\|\/(?:home|Users|tmp|var|etc|root|mnt)\/)[^\s"']+/g, "[path]");
|
|
467
|
+
s = redactScannedSpans(s, /\\\\[^\\\s"']{1,255}\\/g, "[path]");
|
|
468
|
+
// 3. HTTP(S) URLs. The retained pathname keeps the diagnostic useful, so its
|
|
469
|
+
// segments are scrubbed of identifier shapes rather than trusted: a
|
|
470
|
+
// provider error citing `/users/<uuid>` would otherwise persist an
|
|
471
|
+
// account id that the host rewrite alone does not touch.
|
|
472
|
+
s = s.replace(/https?:\/\/[^\s"']+/gi, (url) => {
|
|
473
|
+
try {
|
|
474
|
+
const u = new URL(url);
|
|
475
|
+
if (u.username || u.password) return "[redacted-url]";
|
|
476
|
+
const path = scrubUrlPath(u.pathname);
|
|
477
|
+
if (/^(localhost|127\.|10\.|192\.168\.|172\.(1[6-9]|2\d|3[0-1])\.)/i.test(u.hostname)) {
|
|
478
|
+
return `${u.protocol}//[private-host]${path}`;
|
|
479
|
+
}
|
|
480
|
+
return `${u.protocol}//[host]${path}`;
|
|
481
|
+
} catch {
|
|
482
|
+
return "[redacted-url]";
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
// 4. Other-scheme URIs, which may carry credentials. Refuses http(s) so it
|
|
486
|
+
// cannot re-match the output of rule 3.
|
|
487
|
+
s = redactScannedSpans(s, /\b[a-z][a-z0-9+.-]{1,15}:\/\//gi, "[uri]", (span) => !/^https?:\/\//i.test(span));
|
|
488
|
+
// 5-8. Bounded token shapes.
|
|
489
|
+
s = s.replace(JWT_RE, "[jwt]");
|
|
490
|
+
s = s.replace(EMAIL_RE, "[email]");
|
|
491
|
+
s = s.replace(PREFIXED_ACCOUNT_RE, "[account]");
|
|
492
|
+
s = redactContextualAccounts(s);
|
|
493
|
+
s = s.replace(MAC_RE, "[mac]");
|
|
494
|
+
// 9-10. Addresses: IPv6 first so mapped forms are replaced whole.
|
|
495
|
+
s = redactIpv6(s);
|
|
496
|
+
s = s.replace(IPV4_RE, (m) => (isIpv4(m) ? "[ip]" : m));
|
|
497
|
+
// 11. Multi-label hostnames, plus single-label names in a host-bearing context.
|
|
498
|
+
s = s.replace(HOSTNAME_RE, "[host]");
|
|
499
|
+
s = s.replace(STRONG_HOST_CONTEXT_RE, (m, tail: string) => {
|
|
500
|
+
// Scan the few tokens after the marker for the first host-shaped one:
|
|
501
|
+
// Go writes `dial tcp: lookup <host>: no such host`, so the destination
|
|
502
|
+
// is not always adjacent to the marker. A resolver marker also licenses a
|
|
503
|
+
// bare name (`ENOTFOUND redis`), which a socket-state marker does not.
|
|
504
|
+
// A name paired with a port is a destination whatever else is true:
|
|
505
|
+
// `dial tcp redis:6379` needs no other evidence. Both notations count —
|
|
506
|
+
// adjacent `host:443` and spelled-out `gateway on port 443` — because the
|
|
507
|
+
// port is the evidence, not the punctuation.
|
|
508
|
+
const ported =
|
|
509
|
+
tail.match(/(?<![\w.-])([A-Za-z0-9_.-]{1,255}):\d{1,5}(?![\w.])/) ??
|
|
510
|
+
tail.match(/(?<![\w.-])([A-Za-z0-9_.-]{1,255})\s+(?:on\s+)?port\s+\d{1,5}\b/i);
|
|
511
|
+
if (ported?.[1] && !PROSE_AFTER_MARKER.has(ported[1].toLowerCase())) {
|
|
512
|
+
return m.replace(ported[1], "[host]");
|
|
513
|
+
}
|
|
514
|
+
// Otherwise only a resolver marker licenses a bare name. Natural-language
|
|
515
|
+
// `connect to` does not: `Unable to connect to your account` is prose.
|
|
516
|
+
const resolver = /ENOTFOUND|EAI_AGAIN|lookup|host/i.test(m);
|
|
517
|
+
for (const token of tail.split(/[\s:]+/)) {
|
|
518
|
+
if (token && isHostCandidate(token, resolver)) return m.replace(token, "[host]");
|
|
519
|
+
}
|
|
520
|
+
return m;
|
|
521
|
+
});
|
|
522
|
+
s = s.replace(WEAK_HOST_CONTEXT_RE, (m, name: string) =>
|
|
523
|
+
CONTEXTUAL_HOST_TOKEN_RE.test(name) && !DOTTED_NAMESPACE_RE.test(name) ? m.replace(name, "[host]") : m,
|
|
524
|
+
);
|
|
525
|
+
const bytes = new TextEncoder().encode(s);
|
|
526
|
+
if (bytes.byteLength > MAX_SANITIZED_STRING_FIELD) {
|
|
527
|
+
return new TextDecoder().decode(bytes.slice(0, MAX_SANITIZED_STRING_FIELD));
|
|
528
|
+
}
|
|
529
|
+
return s;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const TRUNCATION_MARKERS = [
|
|
533
|
+
"[REDACTED]",
|
|
534
|
+
"[redacted-url]",
|
|
535
|
+
"[private-host]",
|
|
536
|
+
"[account]",
|
|
537
|
+
"[email]",
|
|
538
|
+
"[host]",
|
|
539
|
+
"[path]",
|
|
540
|
+
"[uri]",
|
|
541
|
+
"[jwt]",
|
|
542
|
+
"[mac]",
|
|
543
|
+
"[ip]",
|
|
544
|
+
];
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Byte-bounded truncation for persisted summaries.
|
|
548
|
+
*
|
|
549
|
+
* `String.prototype.slice` counts UTF-16 code units, so it can split a
|
|
550
|
+
* surrogate pair or leave a partial `[em` fragment that a reader could mistake
|
|
551
|
+
* for literal content. Bytes are the right unit because
|
|
552
|
+
* `enforceEventStructureLimits` also measures encoded byte length.
|
|
553
|
+
*/
|
|
554
|
+
export function truncateUtf8(value: string, maxBytes: number): string {
|
|
555
|
+
const encoder = new TextEncoder();
|
|
556
|
+
if (encoder.encode(value).byteLength <= maxBytes) return value;
|
|
557
|
+
let cut = 0;
|
|
558
|
+
let used = 0;
|
|
559
|
+
for (const char of value) {
|
|
560
|
+
const size = encoder.encode(char).byteLength;
|
|
561
|
+
if (used + size > maxBytes) break;
|
|
562
|
+
used += size;
|
|
563
|
+
cut += char.length;
|
|
564
|
+
}
|
|
565
|
+
// If the cut lands strictly inside a marker, drop the whole marker: a
|
|
566
|
+
// fragment such as `[accoun` reads as literal content. Sanitization has
|
|
567
|
+
// already replaced the raw value, so the marker's opening bracket is a safe
|
|
568
|
+
// cut point and everything before it is intact.
|
|
569
|
+
const open = value.lastIndexOf("[", cut - 1);
|
|
570
|
+
if (open !== -1) {
|
|
571
|
+
const tail = value.slice(open, cut);
|
|
572
|
+
for (const marker of TRUNCATION_MARKERS) {
|
|
573
|
+
if (tail.length < marker.length && marker.startsWith(tail)) return value.slice(0, open);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
return value.slice(0, cut);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/** Stable privacy boundary for diagnostic text that may be persisted. */
|
|
580
|
+
export function sanitizeDiagnostic(value: unknown): string {
|
|
581
|
+
return scrubString(value instanceof Error ? value.message : String(value));
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
export function sanitizedJsonBytes(value: unknown): Uint8Array {
|
|
585
|
+
return new TextEncoder().encode(jcsStringify(scrubValue(value, 0)));
|
|
586
|
+
}
|