boss-agent-cli 2.0.0__tar.gz → 2.0.1__tar.gz
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.
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/CHANGELOG.md +5 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/PKG-INFO +1 -1
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/capability-matrix.en.md +2 -2
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/capability-matrix.md +2 -2
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/commands.en.md +4 -4
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/commands.md +4 -4
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/pyproject.toml +1 -1
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/__init__.py +1 -1
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/ai_cmd.py +1 -1
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/chat.py +6 -2
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/chat_export.py +20 -17
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/chat_snapshot.py +16 -15
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/chat_summary.py +10 -5
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/chat_utils.py +16 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/chatmsg.py +17 -6
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/contact_lookup.py +53 -10
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/exchange.py +10 -5
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/mark.py +3 -2
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/pipeline.py +1 -1
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/schema.py +4 -4
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/mcp_tools.py +5 -5
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/actions.py +52 -19
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/catalog.py +1 -1
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/prompts.py +5 -1
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_chat_export_extended.py +39 -3
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_chat_snapshot_extended.py +98 -0
- boss_agent_cli-2.0.1/tests/test_chatmsg_uid_flow.py +227 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_commands.py +4 -3
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_contact_lookup.py +91 -1
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_new_commands.py +2 -2
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_wizard.py +63 -1
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/uv.lock +1 -1
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.codecov.yml +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.devcontainer/devcontainer.json +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.dockerignore +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.gitattributes +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/CODEOWNERS +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/ISSUE_TEMPLATE/documentation.yml +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/dependabot.yml +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/social-preview.png +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/workflows/ci.yml +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/workflows/docs.yml +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/workflows/pages.yml +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/workflows/release.yml +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/workflows/triage-cleanup.yml +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.github/workflows/update-contributors.yml +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.gitignore +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/.pre-commit-config.yaml +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/CODE_OF_CONDUCT.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/CONTRIBUTING.en.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/CONTRIBUTING.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/CONTRIBUTORS.svg +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/Dockerfile +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/LICENSE +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/README.en.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/README.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/ROADMAP.en.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/ROADMAP.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/SECURITY.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/demo-en.tape +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/demo-zh.tape +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/demo_showcase.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/demo_showcase_en.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/index.html +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/llms.txt +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/promo-showcase/README.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/promo-showcase/animations.jsx +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/promo-showcase/app.jsx +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/promo-showcase/boss-agent-cli-promo.html +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/promo-showcase/lib.jsx +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/promo-showcase/scenes.jsx +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/demo/showcase/boss-agent-cli-showcase-thumb.jpg +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docker-compose.yml +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/adr/0001-default-low-risk-assistance-mode.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/adr/0002-explicit-research-operating-mode.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/agent-hosts.en.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/agent-hosts.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/agent-quickstart.en.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/agent-quickstart.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/assets/logo.png +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/assets/star-history-dark.svg +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/assets/star-history.svg +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/getting-started.en.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/getting-started.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/integrations/ai-models.en.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/integrations/ai-models.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/integrations/claude-code.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/integrations/codex.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/integrations/cursor.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/integrations/docker.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/integrations/python-sdk.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/integrations/shell-agent.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/integrations/windsurf.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/maintainer/branch-protection.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/maintainer/labels.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/maintainer/release-checklist.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/marketing/awesome-submissions.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/marketing/en-launch-story.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/marketing/promotion-article.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/marketing/zh-launch-story.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/platform-abstraction.en.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/platform-abstraction.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/platform-risk.en.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/platform-risk.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/readme-badge-policy.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/research/bridge-grpc.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/research/platforms/README.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/research/platforms/lagou.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/research/platforms/liepin.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/research/platforms/zhaopin-recruiter-evaluation.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/research/platforms/zhaopin.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/research/platforms/zhipin.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/smoke-testing.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/troubleshooting.en.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/docs/troubleshooting.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/evals/README.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/evals/results/20260624T103356Z-fixture.json +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/evals/results/README.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/evals/results/necessary-conditions-2026-06-24.json +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/evals/run_eval.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/evals/scenarios.json +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/examples/opencode/opencode.json +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/extension/background.js +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/extension/manifest.json +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/glama.json +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/mcp-server/README.en.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/mcp-server/README.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/mcp-server/server.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/scripts/README.md +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/scripts/package_portable.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/scripts/probe_recruiter_chat_frontend.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/scripts/quality_baseline.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/scripts/smoke_p0.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/scripts/update_contributors.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/ai/__init__.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/ai/config.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/ai/local_models.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/ai/prompts.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/ai/service.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/__init__.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/_base_client.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/boss.yaml +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/browser_client.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/browser_source.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/client.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/endpoints.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/endpoints_loader.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/httpx_helpers.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/models.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/recruiter.yaml +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/recruiter_client.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/recruiter_endpoints.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/recruiter_resume.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/throttle.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/zhilian.yaml +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/zhilian_client.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/api/zhipin_errors.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/auth/__init__.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/auth/browser.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/auth/cookie_extract.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/auth/curl_import.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/auth/health.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/auth/manager.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/auth/qr_login.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/auth/token_store.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/__init__.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/adapters.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/boss_adapter.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/config.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/decision.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/events.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/execution.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/mock_adapter.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/models.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/reply_ai.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/runner.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/safety.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/scoring.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/storage.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/zhilian_adapter.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/zhilian_browser.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/zhilian_browser_actions.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/zhilian_cdp.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/automation/zhilian_selectors.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/bridge/__init__.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/bridge/client.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/bridge/daemon.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/bridge/protocol.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/cache/__init__.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/cache/store.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/chat_summary.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/__init__.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/_platform.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/_recruiter_platform.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/agent.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/ai_local.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/apply.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/cities.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/clean.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/config_cmd.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/crawl.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/detail.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/digest.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/doctor.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/doctor_checks.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/export.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/favorites.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/friend_list_pages.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/greet.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/history.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/interviews.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/login.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/logout.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/me.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/platforms.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/preset.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recommend.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/__init__.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/accept_resume.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/applications.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/candidates.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/chat.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/download_resume.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/jobs.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/recommendations.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/reply.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/request_resume.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/resume.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/recruiter/resume_parser.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/register.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/resume_cmd.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/search.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/shortlist.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/show.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/stats.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/status.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/watch.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/commands/wizard.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/compliance.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/config.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/crawler/__init__.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/crawler/exporter.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/crawler/hooks.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/crawler/operations.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/crawler/service.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/crawler/transport.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/digest.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/display.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/hooks.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/index_cache.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/main.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/match_score.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/mcp_args.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/mcp_server.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/output.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/pipeline_state.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/platforms/__init__.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/platforms/base.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/platforms/recruiter_base.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/platforms/zhilian.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/platforms/zhipin.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/platforms/zhipin_recruiter.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/py.typed +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/resume/__init__.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/resume/export.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/resume/models.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/resume/store.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/resume/templates.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/search_filters.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/__init__.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/live_crawl.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/models.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/preflight.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/renderer.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/runner.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/src/boss_agent_cli/wizard/store.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/conftest.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/fixtures/favorites_card.json +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_agent_automation.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_agent_automation_cross_validation.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_agent_crawl.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_agent_docs.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_agent_error_contract.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_agent_host_examples.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_commands.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_config.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_config_cmd.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_cover_letter.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_fit.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_local.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_prompts.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_resume_optimize.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_service.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_ai_suggest_keywords.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_api.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_api_client_methods.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_api_client_retry.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_api_httpx_helpers.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_apply.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_auth.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_auth_and_login_extended.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_auth_browser.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_auth_manager_extended.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_auth_manager_flow.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_bridge.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_bridge_extended.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_browser_client.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_browser_source.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_cache.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_cache_extended.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_chat_summary.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_chat_summary_command.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_chatmsg_extended.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_clean_cmd.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_cli_invariants.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_compliance.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_config_cmd.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_cookie_extract_regression.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_coverage_second_sprint.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_crawl.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_crawl_cli.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_curl_auth_import.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_digest.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_digest_command.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_display.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_doctor_extended.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_doctor_zhilian.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_endpoints_loader.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_eval_harness.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_export_extended.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_favorites.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_greet_detail_extended.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_greet_extended.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_hooks.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_index_cache.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_job_card_browser.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_job_item_lid.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_match_score.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_mcp_handler_registration.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_mcp_server.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_mcp_server_protocol.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_open_source_docs.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_output.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_pipeline_commands.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_pipeline_state.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_platform_base.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_platform_cli.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_platform_not_supported.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_platforms_cmd.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_portable_package.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_preset.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_probe_script.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_public_api.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_qr_login.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_quality_baseline.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_recruiter_client_retry.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_recruiter_recommendations_and_read.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_recruiter_resume_attachments.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_recruiter_safety_regressions.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_resume_commands.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_resume_export.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_resume_import_compat.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_resume_init_hints.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_resume_models.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_resume_store.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_resume_templates.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_schema_contract.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_schema_formats.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_schema_mcp_tools.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_search_filters.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_search_pipeline.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_shortlist.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_smoke_p0.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_stats.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_system_proxy.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_throttle.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_watch.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_watch_run_all.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_welfare_filter.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_zhilian_browser_automation.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_zhilian_client.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_zhilian_interview_data.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_zhilian_stub.py +0 -0
- {boss_agent_cli-2.0.0 → boss_agent_cli-2.0.1}/tests/test_zhipin_errors.py +0 -0
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [2.0.1] - 2026-09-29
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- `boss chat` 现在公开跨请求稳定的联系人 `uid`,`chatmsg` / `chat-summary` / `mark` / `exchange` 优先按 `uid` 重新解析联系人并使用本次沟通列表返回的动态 `securityId`;沟通快照、diff 与导出映射同步改用 `uid` 主键,避免轮换令牌导致联系人查找必然失败和快照重复。既有 `security_id` 参数名与无 `uid` 的旧快照仍兼容,但旧令牌仅作兜底且可能已经失效。
|
|
11
|
+
|
|
7
12
|
## [2.0.0] - 2026-09-15
|
|
8
13
|
|
|
9
14
|
### Changed(含 Breaking Change)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: boss-agent-cli
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.1
|
|
4
4
|
Summary: AI Agent 专用的 BOSS 直聘求职 CLI 工具
|
|
5
5
|
Project-URL: Homepage, https://github.com/can4hou6joeng4/boss-agent-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/can4hou6joeng4/boss-agent-cli
|
|
@@ -43,8 +43,8 @@ Compatibility modes `assisted` and `research` can both call every implemented ca
|
|
|
43
43
|
| Capability | CLI command | Login required | Transport |
|
|
44
44
|
|---|---|---|---|
|
|
45
45
|
| Conversation list | `boss chat` | Yes | Existing-browser read session when Bridge is connected; otherwise httpx with stored credentials |
|
|
46
|
-
| Message history | `boss chatmsg [--raw]` | Yes |
|
|
47
|
-
| Conversation summary | `boss chat-summary` | Yes |
|
|
46
|
+
| Message history | `boss chatmsg [--raw]` | Yes | Prefer the stable `uid` returned by `boss chat`; use the existing-browser read session when Bridge is connected, otherwise httpx with stored credentials; `--raw` preserves structured body/link/job-card fields |
|
|
47
|
+
| Conversation summary | `boss chat-summary` | Yes | Prefer the stable `uid`; platform adapter + local processing |
|
|
48
48
|
| Contact labels | `boss mark` | Yes | Platform adapter |
|
|
49
49
|
| Contact exchange | `boss exchange` | Yes | Platform adapter |
|
|
50
50
|
| Interview invites | `boss interviews` | Yes | httpx |
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
| 能力 | CLI 命令 | 需要登录 | 通道 |
|
|
44
44
|
|---|---|---|---|
|
|
45
45
|
| 沟通列表 | `boss chat` | 是 | Bridge 已连接时复用现有浏览器只读会话;否则使用本地凭据的 httpx |
|
|
46
|
-
| 聊天消息 | `boss chatmsg [--raw]` | 是 | Bridge
|
|
47
|
-
| 聊天摘要 | `boss chat-summary` | 是 |
|
|
46
|
+
| 聊天消息 | `boss chatmsg [--raw]` | 是 | 优先接受 `boss chat` 输出的稳定 `uid`;Bridge 已连接时复用现有浏览器只读会话,否则使用本地凭据的 httpx;`--raw` 保留结构化 body/链接/职位卡片字段 |
|
|
47
|
+
| 聊天摘要 | `boss chat-summary` | 是 | 优先接受稳定 `uid`;平台适配器 + 本地处理 |
|
|
48
48
|
| 联系人标签 | `boss mark` | 是 | 平台适配器 |
|
|
49
49
|
| 交换联系方式 | `boss exchange` | 是 | 平台适配器 |
|
|
50
50
|
| 面试邀请 | `boss interviews` | 是 | httpx |
|
|
@@ -105,16 +105,16 @@ After every page, `<data-dir>/crawl/runs/<run_id>/jobs.json`, `jobs.csv`, and a
|
|
|
105
105
|
| `boss greet <sid> <jid>` | Greet a recruiter; duplicates return `ALREADY_GREETED` |
|
|
106
106
|
| `boss batch-greet <query>` | Search and greet up to an explicit `--limit`; supports `--dry-run`; on `ACCOUNT_RISK` / `ENVIRONMENT_RISK` the batch stops immediately with `ok:false`, and already-greeted items are returned in `error.details.greeted` |
|
|
107
107
|
| `boss apply <sid> <jid>` | Apply or start a conversation; duplicates return `ALREADY_APPLIED` |
|
|
108
|
-
| `boss exchange <sid>` | Request a phone-number or WeChat exchange |
|
|
108
|
+
| `boss exchange <uid-or-sid>` | Request a phone-number or WeChat exchange; prefer the stable `uid` returned by `boss chat` |
|
|
109
109
|
|
|
110
110
|
## Conversation track
|
|
111
111
|
|
|
112
112
|
| Command | Description |
|
|
113
113
|
|---------|-------------|
|
|
114
114
|
| `boss chat` | List conversations with pagination and source filters |
|
|
115
|
-
| `boss chatmsg <sid> [--raw]` | Read message history; `--raw` preserves structured body/link/card fields |
|
|
116
|
-
| `boss chat-summary <sid>` | Build a structured conversation summary |
|
|
117
|
-
| `boss mark <sid> --label X` | Add or remove a contact label |
|
|
115
|
+
| `boss chatmsg <uid-or-sid> [--raw]` | Read message history; prefer the stable `uid` returned by `boss chat`; `--raw` preserves structured body/link/card fields |
|
|
116
|
+
| `boss chat-summary <uid-or-sid>` | Build a structured conversation summary; prefer the stable `uid` |
|
|
117
|
+
| `boss mark <uid-or-sid> --label X` | Add or remove a contact label; prefer the stable `uid` |
|
|
118
118
|
| `boss interviews` | Interview invitations |
|
|
119
119
|
| `boss history` | Browsing history |
|
|
120
120
|
|
|
@@ -101,16 +101,16 @@ boss crawl stop <run_id>
|
|
|
101
101
|
| `boss greet <sid> <jid>` | 向指定招聘者打招呼;重复记录返回 `ALREADY_GREETED` |
|
|
102
102
|
| `boss batch-greet <query>` | 搜索后按显式 `--limit` 批量打招呼,支持 `--dry-run`;命中 `ACCOUNT_RISK` / `ENVIRONMENT_RISK` 立即停批并返回 `ok:false`,已成功项随 `error.details.greeted` 带回 |
|
|
103
103
|
| `boss apply <sid> <jid>` | 发起投递或立即沟通;重复记录返回 `ALREADY_APPLIED` |
|
|
104
|
-
| `boss exchange <sid>` |
|
|
104
|
+
| `boss exchange <uid-or-sid>` | 请求交换手机号或微信;优先使用 `boss chat` 输出的稳定 `uid` |
|
|
105
105
|
|
|
106
106
|
## 沟通跟进
|
|
107
107
|
|
|
108
108
|
| 命令 | 说明 |
|
|
109
109
|
|------|------|
|
|
110
110
|
| `boss chat` | 查看沟通列表,支持分页和来源筛选 |
|
|
111
|
-
| `boss chatmsg <sid> [--raw]` |
|
|
112
|
-
| `boss chat-summary <sid>` |
|
|
113
|
-
| `boss mark <sid> --label X` |
|
|
111
|
+
| `boss chatmsg <uid-or-sid> [--raw]` | 查看聊天历史;优先使用 `boss chat` 输出的稳定 `uid`,`--raw` 保留结构化 body、链接和职位卡片字段 |
|
|
112
|
+
| `boss chat-summary <uid-or-sid>` | 基于聊天历史生成结构化摘要;优先使用稳定 `uid` |
|
|
113
|
+
| `boss mark <uid-or-sid> --label X` | 添加或移除联系人标签;优先使用稳定 `uid` |
|
|
114
114
|
| `boss interviews` | 面试邀请 |
|
|
115
115
|
| `boss history` | 浏览历史 |
|
|
116
116
|
|
|
@@ -501,7 +501,7 @@ def ai_reply_cmd(ctx: click.Context, recruiter_message: str, context: str, resum
|
|
|
501
501
|
render=lambda d: render_ai_result(d, title="ai-reply"),
|
|
502
502
|
hints={"next_actions": [
|
|
503
503
|
"复制草稿到 BOSS 聊天框发送",
|
|
504
|
-
"boss chatmsg <
|
|
504
|
+
"boss chatmsg <uid> 查看完整聊天历史(uid 见 boss chat 输出)",
|
|
505
505
|
]},
|
|
506
506
|
)
|
|
507
507
|
|
|
@@ -99,6 +99,9 @@ def chat_cmd(ctx: click.Context, page: int, from_who: str | None, days: int | No
|
|
|
99
99
|
"last_time": last_time_str,
|
|
100
100
|
"last_ts": last_ts,
|
|
101
101
|
"msg_status": msg_status_label,
|
|
102
|
+
# uid 是跨请求稳定的联系人标识,chatmsg/chat-summary/mark/exchange
|
|
103
|
+
# 都应优先使用它;security_id 是每请求轮换的令牌,仅作参考。
|
|
104
|
+
"uid": item.get("uid") or "",
|
|
102
105
|
"security_id": item.get("securityId") or "",
|
|
103
106
|
"encrypt_job_id": item.get("encryptJobId") or "",
|
|
104
107
|
"unread": item.get("unreadMsgCount") or 0,
|
|
@@ -156,7 +159,7 @@ def chat_cmd(ctx: click.Context, page: int, from_who: str | None, days: int | No
|
|
|
156
159
|
),
|
|
157
160
|
hints={"next_actions": [
|
|
158
161
|
"boss detail <security_id> — 查看职位详情",
|
|
159
|
-
"boss chatmsg <
|
|
162
|
+
"boss chatmsg <uid> — 查看消息后继续沟通(uid 见上方输出)",
|
|
160
163
|
]},
|
|
161
164
|
)
|
|
162
165
|
return
|
|
@@ -179,6 +182,7 @@ def chat_cmd(ctx: click.Context, page: int, from_who: str | None, days: int | No
|
|
|
179
182
|
("Boss", "name", "bold cyan"),
|
|
180
183
|
("职称", "title", "dim"),
|
|
181
184
|
("公司", "brand_name", "green"),
|
|
185
|
+
("uid", "uid", "dim"),
|
|
182
186
|
("发起方", "initiated_by", "magenta"),
|
|
183
187
|
("未读", "unread", "red"),
|
|
184
188
|
("已读", "msg_status", "dim"),
|
|
@@ -192,7 +196,7 @@ def chat_cmd(ctx: click.Context, page: int, from_who: str | None, days: int | No
|
|
|
192
196
|
render=_render,
|
|
193
197
|
hints={"next_actions": [
|
|
194
198
|
"boss detail <security_id> — 查看职位详情",
|
|
195
|
-
"boss chatmsg <
|
|
199
|
+
"boss chatmsg <uid> — 查看消息后继续沟通(uid 见上方输出)",
|
|
196
200
|
]},
|
|
197
201
|
)
|
|
198
202
|
|
|
@@ -7,7 +7,7 @@ import io
|
|
|
7
7
|
import json
|
|
8
8
|
from typing import Any
|
|
9
9
|
|
|
10
|
-
from boss_agent_cli.commands.chat_utils import sanitize_csv_cell, escape_md_cell, GROUP_ORDER
|
|
10
|
+
from boss_agent_cli.commands.chat_utils import sanitize_csv_cell, escape_md_cell, contact_identity, GROUP_ORDER
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def prepare_render_data(
|
|
@@ -32,7 +32,7 @@ def prepare_render_data(
|
|
|
32
32
|
me_read = sum(1 for x in me_items if x.get("msg_status") == "已读")
|
|
33
33
|
me_unread = len(me_items) - me_read
|
|
34
34
|
|
|
35
|
-
added_ids = {item
|
|
35
|
+
added_ids = {contact_identity(item) for item in diff_result.get("added", [])}
|
|
36
36
|
|
|
37
37
|
# diff 摘要
|
|
38
38
|
diff_summary = None
|
|
@@ -82,12 +82,13 @@ def prepare_render_data(
|
|
|
82
82
|
for item in group_items:
|
|
83
83
|
global_idx += 1
|
|
84
84
|
sid = item.get("security_id", "")
|
|
85
|
-
|
|
85
|
+
key = contact_identity(item)
|
|
86
|
+
is_new = key in added_ids
|
|
86
87
|
ref = f"S{global_idx}"
|
|
87
88
|
msg = str(item.get("last_msg") or "-")
|
|
88
89
|
unread = item.get("unread") or 0
|
|
89
90
|
rows.append({
|
|
90
|
-
"ref": ref, "is_new": is_new, "sid": sid,
|
|
91
|
+
"ref": ref, "is_new": is_new, "sid": sid, "key": key,
|
|
91
92
|
"brand_name": item.get("brand_name") or "-",
|
|
92
93
|
"name": item.get("name") or "-",
|
|
93
94
|
"title": item.get("title") or "-",
|
|
@@ -96,7 +97,7 @@ def prepare_render_data(
|
|
|
96
97
|
"msg_status": item.get("msg_status") or "-",
|
|
97
98
|
"last_msg": msg,
|
|
98
99
|
})
|
|
99
|
-
id_map.append((ref,
|
|
100
|
+
id_map.append((ref, key, f"{item.get('brand_name') or '-'} {item.get('name') or '-'}"))
|
|
100
101
|
|
|
101
102
|
sections.append({"subtitle": subtitle, "rows": rows})
|
|
102
103
|
|
|
@@ -135,7 +136,7 @@ def _render_csv(friends: list[dict[str, Any]]) -> str:
|
|
|
135
136
|
fields = [
|
|
136
137
|
"name", "title", "brand_name", "initiated_by",
|
|
137
138
|
"msg_status", "unread", "last_msg", "last_time",
|
|
138
|
-
"security_id", "encrypt_job_id",
|
|
139
|
+
"uid", "security_id", "encrypt_job_id",
|
|
139
140
|
]
|
|
140
141
|
buf = io.StringIO(newline="")
|
|
141
142
|
writer = csv.DictWriter(buf, fieldnames=fields, extrasaction="ignore")
|
|
@@ -205,15 +206,16 @@ def _render_markdown(
|
|
|
205
206
|
|
|
206
207
|
if rd["id_map"]:
|
|
207
208
|
lines.append("<details>")
|
|
208
|
-
lines.append("<summary
|
|
209
|
+
lines.append("<summary>联系人标识映射表(点击展开)</summary>")
|
|
209
210
|
lines.append("")
|
|
210
|
-
lines.append("| 编号 | 公司/联系人 |
|
|
211
|
-
lines.append("
|
|
212
|
-
for ref,
|
|
213
|
-
lines.append(f"| {ref} | {label} | {
|
|
211
|
+
lines.append("| 编号 | 公司/联系人 | 联系人标识(uid 优先) |")
|
|
212
|
+
lines.append("|------|------------|--------------------------|")
|
|
213
|
+
for ref, key, label in rd["id_map"]:
|
|
214
|
+
lines.append(f"| {ref} | {label} | {key} |")
|
|
214
215
|
lines.append("")
|
|
215
216
|
lines.append("</details>")
|
|
216
217
|
lines.append("")
|
|
218
|
+
lines.append("> 优先使用 uid 调用 `boss chatmsg <uid>`;缺少 uid 的旧记录会回退展示可能已失效的 security_id。")
|
|
217
219
|
|
|
218
220
|
return "\n".join(lines) + "\n"
|
|
219
221
|
|
|
@@ -224,7 +226,7 @@ def _render_html(
|
|
|
224
226
|
days: int | None,
|
|
225
227
|
diff_result: dict[str, Any],
|
|
226
228
|
) -> str:
|
|
227
|
-
"""渲染为 HTML 格式,含分组、diff
|
|
229
|
+
"""渲染为 HTML 格式,含分组、diff 标记和联系人标识映射。"""
|
|
228
230
|
esc = _html.escape
|
|
229
231
|
rd = prepare_render_data(friends, from_who, diff_result)
|
|
230
232
|
|
|
@@ -284,17 +286,18 @@ def _render_html(
|
|
|
284
286
|
<tbody>{''.join(rrows)}</tbody>
|
|
285
287
|
</table>"""
|
|
286
288
|
|
|
287
|
-
#
|
|
289
|
+
# 联系人标识映射表
|
|
288
290
|
map_rows = []
|
|
289
|
-
for ref,
|
|
290
|
-
map_rows.append(f"<tr><td>{ref}</td><td>{esc(label)}</td><td class='sid'>{esc(
|
|
291
|
+
for ref, key, label in rd["id_map"]:
|
|
292
|
+
map_rows.append(f"<tr><td>{ref}</td><td>{esc(label)}</td><td class='sid'>{esc(key)}</td></tr>")
|
|
291
293
|
map_html = f"""
|
|
292
294
|
<details>
|
|
293
|
-
<summary
|
|
295
|
+
<summary>联系人标识映射表(点击展开)</summary>
|
|
294
296
|
<table>
|
|
295
|
-
<thead><tr><th>编号</th><th>公司/联系人</th><th
|
|
297
|
+
<thead><tr><th>编号</th><th>公司/联系人</th><th>联系人标识(uid 优先)</th></tr></thead>
|
|
296
298
|
<tbody>{''.join(map_rows)}</tbody>
|
|
297
299
|
</table>
|
|
300
|
+
<p>优先使用 uid;缺少 uid 的旧记录会回退展示可能已失效的 security_id。</p>
|
|
298
301
|
</details>""" if map_rows else ""
|
|
299
302
|
|
|
300
303
|
return f"""<!DOCTYPE html>
|
|
@@ -5,13 +5,14 @@ import json
|
|
|
5
5
|
import os
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
+
from boss_agent_cli.commands.chat_utils import contact_identity
|
|
8
9
|
from boss_agent_cli.output import Logger
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def save_snapshot_and_diff(
|
|
12
13
|
snapshot_dir: str, friends: list[dict[str, Any]], logger: Logger
|
|
13
14
|
) -> dict[str, Any]:
|
|
14
|
-
"""保存当日 JSON
|
|
15
|
+
"""保存当日 JSON 快照(uid 优先,旧数据回退 security_id)并与上次对比。"""
|
|
15
16
|
os.makedirs(snapshot_dir, exist_ok=True)
|
|
16
17
|
today = datetime.date.today().isoformat()
|
|
17
18
|
snapshot_path = os.path.join(snapshot_dir, f"{today}.json")
|
|
@@ -24,16 +25,16 @@ def save_snapshot_and_diff(
|
|
|
24
25
|
prev_today = json.load(f)
|
|
25
26
|
if isinstance(prev_today, list):
|
|
26
27
|
for item in prev_today:
|
|
27
|
-
if isinstance(item, dict) and item
|
|
28
|
-
existing[item
|
|
28
|
+
if isinstance(item, dict) and contact_identity(item):
|
|
29
|
+
existing[contact_identity(item)] = item
|
|
29
30
|
except (json.JSONDecodeError, OSError):
|
|
30
31
|
pass
|
|
31
32
|
|
|
32
33
|
# 用当前数据更新(新数据优先)
|
|
33
34
|
for item in friends:
|
|
34
|
-
|
|
35
|
-
if
|
|
36
|
-
existing[
|
|
35
|
+
key = contact_identity(item)
|
|
36
|
+
if key:
|
|
37
|
+
existing[key] = item
|
|
37
38
|
|
|
38
39
|
merged = list(existing.values())
|
|
39
40
|
|
|
@@ -50,22 +51,22 @@ def save_snapshot_and_diff(
|
|
|
50
51
|
if prev_friends is None:
|
|
51
52
|
return {"is_first": True, "added": [], "removed": [], "new_unread": []}
|
|
52
53
|
|
|
53
|
-
#
|
|
54
|
-
curr_map = {item
|
|
55
|
-
prev_map = {item
|
|
54
|
+
# 用稳定标识(uid 优先)做 key 对比
|
|
55
|
+
curr_map = {contact_identity(item): item for item in merged if contact_identity(item)}
|
|
56
|
+
prev_map = {contact_identity(item): item for item in prev_friends if contact_identity(item)}
|
|
56
57
|
curr_ids = set(curr_map)
|
|
57
58
|
prev_ids = set(prev_map)
|
|
58
59
|
|
|
59
|
-
added = [curr_map[
|
|
60
|
-
removed = [prev_map[
|
|
60
|
+
added = [curr_map[key] for key in (curr_ids - prev_ids)]
|
|
61
|
+
removed = [prev_map[key] for key in (prev_ids - curr_ids)]
|
|
61
62
|
|
|
62
63
|
# 新增未读:检测任何未读增量
|
|
63
64
|
new_unread = []
|
|
64
|
-
for
|
|
65
|
-
prev_unread = prev_map[
|
|
66
|
-
curr_unread = curr_map[
|
|
65
|
+
for key in (curr_ids & prev_ids):
|
|
66
|
+
prev_unread = prev_map[key].get("unread", 0) or 0
|
|
67
|
+
curr_unread = curr_map[key].get("unread", 0) or 0
|
|
67
68
|
if curr_unread > prev_unread:
|
|
68
|
-
new_unread.append(curr_map[
|
|
69
|
+
new_unread.append(curr_map[key])
|
|
69
70
|
|
|
70
71
|
return {
|
|
71
72
|
"is_first": False,
|
|
@@ -4,12 +4,12 @@ from boss_agent_cli.auth.manager import AuthManager
|
|
|
4
4
|
from boss_agent_cli.chat_summary import summarize_messages
|
|
5
5
|
from boss_agent_cli.compliance import require_compliance_allowed
|
|
6
6
|
from boss_agent_cli.commands._platform import get_platform_instance
|
|
7
|
-
from boss_agent_cli.commands.contact_lookup import resolve_friend_or_emit
|
|
7
|
+
from boss_agent_cli.commands.contact_lookup import current_friend_security_id_or_emit, resolve_friend_or_emit
|
|
8
8
|
from boss_agent_cli.display import boss_command_for_ctx, handle_auth_errors, handle_not_supported, handle_output, handle_platform_error_output, render_message_panel
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
@click.command("chat-summary")
|
|
12
|
-
@click.argument("security_id")
|
|
12
|
+
@click.argument("security_id", metavar="UID_OR_SECURITY_ID")
|
|
13
13
|
@click.option("--page", default=1, help="页码")
|
|
14
14
|
@click.option("--count", default=20, help="每页消息数量")
|
|
15
15
|
@click.pass_context
|
|
@@ -28,9 +28,13 @@ def chat_summary_cmd(ctx: click.Context, security_id: str, page: int, count: int
|
|
|
28
28
|
return
|
|
29
29
|
gid = str(friend_item.get("uid", ""))
|
|
30
30
|
friend_name = friend_item.get("name") or "-"
|
|
31
|
+
# securityId 是每请求轮换的令牌,必须用本次 friend_list 返回的新值。
|
|
32
|
+
fresh_security_id = current_friend_security_id_or_emit(ctx, "chat-summary", friend_item)
|
|
33
|
+
if fresh_security_id is None:
|
|
34
|
+
return
|
|
31
35
|
|
|
32
36
|
try:
|
|
33
|
-
resp = platform.chat_history(gid,
|
|
37
|
+
resp = platform.chat_history(gid, fresh_security_id, page=page, count=count)
|
|
34
38
|
except NotImplementedError as exc:
|
|
35
39
|
handle_not_supported(ctx, "chat-summary", exc, fallback_message="当前平台不支持聊天记录能力")
|
|
36
40
|
return
|
|
@@ -45,7 +49,8 @@ def chat_summary_cmd(ctx: click.Context, security_id: str, page: int, count: int
|
|
|
45
49
|
ctx,
|
|
46
50
|
"chat-summary",
|
|
47
51
|
{
|
|
48
|
-
"
|
|
52
|
+
"uid": gid,
|
|
53
|
+
"security_id": fresh_security_id,
|
|
49
54
|
"name": friend_name,
|
|
50
55
|
**summary,
|
|
51
56
|
},
|
|
@@ -53,7 +58,7 @@ def chat_summary_cmd(ctx: click.Context, security_id: str, page: int, count: int
|
|
|
53
58
|
hints={
|
|
54
59
|
"next_actions": [
|
|
55
60
|
boss_command_for_ctx(ctx, "chat"),
|
|
56
|
-
boss_command_for_ctx(ctx, f"chatmsg {
|
|
61
|
+
boss_command_for_ctx(ctx, f"chatmsg {gid}"),
|
|
57
62
|
]
|
|
58
63
|
},
|
|
59
64
|
)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Chat 共享常量和安全工具函数。"""
|
|
2
2
|
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
3
5
|
# relationType 映射:API 返回值 → 可读标签(key 类型放宽为 object,兼容 API 返回 None/未知值走 default)
|
|
4
6
|
RELATION_LABELS: dict[object, str] = {1: "对方主动", 2: "我主动", 3: "投递"}
|
|
5
7
|
FROM_FILTER = {"boss": 1, "me": 2}
|
|
@@ -9,6 +11,20 @@ MSG_STATUS_LABELS = {1: "未读", 2: "已读"}
|
|
|
9
11
|
GROUP_ORDER = ["对方主动", "我主动", "投递"]
|
|
10
12
|
|
|
11
13
|
|
|
14
|
+
def contact_identity(item: dict[str, Any]) -> str:
|
|
15
|
+
"""返回联系人的稳定标识:优先 ``uid``,缺失时回退 ``security_id``。
|
|
16
|
+
|
|
17
|
+
``security_id`` 是 BOSS 每次请求轮换的加密令牌,同一个联系人在两次请求中
|
|
18
|
+
会拿到不同的值。因此凡是需要「跨请求比较同一个联系人」的场景
|
|
19
|
+
(快照合并、diff、映射表)都必须以 ``uid`` 为主键,
|
|
20
|
+
仅对缺少 uid 的历史快照数据回退到 ``security_id``。
|
|
21
|
+
"""
|
|
22
|
+
uid = item.get("uid")
|
|
23
|
+
if uid not in (None, ""):
|
|
24
|
+
return str(uid)
|
|
25
|
+
return str(item.get("security_id") or "")
|
|
26
|
+
|
|
27
|
+
|
|
12
28
|
def sanitize_csv_cell(value: str) -> str:
|
|
13
29
|
"""防止 CSV 公式注入:以 =+@- 开头的值前置单引号;过滤 Tab 和回车。"""
|
|
14
30
|
if not isinstance(value, str):
|
|
@@ -6,7 +6,7 @@ import click
|
|
|
6
6
|
|
|
7
7
|
from boss_agent_cli.auth.manager import AuthManager
|
|
8
8
|
from boss_agent_cli.compliance import require_compliance_allowed
|
|
9
|
-
from boss_agent_cli.commands.contact_lookup import resolve_friend_or_emit
|
|
9
|
+
from boss_agent_cli.commands.contact_lookup import current_friend_security_id_or_emit, resolve_friend_or_emit
|
|
10
10
|
from boss_agent_cli.commands._platform import get_platform_instance
|
|
11
11
|
from boss_agent_cli.display import boss_command_for_ctx, handle_auth_errors, handle_not_supported, handle_output, handle_platform_error_output, render_simple_list
|
|
12
12
|
|
|
@@ -17,14 +17,17 @@ _MSG_TYPE_MAP = {
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
@click.command("chatmsg")
|
|
20
|
-
@click.argument("security_id")
|
|
20
|
+
@click.argument("security_id", metavar="UID_OR_SECURITY_ID")
|
|
21
21
|
@click.option("--page", default=1, help="页码")
|
|
22
22
|
@click.option("--count", default=20, help="每页消息数量")
|
|
23
23
|
@click.option("--raw", "show_raw", is_flag=True, default=False, help="输出保真结构化消息字段")
|
|
24
24
|
@click.pass_context
|
|
25
25
|
@handle_auth_errors("chatmsg")
|
|
26
26
|
def chatmsg_cmd(ctx: click.Context, security_id: str, page: int, count: int, show_raw: bool) -> None:
|
|
27
|
-
"""查看与指定好友的聊天消息历史
|
|
27
|
+
"""查看与指定好友的聊天消息历史
|
|
28
|
+
|
|
29
|
+
SECURITY_ID 可传 boss chat 输出的 uid(推荐,跨请求稳定)或 security_id。
|
|
30
|
+
"""
|
|
28
31
|
if not require_compliance_allowed(ctx, "chatmsg"):
|
|
29
32
|
ctx.exit(1)
|
|
30
33
|
|
|
@@ -38,15 +41,23 @@ def chatmsg_cmd(ctx: click.Context, security_id: str, page: int, count: int, sho
|
|
|
38
41
|
"chatmsg",
|
|
39
42
|
platform,
|
|
40
43
|
security_id,
|
|
41
|
-
not_found_message=
|
|
44
|
+
not_found_message=(
|
|
45
|
+
f"未在沟通列表中找到联系人 {security_id},请确认该联系人存在"
|
|
46
|
+
"(可用 boss chat 输出的 uid 或 security_id)"
|
|
47
|
+
),
|
|
42
48
|
)
|
|
43
49
|
if friend_item is None:
|
|
44
50
|
return
|
|
45
51
|
gid = str(friend_item.get("uid", ""))
|
|
46
52
|
friend_name = friend_item.get("name") or "-"
|
|
53
|
+
# securityId 是每请求轮换的令牌,必须用本次 friend_list 返回的新值,
|
|
54
|
+
# 否则会拿一个已失效的令牌去请求消息历史。
|
|
55
|
+
fresh_security_id = current_friend_security_id_or_emit(ctx, "chatmsg", friend_item)
|
|
56
|
+
if fresh_security_id is None:
|
|
57
|
+
return
|
|
47
58
|
|
|
48
59
|
try:
|
|
49
|
-
resp = platform.chat_history(gid,
|
|
60
|
+
resp = platform.chat_history(gid, fresh_security_id, page=page, count=count)
|
|
50
61
|
except NotImplementedError as exc:
|
|
51
62
|
handle_not_supported(ctx, "chatmsg", exc, fallback_message="当前平台不支持聊天记录能力")
|
|
52
63
|
return
|
|
@@ -77,7 +88,7 @@ def chatmsg_cmd(ctx: click.Context, security_id: str, page: int, count: int, sho
|
|
|
77
88
|
render=_render,
|
|
78
89
|
hints={"next_actions": [
|
|
79
90
|
f"{boss_command_for_ctx(ctx, 'chat')} — 返回沟通列表",
|
|
80
|
-
f"{boss_command_for_ctx(ctx, f'detail {
|
|
91
|
+
f"{boss_command_for_ctx(ctx, f'detail {fresh_security_id}')} — 查看职位详情",
|
|
81
92
|
]},
|
|
82
93
|
)
|
|
83
94
|
|
|
@@ -5,20 +5,24 @@ class FriendLookupLimitExceeded(RuntimeError):
|
|
|
5
5
|
"""Raised when paginated friend lookup cannot prove completion safely."""
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
def
|
|
8
|
+
def find_friend(
|
|
9
9
|
platform: Any,
|
|
10
|
-
|
|
10
|
+
identifier: str | int,
|
|
11
11
|
*,
|
|
12
12
|
start_page: int = 1,
|
|
13
13
|
max_pages: int = 50,
|
|
14
14
|
) -> tuple[dict[str, Any] | None, dict[str, Any] | None]:
|
|
15
|
-
"""分页遍历沟通列表,按 security_id 查找联系人。
|
|
15
|
+
"""分页遍历沟通列表,按 ``uid`` 或 ``security_id`` 查找联系人。
|
|
16
16
|
|
|
17
17
|
返回 (friend_item, error_response):
|
|
18
18
|
- 找到联系人:返回 (item, None)
|
|
19
19
|
- 平台返回失败响应:返回 (None, raw_response)
|
|
20
20
|
- 遍历完成仍未找到:返回 (None, None)
|
|
21
21
|
"""
|
|
22
|
+
needle = str(identifier).strip()
|
|
23
|
+
if not needle:
|
|
24
|
+
return None, None
|
|
25
|
+
|
|
22
26
|
page = start_page
|
|
23
27
|
terminated = False
|
|
24
28
|
seen_signatures: set[tuple[str, ...]] = set()
|
|
@@ -29,11 +33,18 @@ def find_friend_by_security_id(
|
|
|
29
33
|
|
|
30
34
|
platform_data = platform.unwrap_data(resp) or {}
|
|
31
35
|
items = platform_data.get("result") or platform_data.get("friendList") or []
|
|
32
|
-
for item in items
|
|
33
|
-
|
|
36
|
+
dict_items = [item for item in items if isinstance(item, dict)]
|
|
37
|
+
# 同一页先完整检查稳定 uid,再回退到轮换的 securityId。这样即使某个
|
|
38
|
+
# securityId 恰好与另一条记录的 uid 文本相同,也不会命中错误联系人。
|
|
39
|
+
for item in dict_items:
|
|
40
|
+
if str(item.get("uid") or "").strip() == needle:
|
|
41
|
+
return item, None
|
|
42
|
+
for item in dict_items:
|
|
43
|
+
if str(item.get("securityId") or "") == needle:
|
|
34
44
|
return item, None
|
|
35
45
|
|
|
36
|
-
|
|
46
|
+
# securityId 会在请求间轮换,不能用于重复页判定;有 uid 时必须优先用 uid。
|
|
47
|
+
signature = tuple(str(item.get("uid") or item.get("securityId") or "") for item in dict_items)
|
|
37
48
|
if signature in seen_signatures:
|
|
38
49
|
terminated = True
|
|
39
50
|
break
|
|
@@ -50,25 +61,57 @@ def find_friend_by_security_id(
|
|
|
50
61
|
return None, None
|
|
51
62
|
|
|
52
63
|
|
|
64
|
+
# 向后兼容:旧名称保留为别名(匹配逻辑已放宽为 uid 或 security_id)
|
|
65
|
+
find_friend_by_security_id = find_friend
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def current_friend_security_id_or_emit(
|
|
69
|
+
ctx: Any,
|
|
70
|
+
command: str,
|
|
71
|
+
friend_item: dict[str, Any],
|
|
72
|
+
) -> str | None:
|
|
73
|
+
"""读取本次 friend_list 返回的 securityId;缺失时安全停止。
|
|
74
|
+
|
|
75
|
+
调用方传入的标识可能是 uid,绝不能在 securityId 缺失时把 uid 猜作
|
|
76
|
+
securityId,尤其不能用于联系方式交换等写请求。
|
|
77
|
+
"""
|
|
78
|
+
from boss_agent_cli.display import handle_error_output
|
|
79
|
+
|
|
80
|
+
security_id = str(friend_item.get("securityId") or friend_item.get("security_id") or "").strip()
|
|
81
|
+
if security_id:
|
|
82
|
+
return security_id
|
|
83
|
+
handle_error_output(
|
|
84
|
+
ctx,
|
|
85
|
+
command,
|
|
86
|
+
code="NETWORK_ERROR",
|
|
87
|
+
message="沟通列表返回的联系人缺少当前 securityId,已停止执行;请刷新沟通列表后重试",
|
|
88
|
+
)
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
|
|
53
92
|
def resolve_friend_or_emit(
|
|
54
93
|
ctx: Any,
|
|
55
94
|
command: str,
|
|
56
95
|
platform: Any,
|
|
57
|
-
|
|
96
|
+
identifier: str,
|
|
58
97
|
*,
|
|
59
98
|
not_found_message: str | None = None,
|
|
60
99
|
) -> dict[str, Any] | None:
|
|
61
|
-
"""按 security_id
|
|
100
|
+
"""按 uid(或 security_id)定位联系人;失败时输出统一错误信封并返回 None。
|
|
62
101
|
|
|
63
102
|
封装 mark/exchange/chatmsg/chat-summary 共用的「解析 + 错误处理」样板:
|
|
64
103
|
NotImplementedError→NOT_SUPPORTED、FriendLookupLimitExceeded→NETWORK_ERROR、
|
|
65
104
|
平台失败→按错误码、未找到→JOB_NOT_FOUND。成功返回 friend_item;否则输出错误信封
|
|
66
105
|
并返回 None(调用方应立即 return)。
|
|
106
|
+
|
|
107
|
+
注意:调用方**不要**把 identifier 直接当作 securityId 传给平台 API——
|
|
108
|
+
securityId 是每请求轮换的令牌,必须使用本次 friend_list 返回的
|
|
109
|
+
``friend_item["securityId"]``。
|
|
67
110
|
"""
|
|
68
111
|
from boss_agent_cli.display import handle_error_output, handle_not_supported, handle_platform_error_output
|
|
69
112
|
|
|
70
113
|
try:
|
|
71
|
-
friend_item, friends_error =
|
|
114
|
+
friend_item, friends_error = find_friend(platform, identifier)
|
|
72
115
|
except NotImplementedError as exc:
|
|
73
116
|
handle_not_supported(ctx, command, exc, fallback_message="当前平台不支持沟通列表能力")
|
|
74
117
|
return None
|
|
@@ -90,7 +133,7 @@ def resolve_friend_or_emit(
|
|
|
90
133
|
ctx,
|
|
91
134
|
command,
|
|
92
135
|
code="JOB_NOT_FOUND",
|
|
93
|
-
message=not_found_message or f"
|
|
136
|
+
message=not_found_message or f"未在沟通列表中找到联系人 {identifier}",
|
|
94
137
|
)
|
|
95
138
|
return None
|
|
96
139
|
return friend_item
|
|
@@ -3,12 +3,12 @@ import click
|
|
|
3
3
|
from boss_agent_cli.auth.manager import AuthManager
|
|
4
4
|
from boss_agent_cli.compliance import require_compliance_allowed
|
|
5
5
|
from boss_agent_cli.commands._platform import get_platform_instance
|
|
6
|
-
from boss_agent_cli.commands.contact_lookup import resolve_friend_or_emit
|
|
6
|
+
from boss_agent_cli.commands.contact_lookup import current_friend_security_id_or_emit, resolve_friend_or_emit
|
|
7
7
|
from boss_agent_cli.display import boss_command_for_ctx, handle_auth_errors, handle_not_supported, handle_output, handle_platform_error_output, render_message_panel
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
@click.command("exchange")
|
|
11
|
-
@click.argument("security_id")
|
|
11
|
+
@click.argument("security_id", metavar="UID_OR_SECURITY_ID")
|
|
12
12
|
@click.option("--type", "exchange_type", default="phone", type=click.Choice(["phone", "wechat"]), help="交换类型:phone=手机号 / wechat=微信")
|
|
13
13
|
@click.pass_context
|
|
14
14
|
@handle_auth_errors("exchange")
|
|
@@ -30,9 +30,13 @@ def exchange_cmd(ctx: click.Context, security_id: str, exchange_type: str) -> No
|
|
|
30
30
|
return
|
|
31
31
|
uid = str(friend_item.get("uid", ""))
|
|
32
32
|
friend_name: str = friend_item.get("name") or "-"
|
|
33
|
+
# securityId 是每请求轮换的令牌,必须用本次 friend_list 返回的新值。
|
|
34
|
+
fresh_security_id = current_friend_security_id_or_emit(ctx, "exchange", friend_item)
|
|
35
|
+
if fresh_security_id is None:
|
|
36
|
+
return
|
|
33
37
|
|
|
34
38
|
try:
|
|
35
|
-
resp = platform.exchange_contact(
|
|
39
|
+
resp = platform.exchange_contact(fresh_security_id, uid, friend_name, exchange_type=type_id)
|
|
36
40
|
except NotImplementedError as exc:
|
|
37
41
|
handle_not_supported(ctx, "exchange", exc, fallback_message=f"当前平台不支持{type_label}交换能力")
|
|
38
42
|
return
|
|
@@ -41,7 +45,8 @@ def exchange_cmd(ctx: click.Context, security_id: str, exchange_type: str) -> No
|
|
|
41
45
|
return
|
|
42
46
|
|
|
43
47
|
data = {
|
|
44
|
-
"
|
|
48
|
+
"uid": uid,
|
|
49
|
+
"security_id": fresh_security_id,
|
|
45
50
|
"name": friend_name,
|
|
46
51
|
"type": type_label,
|
|
47
52
|
"message": f"已向 {friend_name} 发送{type_label}交换请求",
|
|
@@ -51,6 +56,6 @@ def exchange_cmd(ctx: click.Context, security_id: str, exchange_type: str) -> No
|
|
|
51
56
|
render=lambda d: render_message_panel(d, title="exchange"),
|
|
52
57
|
hints={"next_actions": [
|
|
53
58
|
f"{boss_command_for_ctx(ctx, 'chat')} — 返回沟通列表",
|
|
54
|
-
f"{boss_command_for_ctx(ctx, f'chatmsg {
|
|
59
|
+
f"{boss_command_for_ctx(ctx, f'chatmsg {uid}')} — 查看聊天记录",
|
|
55
60
|
]},
|
|
56
61
|
)
|