opencode-rehydar 0.14.2 → 0.14.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.d.ts +54 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +74 -0
- package/dist/browser.js.map +1 -0
- package/dist/core/anonymizer.d.ts +309 -0
- package/dist/core/anonymizer.d.ts.map +1 -0
- package/dist/core/anonymizer.js +555 -0
- package/dist/core/anonymizer.js.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +6 -0
- package/dist/core/index.js.map +1 -0
- package/dist/crypto/index.d.ts +6 -0
- package/dist/crypto/index.d.ts.map +1 -0
- package/dist/crypto/index.js +6 -0
- package/dist/crypto/index.js.map +1 -0
- package/dist/crypto/pii-map-crypto.d.ts +114 -0
- package/dist/crypto/pii-map-crypto.d.ts.map +1 -0
- package/dist/crypto/pii-map-crypto.js +228 -0
- package/dist/crypto/pii-map-crypto.js.map +1 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +72 -0
- package/dist/index.js.map +1 -0
- package/dist/ner/bio-decoder.d.ts +64 -0
- package/dist/ner/bio-decoder.d.ts.map +1 -0
- package/dist/ner/bio-decoder.js +218 -0
- package/dist/ner/bio-decoder.js.map +1 -0
- package/dist/ner/index.d.ts +12 -0
- package/dist/ner/index.d.ts.map +1 -0
- package/dist/ner/index.js +12 -0
- package/dist/ner/index.js.map +1 -0
- package/dist/ner/inference-server-client.d.ts +81 -0
- package/dist/ner/inference-server-client.d.ts.map +1 -0
- package/dist/ner/inference-server-client.js +120 -0
- package/dist/ner/inference-server-client.js.map +1 -0
- package/dist/ner/inference-server-ner-model.d.ts +43 -0
- package/dist/ner/inference-server-ner-model.d.ts.map +1 -0
- package/dist/ner/inference-server-ner-model.js +119 -0
- package/dist/ner/inference-server-ner-model.js.map +1 -0
- package/dist/ner/model-manager.d.ts +111 -0
- package/dist/ner/model-manager.d.ts.map +1 -0
- package/dist/ner/model-manager.js +334 -0
- package/dist/ner/model-manager.js.map +1 -0
- package/dist/ner/ner-model.d.ts +172 -0
- package/dist/ner/ner-model.d.ts.map +1 -0
- package/dist/ner/ner-model.js +514 -0
- package/dist/ner/ner-model.js.map +1 -0
- package/dist/ner/onnx-runtime.browser.d.ts +46 -0
- package/dist/ner/onnx-runtime.browser.d.ts.map +1 -0
- package/dist/ner/onnx-runtime.browser.js +90 -0
- package/dist/ner/onnx-runtime.browser.js.map +1 -0
- package/dist/ner/onnx-runtime.d.ts +72 -0
- package/dist/ner/onnx-runtime.d.ts.map +1 -0
- package/dist/ner/onnx-runtime.js +132 -0
- package/dist/ner/onnx-runtime.js.map +1 -0
- package/dist/ner/token-window.d.ts +19 -0
- package/dist/ner/token-window.d.ts.map +1 -0
- package/dist/ner/token-window.js +101 -0
- package/dist/ner/token-window.js.map +1 -0
- package/dist/ner/tokenizer.d.ts +142 -0
- package/dist/ner/tokenizer.d.ts.map +1 -0
- package/dist/ner/tokenizer.js +362 -0
- package/dist/ner/tokenizer.js.map +1 -0
- package/dist/opencode-plugin/git-identity.d.ts +3 -0
- package/dist/opencode-plugin/git-identity.d.ts.map +1 -0
- package/dist/opencode-plugin/git-identity.js +37 -0
- package/dist/opencode-plugin/git-identity.js.map +1 -0
- package/dist/opencode-plugin/github-username.d.ts +3 -0
- package/dist/opencode-plugin/github-username.d.ts.map +1 -0
- package/dist/opencode-plugin/github-username.js +41 -0
- package/dist/opencode-plugin/github-username.js.map +1 -0
- package/dist/opencode-plugin/identity-recognizer.d.ts +5 -0
- package/dist/opencode-plugin/identity-recognizer.d.ts.map +1 -0
- package/dist/opencode-plugin/identity-recognizer.js +21 -0
- package/dist/opencode-plugin/identity-recognizer.js.map +1 -0
- package/dist/opencode-plugin/index.d.ts +7 -0
- package/dist/opencode-plugin/index.d.ts.map +1 -0
- package/dist/opencode-plugin/index.js +6 -0
- package/dist/opencode-plugin/index.js.map +1 -0
- package/dist/opencode-plugin/plugin.d.ts +39 -0
- package/dist/opencode-plugin/plugin.d.ts.map +1 -0
- package/dist/opencode-plugin/plugin.js +294 -0
- package/dist/opencode-plugin/plugin.js.map +1 -0
- package/dist/opencode-plugin/types.d.ts +33 -0
- package/dist/opencode-plugin/types.d.ts.map +1 -0
- package/dist/opencode-plugin/types.js +5 -0
- package/dist/opencode-plugin/types.js.map +1 -0
- package/dist/opencode-plugin/v2/anon-state.d.ts +12 -0
- package/dist/opencode-plugin/v2/anon-state.d.ts.map +1 -0
- package/dist/opencode-plugin/v2/anon-state.js +14 -0
- package/dist/opencode-plugin/v2/anon-state.js.map +1 -0
- package/dist/opencode-plugin/v2/deep-rehydrate.d.ts +13 -0
- package/dist/opencode-plugin/v2/deep-rehydrate.d.ts.map +1 -0
- package/dist/opencode-plugin/v2/deep-rehydrate.js +33 -0
- package/dist/opencode-plugin/v2/deep-rehydrate.js.map +1 -0
- package/dist/opencode-plugin/v2/index.d.ts +17 -0
- package/dist/opencode-plugin/v2/index.d.ts.map +1 -0
- package/dist/opencode-plugin/v2/index.js +17 -0
- package/dist/opencode-plugin/v2/index.js.map +1 -0
- package/dist/opencode-plugin/v2/logger.d.ts +24 -0
- package/dist/opencode-plugin/v2/logger.d.ts.map +1 -0
- package/dist/opencode-plugin/v2/logger.js +73 -0
- package/dist/opencode-plugin/v2/logger.js.map +1 -0
- package/dist/opencode-plugin/v2/options.d.ts +63 -0
- package/dist/opencode-plugin/v2/options.d.ts.map +1 -0
- package/dist/opencode-plugin/v2/options.js +183 -0
- package/dist/opencode-plugin/v2/options.js.map +1 -0
- package/dist/opencode-plugin/v2/register-hooks.d.ts +43 -0
- package/dist/opencode-plugin/v2/register-hooks.d.ts.map +1 -0
- package/dist/opencode-plugin/v2/register-hooks.js +160 -0
- package/dist/opencode-plugin/v2/register-hooks.js.map +1 -0
- package/dist/opencode-plugin/v2/rehydrate-tail.d.ts +101 -0
- package/dist/opencode-plugin/v2/rehydrate-tail.d.ts.map +1 -0
- package/dist/opencode-plugin/v2/rehydrate-tail.js +147 -0
- package/dist/opencode-plugin/v2/rehydrate-tail.js.map +1 -0
- package/dist/opencode-plugin/v2/response-rehydrate.d.ts +78 -0
- package/dist/opencode-plugin/v2/response-rehydrate.d.ts.map +1 -0
- package/dist/opencode-plugin/v2/response-rehydrate.js +210 -0
- package/dist/opencode-plugin/v2/response-rehydrate.js.map +1 -0
- package/dist/opencode-plugin/v2/scrub-messages.d.ts +39 -0
- package/dist/opencode-plugin/v2/scrub-messages.d.ts.map +1 -0
- package/dist/opencode-plugin/v2/scrub-messages.js +260 -0
- package/dist/opencode-plugin/v2/scrub-messages.js.map +1 -0
- package/dist/opencode-plugin/v2/sessions.d.ts +17 -0
- package/dist/opencode-plugin/v2/sessions.d.ts.map +1 -0
- package/dist/opencode-plugin/v2/sessions.js +23 -0
- package/dist/opencode-plugin/v2/sessions.js.map +1 -0
- package/dist/opencode-plugin/v2/sse.d.ts +101 -0
- package/dist/opencode-plugin/v2/sse.d.ts.map +1 -0
- package/dist/opencode-plugin/v2/sse.js +235 -0
- package/dist/opencode-plugin/v2/sse.js.map +1 -0
- package/dist/opencode-plugin/v2/types.d.ts +188 -0
- package/dist/opencode-plugin/v2/types.d.ts.map +1 -0
- package/dist/opencode-plugin/v2/types.js +10 -0
- package/dist/opencode-plugin/v2/types.js.map +1 -0
- package/dist/opencode-plugin/vcs-command.d.ts +3 -0
- package/dist/opencode-plugin/vcs-command.d.ts.map +1 -0
- package/dist/opencode-plugin/vcs-command.js +34 -0
- package/dist/opencode-plugin/vcs-command.js.map +1 -0
- package/dist/pipeline/index.d.ts +12 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/pipeline/index.js +12 -0
- package/dist/pipeline/index.js.map +1 -0
- package/dist/pipeline/prenormalize.d.ts +48 -0
- package/dist/pipeline/prenormalize.d.ts.map +1 -0
- package/dist/pipeline/prenormalize.js +94 -0
- package/dist/pipeline/prenormalize.js.map +1 -0
- package/dist/pipeline/resolver.d.ts +56 -0
- package/dist/pipeline/resolver.d.ts.map +1 -0
- package/dist/pipeline/resolver.js +239 -0
- package/dist/pipeline/resolver.js.map +1 -0
- package/dist/pipeline/semantic-data-loader.d.ts +171 -0
- package/dist/pipeline/semantic-data-loader.d.ts.map +1 -0
- package/dist/pipeline/semantic-data-loader.js +677 -0
- package/dist/pipeline/semantic-data-loader.js.map +1 -0
- package/dist/pipeline/semantic-enricher.d.ts +112 -0
- package/dist/pipeline/semantic-enricher.d.ts.map +1 -0
- package/dist/pipeline/semantic-enricher.js +322 -0
- package/dist/pipeline/semantic-enricher.js.map +1 -0
- package/dist/pipeline/tagger.d.ts +121 -0
- package/dist/pipeline/tagger.d.ts.map +1 -0
- package/dist/pipeline/tagger.js +545 -0
- package/dist/pipeline/tagger.js.map +1 -0
- package/dist/pipeline/title-extractor.d.ts +79 -0
- package/dist/pipeline/title-extractor.d.ts.map +1 -0
- package/dist/pipeline/title-extractor.js +801 -0
- package/dist/pipeline/title-extractor.js.map +1 -0
- package/dist/pipeline/validator.d.ts +65 -0
- package/dist/pipeline/validator.d.ts.map +1 -0
- package/dist/pipeline/validator.js +292 -0
- package/dist/pipeline/validator.js.map +1 -0
- package/dist/proxy/index.d.ts +13 -0
- package/dist/proxy/index.d.ts.map +1 -0
- package/dist/proxy/index.js +12 -0
- package/dist/proxy/index.js.map +1 -0
- package/dist/proxy/providers/anthropic.d.ts +28 -0
- package/dist/proxy/providers/anthropic.d.ts.map +1 -0
- package/dist/proxy/providers/anthropic.js +265 -0
- package/dist/proxy/providers/anthropic.js.map +1 -0
- package/dist/proxy/providers/index.d.ts +19 -0
- package/dist/proxy/providers/index.d.ts.map +1 -0
- package/dist/proxy/providers/index.js +41 -0
- package/dist/proxy/providers/index.js.map +1 -0
- package/dist/proxy/providers/openai.d.ts +27 -0
- package/dist/proxy/providers/openai.d.ts.map +1 -0
- package/dist/proxy/providers/openai.js +203 -0
- package/dist/proxy/providers/openai.js.map +1 -0
- package/dist/proxy/providers/streaming-tool-loop.d.ts +12 -0
- package/dist/proxy/providers/streaming-tool-loop.d.ts.map +1 -0
- package/dist/proxy/providers/streaming-tool-loop.js +223 -0
- package/dist/proxy/providers/streaming-tool-loop.js.map +1 -0
- package/dist/proxy/providers/types.d.ts +109 -0
- package/dist/proxy/providers/types.d.ts.map +1 -0
- package/dist/proxy/providers/types.js +2 -0
- package/dist/proxy/providers/types.js.map +1 -0
- package/dist/proxy/proxy-server.d.ts +61 -0
- package/dist/proxy/proxy-server.d.ts.map +1 -0
- package/dist/proxy/proxy-server.js +146 -0
- package/dist/proxy/proxy-server.js.map +1 -0
- package/dist/proxy/rehydra-fetch.d.ts +35 -0
- package/dist/proxy/rehydra-fetch.d.ts.map +1 -0
- package/dist/proxy/rehydra-fetch.js +631 -0
- package/dist/proxy/rehydra-fetch.js.map +1 -0
- package/dist/proxy/rehydra-proxy.d.ts +40 -0
- package/dist/proxy/rehydra-proxy.d.ts.map +1 -0
- package/dist/proxy/rehydra-proxy.js +150 -0
- package/dist/proxy/rehydra-proxy.js.map +1 -0
- package/dist/proxy/sse-parser.d.ts +59 -0
- package/dist/proxy/sse-parser.d.ts.map +1 -0
- package/dist/proxy/sse-parser.js +112 -0
- package/dist/proxy/sse-parser.js.map +1 -0
- package/dist/proxy/system-instruction.d.ts +19 -0
- package/dist/proxy/system-instruction.d.ts.map +1 -0
- package/dist/proxy/system-instruction.js +33 -0
- package/dist/proxy/system-instruction.js.map +1 -0
- package/dist/proxy/tool-loop-stream.d.ts +7 -0
- package/dist/proxy/tool-loop-stream.d.ts.map +1 -0
- package/dist/proxy/tool-loop-stream.js +65 -0
- package/dist/proxy/tool-loop-stream.js.map +1 -0
- package/dist/proxy/types.d.ts +138 -0
- package/dist/proxy/types.d.ts.map +1 -0
- package/dist/proxy/types.js +5 -0
- package/dist/proxy/types.js.map +1 -0
- package/dist/proxy/wrap-client.d.ts +47 -0
- package/dist/proxy/wrap-client.d.ts.map +1 -0
- package/dist/proxy/wrap-client.js +70 -0
- package/dist/proxy/wrap-client.js.map +1 -0
- package/dist/recognizers/address.d.ts +2 -0
- package/dist/recognizers/address.d.ts.map +1 -0
- package/dist/recognizers/address.js +23 -0
- package/dist/recognizers/address.js.map +1 -0
- package/dist/recognizers/base.d.ts +78 -0
- package/dist/recognizers/base.d.ts.map +1 -0
- package/dist/recognizers/base.js +100 -0
- package/dist/recognizers/base.js.map +1 -0
- package/dist/recognizers/bic-swift.d.ts +10 -0
- package/dist/recognizers/bic-swift.d.ts.map +1 -0
- package/dist/recognizers/bic-swift.js +107 -0
- package/dist/recognizers/bic-swift.js.map +1 -0
- package/dist/recognizers/credit-card.d.ts +32 -0
- package/dist/recognizers/credit-card.d.ts.map +1 -0
- package/dist/recognizers/credit-card.js +160 -0
- package/dist/recognizers/credit-card.js.map +1 -0
- package/dist/recognizers/custom-id.d.ts +28 -0
- package/dist/recognizers/custom-id.d.ts.map +1 -0
- package/dist/recognizers/custom-id.js +116 -0
- package/dist/recognizers/custom-id.js.map +1 -0
- package/dist/recognizers/date.d.ts +10 -0
- package/dist/recognizers/date.d.ts.map +1 -0
- package/dist/recognizers/date.js +228 -0
- package/dist/recognizers/date.js.map +1 -0
- package/dist/recognizers/email.d.ts +10 -0
- package/dist/recognizers/email.d.ts.map +1 -0
- package/dist/recognizers/email.js +75 -0
- package/dist/recognizers/email.js.map +1 -0
- package/dist/recognizers/iban.d.ts +14 -0
- package/dist/recognizers/iban.d.ts.map +1 -0
- package/dist/recognizers/iban.js +67 -0
- package/dist/recognizers/iban.js.map +1 -0
- package/dist/recognizers/index.d.ts +25 -0
- package/dist/recognizers/index.d.ts.map +1 -0
- package/dist/recognizers/index.js +53 -0
- package/dist/recognizers/index.js.map +1 -0
- package/dist/recognizers/ip-address.d.ts +14 -0
- package/dist/recognizers/ip-address.d.ts.map +1 -0
- package/dist/recognizers/ip-address.js +183 -0
- package/dist/recognizers/ip-address.js.map +1 -0
- package/dist/recognizers/phone.d.ts +10 -0
- package/dist/recognizers/phone.d.ts.map +1 -0
- package/dist/recognizers/phone.js +145 -0
- package/dist/recognizers/phone.js.map +1 -0
- package/dist/recognizers/postal-code.d.ts +16 -0
- package/dist/recognizers/postal-code.d.ts.map +1 -0
- package/dist/recognizers/postal-code.js +42 -0
- package/dist/recognizers/postal-code.js.map +1 -0
- package/dist/recognizers/registry.d.ts +59 -0
- package/dist/recognizers/registry.d.ts.map +1 -0
- package/dist/recognizers/registry.js +113 -0
- package/dist/recognizers/registry.js.map +1 -0
- package/dist/recognizers/secrets/api-key.d.ts +6 -0
- package/dist/recognizers/secrets/api-key.d.ts.map +1 -0
- package/dist/recognizers/secrets/api-key.js +40 -0
- package/dist/recognizers/secrets/api-key.js.map +1 -0
- package/dist/recognizers/secrets/aws-credentials.d.ts +7 -0
- package/dist/recognizers/secrets/aws-credentials.d.ts.map +1 -0
- package/dist/recognizers/secrets/aws-credentials.js +68 -0
- package/dist/recognizers/secrets/aws-credentials.js.map +1 -0
- package/dist/recognizers/secrets/config-secret.d.ts +8 -0
- package/dist/recognizers/secrets/config-secret.d.ts.map +1 -0
- package/dist/recognizers/secrets/config-secret.js +59 -0
- package/dist/recognizers/secrets/config-secret.js.map +1 -0
- package/dist/recognizers/secrets/connection-string.d.ts +6 -0
- package/dist/recognizers/secrets/connection-string.d.ts.map +1 -0
- package/dist/recognizers/secrets/connection-string.js +40 -0
- package/dist/recognizers/secrets/connection-string.js.map +1 -0
- package/dist/recognizers/secrets/env-var.d.ts +8 -0
- package/dist/recognizers/secrets/env-var.d.ts.map +1 -0
- package/dist/recognizers/secrets/env-var.js +54 -0
- package/dist/recognizers/secrets/env-var.js.map +1 -0
- package/dist/recognizers/secrets/index.d.ts +22 -0
- package/dist/recognizers/secrets/index.d.ts.map +1 -0
- package/dist/recognizers/secrets/index.js +41 -0
- package/dist/recognizers/secrets/index.js.map +1 -0
- package/dist/recognizers/secrets/jwt.d.ts +6 -0
- package/dist/recognizers/secrets/jwt.d.ts.map +1 -0
- package/dist/recognizers/secrets/jwt.js +47 -0
- package/dist/recognizers/secrets/jwt.js.map +1 -0
- package/dist/recognizers/secrets/key-patterns.d.ts +10 -0
- package/dist/recognizers/secrets/key-patterns.d.ts.map +1 -0
- package/dist/recognizers/secrets/key-patterns.js +15 -0
- package/dist/recognizers/secrets/key-patterns.js.map +1 -0
- package/dist/recognizers/secrets/literal-value.d.ts +10 -0
- package/dist/recognizers/secrets/literal-value.d.ts.map +1 -0
- package/dist/recognizers/secrets/literal-value.js +48 -0
- package/dist/recognizers/secrets/literal-value.js.map +1 -0
- package/dist/recognizers/secrets/private-key.d.ts +6 -0
- package/dist/recognizers/secrets/private-key.d.ts.map +1 -0
- package/dist/recognizers/secrets/private-key.js +16 -0
- package/dist/recognizers/secrets/private-key.js.map +1 -0
- package/dist/recognizers/url.d.ts +14 -0
- package/dist/recognizers/url.d.ts.map +1 -0
- package/dist/recognizers/url.js +121 -0
- package/dist/recognizers/url.js.map +1 -0
- package/dist/storage/browser.d.ts +13 -0
- package/dist/storage/browser.d.ts.map +1 -0
- package/dist/storage/browser.js +13 -0
- package/dist/storage/browser.js.map +1 -0
- package/dist/storage/in-memory.d.ts +60 -0
- package/dist/storage/in-memory.d.ts.map +1 -0
- package/dist/storage/in-memory.js +108 -0
- package/dist/storage/in-memory.js.map +1 -0
- package/dist/storage/index.d.ts +11 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +11 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/indexeddb.d.ts +69 -0
- package/dist/storage/indexeddb.d.ts.map +1 -0
- package/dist/storage/indexeddb.js +274 -0
- package/dist/storage/indexeddb.js.map +1 -0
- package/dist/storage/session-base.d.ts +34 -0
- package/dist/storage/session-base.d.ts.map +1 -0
- package/dist/storage/session-base.js +122 -0
- package/dist/storage/session-base.js.map +1 -0
- package/dist/storage/session.d.ts +19 -0
- package/dist/storage/session.d.ts.map +1 -0
- package/dist/storage/session.js +31 -0
- package/dist/storage/session.js.map +1 -0
- package/dist/storage/sqlite.d.ts +80 -0
- package/dist/storage/sqlite.d.ts.map +1 -0
- package/dist/storage/sqlite.js +255 -0
- package/dist/storage/sqlite.js.map +1 -0
- package/dist/storage/types.d.ts +208 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +6 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/streaming/anonymizer-stream.d.ts +63 -0
- package/dist/streaming/anonymizer-stream.d.ts.map +1 -0
- package/dist/streaming/anonymizer-stream.js +184 -0
- package/dist/streaming/anonymizer-stream.js.map +1 -0
- package/dist/streaming/index.d.ts +9 -0
- package/dist/streaming/index.d.ts.map +1 -0
- package/dist/streaming/index.js +8 -0
- package/dist/streaming/index.js.map +1 -0
- package/dist/streaming/sentence-buffer.d.ts +78 -0
- package/dist/streaming/sentence-buffer.d.ts.map +1 -0
- package/dist/streaming/sentence-buffer.js +239 -0
- package/dist/streaming/sentence-buffer.js.map +1 -0
- package/dist/streaming/stream-factory.d.ts +38 -0
- package/dist/streaming/stream-factory.d.ts.map +1 -0
- package/dist/streaming/stream-factory.js +69 -0
- package/dist/streaming/stream-factory.js.map +1 -0
- package/dist/streaming/types.d.ts +121 -0
- package/dist/streaming/types.d.ts.map +1 -0
- package/dist/streaming/types.js +5 -0
- package/dist/streaming/types.js.map +1 -0
- package/dist/types/index.d.ts +269 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +95 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/pii-types.d.ts +64 -0
- package/dist/types/pii-types.d.ts.map +1 -0
- package/dist/types/pii-types.js +153 -0
- package/dist/types/pii-types.js.map +1 -0
- package/dist/utils/iban-checksum.d.ts +23 -0
- package/dist/utils/iban-checksum.d.ts.map +1 -0
- package/dist/utils/iban-checksum.js +106 -0
- package/dist/utils/iban-checksum.js.map +1 -0
- package/dist/utils/index.d.ts +11 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +11 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/json-walk.d.ts +21 -0
- package/dist/utils/json-walk.d.ts.map +1 -0
- package/dist/utils/json-walk.js +44 -0
- package/dist/utils/json-walk.js.map +1 -0
- package/dist/utils/luhn.d.ts +17 -0
- package/dist/utils/luhn.d.ts.map +1 -0
- package/dist/utils/luhn.js +55 -0
- package/dist/utils/luhn.js.map +1 -0
- package/dist/utils/offsets.d.ts +86 -0
- package/dist/utils/offsets.d.ts.map +1 -0
- package/dist/utils/offsets.js +124 -0
- package/dist/utils/offsets.js.map +1 -0
- package/dist/utils/path.d.ts +34 -0
- package/dist/utils/path.d.ts.map +1 -0
- package/dist/utils/path.js +96 -0
- package/dist/utils/path.js.map +1 -0
- package/dist/utils/regex.d.ts +11 -0
- package/dist/utils/regex.d.ts.map +1 -0
- package/dist/utils/regex.js +14 -0
- package/dist/utils/regex.js.map +1 -0
- package/dist/utils/storage-browser.d.ts +51 -0
- package/dist/utils/storage-browser.d.ts.map +1 -0
- package/dist/utils/storage-browser.js +381 -0
- package/dist/utils/storage-browser.js.map +1 -0
- package/dist/utils/storage-node.d.ts +51 -0
- package/dist/utils/storage-node.d.ts.map +1 -0
- package/dist/utils/storage-node.js +153 -0
- package/dist/utils/storage-node.js.map +1 -0
- package/dist/utils/storage.browser.d.ts +75 -0
- package/dist/utils/storage.browser.d.ts.map +1 -0
- package/dist/utils/storage.browser.js +69 -0
- package/dist/utils/storage.browser.js.map +1 -0
- package/dist/utils/storage.d.ts +73 -0
- package/dist/utils/storage.d.ts.map +1 -0
- package/dist/utils/storage.js +69 -0
- package/dist/utils/storage.js.map +1 -0
- package/index.js +6 -4
- package/package.json +19 -5
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message scrubbing for OpenCode Plugin V2 session hooks.
|
|
3
|
+
*
|
|
4
|
+
* Walks the messages attached to a model request and anonymizes every string
|
|
5
|
+
* that can carry secrets: user/assistant/reasoning text, tool arguments,
|
|
6
|
+
* completed tool output, and V1-style part state.
|
|
7
|
+
*
|
|
8
|
+
* The scrub runs on a deep copy; only after it completes successfully are the
|
|
9
|
+
* results written back into the caller's original objects (preserving the
|
|
10
|
+
* references Plugin V2 relies on). If any step throws, the originals are left
|
|
11
|
+
* untouched and the error propagates so the caller can block the request
|
|
12
|
+
* instead of leaking raw PII (fail-closed).
|
|
13
|
+
*/
|
|
14
|
+
import { PIIType } from "../../types/index.js";
|
|
15
|
+
import { vcsCommandTypes } from "../vcs-command.js";
|
|
16
|
+
/** Reads a shell command from a tool input object, if present. */
|
|
17
|
+
function extractCommand(input) {
|
|
18
|
+
if (input === null || typeof input !== "object")
|
|
19
|
+
return undefined;
|
|
20
|
+
const command = input.command;
|
|
21
|
+
return typeof command === "string" ? command : undefined;
|
|
22
|
+
}
|
|
23
|
+
/** Resolves the session ID carried by a message, for diagnostic logging only. */
|
|
24
|
+
function messageSessionID(message) {
|
|
25
|
+
const info = message.info;
|
|
26
|
+
if (info !== undefined && info !== null && typeof info === "object") {
|
|
27
|
+
const sessionID = info.sessionID;
|
|
28
|
+
if (typeof sessionID === "string" && sessionID.length > 0)
|
|
29
|
+
return sessionID;
|
|
30
|
+
}
|
|
31
|
+
const sessionID = message.sessionID;
|
|
32
|
+
if (typeof sessionID === "string" && sessionID.length > 0)
|
|
33
|
+
return sessionID;
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
/** Deep-clones a JSON-like value (message payloads contain no class instances). */
|
|
37
|
+
function deepClone(value) {
|
|
38
|
+
if (Array.isArray(value)) {
|
|
39
|
+
const items = value;
|
|
40
|
+
return items.map((item) => deepClone(item));
|
|
41
|
+
}
|
|
42
|
+
if (value !== null && typeof value === "object") {
|
|
43
|
+
const source = value;
|
|
44
|
+
const out = {};
|
|
45
|
+
for (const [key, item] of Object.entries(source)) {
|
|
46
|
+
out[key] = deepClone(item);
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Copies the fields the scrubber mutates from a scrubbed part back onto the
|
|
54
|
+
* original part, keeping the original object identities intact.
|
|
55
|
+
*/
|
|
56
|
+
function restorePart(original, scrubbed) {
|
|
57
|
+
if (original === undefined ||
|
|
58
|
+
original === null ||
|
|
59
|
+
scrubbed === undefined ||
|
|
60
|
+
scrubbed === null) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
original.text = scrubbed.text;
|
|
64
|
+
original.input = scrubbed.input;
|
|
65
|
+
original.result = scrubbed.result;
|
|
66
|
+
restorePartState(original.state, scrubbed.state);
|
|
67
|
+
}
|
|
68
|
+
/** Copies mutable tool-state fields back onto the original part state. */
|
|
69
|
+
function restorePartState(original, scrubbed) {
|
|
70
|
+
if (original === undefined ||
|
|
71
|
+
original === null ||
|
|
72
|
+
scrubbed === undefined ||
|
|
73
|
+
scrubbed === null) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
original.input = scrubbed.input;
|
|
77
|
+
original.error = scrubbed.error;
|
|
78
|
+
original.output = scrubbed.output;
|
|
79
|
+
}
|
|
80
|
+
/** Applies scrub results from `source` back onto the original message parts. */
|
|
81
|
+
function restoreScrubbedParts(originalMessages, scrubbedMessages) {
|
|
82
|
+
for (let index = 0; index < originalMessages.length; index++) {
|
|
83
|
+
const original = originalMessages[index];
|
|
84
|
+
const scrubbed = scrubbedMessages[index];
|
|
85
|
+
if (original === undefined ||
|
|
86
|
+
original === null ||
|
|
87
|
+
scrubbed === undefined ||
|
|
88
|
+
scrubbed === null) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
restorePartArray(original.content, scrubbed.content);
|
|
92
|
+
restorePartArray(original.parts, scrubbed.parts);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function restorePartArray(originalParts, scrubbedParts) {
|
|
96
|
+
if (!Array.isArray(originalParts) || !Array.isArray(scrubbedParts)) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const length = Math.min(originalParts.length, scrubbedParts.length);
|
|
100
|
+
for (let index = 0; index < length; index++) {
|
|
101
|
+
restorePart(originalParts[index], scrubbedParts[index]);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Anonymizes the messages of a session request in place.
|
|
106
|
+
*
|
|
107
|
+
* @returns the number of entities removed and a per-type breakdown for logging.
|
|
108
|
+
*/
|
|
109
|
+
export async function scrubMessages(eventMessages, ctx, resolveSession, state, options) {
|
|
110
|
+
let scrubbed = 0;
|
|
111
|
+
const byType = {};
|
|
112
|
+
const record = (result) => {
|
|
113
|
+
if (result.stats.totalEntities <= 0)
|
|
114
|
+
return;
|
|
115
|
+
state.hasAnonymized = true;
|
|
116
|
+
scrubbed += result.stats.totalEntities;
|
|
117
|
+
for (const [type, count] of Object.entries(result.stats.countsByType)) {
|
|
118
|
+
byType[type] = (byType[type] ?? 0) + count;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
// Recursively anonymize a value. Object keys are preserved so execution
|
|
122
|
+
// arguments and JSON property names stay intact.
|
|
123
|
+
const scrubValue = async (value, session, policy) => {
|
|
124
|
+
if (typeof value === "string") {
|
|
125
|
+
const result = await session.anonymize(value, options.locale, policy);
|
|
126
|
+
record(result);
|
|
127
|
+
return result.anonymizedText;
|
|
128
|
+
}
|
|
129
|
+
if (Array.isArray(value)) {
|
|
130
|
+
const items = value;
|
|
131
|
+
const out = [];
|
|
132
|
+
for (const item of items) {
|
|
133
|
+
out.push(await scrubValue(item, session, policy));
|
|
134
|
+
}
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
if (value !== null && typeof value === "object") {
|
|
138
|
+
const source = value;
|
|
139
|
+
const out = {};
|
|
140
|
+
for (const [key, item] of Object.entries(source)) {
|
|
141
|
+
out[key] = await scrubValue(item, session, policy);
|
|
142
|
+
}
|
|
143
|
+
return out;
|
|
144
|
+
}
|
|
145
|
+
return value;
|
|
146
|
+
};
|
|
147
|
+
// Tool call id → shell command, so a later `tool-result` can use the identity
|
|
148
|
+
// policy its matching `tool-call` requires.
|
|
149
|
+
const commandByCallID = new Map();
|
|
150
|
+
const policyForCommand = (command) => {
|
|
151
|
+
if (!options.vcsIdentities || typeof command !== "string")
|
|
152
|
+
return undefined;
|
|
153
|
+
const commands = vcsCommandTypes(command);
|
|
154
|
+
const types = [];
|
|
155
|
+
if (commands.has("github"))
|
|
156
|
+
types.push(PIIType.GITHUB_USERNAME);
|
|
157
|
+
if (commands.has("git"))
|
|
158
|
+
types.push(PIIType.PERSON);
|
|
159
|
+
if (types.length === 0)
|
|
160
|
+
return undefined;
|
|
161
|
+
return options.identityPolicy(types);
|
|
162
|
+
};
|
|
163
|
+
const commandFor = (part) => {
|
|
164
|
+
const callID = part.callID ?? part.id;
|
|
165
|
+
if (callID === undefined)
|
|
166
|
+
return undefined;
|
|
167
|
+
return commandByCallID.get(callID);
|
|
168
|
+
};
|
|
169
|
+
const scrubPart = async (part, session) => {
|
|
170
|
+
// V1-style part state (kept so the scrubber works for both message shapes).
|
|
171
|
+
const partState = part.state;
|
|
172
|
+
if (partState !== undefined &&
|
|
173
|
+
partState !== null &&
|
|
174
|
+
typeof partState === "object") {
|
|
175
|
+
if (partState.input !== undefined) {
|
|
176
|
+
const command = extractCommand(partState.input);
|
|
177
|
+
if (command !== undefined) {
|
|
178
|
+
commandByCallID.set(part.callID ?? part.id ?? "", command);
|
|
179
|
+
}
|
|
180
|
+
partState.input = await scrubValue(partState.input, session, options.policy);
|
|
181
|
+
}
|
|
182
|
+
if (typeof partState.error === "string") {
|
|
183
|
+
partState.error = (await scrubValue(partState.error, session, options.policy));
|
|
184
|
+
}
|
|
185
|
+
if (partState.status === "completed" && typeof partState.output === "string") {
|
|
186
|
+
const command = extractCommand(partState.input) ?? commandFor(part);
|
|
187
|
+
const policyOverride = policyForCommand(command) ?? options.policy;
|
|
188
|
+
partState.output = (await scrubValue(partState.output, session, policyOverride));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// V2 tool arguments.
|
|
192
|
+
if (part.type === "tool-call" && part.input !== undefined) {
|
|
193
|
+
const command = extractCommand(part.input);
|
|
194
|
+
if (command !== undefined && part.id !== undefined) {
|
|
195
|
+
commandByCallID.set(part.id, command);
|
|
196
|
+
}
|
|
197
|
+
part.input = await scrubValue(part.input, session, options.policy);
|
|
198
|
+
}
|
|
199
|
+
// V2 tool output.
|
|
200
|
+
if (part.type === "tool-result" && part.result !== undefined) {
|
|
201
|
+
const command = extractCommand(part.input) ?? commandFor(part);
|
|
202
|
+
const policyOverride = policyForCommand(command) ?? options.policy;
|
|
203
|
+
part.result = await scrubValue(part.result, session, policyOverride);
|
|
204
|
+
}
|
|
205
|
+
// Any textual part (text, reasoning, compaction).
|
|
206
|
+
if (typeof part.text === "string") {
|
|
207
|
+
const result = await session.anonymize(part.text, options.locale, options.policy);
|
|
208
|
+
record(result);
|
|
209
|
+
part.text = result.anonymizedText;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
// Scrub a deep copy first; the originals are only touched after the whole
|
|
213
|
+
// pass succeeds, so a mid-way failure cannot send partially scrubbed PII.
|
|
214
|
+
const workingMessages = deepClone(eventMessages);
|
|
215
|
+
const session = resolveSession(ctx.sessionID);
|
|
216
|
+
for (const message of workingMessages) {
|
|
217
|
+
if (message === null || typeof message !== "object")
|
|
218
|
+
continue;
|
|
219
|
+
const messageSession = messageSessionID(message);
|
|
220
|
+
if (messageSession !== undefined && messageSession !== ctx.sessionID) {
|
|
221
|
+
ctx.logger.debug("message sessionID differs from event sessionID", {
|
|
222
|
+
eventSessionID: ctx.sessionID,
|
|
223
|
+
messageSessionID: messageSession,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
if (Array.isArray(message.content)) {
|
|
227
|
+
for (const part of message.content) {
|
|
228
|
+
if (part !== null && typeof part === "object") {
|
|
229
|
+
await scrubPart(part, session);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (Array.isArray(message.parts)) {
|
|
234
|
+
for (const part of message.parts) {
|
|
235
|
+
if (part !== null && typeof part === "object") {
|
|
236
|
+
await scrubPart(part, session);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
restoreScrubbedParts(eventMessages, workingMessages);
|
|
242
|
+
if (scrubbed > 0) {
|
|
243
|
+
const nonZero = {};
|
|
244
|
+
for (const [type, count] of Object.entries(byType)) {
|
|
245
|
+
if (count > 0)
|
|
246
|
+
nonZero[type] = count;
|
|
247
|
+
}
|
|
248
|
+
ctx.logger.info(`scrubbed ${scrubbed} secret(s) from messages`, {
|
|
249
|
+
scrubbed: nonZero,
|
|
250
|
+
messageCount: eventMessages.length,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
ctx.logger.debug("no secrets found in messages", {
|
|
255
|
+
messageCount: eventMessages.length,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
return { scrubbed, byType };
|
|
259
|
+
}
|
|
260
|
+
//# sourceMappingURL=scrub-messages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scrub-messages.js","sourceRoot":"","sources":["../../../src/opencode-plugin/v2/scrub-messages.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory session registry for the V2 plugin.
|
|
3
|
+
*
|
|
4
|
+
* OpenCode identifies a conversation by `sessionID`; each conversation gets
|
|
5
|
+
* its own {@link AnonymizerSessionImpl} so PII maps stay isolated. Sessions are
|
|
6
|
+
* process-local and intentionally not persisted here.
|
|
7
|
+
*/
|
|
8
|
+
import type { AnonymizerSessionImpl } from "../../storage/session-base.js";
|
|
9
|
+
/** Lazily creates and memoizes one session per OpenCode session ID. */
|
|
10
|
+
export declare class SessionStore {
|
|
11
|
+
private readonly map;
|
|
12
|
+
/**
|
|
13
|
+
* Returns the session for `sessionID`, creating it via `factory` on first use.
|
|
14
|
+
*/
|
|
15
|
+
get(sessionID: string, factory: () => AnonymizerSessionImpl): AnonymizerSessionImpl;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=sessions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../../src/opencode-plugin/v2/sessions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAE3E,uEAAuE;AACvE,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAiD;IAErE;;OAEG;IACH,GAAG,CACD,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,qBAAqB,GACnC,qBAAqB;CAQzB"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory session registry for the V2 plugin.
|
|
3
|
+
*
|
|
4
|
+
* OpenCode identifies a conversation by `sessionID`; each conversation gets
|
|
5
|
+
* its own {@link AnonymizerSessionImpl} so PII maps stay isolated. Sessions are
|
|
6
|
+
* process-local and intentionally not persisted here.
|
|
7
|
+
*/
|
|
8
|
+
/** Lazily creates and memoizes one session per OpenCode session ID. */
|
|
9
|
+
export class SessionStore {
|
|
10
|
+
map = new Map();
|
|
11
|
+
/**
|
|
12
|
+
* Returns the session for `sessionID`, creating it via `factory` on first use.
|
|
13
|
+
*/
|
|
14
|
+
get(sessionID, factory) {
|
|
15
|
+
let session = this.map.get(sessionID);
|
|
16
|
+
if (session === undefined) {
|
|
17
|
+
session = factory();
|
|
18
|
+
this.map.set(sessionID, session);
|
|
19
|
+
}
|
|
20
|
+
return session;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../../src/opencode-plugin/v2/sessions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,uEAAuE;AACvE,MAAM,OAAO,YAAY;IACN,GAAG,GAAuC,IAAI,GAAG,EAAE,CAAC;IAErE;;OAEG;IACH,GAAG,CACD,SAAiB,EACjB,OAAoC;QAEpC,IAAI,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACtC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,GAAG,OAAO,EAAE,CAAC;YACpB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;CACF"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-Sent Events body rewriting for the V2 `http.response` hook.
|
|
3
|
+
*
|
|
4
|
+
* When the model streams its answer as SSE (`text/event-stream`), each `data:`
|
|
5
|
+
* line may carry PII tags that must be restored before OpenCode renders the
|
|
6
|
+
* text. `event:` / `id:` / comment lines are structural and never modified.
|
|
7
|
+
*
|
|
8
|
+
* Two strategies are applied per `data:` payload:
|
|
9
|
+
* - JSON payloads are parsed, every string leaf is restored, and the value is
|
|
10
|
+
* re-serialised. This keeps quotes / backslashes / newlines inside restored
|
|
11
|
+
* values correctly escaped. A tag split across the delta field of two frames
|
|
12
|
+
* is carried on the matching JSON pointer.
|
|
13
|
+
* - Non-JSON payloads fall back to raw string replacement. A tag split across
|
|
14
|
+
* two events is carried in a per-response text tail.
|
|
15
|
+
*
|
|
16
|
+
* Frames are only rewritten once their blank-line terminator has been seen, so
|
|
17
|
+
* a tag is never processed while its frame is still buffered. Frame boundaries
|
|
18
|
+
* may be `\n\n`, `\r\n\r\n`, or a bare `\r\r`; the original separator bytes are
|
|
19
|
+
* preserved on output. On failure the original body is passed through and the
|
|
20
|
+
* reason is reported via `error`.
|
|
21
|
+
*/
|
|
22
|
+
import type { RehydrateTailStore } from "./rehydrate-tail.js";
|
|
23
|
+
/** Which strategy produced a rewritten body. */
|
|
24
|
+
export type SseRewriteMode = "json" | "text" | "passthrough";
|
|
25
|
+
/** Outcome of rewriting an SSE body. */
|
|
26
|
+
export interface SseRewriteResult {
|
|
27
|
+
/** Full rewritten SSE body. */
|
|
28
|
+
body: string;
|
|
29
|
+
/** Whether a `data:` line actually changed (i.e. a PII tag was restored). */
|
|
30
|
+
rewrote: boolean;
|
|
31
|
+
/** Strategy used: `json`/`text` when rewritten, `passthrough` otherwise. */
|
|
32
|
+
mode: SseRewriteMode;
|
|
33
|
+
/** Set when an internal failure forced the original body to pass through. */
|
|
34
|
+
error?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Per-response state shared across the events of one SSE stream.
|
|
38
|
+
*
|
|
39
|
+
* `dataTail` holds an unterminated tag fragment from the previous text event;
|
|
40
|
+
* `store` holds per-JSON-pointer fragments so a tag split across two JSON
|
|
41
|
+
* delta frames is reassembled.
|
|
42
|
+
*/
|
|
43
|
+
export interface SseTailState {
|
|
44
|
+
/** Unterminated tag fragment carried between SSE events (text payloads). */
|
|
45
|
+
dataTail: string;
|
|
46
|
+
readonly store: RehydrateTailStore;
|
|
47
|
+
readonly sessionID: string;
|
|
48
|
+
readonly tagPrefix: string;
|
|
49
|
+
readonly tagClose: string;
|
|
50
|
+
}
|
|
51
|
+
/** Creates a fresh per-response tail state, applying option defaults. */
|
|
52
|
+
export declare function createSseTailState(options?: {
|
|
53
|
+
store?: RehydrateTailStore;
|
|
54
|
+
sessionID?: string;
|
|
55
|
+
tagPrefix?: string;
|
|
56
|
+
tagClose?: string;
|
|
57
|
+
}): SseTailState;
|
|
58
|
+
/** Options accepted by {@link SseStreamRewriter}. */
|
|
59
|
+
export interface SseStreamRewriterOptions {
|
|
60
|
+
tailStore?: RehydrateTailStore;
|
|
61
|
+
sessionID?: string;
|
|
62
|
+
tagPrefix?: string;
|
|
63
|
+
tagClose?: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Rewrites every PII tag in a complete SSE body.
|
|
67
|
+
*
|
|
68
|
+
* The body is split on blank-line frame separators (`\n\n`, `\r\n\r\n`, or
|
|
69
|
+
* `\r\r`), each frame is rewritten, and the original separators are rejoined so
|
|
70
|
+
* CRLF streams stay CRLF.
|
|
71
|
+
*
|
|
72
|
+
* @param body - Raw SSE body as read from the response.
|
|
73
|
+
* @param rehydrate - Restores PII tags in a single string.
|
|
74
|
+
* @param state - Optional per-response tail state; a fresh one is created when
|
|
75
|
+
* omitted so tags split across the frames of a single body still reassemble.
|
|
76
|
+
*/
|
|
77
|
+
export declare function rewriteSseBody(body: string, rehydrate: (input: string) => Promise<string>, state?: SseTailState): Promise<SseRewriteResult>;
|
|
78
|
+
/**
|
|
79
|
+
* Streaming SSE rewriter for `http.response` bodies.
|
|
80
|
+
*
|
|
81
|
+
* Chunks are decoded and appended to an internal buffer. Only complete frames
|
|
82
|
+
* (terminated by a blank line, LF or CRLF) are rewritten and emitted, so a PII
|
|
83
|
+
* tag split across two chunks is held back until the frame completes. The
|
|
84
|
+
* per-response {@link SseTailState} additionally carries tag fragments split
|
|
85
|
+
* across events. {@link flush} rewrites any trailing frame that was never
|
|
86
|
+
* terminated and emits the final held fragment.
|
|
87
|
+
*/
|
|
88
|
+
export declare class SseStreamRewriter {
|
|
89
|
+
private readonly rehydrate;
|
|
90
|
+
private readonly onError?;
|
|
91
|
+
private readonly decoder;
|
|
92
|
+
private readonly state;
|
|
93
|
+
private pending;
|
|
94
|
+
constructor(rehydrate: (input: string) => Promise<string>, onError?: ((message: string) => void) | undefined, options?: SseStreamRewriterOptions);
|
|
95
|
+
/** Feeds one byte chunk; returns the text confirmed safe to emit. */
|
|
96
|
+
push(chunk: Uint8Array): Promise<string>;
|
|
97
|
+
/** Rewrites and returns any trailing frame at end of stream. */
|
|
98
|
+
flush(): Promise<string>;
|
|
99
|
+
private drainCompleteFrames;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=sse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../../src/opencode-plugin/v2/sse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AASH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D,gDAAgD;AAChD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,aAAa,CAAC;AAE7D,wCAAwC;AACxC,MAAM,WAAW,gBAAgB;IAC/B,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,OAAO,EAAE,OAAO,CAAC;IACjB,4EAA4E;IAC5E,IAAI,EAAE,cAAc,CAAC;IACrB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAC;IACnC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,yEAAyE;AACzE,wBAAgB,kBAAkB,CAChC,OAAO,GAAE;IACP,KAAK,CAAC,EAAE,kBAAkB,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACd,GACL,YAAY,CAQd;AAED,qDAAqD;AACrD,MAAM,WAAW,wBAAwB;IACvC,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AA8GD;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,EAC7C,KAAK,GAAE,YAAmC,GACzC,OAAO,CAAC,gBAAgB,CAAC,CAsC3B;AAcD;;;;;;;;;GASG;AACH,qBAAa,iBAAiB;IAM1B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IAN3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,OAAO,CAAM;gBAGF,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,EAC7C,OAAO,CAAC,GAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,aAAA,EACpD,OAAO,GAAE,wBAA6B;IAKxC,qEAAqE;IAC/D,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IAK9C,gEAAgE;IAC1D,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;YA0BhB,mBAAmB;CAalC"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-Sent Events body rewriting for the V2 `http.response` hook.
|
|
3
|
+
*
|
|
4
|
+
* When the model streams its answer as SSE (`text/event-stream`), each `data:`
|
|
5
|
+
* line may carry PII tags that must be restored before OpenCode renders the
|
|
6
|
+
* text. `event:` / `id:` / comment lines are structural and never modified.
|
|
7
|
+
*
|
|
8
|
+
* Two strategies are applied per `data:` payload:
|
|
9
|
+
* - JSON payloads are parsed, every string leaf is restored, and the value is
|
|
10
|
+
* re-serialised. This keeps quotes / backslashes / newlines inside restored
|
|
11
|
+
* values correctly escaped. A tag split across the delta field of two frames
|
|
12
|
+
* is carried on the matching JSON pointer.
|
|
13
|
+
* - Non-JSON payloads fall back to raw string replacement. A tag split across
|
|
14
|
+
* two events is carried in a per-response text tail.
|
|
15
|
+
*
|
|
16
|
+
* Frames are only rewritten once their blank-line terminator has been seen, so
|
|
17
|
+
* a tag is never processed while its frame is still buffered. Frame boundaries
|
|
18
|
+
* may be `\n\n`, `\r\n\r\n`, or a bare `\r\r`; the original separator bytes are
|
|
19
|
+
* preserved on output. On failure the original body is passed through and the
|
|
20
|
+
* reason is reported via `error`.
|
|
21
|
+
*/
|
|
22
|
+
import { DEFAULT_TAG_CLOSE, DEFAULT_TAG_PREFIX, InMemoryRehydrateTailStore, splitTrailingTag, walkJsonWithTail, } from "./rehydrate-tail.js";
|
|
23
|
+
/** Creates a fresh per-response tail state, applying option defaults. */
|
|
24
|
+
export function createSseTailState(options = {}) {
|
|
25
|
+
return {
|
|
26
|
+
dataTail: "",
|
|
27
|
+
store: options.store ?? new InMemoryRehydrateTailStore(),
|
|
28
|
+
sessionID: options.sessionID ?? "",
|
|
29
|
+
tagPrefix: options.tagPrefix ?? DEFAULT_TAG_PREFIX,
|
|
30
|
+
tagClose: options.tagClose ?? DEFAULT_TAG_CLOSE,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** Splits a frame into lines while capturing the exact line endings. */
|
|
34
|
+
const LINE_SEPARATOR = /(\r\n|\n|\r)/;
|
|
35
|
+
/** Splits a body into frames while capturing the exact frame separators. */
|
|
36
|
+
const FRAME_SEPARATOR = /(\r\n\r\n|\n\n|\r\r)/;
|
|
37
|
+
/** Normalizes an unknown thrown value into a loggable message. */
|
|
38
|
+
function errorMessage(error) {
|
|
39
|
+
return error instanceof Error ? error.message : String(error);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Restores a single `data:` payload.
|
|
43
|
+
*
|
|
44
|
+
* JSON payloads are parsed so string leaves are restored before
|
|
45
|
+
* re-serialisation; a payload that parses but contains no tag is returned byte
|
|
46
|
+
* for byte unchanged so formatting/whitespace is preserved. Non-JSON payloads
|
|
47
|
+
* are rehydrated as text, carrying an incomplete trailing fragment in `state`
|
|
48
|
+
* so the next event can complete it.
|
|
49
|
+
*/
|
|
50
|
+
async function rewriteDataPayload(payload, rehydrate, state) {
|
|
51
|
+
let parsed;
|
|
52
|
+
let isJson = true;
|
|
53
|
+
try {
|
|
54
|
+
parsed = JSON.parse(payload);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
isJson = false;
|
|
58
|
+
}
|
|
59
|
+
if (isJson) {
|
|
60
|
+
const result = await walkJsonWithTail(parsed, rehydrate, {
|
|
61
|
+
store: state.store,
|
|
62
|
+
sessionID: state.sessionID,
|
|
63
|
+
pointerPrefix: "sse:",
|
|
64
|
+
tagPrefix: state.tagPrefix,
|
|
65
|
+
tagClose: state.tagClose,
|
|
66
|
+
});
|
|
67
|
+
if (!result.changed) {
|
|
68
|
+
return { payload, changed: false, mode: "json" };
|
|
69
|
+
}
|
|
70
|
+
return { payload: JSON.stringify(result.value), changed: true, mode: "json" };
|
|
71
|
+
}
|
|
72
|
+
const combined = state.dataTail + payload;
|
|
73
|
+
const restored = await rehydrate(combined);
|
|
74
|
+
const { emitted, tail } = splitTrailingTag(restored, state.tagPrefix, state.tagClose);
|
|
75
|
+
state.dataTail = tail;
|
|
76
|
+
return { payload: emitted, changed: emitted !== payload, mode: "text" };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Rewrites the `data:` lines of a single complete SSE frame. The original
|
|
80
|
+
* `data: ` / `data:` prefix and spacing are preserved, as are the line endings
|
|
81
|
+
* (`\n`, `\r\n`, or `\r`).
|
|
82
|
+
*/
|
|
83
|
+
async function rewriteFrame(frame, rehydrate, state) {
|
|
84
|
+
if (frame.length === 0) {
|
|
85
|
+
return { frame, rewrote: false, mode: "passthrough" };
|
|
86
|
+
}
|
|
87
|
+
let rewrote = false;
|
|
88
|
+
let mode = "passthrough";
|
|
89
|
+
const parts = frame.split(LINE_SEPARATOR);
|
|
90
|
+
for (let index = 0; index < parts.length; index += 2) {
|
|
91
|
+
const line = parts[index] ?? "";
|
|
92
|
+
if (!line.startsWith("data:")) {
|
|
93
|
+
// `event:`, `id:`, `retry:` and `:` comments pass through untouched.
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const afterPrefix = line.slice("data:".length);
|
|
97
|
+
const hasSpace = afterPrefix.startsWith(" ");
|
|
98
|
+
const prefix = hasSpace ? "data: " : "data:";
|
|
99
|
+
const payload = hasSpace ? afterPrefix.slice(1) : afterPrefix;
|
|
100
|
+
const result = await rewriteDataPayload(payload, rehydrate, state);
|
|
101
|
+
if (result.changed) {
|
|
102
|
+
rewrote = true;
|
|
103
|
+
mode = mode === "text" || result.mode === "text" ? "text" : "json";
|
|
104
|
+
}
|
|
105
|
+
parts[index] = prefix + result.payload;
|
|
106
|
+
}
|
|
107
|
+
return { frame: parts.join(""), rewrote, mode };
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Rewrites every PII tag in a complete SSE body.
|
|
111
|
+
*
|
|
112
|
+
* The body is split on blank-line frame separators (`\n\n`, `\r\n\r\n`, or
|
|
113
|
+
* `\r\r`), each frame is rewritten, and the original separators are rejoined so
|
|
114
|
+
* CRLF streams stay CRLF.
|
|
115
|
+
*
|
|
116
|
+
* @param body - Raw SSE body as read from the response.
|
|
117
|
+
* @param rehydrate - Restores PII tags in a single string.
|
|
118
|
+
* @param state - Optional per-response tail state; a fresh one is created when
|
|
119
|
+
* omitted so tags split across the frames of a single body still reassemble.
|
|
120
|
+
*/
|
|
121
|
+
export async function rewriteSseBody(body, rehydrate, state = createSseTailState()) {
|
|
122
|
+
try {
|
|
123
|
+
const parts = body.split(FRAME_SEPARATOR);
|
|
124
|
+
let rewrote = false;
|
|
125
|
+
let sawJson = false;
|
|
126
|
+
let sawText = false;
|
|
127
|
+
for (let index = 0; index < parts.length; index += 2) {
|
|
128
|
+
const result = await rewriteFrame(parts[index] ?? "", rehydrate, state);
|
|
129
|
+
parts[index] = result.frame;
|
|
130
|
+
if (result.rewrote) {
|
|
131
|
+
rewrote = true;
|
|
132
|
+
if (result.mode === "text") {
|
|
133
|
+
sawText = true;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
sawJson = true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const rewrittenBody = parts.join("");
|
|
141
|
+
const mode = !rewrote
|
|
142
|
+
? "passthrough"
|
|
143
|
+
: sawText
|
|
144
|
+
? "text"
|
|
145
|
+
: sawJson
|
|
146
|
+
? "json"
|
|
147
|
+
: "passthrough";
|
|
148
|
+
return { body: rewrittenBody, rewrote, mode };
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
// The original body was not emitted by us, so drop any held fragment to
|
|
152
|
+
// avoid it leaking into a later, unrelated frame.
|
|
153
|
+
state.dataTail = "";
|
|
154
|
+
return {
|
|
155
|
+
body,
|
|
156
|
+
rewrote: false,
|
|
157
|
+
mode: "passthrough",
|
|
158
|
+
error: errorMessage(error),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/** Finds the end offset of the last complete frame separator in `text`. */
|
|
163
|
+
function lastFrameBoundaryEnd(text) {
|
|
164
|
+
const matcher = new RegExp(FRAME_SEPARATOR.source, "g");
|
|
165
|
+
let end = -1;
|
|
166
|
+
let match = matcher.exec(text);
|
|
167
|
+
while (match !== null) {
|
|
168
|
+
end = match.index + match[0].length;
|
|
169
|
+
match = matcher.exec(text);
|
|
170
|
+
}
|
|
171
|
+
return end;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Streaming SSE rewriter for `http.response` bodies.
|
|
175
|
+
*
|
|
176
|
+
* Chunks are decoded and appended to an internal buffer. Only complete frames
|
|
177
|
+
* (terminated by a blank line, LF or CRLF) are rewritten and emitted, so a PII
|
|
178
|
+
* tag split across two chunks is held back until the frame completes. The
|
|
179
|
+
* per-response {@link SseTailState} additionally carries tag fragments split
|
|
180
|
+
* across events. {@link flush} rewrites any trailing frame that was never
|
|
181
|
+
* terminated and emits the final held fragment.
|
|
182
|
+
*/
|
|
183
|
+
export class SseStreamRewriter {
|
|
184
|
+
rehydrate;
|
|
185
|
+
onError;
|
|
186
|
+
decoder = new TextDecoder();
|
|
187
|
+
state;
|
|
188
|
+
pending = "";
|
|
189
|
+
constructor(rehydrate, onError, options = {}) {
|
|
190
|
+
this.rehydrate = rehydrate;
|
|
191
|
+
this.onError = onError;
|
|
192
|
+
this.state = createSseTailState(options);
|
|
193
|
+
}
|
|
194
|
+
/** Feeds one byte chunk; returns the text confirmed safe to emit. */
|
|
195
|
+
async push(chunk) {
|
|
196
|
+
this.pending += this.decoder.decode(chunk, { stream: true });
|
|
197
|
+
return this.drainCompleteFrames();
|
|
198
|
+
}
|
|
199
|
+
/** Rewrites and returns any trailing frame at end of stream. */
|
|
200
|
+
async flush() {
|
|
201
|
+
this.pending += this.decoder.decode();
|
|
202
|
+
const remainder = this.pending;
|
|
203
|
+
this.pending = "";
|
|
204
|
+
let output = "";
|
|
205
|
+
if (remainder.length > 0) {
|
|
206
|
+
const result = await rewriteSseBody(remainder, this.rehydrate, this.state);
|
|
207
|
+
if (result.error !== undefined) {
|
|
208
|
+
this.onError?.(result.error);
|
|
209
|
+
}
|
|
210
|
+
output += result.body;
|
|
211
|
+
}
|
|
212
|
+
// Emit any fragment still held from the final event, then release the
|
|
213
|
+
// per-response state so it cannot leak into a later stream.
|
|
214
|
+
if (this.state.dataTail.length > 0) {
|
|
215
|
+
output += this.state.dataTail;
|
|
216
|
+
this.state.dataTail = "";
|
|
217
|
+
}
|
|
218
|
+
this.state.store.clear();
|
|
219
|
+
return output;
|
|
220
|
+
}
|
|
221
|
+
async drainCompleteFrames() {
|
|
222
|
+
const boundaryEnd = lastFrameBoundaryEnd(this.pending);
|
|
223
|
+
if (boundaryEnd < 0) {
|
|
224
|
+
return "";
|
|
225
|
+
}
|
|
226
|
+
const complete = this.pending.slice(0, boundaryEnd);
|
|
227
|
+
this.pending = this.pending.slice(boundaryEnd);
|
|
228
|
+
const result = await rewriteSseBody(complete, this.rehydrate, this.state);
|
|
229
|
+
if (result.error !== undefined) {
|
|
230
|
+
this.onError?.(result.error);
|
|
231
|
+
}
|
|
232
|
+
return result.body;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=sse.js.map
|