docagent-cli 0.0.35__py3-none-any.whl
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.
- docagent_cli/__init__.py +36 -0
- docagent_cli/__main__.py +6 -0
- docagent_cli/_ask_user_types.py +90 -0
- docagent_cli/_cli_context.py +27 -0
- docagent_cli/_debug.py +52 -0
- docagent_cli/_env_vars.py +56 -0
- docagent_cli/_server_config.py +352 -0
- docagent_cli/_session_stats.py +114 -0
- docagent_cli/_testing_models.py +144 -0
- docagent_cli/_version.py +17 -0
- docagent_cli/agent.py +1193 -0
- docagent_cli/app.py +4979 -0
- docagent_cli/app.tcss +283 -0
- docagent_cli/ask_user.py +301 -0
- docagent_cli/built_in_skills/__init__.py +5 -0
- docagent_cli/built_in_skills/doc-coauthoring/SKILL.md +375 -0
- docagent_cli/built_in_skills/docx/LICENSE.txt +30 -0
- docagent_cli/built_in_skills/docx/SKILL.md +590 -0
- docagent_cli/built_in_skills/docx/scripts/__init__.py +1 -0
- docagent_cli/built_in_skills/docx/scripts/accept_changes.py +135 -0
- docagent_cli/built_in_skills/docx/scripts/comment.py +318 -0
- docagent_cli/built_in_skills/docx/scripts/office/helpers/__init__.py +0 -0
- docagent_cli/built_in_skills/docx/scripts/office/helpers/merge_runs.py +199 -0
- docagent_cli/built_in_skills/docx/scripts/office/helpers/simplify_redlines.py +197 -0
- docagent_cli/built_in_skills/docx/scripts/office/pack.py +159 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/mce/mc.xsd +75 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- docagent_cli/built_in_skills/docx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- docagent_cli/built_in_skills/docx/scripts/office/soffice.py +183 -0
- docagent_cli/built_in_skills/docx/scripts/office/unpack.py +132 -0
- docagent_cli/built_in_skills/docx/scripts/office/validate.py +111 -0
- docagent_cli/built_in_skills/docx/scripts/office/validators/__init__.py +15 -0
- docagent_cli/built_in_skills/docx/scripts/office/validators/base.py +847 -0
- docagent_cli/built_in_skills/docx/scripts/office/validators/docx.py +446 -0
- docagent_cli/built_in_skills/docx/scripts/office/validators/pptx.py +275 -0
- docagent_cli/built_in_skills/docx/scripts/office/validators/redlining.py +247 -0
- docagent_cli/built_in_skills/docx/scripts/templates/comments.xml +3 -0
- docagent_cli/built_in_skills/docx/scripts/templates/commentsExtended.xml +3 -0
- docagent_cli/built_in_skills/docx/scripts/templates/commentsExtensible.xml +3 -0
- docagent_cli/built_in_skills/docx/scripts/templates/commentsIds.xml +3 -0
- docagent_cli/built_in_skills/docx/scripts/templates/people.xml +3 -0
- docagent_cli/built_in_skills/pdf/LICENSE.txt +30 -0
- docagent_cli/built_in_skills/pdf/SKILL.md +314 -0
- docagent_cli/built_in_skills/pdf/forms.md +294 -0
- docagent_cli/built_in_skills/pdf/reference.md +612 -0
- docagent_cli/built_in_skills/pdf/scripts/check_bounding_boxes.py +65 -0
- docagent_cli/built_in_skills/pdf/scripts/check_fillable_fields.py +11 -0
- docagent_cli/built_in_skills/pdf/scripts/convert_pdf_to_images.py +33 -0
- docagent_cli/built_in_skills/pdf/scripts/create_validation_image.py +37 -0
- docagent_cli/built_in_skills/pdf/scripts/extract_form_field_info.py +122 -0
- docagent_cli/built_in_skills/pdf/scripts/extract_form_structure.py +115 -0
- docagent_cli/built_in_skills/pdf/scripts/fill_fillable_fields.py +98 -0
- docagent_cli/built_in_skills/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
- docagent_cli/built_in_skills/pptx/LICENSE.txt +30 -0
- docagent_cli/built_in_skills/pptx/SKILL.md +232 -0
- docagent_cli/built_in_skills/pptx/editing.md +205 -0
- docagent_cli/built_in_skills/pptx/pptxgenjs.md +420 -0
- docagent_cli/built_in_skills/pptx/scripts/__init__.py +0 -0
- docagent_cli/built_in_skills/pptx/scripts/add_slide.py +195 -0
- docagent_cli/built_in_skills/pptx/scripts/clean.py +286 -0
- docagent_cli/built_in_skills/pptx/scripts/office/helpers/__init__.py +0 -0
- docagent_cli/built_in_skills/pptx/scripts/office/helpers/merge_runs.py +199 -0
- docagent_cli/built_in_skills/pptx/scripts/office/helpers/simplify_redlines.py +197 -0
- docagent_cli/built_in_skills/pptx/scripts/office/pack.py +159 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/mce/mc.xsd +75 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- docagent_cli/built_in_skills/pptx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- docagent_cli/built_in_skills/pptx/scripts/office/soffice.py +183 -0
- docagent_cli/built_in_skills/pptx/scripts/office/unpack.py +132 -0
- docagent_cli/built_in_skills/pptx/scripts/office/validate.py +111 -0
- docagent_cli/built_in_skills/pptx/scripts/office/validators/__init__.py +15 -0
- docagent_cli/built_in_skills/pptx/scripts/office/validators/base.py +847 -0
- docagent_cli/built_in_skills/pptx/scripts/office/validators/docx.py +446 -0
- docagent_cli/built_in_skills/pptx/scripts/office/validators/pptx.py +275 -0
- docagent_cli/built_in_skills/pptx/scripts/office/validators/redlining.py +247 -0
- docagent_cli/built_in_skills/pptx/scripts/thumbnail.py +289 -0
- docagent_cli/built_in_skills/remember/SKILL.md +118 -0
- docagent_cli/built_in_skills/skill-creator/LICENSE.txt +202 -0
- docagent_cli/built_in_skills/skill-creator/SKILL.md +485 -0
- docagent_cli/built_in_skills/skill-creator/agents/analyzer.md +274 -0
- docagent_cli/built_in_skills/skill-creator/agents/comparator.md +202 -0
- docagent_cli/built_in_skills/skill-creator/agents/grader.md +223 -0
- docagent_cli/built_in_skills/skill-creator/assets/eval_review.html +146 -0
- docagent_cli/built_in_skills/skill-creator/eval-viewer/generate_review.py +471 -0
- docagent_cli/built_in_skills/skill-creator/eval-viewer/viewer.html +1325 -0
- docagent_cli/built_in_skills/skill-creator/references/schemas.md +430 -0
- docagent_cli/built_in_skills/skill-creator/scripts/__init__.py +0 -0
- docagent_cli/built_in_skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- docagent_cli/built_in_skills/skill-creator/scripts/generate_report.py +326 -0
- docagent_cli/built_in_skills/skill-creator/scripts/improve_description.py +247 -0
- docagent_cli/built_in_skills/skill-creator/scripts/package_skill.py +136 -0
- docagent_cli/built_in_skills/skill-creator/scripts/quick_validate.py +103 -0
- docagent_cli/built_in_skills/skill-creator/scripts/run_eval.py +310 -0
- docagent_cli/built_in_skills/skill-creator/scripts/run_loop.py +328 -0
- docagent_cli/built_in_skills/skill-creator/scripts/utils.py +47 -0
- docagent_cli/built_in_skills/theme-factory/LICENSE.txt +202 -0
- docagent_cli/built_in_skills/theme-factory/SKILL.md +59 -0
- docagent_cli/built_in_skills/theme-factory/theme-showcase.pdf +0 -0
- docagent_cli/built_in_skills/theme-factory/themes/arctic-frost.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/botanical-garden.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/desert-rose.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/forest-canopy.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/golden-hour.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/midnight-galaxy.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/modern-minimalist.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/ocean-depths.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/sunset-boulevard.md +19 -0
- docagent_cli/built_in_skills/theme-factory/themes/tech-innovation.md +19 -0
- docagent_cli/built_in_skills/xlsx/LICENSE.txt +30 -0
- docagent_cli/built_in_skills/xlsx/SKILL.md +292 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/helpers/__init__.py +0 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/helpers/merge_runs.py +199 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/helpers/simplify_redlines.py +197 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/pack.py +159 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/mce/mc.xsd +75 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-2010.xsd +560 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-2012.xsd +67 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-2018.xsd +14 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-cex-2018.xsd +20 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-cid-2016.xsd +13 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/schemas/microsoft/wml-symex-2015.xsd +8 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/soffice.py +183 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/unpack.py +132 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/validate.py +111 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/validators/__init__.py +15 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/validators/base.py +847 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/validators/docx.py +446 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/validators/pptx.py +275 -0
- docagent_cli/built_in_skills/xlsx/scripts/office/validators/redlining.py +247 -0
- docagent_cli/built_in_skills/xlsx/scripts/recalc.py +184 -0
- docagent_cli/clipboard.py +128 -0
- docagent_cli/command_registry.py +284 -0
- docagent_cli/config.py +2418 -0
- docagent_cli/configurable_model.py +162 -0
- docagent_cli/default_agent_prompt.md +12 -0
- docagent_cli/editor.py +142 -0
- docagent_cli/file_ops.py +473 -0
- docagent_cli/formatting.py +28 -0
- docagent_cli/hooks.py +206 -0
- docagent_cli/input.py +787 -0
- docagent_cli/integrations/__init__.py +1 -0
- docagent_cli/integrations/sandbox_factory.py +873 -0
- docagent_cli/integrations/sandbox_provider.py +71 -0
- docagent_cli/local_context.py +718 -0
- docagent_cli/main.py +1641 -0
- docagent_cli/mcp_tools.py +707 -0
- docagent_cli/mcp_trust.py +168 -0
- docagent_cli/media_utils.py +478 -0
- docagent_cli/model_config.py +1620 -0
- docagent_cli/non_interactive.py +948 -0
- docagent_cli/offload.py +371 -0
- docagent_cli/output.py +69 -0
- docagent_cli/project_utils.py +188 -0
- docagent_cli/py.typed +0 -0
- docagent_cli/remote_client.py +515 -0
- docagent_cli/server.py +520 -0
- docagent_cli/server_graph.py +196 -0
- docagent_cli/server_manager.py +365 -0
- docagent_cli/sessions.py +1262 -0
- docagent_cli/skills/__init__.py +18 -0
- docagent_cli/skills/commands.py +1090 -0
- docagent_cli/skills/load.py +192 -0
- docagent_cli/subagents.py +173 -0
- docagent_cli/system_prompt.md +247 -0
- docagent_cli/textual_adapter.py +1352 -0
- docagent_cli/theme.py +842 -0
- docagent_cli/token_state.py +31 -0
- docagent_cli/tool_display.py +298 -0
- docagent_cli/tools.py +236 -0
- docagent_cli/ui.py +420 -0
- docagent_cli/unicode_security.py +516 -0
- docagent_cli/update_check.py +454 -0
- docagent_cli/widgets/__init__.py +9 -0
- docagent_cli/widgets/_links.py +63 -0
- docagent_cli/widgets/approval.py +442 -0
- docagent_cli/widgets/ask_user.py +398 -0
- docagent_cli/widgets/autocomplete.py +691 -0
- docagent_cli/widgets/chat_input.py +1827 -0
- docagent_cli/widgets/diff.py +248 -0
- docagent_cli/widgets/history.py +188 -0
- docagent_cli/widgets/loading.py +177 -0
- docagent_cli/widgets/mcp_viewer.py +362 -0
- docagent_cli/widgets/message_store.py +675 -0
- docagent_cli/widgets/messages.py +1751 -0
- docagent_cli/widgets/model_selector.py +964 -0
- docagent_cli/widgets/status.py +372 -0
- docagent_cli/widgets/theme_selector.py +164 -0
- docagent_cli/widgets/thread_selector.py +1905 -0
- docagent_cli/widgets/tool_renderers.py +148 -0
- docagent_cli/widgets/tool_widgets.py +274 -0
- docagent_cli/widgets/welcome.py +339 -0
- docagent_cli-0.0.35.data/data/docagent_cli/default_agent_prompt.md +12 -0
- docagent_cli-0.0.35.dist-info/METADATA +200 -0
- docagent_cli-0.0.35.dist-info/RECORD +300 -0
- docagent_cli-0.0.35.dist-info/WHEEL +4 -0
- docagent_cli-0.0.35.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
"""Update lifecycle for `docagent-cli`.
|
|
2
|
+
|
|
3
|
+
Handles version checking against PyPI (with caching), install-method detection,
|
|
4
|
+
auto-upgrade execution, config-driven opt-in/out, and "what's new" tracking.
|
|
5
|
+
|
|
6
|
+
Most public entry points absorb errors and return sentinel values.
|
|
7
|
+
`set_auto_update` raises on write failures so callers can surface
|
|
8
|
+
actionable feedback.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import asyncio
|
|
14
|
+
import json
|
|
15
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
import shutil
|
|
18
|
+
import sys
|
|
19
|
+
import time
|
|
20
|
+
import tomllib
|
|
21
|
+
from typing import TYPE_CHECKING, Literal
|
|
22
|
+
|
|
23
|
+
from packaging.version import InvalidVersion, Version
|
|
24
|
+
|
|
25
|
+
from docagent_cli._version import PYPI_URL, USER_AGENT, __version__
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
from docagent_cli.model_config import DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_PATH
|
|
31
|
+
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
CACHE_FILE: Path = DEFAULT_CONFIG_DIR / "latest_version.json"
|
|
35
|
+
SEEN_VERSION_FILE: Path = DEFAULT_CONFIG_DIR / "seen_version.json"
|
|
36
|
+
CACHE_TTL = 86_400 # 24 hours
|
|
37
|
+
|
|
38
|
+
InstallMethod = Literal["uv", "pip", "brew", "unknown"]
|
|
39
|
+
|
|
40
|
+
_UPGRADE_COMMANDS: dict[InstallMethod, str] = {
|
|
41
|
+
"uv": "uv tool upgrade docagent-cli",
|
|
42
|
+
"brew": "brew upgrade docagent-cli",
|
|
43
|
+
"pip": "pip install --upgrade docagent-cli",
|
|
44
|
+
}
|
|
45
|
+
"""Upgrade commands keyed by install method.
|
|
46
|
+
|
|
47
|
+
`perform_upgrade` runs only the command matching the detected install method;
|
|
48
|
+
no fallback chain.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
_UPGRADE_TIMEOUT = 120 # seconds
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _parse_version(v: str) -> Version:
|
|
55
|
+
"""Parse a PEP 440 version string into a comparable `Version` object.
|
|
56
|
+
|
|
57
|
+
Supports stable (`1.2.3`) and pre-release (`1.2.3a1`, `1.2.3rc2`) versions.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
v: Version string like `'1.2.3'` or `'1.2.3a1'`.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
A `packaging.version.Version` instance.
|
|
64
|
+
"""
|
|
65
|
+
return Version(v.strip()) # raises InvalidVersion for non-PEP 440 strings
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _latest_from_releases(
|
|
69
|
+
releases: dict[str, list[object]],
|
|
70
|
+
*,
|
|
71
|
+
include_prereleases: bool,
|
|
72
|
+
) -> str | None:
|
|
73
|
+
"""Pick the newest version from a PyPI `releases` mapping.
|
|
74
|
+
|
|
75
|
+
Skips versions with no uploaded files (empty entries) and, when
|
|
76
|
+
*include_prereleases* is `False`, skips pre-release versions.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
releases: The `releases` dict from the PyPI JSON API.
|
|
80
|
+
include_prereleases: Whether to consider pre-release versions.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
The highest matching version string, or `None` if none qualify.
|
|
84
|
+
"""
|
|
85
|
+
best: Version | None = None
|
|
86
|
+
best_str: str | None = None
|
|
87
|
+
for ver_str, files in releases.items():
|
|
88
|
+
if not files:
|
|
89
|
+
continue
|
|
90
|
+
try:
|
|
91
|
+
ver = Version(ver_str)
|
|
92
|
+
except InvalidVersion:
|
|
93
|
+
logger.debug("Skipping unparseable release key: %s", ver_str)
|
|
94
|
+
continue
|
|
95
|
+
if not include_prereleases and ver.is_prerelease:
|
|
96
|
+
continue
|
|
97
|
+
if best is None or ver > best:
|
|
98
|
+
best = ver
|
|
99
|
+
best_str = ver_str
|
|
100
|
+
return best_str
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def get_latest_version(
|
|
104
|
+
*,
|
|
105
|
+
bypass_cache: bool = False,
|
|
106
|
+
include_prereleases: bool = False,
|
|
107
|
+
) -> str | None:
|
|
108
|
+
"""Fetch the latest docagent-cli version from PyPI, with caching.
|
|
109
|
+
|
|
110
|
+
Results are cached to `CACHE_FILE` to avoid repeated network calls.
|
|
111
|
+
The cache stores both the latest stable and pre-release versions so a
|
|
112
|
+
single PyPI request serves both code paths.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
bypass_cache: Skip the cache and always hit PyPI.
|
|
116
|
+
include_prereleases: When `True`, consider pre-release versions
|
|
117
|
+
(alpha, beta, rc). Stable users should leave this `False`.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
The latest version string, or `None` on any failure.
|
|
121
|
+
"""
|
|
122
|
+
cache_key = "version_prerelease" if include_prereleases else "version"
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
if not bypass_cache and CACHE_FILE.exists():
|
|
126
|
+
data = json.loads(CACHE_FILE.read_text(encoding="utf-8"))
|
|
127
|
+
fresh = time.time() - data.get("checked_at", 0) < CACHE_TTL
|
|
128
|
+
if fresh and cache_key in data:
|
|
129
|
+
return data[cache_key]
|
|
130
|
+
except (OSError, json.JSONDecodeError, TypeError):
|
|
131
|
+
logger.debug("Failed to read update-check cache", exc_info=True)
|
|
132
|
+
|
|
133
|
+
try:
|
|
134
|
+
import requests
|
|
135
|
+
except ImportError:
|
|
136
|
+
logger.warning(
|
|
137
|
+
"requests package not installed — update checks disabled. "
|
|
138
|
+
"Install with: pip install requests"
|
|
139
|
+
)
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
resp = requests.get(
|
|
144
|
+
PYPI_URL,
|
|
145
|
+
headers={"User-Agent": USER_AGENT},
|
|
146
|
+
timeout=3,
|
|
147
|
+
)
|
|
148
|
+
resp.raise_for_status()
|
|
149
|
+
payload = resp.json()
|
|
150
|
+
stable: str = payload["info"]["version"]
|
|
151
|
+
releases: dict[str, list[object]] = payload.get("releases", {})
|
|
152
|
+
if not releases:
|
|
153
|
+
logger.debug("PyPI response missing or empty 'releases' key")
|
|
154
|
+
prerelease = _latest_from_releases(releases, include_prereleases=True)
|
|
155
|
+
except (requests.RequestException, OSError, KeyError, json.JSONDecodeError):
|
|
156
|
+
logger.debug("Failed to fetch latest version from PyPI", exc_info=True)
|
|
157
|
+
return None
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
CACHE_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
161
|
+
CACHE_FILE.write_text(
|
|
162
|
+
json.dumps(
|
|
163
|
+
{
|
|
164
|
+
"version": stable,
|
|
165
|
+
"version_prerelease": prerelease,
|
|
166
|
+
"checked_at": time.time(),
|
|
167
|
+
}
|
|
168
|
+
),
|
|
169
|
+
encoding="utf-8",
|
|
170
|
+
)
|
|
171
|
+
except OSError:
|
|
172
|
+
logger.debug("Failed to write update-check cache", exc_info=True)
|
|
173
|
+
|
|
174
|
+
return prerelease if include_prereleases else stable
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def is_update_available(*, bypass_cache: bool = False) -> tuple[bool, str | None]:
|
|
178
|
+
"""Check whether a newer version of docagent-cli is available.
|
|
179
|
+
|
|
180
|
+
When the installed version is a pre-release (e.g. `0.0.35a1`),
|
|
181
|
+
pre-release versions on PyPI are included in the comparison so alpha
|
|
182
|
+
testers are notified of newer alphas and the eventual stable release.
|
|
183
|
+
Stable installs only compare against stable PyPI releases.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
bypass_cache: Skip the cache and always hit PyPI.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
A `(available, latest)` tuple.
|
|
190
|
+
|
|
191
|
+
`available` is `True` when the PyPI version is strictly newer than
|
|
192
|
+
the installed version; `latest` is the version string (or `None`
|
|
193
|
+
when the check fails).
|
|
194
|
+
"""
|
|
195
|
+
try:
|
|
196
|
+
installed = _parse_version(__version__)
|
|
197
|
+
except InvalidVersion:
|
|
198
|
+
logger.warning(
|
|
199
|
+
"Installed version %r is not PEP 440 compliant; "
|
|
200
|
+
"update checks disabled for this install",
|
|
201
|
+
__version__,
|
|
202
|
+
)
|
|
203
|
+
return False, None
|
|
204
|
+
|
|
205
|
+
include_prereleases = installed.is_prerelease
|
|
206
|
+
latest = get_latest_version(
|
|
207
|
+
bypass_cache=bypass_cache,
|
|
208
|
+
include_prereleases=include_prereleases,
|
|
209
|
+
)
|
|
210
|
+
if latest is None:
|
|
211
|
+
return False, None
|
|
212
|
+
|
|
213
|
+
try:
|
|
214
|
+
if _parse_version(latest) > installed:
|
|
215
|
+
return True, latest
|
|
216
|
+
except InvalidVersion:
|
|
217
|
+
logger.debug("Failed to compare versions", exc_info=True)
|
|
218
|
+
|
|
219
|
+
return False, None
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
# ---------------------------------------------------------------------------
|
|
223
|
+
# Install method detection
|
|
224
|
+
# ---------------------------------------------------------------------------
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def detect_install_method() -> InstallMethod:
|
|
228
|
+
"""Detect how `docagent-cli` was installed.
|
|
229
|
+
|
|
230
|
+
Checks `sys.prefix` against known paths for uv and Homebrew.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
The detected install method: `'uv'`, `'brew'`, `'pip'`, or `'unknown'`
|
|
234
|
+
(editable/dev installs).
|
|
235
|
+
"""
|
|
236
|
+
from docagent_cli.config import _is_editable_install
|
|
237
|
+
|
|
238
|
+
prefix = sys.prefix
|
|
239
|
+
# uv tool installs live under ~/.local/share/uv/tools/
|
|
240
|
+
if "/uv/tools/" in prefix or "\\uv\\tools\\" in prefix:
|
|
241
|
+
return "uv"
|
|
242
|
+
# Homebrew prefixes
|
|
243
|
+
if any(
|
|
244
|
+
prefix.startswith(p)
|
|
245
|
+
for p in ("/opt/homebrew", "/usr/local/Cellar", "/home/linuxbrew")
|
|
246
|
+
):
|
|
247
|
+
return "brew"
|
|
248
|
+
# Editable / dev installs — don't auto-upgrade
|
|
249
|
+
if _is_editable_install():
|
|
250
|
+
return "unknown"
|
|
251
|
+
return "pip"
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def upgrade_command(method: InstallMethod | None = None) -> str:
|
|
255
|
+
"""Return the shell command to upgrade `docagent-cli`.
|
|
256
|
+
|
|
257
|
+
Falls back to the pip command for unrecognized install methods.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
method: Install method override.
|
|
261
|
+
|
|
262
|
+
Auto-detected if `None`.
|
|
263
|
+
"""
|
|
264
|
+
if method is None:
|
|
265
|
+
method = detect_install_method()
|
|
266
|
+
return _UPGRADE_COMMANDS.get(method, _UPGRADE_COMMANDS["pip"])
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
async def perform_upgrade() -> tuple[bool, str]:
|
|
270
|
+
"""Attempt to upgrade `docagent-cli` using the detected install method.
|
|
271
|
+
|
|
272
|
+
Only tries the detected method — does not fall back to other package
|
|
273
|
+
managers to avoid cross-environment contamination.
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
`(success, output)` — *output* is the combined stdout/stderr.
|
|
277
|
+
"""
|
|
278
|
+
method = detect_install_method()
|
|
279
|
+
if method == "unknown":
|
|
280
|
+
return False, "Editable install detected — skipping auto-update."
|
|
281
|
+
|
|
282
|
+
cmd = _UPGRADE_COMMANDS.get(method)
|
|
283
|
+
if cmd is None:
|
|
284
|
+
return False, f"No upgrade command for install method: {method}"
|
|
285
|
+
|
|
286
|
+
# Skip brew if binary not on PATH
|
|
287
|
+
if method == "brew" and not shutil.which("brew"):
|
|
288
|
+
return False, "brew not found on PATH."
|
|
289
|
+
|
|
290
|
+
try:
|
|
291
|
+
proc = await asyncio.create_subprocess_shell(
|
|
292
|
+
cmd,
|
|
293
|
+
stdout=asyncio.subprocess.PIPE,
|
|
294
|
+
stderr=asyncio.subprocess.PIPE,
|
|
295
|
+
stdin=asyncio.subprocess.DEVNULL,
|
|
296
|
+
)
|
|
297
|
+
stdout, stderr = await asyncio.wait_for(
|
|
298
|
+
proc.communicate(), timeout=_UPGRADE_TIMEOUT
|
|
299
|
+
)
|
|
300
|
+
output = (stdout or b"").decode() + (stderr or b"").decode()
|
|
301
|
+
if proc.returncode == 0:
|
|
302
|
+
return True, output.strip()
|
|
303
|
+
logger.warning(
|
|
304
|
+
"Upgrade via %s exited with code %d: %s",
|
|
305
|
+
method,
|
|
306
|
+
proc.returncode,
|
|
307
|
+
output.strip(),
|
|
308
|
+
)
|
|
309
|
+
return False, output.strip()
|
|
310
|
+
except TimeoutError:
|
|
311
|
+
proc.kill()
|
|
312
|
+
await proc.wait()
|
|
313
|
+
msg = f"Upgrade command timed out after {_UPGRADE_TIMEOUT}s: {cmd}"
|
|
314
|
+
logger.warning(msg)
|
|
315
|
+
return False, msg
|
|
316
|
+
except OSError:
|
|
317
|
+
logger.warning("Failed to execute upgrade command: %s", cmd, exc_info=True)
|
|
318
|
+
return False, f"Failed to execute: {cmd}"
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
# ---------------------------------------------------------------------------
|
|
322
|
+
# Config helpers
|
|
323
|
+
# ---------------------------------------------------------------------------
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def is_update_check_enabled() -> bool:
|
|
327
|
+
"""Return whether update checks are enabled.
|
|
328
|
+
|
|
329
|
+
Checks `DEEPAGENTS_CLI_NO_UPDATE_CHECK` env var and the `[update].check` key
|
|
330
|
+
in `config.toml`.
|
|
331
|
+
|
|
332
|
+
Defaults to enabled.
|
|
333
|
+
"""
|
|
334
|
+
from docagent_cli._env_vars import NO_UPDATE_CHECK
|
|
335
|
+
|
|
336
|
+
if os.environ.get(NO_UPDATE_CHECK):
|
|
337
|
+
return False
|
|
338
|
+
return _read_update_config().get("check", True)
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def is_auto_update_enabled() -> bool:
|
|
342
|
+
"""Return whether auto-update is enabled.
|
|
343
|
+
|
|
344
|
+
Opt-in via `DEEPAGENTS_CLI_AUTO_UPDATE=1` env var or
|
|
345
|
+
`[update].auto_update = true` in `config.toml`.
|
|
346
|
+
|
|
347
|
+
Defaults to `False`.
|
|
348
|
+
|
|
349
|
+
Always disabled for editable installs.
|
|
350
|
+
"""
|
|
351
|
+
from docagent_cli._env_vars import AUTO_UPDATE
|
|
352
|
+
from docagent_cli.config import _is_editable_install
|
|
353
|
+
|
|
354
|
+
if _is_editable_install():
|
|
355
|
+
return False
|
|
356
|
+
if os.environ.get(AUTO_UPDATE, "").lower() in {"1", "true", "yes"}:
|
|
357
|
+
return True
|
|
358
|
+
return _read_update_config().get("auto_update", False)
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def set_auto_update(enabled: bool) -> None:
|
|
362
|
+
"""Persist the auto-update preference to `config.toml`.
|
|
363
|
+
|
|
364
|
+
Writes `[update].auto_update` so the setting survives across sessions.
|
|
365
|
+
|
|
366
|
+
Args:
|
|
367
|
+
enabled: Whether auto-update should be enabled.
|
|
368
|
+
"""
|
|
369
|
+
import contextlib
|
|
370
|
+
import tempfile
|
|
371
|
+
from pathlib import Path
|
|
372
|
+
|
|
373
|
+
import tomli_w
|
|
374
|
+
|
|
375
|
+
DEFAULT_CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
376
|
+
if DEFAULT_CONFIG_PATH.exists():
|
|
377
|
+
with DEFAULT_CONFIG_PATH.open("rb") as f:
|
|
378
|
+
data = tomllib.load(f)
|
|
379
|
+
else:
|
|
380
|
+
data = {}
|
|
381
|
+
|
|
382
|
+
if "update" not in data:
|
|
383
|
+
data["update"] = {}
|
|
384
|
+
data["update"]["auto_update"] = enabled
|
|
385
|
+
|
|
386
|
+
fd, tmp_path = tempfile.mkstemp(dir=DEFAULT_CONFIG_PATH.parent, suffix=".tmp")
|
|
387
|
+
try:
|
|
388
|
+
with os.fdopen(fd, "wb") as f:
|
|
389
|
+
tomli_w.dump(data, f)
|
|
390
|
+
Path(tmp_path).replace(DEFAULT_CONFIG_PATH)
|
|
391
|
+
except BaseException:
|
|
392
|
+
with contextlib.suppress(OSError):
|
|
393
|
+
Path(tmp_path).unlink()
|
|
394
|
+
raise
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def _read_update_config() -> dict[str, bool]:
|
|
398
|
+
"""Read `[update]` section from `config.toml`.
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
A dict of boolean config values, empty on missing/unreadable file.
|
|
402
|
+
"""
|
|
403
|
+
try:
|
|
404
|
+
if not DEFAULT_CONFIG_PATH.exists():
|
|
405
|
+
return {}
|
|
406
|
+
with DEFAULT_CONFIG_PATH.open("rb") as f:
|
|
407
|
+
data = tomllib.load(f)
|
|
408
|
+
section = data.get("update", {})
|
|
409
|
+
return {k: v for k, v in section.items() if isinstance(v, bool)}
|
|
410
|
+
except (OSError, tomllib.TOMLDecodeError):
|
|
411
|
+
logger.warning("Could not read [update] config — using defaults", exc_info=True)
|
|
412
|
+
return {}
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
# ---------------------------------------------------------------------------
|
|
416
|
+
# "What's new" tracking
|
|
417
|
+
# ---------------------------------------------------------------------------
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def get_seen_version() -> str | None:
|
|
421
|
+
"""Return the last version the user saw the "what's new" banner for."""
|
|
422
|
+
try:
|
|
423
|
+
if SEEN_VERSION_FILE.exists():
|
|
424
|
+
data = json.loads(SEEN_VERSION_FILE.read_text(encoding="utf-8"))
|
|
425
|
+
return data.get("version")
|
|
426
|
+
except (OSError, json.JSONDecodeError, KeyError, TypeError):
|
|
427
|
+
logger.debug("Failed to read seen-version file", exc_info=True)
|
|
428
|
+
return None
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def mark_version_seen(version: str) -> None:
|
|
432
|
+
"""Record that the user has seen the "what's new" banner for *version*."""
|
|
433
|
+
try:
|
|
434
|
+
SEEN_VERSION_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
435
|
+
SEEN_VERSION_FILE.write_text(
|
|
436
|
+
json.dumps({"version": version, "seen_at": time.time()}),
|
|
437
|
+
encoding="utf-8",
|
|
438
|
+
)
|
|
439
|
+
except OSError:
|
|
440
|
+
logger.debug("Failed to write seen-version file", exc_info=True)
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def should_show_whats_new() -> bool:
|
|
444
|
+
"""Return `True` if this is the first launch on a newer version."""
|
|
445
|
+
seen = get_seen_version()
|
|
446
|
+
if seen is None:
|
|
447
|
+
# First run ever — mark current as seen, don't show banner.
|
|
448
|
+
mark_version_seen(__version__)
|
|
449
|
+
return False
|
|
450
|
+
try:
|
|
451
|
+
return _parse_version(__version__) > _parse_version(seen)
|
|
452
|
+
except InvalidVersion:
|
|
453
|
+
logger.debug("Failed to compare versions for what's-new check", exc_info=True)
|
|
454
|
+
return False
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""Shared link-click handling for Textual widgets."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import webbrowser
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
from docagent_cli.unicode_security import check_url_safety, strip_dangerous_unicode
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from textual.events import Click
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def open_style_link(event: Click) -> None:
|
|
18
|
+
"""Open the URL from a Rich link style on click, if present.
|
|
19
|
+
|
|
20
|
+
Rich `Style(link=...)` embeds OSC 8 terminal hyperlinks, but Textual's
|
|
21
|
+
mouse capture intercepts normal clicks before the terminal can act on them.
|
|
22
|
+
By handling the Textual click event directly we open the URL with a single
|
|
23
|
+
click, matching the behavior of links in the Markdown widget.
|
|
24
|
+
|
|
25
|
+
URLs that fail the safety check (e.g. containing hidden Unicode or
|
|
26
|
+
homograph domains) are blocked and not opened; the event bubbles and a
|
|
27
|
+
warning is logged and displayed as a Textual notification.
|
|
28
|
+
|
|
29
|
+
On success the event is stopped so it does not bubble further. On failure
|
|
30
|
+
(e.g. no browser available in a headless environment) the error is logged at
|
|
31
|
+
debug level and the event bubbles normally.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
event: The Textual click event to inspect.
|
|
35
|
+
"""
|
|
36
|
+
url = event.style.link
|
|
37
|
+
if not url:
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
safety = check_url_safety(url)
|
|
41
|
+
if not safety.safe:
|
|
42
|
+
detail = safety.warnings[0] if safety.warnings else "Suspicious URL"
|
|
43
|
+
logger.warning("Blocked suspicious URL: %s (%s)", url, detail)
|
|
44
|
+
try:
|
|
45
|
+
app = getattr(event, "app", None)
|
|
46
|
+
notify = getattr(app, "notify", None)
|
|
47
|
+
if callable(notify):
|
|
48
|
+
safe_url = strip_dangerous_unicode(url)
|
|
49
|
+
notify(
|
|
50
|
+
f"Blocked suspicious URL: {safe_url}\n{detail}",
|
|
51
|
+
severity="warning",
|
|
52
|
+
markup=False,
|
|
53
|
+
)
|
|
54
|
+
except (AttributeError, TypeError):
|
|
55
|
+
logger.debug("Could not send URL-blocked notification", exc_info=True)
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
webbrowser.open(url)
|
|
60
|
+
except Exception:
|
|
61
|
+
logger.debug("Could not open browser for URL: %s", url, exc_info=True)
|
|
62
|
+
return
|
|
63
|
+
event.stop()
|