switchroom 0.5.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/LICENSE +21 -0
- package/README.md +447 -0
- package/bin/autoaccept.exp +81 -0
- package/bin/boot-self-test.sh +149 -0
- package/bin/bridge-watchdog.sh +967 -0
- package/bin/handoff-briefing.sh +206 -0
- package/bin/run-hook.sh +228 -0
- package/bin/switchroom.ts +4 -0
- package/bin/timezone-hook.sh +67 -0
- package/bin/user-profile-refresh-hook.sh +38 -0
- package/bin/workspace-dynamic-hook.sh +142 -0
- package/bin/workspace-stable-hook.sh +57 -0
- package/dist/cli/autoaccept-poll.js +118 -0
- package/dist/cli/switchroom.js +48557 -0
- package/package.json +95 -0
- package/profiles/_base/settings.json.hbs +15 -0
- package/profiles/_base/start.sh.hbs +383 -0
- package/profiles/_shared/telegram-style.md.hbs +140 -0
- package/profiles/coding/CLAUDE.md.hbs +57 -0
- package/profiles/coding/skills/architecture/SKILL.md +70 -0
- package/profiles/coding/skills/code-review/SKILL.md +58 -0
- package/profiles/coding/workspace/SOUL.md.hbs +25 -0
- package/profiles/default/CLAUDE.md +238 -0
- package/profiles/default/CLAUDE.md.hbs +113 -0
- package/profiles/default/workspace/CLAUDE.md.hbs +126 -0
- package/profiles/default/workspace/HEARTBEAT.md.hbs +40 -0
- package/profiles/default/workspace/IDENTITY.md.hbs +32 -0
- package/profiles/default/workspace/MEMORY.md.hbs +29 -0
- package/profiles/default/workspace/SOUL.md.hbs +61 -0
- package/profiles/default/workspace/TOOLS.md.hbs +29 -0
- package/profiles/default/workspace/USER.md.hbs +52 -0
- package/profiles/default/workspace/memory/.gitkeep +0 -0
- package/profiles/executive-assistant/CLAUDE.md.hbs +51 -0
- package/profiles/executive-assistant/skills/daily-briefing/SKILL.md +55 -0
- package/profiles/executive-assistant/skills/meeting-prep/SKILL.md +58 -0
- package/profiles/executive-assistant/workspace/SOUL.md.hbs +25 -0
- package/profiles/health-coach/CLAUDE.md.hbs +45 -0
- package/profiles/health-coach/skills/check-in/SKILL.md +41 -0
- package/profiles/health-coach/skills/weekly-review/SKILL.md +53 -0
- package/profiles/health-coach/workspace/SOUL.md.hbs +25 -0
- package/skills/buildkite-agent-infrastructure/SKILL.md +302 -0
- package/skills/buildkite-agent-infrastructure/agents/openai.yaml +6 -0
- package/skills/buildkite-agent-infrastructure/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-agent-infrastructure/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-agent-infrastructure/references/audit-logging.md +87 -0
- package/skills/buildkite-agent-infrastructure/references/graphql-mutations.md +690 -0
- package/skills/buildkite-agent-infrastructure/references/instance-shapes.md +38 -0
- package/skills/buildkite-agent-infrastructure/references/pipeline-templates.md +73 -0
- package/skills/buildkite-agent-infrastructure/references/self-hosted-agents.md +137 -0
- package/skills/buildkite-agent-infrastructure/references/sso-saml.md +92 -0
- package/skills/buildkite-agent-runtime/SKILL.md +476 -0
- package/skills/buildkite-agent-runtime/agents/openai.yaml +6 -0
- package/skills/buildkite-agent-runtime/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-agent-runtime/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-agent-runtime/references/flag-reference.md +417 -0
- package/skills/buildkite-agent-runtime/references/patterns-and-recipes.md +555 -0
- package/skills/buildkite-api/SKILL.md +285 -0
- package/skills/buildkite-api/agents/openai.yaml +6 -0
- package/skills/buildkite-api/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-api/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-api/references/graphql-reference.md +195 -0
- package/skills/buildkite-api/references/patterns.md +44 -0
- package/skills/buildkite-api/references/webhooks.md +161 -0
- package/skills/buildkite-cli/SKILL.md +379 -0
- package/skills/buildkite-cli/agents/openai.yaml +6 -0
- package/skills/buildkite-cli/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-cli/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-cli/references/command-reference.md +181 -0
- package/skills/buildkite-migration/SKILL.md +182 -0
- package/skills/buildkite-pipelines/SKILL.md +464 -0
- package/skills/buildkite-pipelines/agents/openai.yaml +6 -0
- package/skills/buildkite-pipelines/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-pipelines/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-pipelines/examples/basic-pipeline.yml +24 -0
- package/skills/buildkite-pipelines/examples/optimized-pipeline.yml +100 -0
- package/skills/buildkite-pipelines/references/advanced-patterns.md +286 -0
- package/skills/buildkite-pipelines/references/retry-and-error-codes.md +131 -0
- package/skills/buildkite-pipelines/references/step-types-reference.md +225 -0
- package/skills/buildkite-secure-delivery/SKILL.md +168 -0
- package/skills/buildkite-secure-delivery/agents/openai.yaml +6 -0
- package/skills/buildkite-secure-delivery/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-secure-delivery/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-secure-delivery/references/oidc-cloud-providers.md +83 -0
- package/skills/buildkite-secure-delivery/references/package-publishing.md +100 -0
- package/skills/buildkite-test-engine/SKILL.md +239 -0
- package/skills/buildkite-test-engine/agents/openai.yaml +6 -0
- package/skills/buildkite-test-engine/assets/buildkite-icon-large.png +0 -0
- package/skills/buildkite-test-engine/assets/buildkite-icon-small.png +0 -0
- package/skills/buildkite-test-engine/examples/bktec-splitting.yml +16 -0
- package/skills/buildkite-test-engine/examples/collector-pipeline.yml +11 -0
- package/skills/buildkite-test-engine/references/collectors.md +198 -0
- package/skills/buildkite-test-engine/references/splitting-examples.md +93 -0
- package/skills/docx/LICENSE.txt +30 -0
- package/skills/docx/SKILL.md +590 -0
- package/skills/docx/VENDORED.md +32 -0
- package/skills/docx/scripts/__init__.py +1 -0
- package/skills/docx/scripts/accept_changes.py +135 -0
- package/skills/docx/scripts/comment.py +318 -0
- package/skills/docx/scripts/office/helpers/__init__.py +0 -0
- package/skills/docx/scripts/office/helpers/merge_runs.py +199 -0
- package/skills/docx/scripts/office/helpers/simplify_redlines.py +197 -0
- package/skills/docx/scripts/office/pack.py +159 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/skills/docx/scripts/office/schemas/mce/mc.xsd +75 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/docx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/docx/scripts/office/soffice.py +183 -0
- package/skills/docx/scripts/office/unpack.py +132 -0
- package/skills/docx/scripts/office/validate.py +111 -0
- package/skills/docx/scripts/office/validators/__init__.py +15 -0
- package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-313.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/base.cpython-313.pyc +0 -0
- package/skills/docx/scripts/office/validators/base.py +847 -0
- package/skills/docx/scripts/office/validators/docx.py +446 -0
- package/skills/docx/scripts/office/validators/pptx.py +275 -0
- package/skills/docx/scripts/office/validators/redlining.py +247 -0
- package/skills/docx/scripts/templates/comments.xml +3 -0
- package/skills/docx/scripts/templates/commentsExtended.xml +3 -0
- package/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
- package/skills/docx/scripts/templates/commentsIds.xml +3 -0
- package/skills/docx/scripts/templates/people.xml +3 -0
- package/skills/file-bug/SKILL.md +129 -0
- package/skills/humanizer/LICENSE +21 -0
- package/skills/humanizer/SKILL.md +559 -0
- package/skills/humanizer/VENDORED.md +38 -0
- package/skills/humanizer-calibrate/SKILL.md +144 -0
- package/skills/mcp-builder/LICENSE.txt +202 -0
- package/skills/mcp-builder/SKILL.md +236 -0
- package/skills/mcp-builder/VENDORED.md +32 -0
- package/skills/mcp-builder/reference/evaluation.md +602 -0
- package/skills/mcp-builder/reference/mcp_best_practices.md +249 -0
- package/skills/mcp-builder/reference/node_mcp_server.md +970 -0
- package/skills/mcp-builder/reference/python_mcp_server.md +719 -0
- package/skills/mcp-builder/scripts/connections.py +151 -0
- package/skills/mcp-builder/scripts/evaluation.py +373 -0
- package/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
- package/skills/mcp-builder/scripts/requirements.txt +2 -0
- package/skills/pdf/LICENSE.txt +30 -0
- package/skills/pdf/SKILL.md +314 -0
- package/skills/pdf/VENDORED.md +32 -0
- package/skills/pdf/forms.md +294 -0
- package/skills/pdf/reference.md +612 -0
- package/skills/pdf/scripts/check_bounding_boxes.py +65 -0
- package/skills/pdf/scripts/check_fillable_fields.py +11 -0
- package/skills/pdf/scripts/convert_pdf_to_images.py +33 -0
- package/skills/pdf/scripts/create_validation_image.py +37 -0
- package/skills/pdf/scripts/extract_form_field_info.py +122 -0
- package/skills/pdf/scripts/extract_form_structure.py +115 -0
- package/skills/pdf/scripts/fill_fillable_fields.py +98 -0
- package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
- package/skills/pptx/LICENSE.txt +30 -0
- package/skills/pptx/SKILL.md +232 -0
- package/skills/pptx/VENDORED.md +32 -0
- package/skills/pptx/editing.md +205 -0
- package/skills/pptx/pptxgenjs.md +420 -0
- package/skills/pptx/scripts/__init__.py +0 -0
- package/skills/pptx/scripts/add_slide.py +195 -0
- package/skills/pptx/scripts/clean.py +286 -0
- package/skills/pptx/scripts/office/helpers/__init__.py +0 -0
- package/skills/pptx/scripts/office/helpers/merge_runs.py +199 -0
- package/skills/pptx/scripts/office/helpers/simplify_redlines.py +197 -0
- package/skills/pptx/scripts/office/pack.py +159 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/skills/pptx/scripts/office/schemas/mce/mc.xsd +75 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/pptx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/pptx/scripts/office/soffice.py +183 -0
- package/skills/pptx/scripts/office/unpack.py +132 -0
- package/skills/pptx/scripts/office/validate.py +111 -0
- package/skills/pptx/scripts/office/validators/__init__.py +15 -0
- package/skills/pptx/scripts/office/validators/base.py +847 -0
- package/skills/pptx/scripts/office/validators/docx.py +446 -0
- package/skills/pptx/scripts/office/validators/pptx.py +275 -0
- package/skills/pptx/scripts/office/validators/redlining.py +247 -0
- package/skills/pptx/scripts/thumbnail.py +289 -0
- package/skills/skill-creator/LICENSE.txt +202 -0
- package/skills/skill-creator/SKILL.md +485 -0
- package/skills/skill-creator/VENDORED.md +32 -0
- package/skills/skill-creator/agents/analyzer.md +274 -0
- package/skills/skill-creator/agents/comparator.md +202 -0
- package/skills/skill-creator/agents/grader.md +223 -0
- package/skills/skill-creator/assets/eval_review.html +146 -0
- package/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/skills/skill-creator/references/schemas.md +430 -0
- package/skills/skill-creator/scripts/__init__.py +0 -0
- package/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/skills/skill-creator/scripts/generate_report.py +326 -0
- package/skills/skill-creator/scripts/improve_description.py +247 -0
- package/skills/skill-creator/scripts/package_skill.py +136 -0
- package/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/skills/skill-creator/scripts/run_eval.py +310 -0
- package/skills/skill-creator/scripts/run_loop.py +328 -0
- package/skills/skill-creator/scripts/utils.py +47 -0
- package/skills/switchroom-architecture/SKILL.md +60 -0
- package/skills/switchroom-architecture/cascade.md +112 -0
- package/skills/switchroom-architecture/sub-agents.md +87 -0
- package/skills/switchroom-architecture/telegram.md +94 -0
- package/skills/switchroom-cli/SKILL.md +274 -0
- package/skills/switchroom-health/SKILL.md +101 -0
- package/skills/switchroom-install/SKILL.md +116 -0
- package/skills/switchroom-manage/SKILL.md +90 -0
- package/skills/switchroom-status/SKILL.md +69 -0
- package/skills/switchroom-status/scripts/status.sh +69 -0
- package/skills/telegram-test-harness/SKILL.md +191 -0
- package/skills/token-helpers/SKILL.md +73 -0
- package/skills/token-helpers/scripts/google-cal-token.sh +62 -0
- package/skills/token-helpers/scripts/ms-graph-token.sh +70 -0
- package/skills/webapp-testing/LICENSE.txt +202 -0
- package/skills/webapp-testing/SKILL.md +96 -0
- package/skills/webapp-testing/VENDORED.md +32 -0
- package/skills/webapp-testing/examples/console_logging.py +35 -0
- package/skills/webapp-testing/examples/element_discovery.py +40 -0
- package/skills/webapp-testing/examples/static_html_automation.py +33 -0
- package/skills/webapp-testing/scripts/with_server.py +106 -0
- package/skills/xlsx/LICENSE.txt +30 -0
- package/skills/xlsx/SKILL.md +292 -0
- package/skills/xlsx/VENDORED.md +32 -0
- package/skills/xlsx/scripts/office/helpers/__init__.py +0 -0
- package/skills/xlsx/scripts/office/helpers/merge_runs.py +199 -0
- package/skills/xlsx/scripts/office/helpers/simplify_redlines.py +197 -0
- package/skills/xlsx/scripts/office/pack.py +159 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/skills/xlsx/scripts/office/schemas/mce/mc.xsd +75 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/skills/xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/skills/xlsx/scripts/office/soffice.py +183 -0
- package/skills/xlsx/scripts/office/unpack.py +132 -0
- package/skills/xlsx/scripts/office/validate.py +111 -0
- package/skills/xlsx/scripts/office/validators/__init__.py +15 -0
- package/skills/xlsx/scripts/office/validators/base.py +847 -0
- package/skills/xlsx/scripts/office/validators/docx.py +446 -0
- package/skills/xlsx/scripts/office/validators/pptx.py +275 -0
- package/skills/xlsx/scripts/office/validators/redlining.py +247 -0
- package/skills/xlsx/scripts/recalc.py +184 -0
- package/telegram-plugin/.claude-plugin/plugin.json +20 -0
- package/telegram-plugin/.mcp.json +14 -0
- package/telegram-plugin/LICENSE +21 -0
- package/telegram-plugin/README.md +352 -0
- package/telegram-plugin/active-pins-sweep.ts +204 -0
- package/telegram-plugin/active-pins.ts +146 -0
- package/telegram-plugin/active-reactions-sweep.ts +79 -0
- package/telegram-plugin/active-reactions.ts +134 -0
- package/telegram-plugin/admin-commands/dispatch.test.ts +149 -0
- package/telegram-plugin/admin-commands/index.ts +106 -0
- package/telegram-plugin/answer-stream.ts +565 -0
- package/telegram-plugin/ask-user.ts +179 -0
- package/telegram-plugin/attachment-path.ts +80 -0
- package/telegram-plugin/auth-code-redact.ts +83 -0
- package/telegram-plugin/auth-dashboard.ts +1104 -0
- package/telegram-plugin/auth-slot-parser.ts +497 -0
- package/telegram-plugin/auto-fallback-dispatcher.ts +68 -0
- package/telegram-plugin/auto-fallback.ts +348 -0
- package/telegram-plugin/bridge/bridge.ts +687 -0
- package/telegram-plugin/bridge/ipc-client.ts +326 -0
- package/telegram-plugin/bun.lock +218 -0
- package/telegram-plugin/card-format.ts +62 -0
- package/telegram-plugin/channel-envelope-safety.test.ts +56 -0
- package/telegram-plugin/channel-envelope-safety.ts +56 -0
- package/telegram-plugin/chat-lock.ts +65 -0
- package/telegram-plugin/context-exhaustion.ts +38 -0
- package/telegram-plugin/credits-watch.ts +220 -0
- package/telegram-plugin/dist/bridge/bridge.js +24758 -0
- package/telegram-plugin/dist/foreman/foreman.js +30723 -0
- package/telegram-plugin/dist/gateway/gateway.js +46497 -0
- package/telegram-plugin/dist/server.js +24551 -0
- package/telegram-plugin/dm-command-gate.ts +56 -0
- package/telegram-plugin/docs/gateway-server-split.md +133 -0
- package/telegram-plugin/docs/multi-agent-card-design.md +847 -0
- package/telegram-plugin/docs/pinned-progress-card-reliability.md +144 -0
- package/telegram-plugin/docs/stream-json-daemon-mode.md +477 -0
- package/telegram-plugin/docs/waiting-ux-spec.md +233 -0
- package/telegram-plugin/draft-stream.ts +442 -0
- package/telegram-plugin/draft-transport.ts +72 -0
- package/telegram-plugin/first-paint.ts +246 -0
- package/telegram-plugin/fleet-state.ts +246 -0
- package/telegram-plugin/foreman/foreman-create-flow.ts +202 -0
- package/telegram-plugin/foreman/foreman-handlers.ts +493 -0
- package/telegram-plugin/foreman/foreman.ts +1130 -0
- package/telegram-plugin/foreman/setup-flow.ts +345 -0
- package/telegram-plugin/foreman/setup-state.ts +239 -0
- package/telegram-plugin/foreman/state.ts +203 -0
- package/telegram-plugin/format.ts +685 -0
- package/telegram-plugin/gateway/access-validator.test.ts +95 -0
- package/telegram-plugin/gateway/access-validator.ts +37 -0
- package/telegram-plugin/gateway/boot-card.ts +582 -0
- package/telegram-plugin/gateway/boot-probes.ts +863 -0
- package/telegram-plugin/gateway/boot-reason.ts +51 -0
- package/telegram-plugin/gateway/boot-sweep-filter.test.ts +54 -0
- package/telegram-plugin/gateway/boot-sweep-filter.ts +32 -0
- package/telegram-plugin/gateway/clean-shutdown-marker.ts +183 -0
- package/telegram-plugin/gateway/disconnect-flush.ts +109 -0
- package/telegram-plugin/gateway/gateway.ts +10202 -0
- package/telegram-plugin/gateway/inbound-coalesce.ts +147 -0
- package/telegram-plugin/gateway/inject-handler.test.ts +221 -0
- package/telegram-plugin/gateway/inject-handler.ts +190 -0
- package/telegram-plugin/gateway/ipc-protocol.ts +151 -0
- package/telegram-plugin/gateway/ipc-server.ts +494 -0
- package/telegram-plugin/gateway/pid-file.ts +103 -0
- package/telegram-plugin/gateway/poll-health.ts +156 -0
- package/telegram-plugin/gateway/preamble-suppressor.ts +154 -0
- package/telegram-plugin/gateway/quota-cache.ts +125 -0
- package/telegram-plugin/gateway/resolve-calling-subagent.ts +78 -0
- package/telegram-plugin/gateway/restart-watchdog.ts +200 -0
- package/telegram-plugin/gateway/session-marker.ts +83 -0
- package/telegram-plugin/gateway/shutdown-drain.ts +162 -0
- package/telegram-plugin/gateway/startup-mutex.ts +285 -0
- package/telegram-plugin/gateway/startup-network-retry.ts +142 -0
- package/telegram-plugin/gateway/turn-active-marker.ts +176 -0
- package/telegram-plugin/gateway/unhandled-rejection-policy.ts +78 -0
- package/telegram-plugin/handoff-continuity.ts +200 -0
- package/telegram-plugin/history.ts +468 -0
- package/telegram-plugin/hooks/hooks.json +58 -0
- package/telegram-plugin/hooks/secret-guard-pretool.mjs +208 -0
- package/telegram-plugin/hooks/secret-scrub-stop.mjs +98 -0
- package/telegram-plugin/hooks/silent-end-interrupt-stop.mjs +111 -0
- package/telegram-plugin/hooks/subagent-tracker-posttool.mjs +296 -0
- package/telegram-plugin/hooks/subagent-tracker-pretool.mjs +261 -0
- package/telegram-plugin/html-sanitize.ts +244 -0
- package/telegram-plugin/idle-footer.ts +65 -0
- package/telegram-plugin/inline-keyboard-callbacks.ts +166 -0
- package/telegram-plugin/interrupt-marker.ts +66 -0
- package/telegram-plugin/issues-card.ts +371 -0
- package/telegram-plugin/issues-watcher.ts +125 -0
- package/telegram-plugin/model-unavailable.ts +325 -0
- package/telegram-plugin/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/telegram-plugin/operator-events-history.ts +94 -0
- package/telegram-plugin/operator-events.fixtures.json +161 -0
- package/telegram-plugin/operator-events.ts +421 -0
- package/telegram-plugin/package.json +55 -0
- package/telegram-plugin/permission-rule.ts +133 -0
- package/telegram-plugin/permission-title.ts +117 -0
- package/telegram-plugin/pin-event-log.ts +76 -0
- package/telegram-plugin/plugin-logger.ts +136 -0
- package/telegram-plugin/progress-card-driver.ts +2697 -0
- package/telegram-plugin/progress-card-pin-manager.ts +589 -0
- package/telegram-plugin/progress-card-pin-watchdog.ts +98 -0
- package/telegram-plugin/progress-card.ts +1409 -0
- package/telegram-plugin/pty-partial-handler.ts +247 -0
- package/telegram-plugin/pty-tail.ts +730 -0
- package/telegram-plugin/quota-check.ts +474 -0
- package/telegram-plugin/recent-outbound-dedup.ts +169 -0
- package/telegram-plugin/registry/api-registry.test.ts +201 -0
- package/telegram-plugin/registry/subagents-bugs.test.ts +454 -0
- package/telegram-plugin/registry/subagents-schema.ts +509 -0
- package/telegram-plugin/registry/subagents.test.ts +476 -0
- package/telegram-plugin/registry/turns-schema.test.ts +101 -0
- package/telegram-plugin/registry/turns-schema.ts +417 -0
- package/telegram-plugin/retry-api-call.ts +172 -0
- package/telegram-plugin/scripts/build.mjs +78 -0
- package/telegram-plugin/secret-detect/audit.ts +66 -0
- package/telegram-plugin/secret-detect/chunker.ts +37 -0
- package/telegram-plugin/secret-detect/entropy.ts +20 -0
- package/telegram-plugin/secret-detect/gitleaks-loader.ts +74 -0
- package/telegram-plugin/secret-detect/gitleaks.toml +27 -0
- package/telegram-plugin/secret-detect/index.ts +218 -0
- package/telegram-plugin/secret-detect/kv-scanner.ts +60 -0
- package/telegram-plugin/secret-detect/mask.ts +13 -0
- package/telegram-plugin/secret-detect/patterns.ts +115 -0
- package/telegram-plugin/secret-detect/pipeline.ts +144 -0
- package/telegram-plugin/secret-detect/rewrite.ts +26 -0
- package/telegram-plugin/secret-detect/secretlint-source.ts +95 -0
- package/telegram-plugin/secret-detect/slug.ts +44 -0
- package/telegram-plugin/secret-detect/staging.ts +85 -0
- package/telegram-plugin/secret-detect/suppressor.ts +34 -0
- package/telegram-plugin/secret-detect/url-redact.ts +60 -0
- package/telegram-plugin/secret-detect/vault-write.ts +56 -0
- package/telegram-plugin/server.js +41795 -0
- package/telegram-plugin/server.ts +171 -0
- package/telegram-plugin/session-tail.ts +884 -0
- package/telegram-plugin/shared/bot-runtime.ts +324 -0
- package/telegram-plugin/silent-reply.ts +58 -0
- package/telegram-plugin/slot-banner-driver.ts +147 -0
- package/telegram-plugin/slot-banner.ts +86 -0
- package/telegram-plugin/start.js +26 -0
- package/telegram-plugin/startup-reset.ts +45 -0
- package/telegram-plugin/status-reactions.ts +332 -0
- package/telegram-plugin/steering.ts +155 -0
- package/telegram-plugin/sticker-aliases.ts +249 -0
- package/telegram-plugin/stream-controller.ts +311 -0
- package/telegram-plugin/stream-reply-handler.ts +664 -0
- package/telegram-plugin/streaming-metrics.ts +134 -0
- package/telegram-plugin/streaming-report.ts +204 -0
- package/telegram-plugin/subagent-watcher.ts +880 -0
- package/telegram-plugin/telegram-button-constraints.ts +191 -0
- package/telegram-plugin/telegraph.ts +381 -0
- package/telegram-plugin/tests/HARNESS.md +340 -0
- package/telegram-plugin/tests/_progress-card-harness.ts +105 -0
- package/telegram-plugin/tests/active-pins-boot-reaper.test.ts +211 -0
- package/telegram-plugin/tests/active-pins-sweep.test.ts +309 -0
- package/telegram-plugin/tests/active-pins.test.ts +187 -0
- package/telegram-plugin/tests/active-reactions-sweep.test.ts +116 -0
- package/telegram-plugin/tests/active-reactions.test.ts +198 -0
- package/telegram-plugin/tests/answer-stream-dedup.test.ts +352 -0
- package/telegram-plugin/tests/answer-stream-silent-markers.test.ts +236 -0
- package/telegram-plugin/tests/answer-stream.test.ts +878 -0
- package/telegram-plugin/tests/ask-user.test.ts +203 -0
- package/telegram-plugin/tests/attachment-path.test.ts +199 -0
- package/telegram-plugin/tests/auth-account-identity-surface.test.ts +118 -0
- package/telegram-plugin/tests/auth-code-auto-capture.test.ts +144 -0
- package/telegram-plugin/tests/auth-code-redact.test.ts +248 -0
- package/telegram-plugin/tests/auth-dashboard-edge-cases.test.ts +260 -0
- package/telegram-plugin/tests/auth-dashboard-restart-flow.test.ts +140 -0
- package/telegram-plugin/tests/auth-dashboard-v3b.test.ts +559 -0
- package/telegram-plugin/tests/auth-dashboard.test.ts +1045 -0
- package/telegram-plugin/tests/auth-login-url-button.test.ts +122 -0
- package/telegram-plugin/tests/auth-slot-commands.test.ts +640 -0
- package/telegram-plugin/tests/auto-fallback-dispatcher.e2e.test.ts +183 -0
- package/telegram-plugin/tests/auto-fallback.test.ts +381 -0
- package/telegram-plugin/tests/boot-card-account-quota.test.ts +137 -0
- package/telegram-plugin/tests/boot-card-dedupe.test.ts +154 -0
- package/telegram-plugin/tests/boot-card-probe-target.test.ts +194 -0
- package/telegram-plugin/tests/boot-card-reason.test.ts +103 -0
- package/telegram-plugin/tests/boot-card-render.test.ts +219 -0
- package/telegram-plugin/tests/boot-probes.test.ts +451 -0
- package/telegram-plugin/tests/bot-api.harness.ts +116 -0
- package/telegram-plugin/tests/bot-runtime.test.ts +190 -0
- package/telegram-plugin/tests/bridge-anonymous-refuse.test.ts +60 -0
- package/telegram-plugin/tests/context-exhaustion.test.ts +114 -0
- package/telegram-plugin/tests/credits-watch.test.ts +221 -0
- package/telegram-plugin/tests/dm-command-gate.test.ts +176 -0
- package/telegram-plugin/tests/draft-stream.test.ts +752 -0
- package/telegram-plugin/tests/draft-transport.test.ts +141 -0
- package/telegram-plugin/tests/e2e.test.ts +436 -0
- package/telegram-plugin/tests/fake-bot-api.test.ts +213 -0
- package/telegram-plugin/tests/fake-bot-api.ts +617 -0
- package/telegram-plugin/tests/false-restart-banner.test.ts +253 -0
- package/telegram-plugin/tests/first-paint.test.ts +257 -0
- package/telegram-plugin/tests/fixtures/pty-tail-tmux-fragment.bin +6 -0
- package/telegram-plugin/tests/fixtures/service-log-current-claude-code.bin +3624 -0
- package/telegram-plugin/tests/fleet-state-watcher.test.ts +101 -0
- package/telegram-plugin/tests/fleet-state.test.ts +185 -0
- package/telegram-plugin/tests/foreman-create-flow.test.ts +359 -0
- package/telegram-plugin/tests/foreman-handlers.test.ts +347 -0
- package/telegram-plugin/tests/foreman-state.test.ts +164 -0
- package/telegram-plugin/tests/foreman-write-ops.test.ts +214 -0
- package/telegram-plugin/tests/gateway-409-retry-banner.test.ts +173 -0
- package/telegram-plugin/tests/gateway-boot-marker-clear.test.ts +72 -0
- package/telegram-plugin/tests/gateway-bridge.test.ts +811 -0
- package/telegram-plugin/tests/gateway-clean-shutdown-marker.test.ts +414 -0
- package/telegram-plugin/tests/gateway-disconnect-flush.test.ts +144 -0
- package/telegram-plugin/tests/gateway-message-validator.test.ts +133 -0
- package/telegram-plugin/tests/gateway-no-reply-single-emit.test.ts +103 -0
- package/telegram-plugin/tests/gateway-secret-detect.test.ts +127 -0
- package/telegram-plugin/tests/gateway-startup-mutex.test.ts +284 -0
- package/telegram-plugin/tests/gateway-startup-network-retry.test.ts +185 -0
- package/telegram-plugin/tests/gateway-startup-reset.test.ts +72 -0
- package/telegram-plugin/tests/gateway-update-placeholder-dispatch.test.ts +125 -0
- package/telegram-plugin/tests/handoff-continuity.test.ts +249 -0
- package/telegram-plugin/tests/harness-ordering-invariants.test.ts +243 -0
- package/telegram-plugin/tests/harness-parse-mode-validation.test.ts +114 -0
- package/telegram-plugin/tests/history.test.ts +364 -0
- package/telegram-plugin/tests/html-balanced.ts +63 -0
- package/telegram-plugin/tests/html-sanitize.test.ts +146 -0
- package/telegram-plugin/tests/idle-footer-wiring.test.ts +88 -0
- package/telegram-plugin/tests/idle-footer.test.ts +66 -0
- package/telegram-plugin/tests/inbound-coalesce.test.ts +127 -0
- package/telegram-plugin/tests/inline-keyboard-callbacks.test.ts +150 -0
- package/telegram-plugin/tests/interrupt-marker.test.ts +126 -0
- package/telegram-plugin/tests/ipc-protocol.test.ts +218 -0
- package/telegram-plugin/tests/ipc-server-anonymous-refuse.test.ts +82 -0
- package/telegram-plugin/tests/ipc-server-client.test.ts +323 -0
- package/telegram-plugin/tests/ipc-server-race.test.ts +183 -0
- package/telegram-plugin/tests/ipc-server-validate-operator.test.ts +91 -0
- package/telegram-plugin/tests/ipc-server-validate-pty-partial.test.ts +64 -0
- package/telegram-plugin/tests/ipc-server-validate-update-placeholder.test.ts +77 -0
- package/telegram-plugin/tests/ipc-validator.test.ts +274 -0
- package/telegram-plugin/tests/issues-card.test.ts +495 -0
- package/telegram-plugin/tests/issues-watcher.test.ts +165 -0
- package/telegram-plugin/tests/model-unavailable.test.ts +303 -0
- package/telegram-plugin/tests/multi-turn-continuity.test.ts +159 -0
- package/telegram-plugin/tests/operator-events-history.test.ts +125 -0
- package/telegram-plugin/tests/operator-events-session-tail.test.ts +192 -0
- package/telegram-plugin/tests/operator-events.test.ts +331 -0
- package/telegram-plugin/tests/outbound-ordering.test.ts +293 -0
- package/telegram-plugin/tests/parse-mode-rotation.test.ts +164 -0
- package/telegram-plugin/tests/permission-rule.test.ts +121 -0
- package/telegram-plugin/tests/permission-title.test.ts +106 -0
- package/telegram-plugin/tests/pin-event-log.test.ts +124 -0
- package/telegram-plugin/tests/plugin-logger.test.ts +97 -0
- package/telegram-plugin/tests/poll-health.test.ts +86 -0
- package/telegram-plugin/tests/preamble-suppressor.test.ts +285 -0
- package/telegram-plugin/tests/progress-card-api-failure-during-deferred.test.ts +73 -0
- package/telegram-plugin/tests/progress-card-close-paths-converge.test.ts +272 -0
- package/telegram-plugin/tests/progress-card-cross-turn.test.ts +258 -0
- package/telegram-plugin/tests/progress-card-dispose-preservepending.test.ts +81 -0
- package/telegram-plugin/tests/progress-card-draft-flag.test.ts +80 -0
- package/telegram-plugin/tests/progress-card-driver-eviction.test.ts +215 -0
- package/telegram-plugin/tests/progress-card-driver-fleet-shadow.test.ts +123 -0
- package/telegram-plugin/tests/progress-card-driver-force-complete-parent-done.test.ts +76 -0
- package/telegram-plugin/tests/progress-card-edit-timestamps-budget.test.ts +62 -0
- package/telegram-plugin/tests/progress-card-memory-bounds.test.ts +84 -0
- package/telegram-plugin/tests/progress-card-pin-failure-paths.test.ts +139 -0
- package/telegram-plugin/tests/progress-card-pin-manager.test.ts +773 -0
- package/telegram-plugin/tests/progress-card-pin-race-fast-turn.test.ts +66 -0
- package/telegram-plugin/tests/progress-card-pin-sidecar-partial-write.test.ts +64 -0
- package/telegram-plugin/tests/progress-card-pin-watchdog.test.ts +190 -0
- package/telegram-plugin/tests/progress-card-sigterm-pin-flush.test.ts +146 -0
- package/telegram-plugin/tests/progress-update.test.ts +236 -0
- package/telegram-plugin/tests/protocol-fixtures.test.ts +59 -0
- package/telegram-plugin/tests/protocol-fixtures.ts +198 -0
- package/telegram-plugin/tests/pty-partial-handler.test.ts +326 -0
- package/telegram-plugin/tests/pty-tail-real-fixture.test.ts +114 -0
- package/telegram-plugin/tests/pty-tail-tmux-fragment.test.ts +71 -0
- package/telegram-plugin/tests/pty-tail.test.ts +525 -0
- package/telegram-plugin/tests/quota-cache.test.ts +187 -0
- package/telegram-plugin/tests/quota-check.test.ts +622 -0
- package/telegram-plugin/tests/races.test.ts +842 -0
- package/telegram-plugin/tests/real-gateway-f1-ladder-integrity.test.ts +123 -0
- package/telegram-plugin/tests/real-gateway-f2-instant-draft.test.ts +82 -0
- package/telegram-plugin/tests/real-gateway-f3-late-card.test.ts +114 -0
- package/telegram-plugin/tests/real-gateway-harness.ts +699 -0
- package/telegram-plugin/tests/real-gateway-i6-turn-flush-replay-dedup.test.ts +313 -0
- package/telegram-plugin/tests/real-gateway-ipc-lifecycle.test.ts +299 -0
- package/telegram-plugin/tests/real-gateway-spec.test.ts +487 -0
- package/telegram-plugin/tests/real-gateway.smoke.test.ts +101 -0
- package/telegram-plugin/tests/recent-outbound-dedup.test.ts +192 -0
- package/telegram-plugin/tests/registry-turns.test.ts +432 -0
- package/telegram-plugin/tests/reply-terminal-reaction.test.ts +149 -0
- package/telegram-plugin/tests/resolve-calling-subagent.test.ts +269 -0
- package/telegram-plugin/tests/restart-watchdog.test.ts +224 -0
- package/telegram-plugin/tests/retry-api-call.test.ts +287 -0
- package/telegram-plugin/tests/secret-detect-audit.test.ts +58 -0
- package/telegram-plugin/tests/secret-detect-fail-closed.test.ts +83 -0
- package/telegram-plugin/tests/secret-detect-gitleaks.test.ts +32 -0
- package/telegram-plugin/tests/secret-detect-oauth-code.test.ts +308 -0
- package/telegram-plugin/tests/secret-detect-pipeline.test.ts +123 -0
- package/telegram-plugin/tests/secret-detect-secretlint.test.ts +101 -0
- package/telegram-plugin/tests/secret-detect-staging.test.ts +45 -0
- package/telegram-plugin/tests/secret-detect-suppressor-no-silent-allow.test.ts +67 -0
- package/telegram-plugin/tests/secret-detect.test.ts +223 -0
- package/telegram-plugin/tests/secret-guard-pretool.test.ts +194 -0
- package/telegram-plugin/tests/send-typing-action-validation.test.ts +61 -0
- package/telegram-plugin/tests/session-tail-capped.test.ts +285 -0
- package/telegram-plugin/tests/session-tail.test.ts +524 -0
- package/telegram-plugin/tests/setup-flow.test.ts +510 -0
- package/telegram-plugin/tests/setup-state.test.ts +146 -0
- package/telegram-plugin/tests/silent-reply-guard.test.ts +122 -0
- package/telegram-plugin/tests/slot-banner-driver.e2e.test.ts +350 -0
- package/telegram-plugin/tests/slot-banner.test.ts +74 -0
- package/telegram-plugin/tests/snapshot-serializer.ts +79 -0
- package/telegram-plugin/tests/spawn-detached-cgroup-escape.test.ts +51 -0
- package/telegram-plugin/tests/status-accent.test.ts +186 -0
- package/telegram-plugin/tests/status-reactions-allowed-filter.test.ts +132 -0
- package/telegram-plugin/tests/status-reactions.test.ts +314 -0
- package/telegram-plugin/tests/steering.test.ts +282 -0
- package/telegram-plugin/tests/sticker-aliases.test.ts +232 -0
- package/telegram-plugin/tests/stream-controller-html-fallback.test.ts +127 -0
- package/telegram-plugin/tests/stream-controller.test.ts +262 -0
- package/telegram-plugin/tests/stream-reply-error-paths.test.ts +208 -0
- package/telegram-plugin/tests/stream-reply-handler.test.ts +1292 -0
- package/telegram-plugin/tests/streaming-e2e.test.ts +389 -0
- package/telegram-plugin/tests/streaming-metrics.test.ts +201 -0
- package/telegram-plugin/tests/streaming-orchestration.test.ts +756 -0
- package/telegram-plugin/tests/subagent-registry-bugs.test.ts +725 -0
- package/telegram-plugin/tests/subagent-tracker-hooks.test.ts +213 -0
- package/telegram-plugin/tests/subagent-watcher-parent-marker.test.ts +274 -0
- package/telegram-plugin/tests/subagent-watcher-stall-notification.test.ts +243 -0
- package/telegram-plugin/tests/subagent-watcher.test.ts +877 -0
- package/telegram-plugin/tests/subagents-schema-init-order.test.ts +109 -0
- package/telegram-plugin/tests/sync-chat-running-subagents.test.ts +116 -0
- package/telegram-plugin/tests/telegram-button-constraints.test.ts +194 -0
- package/telegram-plugin/tests/telegram-format.test.ts +1093 -0
- package/telegram-plugin/tests/telegraph.test.ts +246 -0
- package/telegram-plugin/tests/tool-labels.test.ts +383 -0
- package/telegram-plugin/tests/turn-active-marker.test.ts +195 -0
- package/telegram-plugin/tests/turn-end-regressions.test.ts +489 -0
- package/telegram-plugin/tests/turn-flush-card-takeover.test.ts +218 -0
- package/telegram-plugin/tests/turn-flush-dedup-controller.test.ts +144 -0
- package/telegram-plugin/tests/turn-flush-prose-recovery.test.ts +78 -0
- package/telegram-plugin/tests/turn-flush-safety.test.ts +189 -0
- package/telegram-plugin/tests/turn-signal-tracker.test.ts +107 -0
- package/telegram-plugin/tests/turns-writer.test.ts +323 -0
- package/telegram-plugin/tests/two-zone-bg-carry-full-lifecycle.test.ts +131 -0
- package/telegram-plugin/tests/two-zone-bg-detection.test.ts +120 -0
- package/telegram-plugin/tests/two-zone-bg-done-when-all-terminal.test.ts +114 -0
- package/telegram-plugin/tests/two-zone-bg-early-turn-end.test.ts +87 -0
- package/telegram-plugin/tests/two-zone-bg-survives-next-turn.test.ts +211 -0
- package/telegram-plugin/tests/two-zone-card-cap.test.ts +62 -0
- package/telegram-plugin/tests/two-zone-card-fleet-row.test.ts +101 -0
- package/telegram-plugin/tests/two-zone-card-header-phases.test.ts +68 -0
- package/telegram-plugin/tests/two-zone-card-html-balance.test.ts +110 -0
- package/telegram-plugin/tests/two-zone-card-lifecycle.test.ts +128 -0
- package/telegram-plugin/tests/two-zone-card-sanitise.test.ts +58 -0
- package/telegram-plugin/tests/two-zone-card-snapshot.test.ts +133 -0
- package/telegram-plugin/tests/two-zone-concurrent-turns-isolation.test.ts +155 -0
- package/telegram-plugin/tests/two-zone-phasefor-precedence.test.ts +117 -0
- package/telegram-plugin/tests/two-zone-snapshot-extras.test.ts +143 -0
- package/telegram-plugin/tests/two-zone-stuck-edit-throttle.test.ts +149 -0
- package/telegram-plugin/tests/two-zone-stuck-header-escalation.test.ts +101 -0
- package/telegram-plugin/tests/two-zone-stuck-per-member.test.ts +114 -0
- package/telegram-plugin/tests/two-zone-stuck-recovery.test.ts +105 -0
- package/telegram-plugin/tests/typing-wrap.test.ts +141 -0
- package/telegram-plugin/tests/unhandled-rejection-policy.test.ts +147 -0
- package/telegram-plugin/tests/update-factory-edited-and-reactions.test.ts +108 -0
- package/telegram-plugin/tests/update-factory.ts +305 -0
- package/telegram-plugin/tests/vault-grant-wizard.test.ts +84 -0
- package/telegram-plugin/tests/vault-grants-revoke.test.ts +265 -0
- package/telegram-plugin/tests/vault-subcommands.test.ts +234 -0
- package/telegram-plugin/tests/voice-transcribe.test.ts +196 -0
- package/telegram-plugin/tests/waiting-ux-harness.ts +381 -0
- package/telegram-plugin/tests/waiting-ux.e2e.test.ts +233 -0
- package/telegram-plugin/tests/welcome-text.test.ts +407 -0
- package/telegram-plugin/tool-error-filter.ts +89 -0
- package/telegram-plugin/tool-labels.ts +330 -0
- package/telegram-plugin/tool-names.ts +53 -0
- package/telegram-plugin/turn-flush-prose-recovery.ts +40 -0
- package/telegram-plugin/turn-flush-safety.ts +109 -0
- package/telegram-plugin/turn-signal-tracker.ts +79 -0
- package/telegram-plugin/two-zone-card.ts +249 -0
- package/telegram-plugin/typing-wrap.ts +92 -0
- package/telegram-plugin/voice-transcribe.ts +166 -0
- package/telegram-plugin/welcome-text.ts +359 -0
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fidelity-(c) fake Telegram Bot API for integration tests.
|
|
3
|
+
*
|
|
4
|
+
* Built on top of the simpler `bot-api.harness.ts` (which uses plain
|
|
5
|
+
* `vi.fn()` stubs). This module adds two things the simpler harness
|
|
6
|
+
* doesn't have:
|
|
7
|
+
*
|
|
8
|
+
* 1. An in-memory "chat model" — tracks sent message ids, pinned
|
|
9
|
+
* messages, reactions per (chat_id, thread_id). Lets tests assert
|
|
10
|
+
* the full outbound state, not just the most recent call.
|
|
11
|
+
*
|
|
12
|
+
* 2. A fault-injection DSL that produces REAL `GrammyError` shapes
|
|
13
|
+
* (error_code, description, parameters.retry_after). `robustApiCall`
|
|
14
|
+
* checks `err instanceof GrammyError`, so throwing anything else
|
|
15
|
+
* would miss the production retry/fallback branches entirely —
|
|
16
|
+
* which is why the existing harness couldn't test flood-wait or
|
|
17
|
+
* "message to edit not found" behaviour.
|
|
18
|
+
*
|
|
19
|
+
* Existing tests using `createMockBot()` keep working; new tests that
|
|
20
|
+
* need realistic error paths use `createFakeBotApi()`.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { GrammyError } from 'grammy'
|
|
24
|
+
import { vi, beforeEach } from 'vitest'
|
|
25
|
+
|
|
26
|
+
export interface SentMessage {
|
|
27
|
+
readonly message_id: number
|
|
28
|
+
readonly chat_id: string
|
|
29
|
+
readonly text: string
|
|
30
|
+
readonly parse_mode?: string
|
|
31
|
+
readonly reply_to_message_id?: number
|
|
32
|
+
readonly message_thread_id?: number
|
|
33
|
+
readonly disable_notification?: boolean
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface PinnedRef {
|
|
37
|
+
readonly chat_id: string
|
|
38
|
+
readonly message_id: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ReactionRef {
|
|
42
|
+
readonly chat_id: string
|
|
43
|
+
readonly message_id: number
|
|
44
|
+
readonly reactions: ReadonlyArray<unknown>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ChatModel {
|
|
48
|
+
/** Every sendMessage call, oldest first. */
|
|
49
|
+
readonly sent: ReadonlyArray<SentMessage>
|
|
50
|
+
/** Latest text per message_id (reflects edits). */
|
|
51
|
+
readonly currentText: ReadonlyMap<number, string>
|
|
52
|
+
/** Pinned messages. Unpin removes. */
|
|
53
|
+
readonly pinned: ReadonlyArray<PinnedRef>
|
|
54
|
+
/** Reactions set per message. Later calls overwrite earlier. */
|
|
55
|
+
readonly reactions: ReadonlyArray<ReactionRef>
|
|
56
|
+
/** Deleted message ids. */
|
|
57
|
+
readonly deleted: ReadonlySet<number>
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface FaultQueueEntry {
|
|
61
|
+
method: string
|
|
62
|
+
chat_id?: string
|
|
63
|
+
error: unknown
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface FaultInjector {
|
|
67
|
+
/**
|
|
68
|
+
* Next call matching `method` (optionally also matching chat_id) will
|
|
69
|
+
* throw `error`. Consumed after one hit. FIFO across multiple queued
|
|
70
|
+
* faults for the same method.
|
|
71
|
+
*/
|
|
72
|
+
next(method: string, error: unknown, chat_id?: string): void
|
|
73
|
+
/** Clear all queued faults. */
|
|
74
|
+
reset(): void
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface HoldHandle {
|
|
78
|
+
/** Resolve the held call so it completes its normal mutation + return. */
|
|
79
|
+
release(): void
|
|
80
|
+
/** Reject the held call with `error` instead of letting it complete. */
|
|
81
|
+
fail(error: unknown): void
|
|
82
|
+
/** Whether the held call has been entered (the API method awaited the gate). */
|
|
83
|
+
triggered(): boolean
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Hold queue entry — when a matching call hits, it `await`s `gate`
|
|
88
|
+
* before doing its normal work. `release()` resolves the gate;
|
|
89
|
+
* `fail(err)` rejects it. The handle's `triggered` flag flips true
|
|
90
|
+
* once the production call enters the await — useful for tests to
|
|
91
|
+
* confirm "yes, the in-flight call is parked here, now I can fire the
|
|
92
|
+
* other event."
|
|
93
|
+
*/
|
|
94
|
+
interface HoldQueueEntry {
|
|
95
|
+
method: string
|
|
96
|
+
chat_id?: string
|
|
97
|
+
gate: Promise<void>
|
|
98
|
+
resolve: () => void
|
|
99
|
+
reject: (err: unknown) => void
|
|
100
|
+
setTriggered: () => void
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Build a `GrammyError` with realistic payload — grammy's own
|
|
105
|
+
* constructor needs `(message, ApiError, method, payload)`.
|
|
106
|
+
*/
|
|
107
|
+
export function makeGrammyError(opts: {
|
|
108
|
+
error_code: number
|
|
109
|
+
description: string
|
|
110
|
+
method: string
|
|
111
|
+
retry_after?: number
|
|
112
|
+
migrate_to_chat_id?: number
|
|
113
|
+
payload?: Record<string, unknown>
|
|
114
|
+
}): GrammyError {
|
|
115
|
+
const parameters: Record<string, unknown> = {}
|
|
116
|
+
if (opts.retry_after != null) parameters.retry_after = opts.retry_after
|
|
117
|
+
if (opts.migrate_to_chat_id != null) parameters.migrate_to_chat_id = opts.migrate_to_chat_id
|
|
118
|
+
return new GrammyError(
|
|
119
|
+
`Call to '${opts.method}' failed! (${opts.error_code}: ${opts.description})`,
|
|
120
|
+
{
|
|
121
|
+
ok: false,
|
|
122
|
+
error_code: opts.error_code,
|
|
123
|
+
description: opts.description,
|
|
124
|
+
parameters: parameters as GrammyError['parameters'],
|
|
125
|
+
},
|
|
126
|
+
opts.method,
|
|
127
|
+
opts.payload ?? {},
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Pre-built error factories for the cases production code actually handles. */
|
|
132
|
+
export const errors = {
|
|
133
|
+
floodWait(retry_after = 5, method = 'sendMessage'): GrammyError {
|
|
134
|
+
return makeGrammyError({
|
|
135
|
+
error_code: 429,
|
|
136
|
+
description: 'Too Many Requests: retry after ' + retry_after,
|
|
137
|
+
method,
|
|
138
|
+
retry_after,
|
|
139
|
+
})
|
|
140
|
+
},
|
|
141
|
+
notModified(method = 'editMessageText'): GrammyError {
|
|
142
|
+
return makeGrammyError({
|
|
143
|
+
error_code: 400,
|
|
144
|
+
description: 'Bad Request: message is not modified',
|
|
145
|
+
method,
|
|
146
|
+
})
|
|
147
|
+
},
|
|
148
|
+
messageToEditNotFound(method = 'editMessageText'): GrammyError {
|
|
149
|
+
return makeGrammyError({
|
|
150
|
+
error_code: 400,
|
|
151
|
+
description: 'Bad Request: message to edit not found',
|
|
152
|
+
method,
|
|
153
|
+
})
|
|
154
|
+
},
|
|
155
|
+
messageToDeleteNotFound(): GrammyError {
|
|
156
|
+
return makeGrammyError({
|
|
157
|
+
error_code: 400,
|
|
158
|
+
description: 'Bad Request: message to delete not found',
|
|
159
|
+
method: 'deleteMessage',
|
|
160
|
+
})
|
|
161
|
+
},
|
|
162
|
+
threadNotFound(method = 'sendMessage'): GrammyError {
|
|
163
|
+
return makeGrammyError({
|
|
164
|
+
error_code: 400,
|
|
165
|
+
description: 'Bad Request: message thread not found',
|
|
166
|
+
method,
|
|
167
|
+
})
|
|
168
|
+
},
|
|
169
|
+
forbidden(method = 'sendMessage'): GrammyError {
|
|
170
|
+
return makeGrammyError({
|
|
171
|
+
error_code: 403,
|
|
172
|
+
description: 'Forbidden: bot was blocked by the user',
|
|
173
|
+
method,
|
|
174
|
+
})
|
|
175
|
+
},
|
|
176
|
+
badRequest(description: string, method = 'sendMessage'): GrammyError {
|
|
177
|
+
return makeGrammyError({ error_code: 400, description: 'Bad Request: ' + description, method })
|
|
178
|
+
},
|
|
179
|
+
/** Simulate a network-level fetch failure (grammy wraps these differently). */
|
|
180
|
+
networkError(reason = 'ECONNRESET'): Error {
|
|
181
|
+
return new Error('fetch failed: ' + reason)
|
|
182
|
+
},
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export interface FakeBotApi {
|
|
186
|
+
sendMessage: ReturnType<typeof vi.fn>
|
|
187
|
+
editMessageText: ReturnType<typeof vi.fn>
|
|
188
|
+
deleteMessage: ReturnType<typeof vi.fn>
|
|
189
|
+
setMessageReaction: ReturnType<typeof vi.fn>
|
|
190
|
+
editMessageReplyMarkup: ReturnType<typeof vi.fn>
|
|
191
|
+
sendChatAction: ReturnType<typeof vi.fn>
|
|
192
|
+
pinChatMessage: ReturnType<typeof vi.fn>
|
|
193
|
+
unpinChatMessage: ReturnType<typeof vi.fn>
|
|
194
|
+
getFile: ReturnType<typeof vi.fn>
|
|
195
|
+
getMe: ReturnType<typeof vi.fn>
|
|
196
|
+
setMyCommands: ReturnType<typeof vi.fn>
|
|
197
|
+
forwardMessage: ReturnType<typeof vi.fn>
|
|
198
|
+
getChat: ReturnType<typeof vi.fn>
|
|
199
|
+
sendDocument: ReturnType<typeof vi.fn>
|
|
200
|
+
sendPhoto: ReturnType<typeof vi.fn>
|
|
201
|
+
answerCallbackQuery: ReturnType<typeof vi.fn>
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export interface FakeBot {
|
|
205
|
+
api: FakeBotApi
|
|
206
|
+
state: ChatModel
|
|
207
|
+
faults: FaultInjector
|
|
208
|
+
/** Force the next message_id to a specific value. */
|
|
209
|
+
setNextMessageId(n: number): void
|
|
210
|
+
/** Snapshot the messages that landed in a chat (oldest first). */
|
|
211
|
+
messagesIn(chat_id: string): ReadonlyArray<SentMessage>
|
|
212
|
+
/** Current visible text of message_id, or null if deleted/unknown. */
|
|
213
|
+
textOf(message_id: number): string | null
|
|
214
|
+
/** Is a given message currently pinned? */
|
|
215
|
+
isPinned(chat_id: string, message_id: number): boolean
|
|
216
|
+
/**
|
|
217
|
+
* Hold the next matching call in-flight. Returns a handle whose
|
|
218
|
+
* `release()` lets the call complete. Used to assert ordering between
|
|
219
|
+
* an in-flight outbound and an unrelated inbound event without timer
|
|
220
|
+
* fragility — e.g. "👍 must NOT fire while editMessageText is still
|
|
221
|
+
* pending." See HARNESS.md Pattern 7.
|
|
222
|
+
*
|
|
223
|
+
* FIFO across multiple holds for the same method. Hold matches before
|
|
224
|
+
* fault matches, so combining the two yields surprising results —
|
|
225
|
+
* pick one per call.
|
|
226
|
+
*/
|
|
227
|
+
holdNext(method: string, chat_id?: string): HoldHandle
|
|
228
|
+
/** Reset all state, fault queue, and hold queue. */
|
|
229
|
+
reset(): void
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Parse-mode validation strictness. Real Telegram returns 400 on
|
|
234
|
+
* unbalanced MarkdownV2/HTML entities; the fake accepts everything by
|
|
235
|
+
* default to preserve back-compat with all existing tests. Tests that
|
|
236
|
+
* want catch-malformed-markdown coverage opt in via 'lenient' (catches
|
|
237
|
+
* the common imbalances) or 'strict' (reserved for future stricter
|
|
238
|
+
* checks).
|
|
239
|
+
*/
|
|
240
|
+
export type ParseModeValidation = 'off' | 'lenient' | 'strict'
|
|
241
|
+
|
|
242
|
+
export interface CreateFakeBotApiOpts {
|
|
243
|
+
startMessageId?: number
|
|
244
|
+
/**
|
|
245
|
+
* If 'lenient' or 'strict', sendMessage/editMessageText with
|
|
246
|
+
* `parse_mode: 'MarkdownV2'` reject unbalanced markers (`*`, `_`,
|
|
247
|
+
* `` ` ``, `[`, `]`). Default 'off'. See parseModeBalanced.
|
|
248
|
+
*/
|
|
249
|
+
validateParseMode?: ParseModeValidation
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Create a fake bot. The `api` methods apply fault-injection first
|
|
254
|
+
* (if a fault is queued for this method + chat), then mutate the in-memory
|
|
255
|
+
* chat model, then return the shape grammy would return.
|
|
256
|
+
*
|
|
257
|
+
* This gives tests three orthogonal observations:
|
|
258
|
+
* - `fake.state` — did the outbound message model converge to what we expect?
|
|
259
|
+
* - `fake.api.sendMessage.mock.calls` — exactly which call sequence fired?
|
|
260
|
+
* - `fake.faults.next(...)` — "the Telegram API happens to fail this way now".
|
|
261
|
+
* - `fake.holdNext(...)` — "park this call mid-flight while I fire something else".
|
|
262
|
+
*/
|
|
263
|
+
export function createFakeBotApi(opts: CreateFakeBotApiOpts = {}): FakeBot {
|
|
264
|
+
let nextMessageId = opts.startMessageId ?? 500
|
|
265
|
+
const validateParseMode: ParseModeValidation = opts.validateParseMode ?? 'off'
|
|
266
|
+
|
|
267
|
+
const sent: SentMessage[] = []
|
|
268
|
+
const currentText = new Map<number, string>()
|
|
269
|
+
const pinned: PinnedRef[] = []
|
|
270
|
+
const reactions: ReactionRef[] = []
|
|
271
|
+
const deleted = new Set<number>()
|
|
272
|
+
|
|
273
|
+
// Keyed `${method}` or `${method}|${chat_id}` → queue (FIFO). When a
|
|
274
|
+
// request comes in, we try the chat-scoped queue first, then the
|
|
275
|
+
// method-wide queue.
|
|
276
|
+
const faultQueue: FaultQueueEntry[] = []
|
|
277
|
+
const holdQueue: HoldQueueEntry[] = []
|
|
278
|
+
// Holds that have matched a call and are awaiting `release()` or
|
|
279
|
+
// `fail()`. Tracked separately so `reset()` can reject any in-flight
|
|
280
|
+
// holds left dangling by a forgotten cleanup.
|
|
281
|
+
const triggeredHolds = new Set<HoldQueueEntry>()
|
|
282
|
+
|
|
283
|
+
function pullFault(method: string, chat_id: string | undefined): unknown | null {
|
|
284
|
+
// Chat-scoped match first
|
|
285
|
+
for (let i = 0; i < faultQueue.length; i++) {
|
|
286
|
+
const f = faultQueue[i]
|
|
287
|
+
if (f.method === method && f.chat_id != null && f.chat_id === chat_id) {
|
|
288
|
+
faultQueue.splice(i, 1)
|
|
289
|
+
return f.error
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// Then method-wide match
|
|
293
|
+
for (let i = 0; i < faultQueue.length; i++) {
|
|
294
|
+
const f = faultQueue[i]
|
|
295
|
+
if (f.method === method && f.chat_id == null) {
|
|
296
|
+
faultQueue.splice(i, 1)
|
|
297
|
+
return f.error
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return null
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function pullHold(method: string, chat_id: string | undefined): HoldQueueEntry | null {
|
|
304
|
+
for (let i = 0; i < holdQueue.length; i++) {
|
|
305
|
+
const h = holdQueue[i]
|
|
306
|
+
if (h.method === method && h.chat_id != null && h.chat_id === chat_id) {
|
|
307
|
+
holdQueue.splice(i, 1)
|
|
308
|
+
return h
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
for (let i = 0; i < holdQueue.length; i++) {
|
|
312
|
+
const h = holdQueue[i]
|
|
313
|
+
if (h.method === method && h.chat_id == null) {
|
|
314
|
+
holdQueue.splice(i, 1)
|
|
315
|
+
return h
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return null
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function maybeThrow(method: string, chat_id: string | undefined): void {
|
|
322
|
+
const err = pullFault(method, chat_id)
|
|
323
|
+
if (err != null) throw err
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Common entry for any API method: pull a fault (throw), then await
|
|
328
|
+
* a hold gate (if matched). Order matters — fault checks happen
|
|
329
|
+
* before the hold so a queued fault still fires synchronously the
|
|
330
|
+
* way real grammy errors do, and tests that hold + fault don't
|
|
331
|
+
* accidentally trigger both.
|
|
332
|
+
*/
|
|
333
|
+
async function applyEntryGates(method: string, chat_id: string | undefined): Promise<void> {
|
|
334
|
+
maybeThrow(method, chat_id)
|
|
335
|
+
const hold = pullHold(method, chat_id)
|
|
336
|
+
if (hold != null) {
|
|
337
|
+
hold.setTriggered()
|
|
338
|
+
triggeredHolds.add(hold)
|
|
339
|
+
try {
|
|
340
|
+
await hold.gate
|
|
341
|
+
} finally {
|
|
342
|
+
triggeredHolds.delete(hold)
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function checkParseMode(method: string, text: string, parse_mode: string | undefined): void {
|
|
348
|
+
if (validateParseMode === 'off') return
|
|
349
|
+
if (parse_mode !== 'MarkdownV2') return
|
|
350
|
+
const issue = parseModeBalanced(text)
|
|
351
|
+
if (issue == null) return
|
|
352
|
+
throw makeGrammyError({
|
|
353
|
+
error_code: 400,
|
|
354
|
+
description: `Bad Request: can't parse entities: ${issue}`,
|
|
355
|
+
method,
|
|
356
|
+
})
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const api: FakeBotApi = {
|
|
360
|
+
sendMessage: vi.fn(async (chat_id: string, text: string, opts?: Record<string, unknown>) => {
|
|
361
|
+
await applyEntryGates('sendMessage', chat_id)
|
|
362
|
+
checkParseMode('sendMessage', text, opts?.parse_mode as string | undefined)
|
|
363
|
+
const message_id = nextMessageId++
|
|
364
|
+
const record: SentMessage = {
|
|
365
|
+
message_id,
|
|
366
|
+
chat_id,
|
|
367
|
+
text,
|
|
368
|
+
parse_mode: opts?.parse_mode as string | undefined,
|
|
369
|
+
reply_to_message_id: opts?.reply_to_message_id as number | undefined,
|
|
370
|
+
message_thread_id: opts?.message_thread_id as number | undefined,
|
|
371
|
+
disable_notification: opts?.disable_notification as boolean | undefined,
|
|
372
|
+
}
|
|
373
|
+
sent.push(record)
|
|
374
|
+
currentText.set(message_id, text)
|
|
375
|
+
return { message_id, chat: { id: chat_id }, date: Math.floor(Date.now() / 1000), text }
|
|
376
|
+
}),
|
|
377
|
+
|
|
378
|
+
editMessageText: vi.fn(
|
|
379
|
+
async (chat_id: string, message_id: number, text: string, opts?: Record<string, unknown>) => {
|
|
380
|
+
await applyEntryGates('editMessageText', chat_id)
|
|
381
|
+
checkParseMode('editMessageText', text, opts?.parse_mode as string | undefined)
|
|
382
|
+
if (deleted.has(message_id) || !currentText.has(message_id)) {
|
|
383
|
+
// Simulate Telegram's real 400 when the target is gone.
|
|
384
|
+
throw errors.messageToEditNotFound()
|
|
385
|
+
}
|
|
386
|
+
const prev = currentText.get(message_id)
|
|
387
|
+
if (prev === text) {
|
|
388
|
+
throw errors.notModified()
|
|
389
|
+
}
|
|
390
|
+
currentText.set(message_id, text)
|
|
391
|
+
return true
|
|
392
|
+
},
|
|
393
|
+
),
|
|
394
|
+
|
|
395
|
+
deleteMessage: vi.fn(async (chat_id: string, message_id: number) => {
|
|
396
|
+
await applyEntryGates('deleteMessage', chat_id)
|
|
397
|
+
if (deleted.has(message_id) || !currentText.has(message_id)) {
|
|
398
|
+
throw errors.messageToDeleteNotFound()
|
|
399
|
+
}
|
|
400
|
+
deleted.add(message_id)
|
|
401
|
+
currentText.delete(message_id)
|
|
402
|
+
// Also drop any pin referencing it.
|
|
403
|
+
for (let i = pinned.length - 1; i >= 0; i--) {
|
|
404
|
+
if (pinned[i].message_id === message_id) pinned.splice(i, 1)
|
|
405
|
+
}
|
|
406
|
+
return true as const
|
|
407
|
+
}),
|
|
408
|
+
|
|
409
|
+
setMessageReaction: vi.fn(
|
|
410
|
+
async (chat_id: string, message_id: number, react: unknown) => {
|
|
411
|
+
await applyEntryGates('setMessageReaction', chat_id)
|
|
412
|
+
// Overwrite existing
|
|
413
|
+
const idx = reactions.findIndex(
|
|
414
|
+
(r) => r.chat_id === chat_id && r.message_id === message_id,
|
|
415
|
+
)
|
|
416
|
+
const entry: ReactionRef = { chat_id, message_id, reactions: react as ReadonlyArray<unknown> }
|
|
417
|
+
if (idx >= 0) reactions[idx] = entry
|
|
418
|
+
else reactions.push(entry)
|
|
419
|
+
return true as const
|
|
420
|
+
},
|
|
421
|
+
),
|
|
422
|
+
|
|
423
|
+
editMessageReplyMarkup: vi.fn(
|
|
424
|
+
async (chat_id: string, _message_id: number, _opts?: unknown) => {
|
|
425
|
+
await applyEntryGates('editMessageReplyMarkup', chat_id)
|
|
426
|
+
return true as const
|
|
427
|
+
},
|
|
428
|
+
),
|
|
429
|
+
|
|
430
|
+
sendChatAction: vi.fn(async (chat_id: string, _action: string) => {
|
|
431
|
+
await applyEntryGates('sendChatAction', chat_id)
|
|
432
|
+
return true as const
|
|
433
|
+
}),
|
|
434
|
+
|
|
435
|
+
pinChatMessage: vi.fn(async (chat_id: string, message_id: number, _opts?: unknown) => {
|
|
436
|
+
await applyEntryGates('pinChatMessage', chat_id)
|
|
437
|
+
pinned.push({ chat_id, message_id })
|
|
438
|
+
return true as const
|
|
439
|
+
}),
|
|
440
|
+
|
|
441
|
+
unpinChatMessage: vi.fn(async (chat_id: string, message_id: number) => {
|
|
442
|
+
await applyEntryGates('unpinChatMessage', chat_id)
|
|
443
|
+
const idx = pinned.findIndex((p) => p.chat_id === chat_id && p.message_id === message_id)
|
|
444
|
+
if (idx >= 0) pinned.splice(idx, 1)
|
|
445
|
+
return true as const
|
|
446
|
+
}),
|
|
447
|
+
|
|
448
|
+
getFile: vi.fn(async (file_id: string) => {
|
|
449
|
+
await applyEntryGates('getFile', undefined)
|
|
450
|
+
return { file_id, file_unique_id: 'uniq-' + file_id, file_size: 1024, file_path: 'documents/file.bin' }
|
|
451
|
+
}),
|
|
452
|
+
|
|
453
|
+
getMe: vi.fn(async () => {
|
|
454
|
+
await applyEntryGates('getMe', undefined)
|
|
455
|
+
return { id: 999, is_bot: true, first_name: 'TestBot', username: 'test_bot', can_join_groups: true, can_read_all_group_messages: false, supports_inline_queries: false }
|
|
456
|
+
}),
|
|
457
|
+
|
|
458
|
+
setMyCommands: vi.fn(async () => true as const),
|
|
459
|
+
|
|
460
|
+
forwardMessage: vi.fn(async (chat_id: string) => {
|
|
461
|
+
await applyEntryGates('forwardMessage', chat_id)
|
|
462
|
+
const message_id = nextMessageId++
|
|
463
|
+
return { message_id, chat: { id: chat_id }, date: Math.floor(Date.now() / 1000) }
|
|
464
|
+
}),
|
|
465
|
+
|
|
466
|
+
getChat: vi.fn(async (chat_id: string) => {
|
|
467
|
+
await applyEntryGates('getChat', String(chat_id))
|
|
468
|
+
return { id: chat_id, type: 'supergroup' as const, is_forum: true }
|
|
469
|
+
}),
|
|
470
|
+
|
|
471
|
+
sendDocument: vi.fn(async (chat_id: string) => {
|
|
472
|
+
await applyEntryGates('sendDocument', chat_id)
|
|
473
|
+
const message_id = nextMessageId++
|
|
474
|
+
sent.push({ message_id, chat_id, text: '' })
|
|
475
|
+
currentText.set(message_id, '')
|
|
476
|
+
return { message_id, chat: { id: chat_id }, date: Math.floor(Date.now() / 1000) }
|
|
477
|
+
}),
|
|
478
|
+
|
|
479
|
+
sendPhoto: vi.fn(async (chat_id: string, _photo: unknown, opts?: Record<string, unknown>) => {
|
|
480
|
+
await applyEntryGates('sendPhoto', chat_id)
|
|
481
|
+
const message_id = nextMessageId++
|
|
482
|
+
const caption = (opts?.caption as string | undefined) ?? ''
|
|
483
|
+
sent.push({ message_id, chat_id, text: caption })
|
|
484
|
+
currentText.set(message_id, caption)
|
|
485
|
+
return { message_id, chat: { id: chat_id }, date: Math.floor(Date.now() / 1000) }
|
|
486
|
+
}),
|
|
487
|
+
|
|
488
|
+
answerCallbackQuery: vi.fn(async (_id: string, _opts?: unknown) => true as const),
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const faults: FaultInjector = {
|
|
492
|
+
next(method, error, chat_id) {
|
|
493
|
+
faultQueue.push({ method, error, chat_id })
|
|
494
|
+
},
|
|
495
|
+
reset() {
|
|
496
|
+
faultQueue.length = 0
|
|
497
|
+
},
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function holdNext(method: string, chat_id?: string): HoldHandle {
|
|
501
|
+
let resolve!: () => void
|
|
502
|
+
let reject!: (err: unknown) => void
|
|
503
|
+
const gate = new Promise<void>((res, rej) => {
|
|
504
|
+
resolve = res
|
|
505
|
+
reject = rej
|
|
506
|
+
})
|
|
507
|
+
let triggered = false
|
|
508
|
+
const entry: HoldQueueEntry = {
|
|
509
|
+
method,
|
|
510
|
+
chat_id,
|
|
511
|
+
gate,
|
|
512
|
+
resolve,
|
|
513
|
+
reject,
|
|
514
|
+
setTriggered: () => {
|
|
515
|
+
triggered = true
|
|
516
|
+
},
|
|
517
|
+
}
|
|
518
|
+
holdQueue.push(entry)
|
|
519
|
+
return {
|
|
520
|
+
release: () => entry.resolve(),
|
|
521
|
+
fail: (err) => entry.reject(err),
|
|
522
|
+
triggered: () => triggered,
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const bot: FakeBot = {
|
|
527
|
+
api,
|
|
528
|
+
state: {
|
|
529
|
+
get sent() { return sent },
|
|
530
|
+
get currentText() { return currentText },
|
|
531
|
+
get pinned() { return pinned },
|
|
532
|
+
get reactions() { return reactions },
|
|
533
|
+
get deleted() { return deleted },
|
|
534
|
+
} as ChatModel,
|
|
535
|
+
faults,
|
|
536
|
+
setNextMessageId(n) { nextMessageId = n },
|
|
537
|
+
messagesIn(chat_id) { return sent.filter((s) => s.chat_id === chat_id) },
|
|
538
|
+
textOf(message_id) { return currentText.get(message_id) ?? null },
|
|
539
|
+
isPinned(chat_id, message_id) {
|
|
540
|
+
return pinned.some((p) => p.chat_id === chat_id && p.message_id === message_id)
|
|
541
|
+
},
|
|
542
|
+
holdNext,
|
|
543
|
+
reset() {
|
|
544
|
+
sent.length = 0
|
|
545
|
+
currentText.clear()
|
|
546
|
+
pinned.length = 0
|
|
547
|
+
reactions.length = 0
|
|
548
|
+
deleted.clear()
|
|
549
|
+
faultQueue.length = 0
|
|
550
|
+
// Drain any unreleased holds — letting them survive a reset would
|
|
551
|
+
// cause a later test to inherit a "stuck" call. Reject both
|
|
552
|
+
// queued (never matched) and triggered (matched, awaiting
|
|
553
|
+
// release) holds so any dangling awaits surface as test
|
|
554
|
+
// failures, not silent hangs.
|
|
555
|
+
for (const h of holdQueue) h.reject(new Error('fake-bot reset() called while hold pending'))
|
|
556
|
+
holdQueue.length = 0
|
|
557
|
+
for (const h of triggeredHolds) h.reject(new Error('fake-bot reset() called while hold in flight'))
|
|
558
|
+
triggeredHolds.clear()
|
|
559
|
+
nextMessageId = opts.startMessageId ?? 500
|
|
560
|
+
for (const fn of Object.values(api)) (fn as ReturnType<typeof vi.fn>).mockClear()
|
|
561
|
+
},
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
return bot
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Install a `beforeEach` that resets the bot between tests.
|
|
569
|
+
* Call once at the top of a describe() block.
|
|
570
|
+
*/
|
|
571
|
+
export function installFakeBotResetHook(bot: FakeBot): void {
|
|
572
|
+
beforeEach(() => bot.reset())
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Lenient MarkdownV2 balance check. Returns null if the text looks
|
|
577
|
+
* balanced; returns a one-line description if it doesn't. Used by
|
|
578
|
+
* `validateParseMode: 'lenient'` to mimic Telegram's
|
|
579
|
+
* `can't parse entities` 400.
|
|
580
|
+
*
|
|
581
|
+
* Not a real parser — just checks the count of unescaped marker
|
|
582
|
+
* characters. Telegram's full grammar is more complex (nested entities,
|
|
583
|
+
* the e2e parser at https://core.telegram.org/bots/api#markdownv2-style),
|
|
584
|
+
* but unbalanced counts catch the most common mistakes (forgetting to
|
|
585
|
+
* close a `*` after a token break in a streamed message).
|
|
586
|
+
*
|
|
587
|
+
* Markers checked: `*`, `_`, `` ` ``, `[` / `]`. Backslash-escaped
|
|
588
|
+
* markers (`\*`) are ignored. Triple-backtick code blocks are
|
|
589
|
+
* collapsed first (their inner content is exempt from emphasis rules).
|
|
590
|
+
*/
|
|
591
|
+
export function parseModeBalanced(text: string): string | null {
|
|
592
|
+
// Strip code blocks (```...```) — content inside is verbatim.
|
|
593
|
+
const stripped = text.replace(/```[\s\S]*?```/g, '')
|
|
594
|
+
// Strip inline code (`...`) — also verbatim.
|
|
595
|
+
const stripped2 = stripped.replace(/`[^`\n]*`/g, '')
|
|
596
|
+
// Strip backslash-escaped chars.
|
|
597
|
+
const stripped3 = stripped2.replace(/\\[*_`\[\]()~>#+\-=|{}.!\\]/g, '')
|
|
598
|
+
|
|
599
|
+
const counts: Record<string, number> = { '*': 0, _: 0 }
|
|
600
|
+
let bracketDepth = 0
|
|
601
|
+
for (const ch of stripped3) {
|
|
602
|
+
if (ch === '*' || ch === '_') counts[ch]++
|
|
603
|
+
else if (ch === '[') bracketDepth++
|
|
604
|
+
else if (ch === ']') {
|
|
605
|
+
bracketDepth--
|
|
606
|
+
if (bracketDepth < 0) return "unbalanced ']' bracket"
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
if (counts['*'] % 2 !== 0) return "unbalanced '*' (bold marker)"
|
|
610
|
+
if (counts['_'] % 2 !== 0) return "unbalanced '_' (italic marker)"
|
|
611
|
+
if (bracketDepth !== 0) return "unbalanced '[' bracket"
|
|
612
|
+
// Backticks: count remaining (unstripped) — should be 0 after the
|
|
613
|
+
// inline-code strip above.
|
|
614
|
+
const tickCount = (stripped3.match(/`/g) ?? []).length
|
|
615
|
+
if (tickCount !== 0) return 'unbalanced backtick'
|
|
616
|
+
return null
|
|
617
|
+
}
|