affinity-sdk 2.0.2__tar.gz → 2.0.3__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.
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/CHANGELOG.md +59 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/PKG-INFO +1 -1
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/__init__.py +2 -0
- affinity_sdk-2.0.3/affinity/_filtering/__init__.py +5 -0
- {affinity_sdk-2.0.2/affinity/cli/query → affinity_sdk-2.0.3/affinity/_filtering}/dates.py +26 -12
- {affinity_sdk-2.0.2/affinity/cli/query → affinity_sdk-2.0.3/affinity/_filtering}/filter_text.py +4 -4
- {affinity_sdk-2.0.2/affinity/cli/query → affinity_sdk-2.0.3/affinity/_filtering}/pushdown.py +11 -5
- affinity_sdk-2.0.3/affinity/_filtering/search.py +305 -0
- affinity_sdk-2.0.3/affinity/_filtering/where.py +108 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/company_cmds.py +2 -2
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/list_cmds.py +49 -18
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/org_cmds.py +75 -1
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/person_cmds.py +2 -2
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/query_cmd.py +4 -3
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/entity_search.py +18 -13
- affinity_sdk-2.0.3/affinity/cli/list_search.py +90 -0
- affinity_sdk-2.0.3/affinity/cli/query/dates.py +21 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/query/executor.py +137 -32
- affinity_sdk-2.0.3/affinity/cli/query/filter_text.py +5 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/query/filters.py +3 -2
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/query/models.py +6 -93
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/query/planner.py +35 -2
- affinity_sdk-2.0.3/affinity/cli/query/pushdown.py +12 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/compare.py +43 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/exceptions.py +9 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/models/org.py +9 -3
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/lists.py +493 -349
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/org.py +173 -2
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/cli/commands.md +11 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/filtering.md +11 -6
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/api-coverage.md +3 -1
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.claude-plugin/plugin.json +1 -1
- affinity_sdk-2.0.3/mcp/.mcp-bash/VERSION +1 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/core.sh +60 -7
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/tools.sh +13 -3
- affinity_sdk-2.0.3/mcp/.mcp-bash/vendor.json +1 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.registry/commands.generated.json +123 -5
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.registry/mcp-commands.json +10 -2
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/CHANGELOG.md +19 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/COMPATIBILITY +7 -2
- affinity_sdk-2.0.3/mcp/VERSION +1 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/mcp-bash.lock +2 -2
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/server.d/requirements.json +1 -1
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/server.d/server.meta.json +1 -1
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/tools/query/tool.meta.json +1 -1
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/plugins/affinity-sdk/.claude-plugin/plugin.json +1 -1
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/plugins/affinity-sdk/.claude-plugin/skills/affinity-python-sdk/SKILL.md +7 -4
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/pyproject.toml +1 -1
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/cassettes/company-lists-status.cassette.json +86 -86
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/cassettes/hook-blocks-without-key.cassette.json +63 -63
- affinity_sdk-2.0.3/tests/cowork/cassettes/no-key-skill-path.cassette.json +417 -0
- affinity_sdk-2.0.3/tests/cowork/cassettes/status-history.cassette.json +492 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_entity_ls_server_filter.py +84 -1
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_list_export_filter_safety.py +5 -3
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_list_export_server_filter.py +16 -2
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_org.py +32 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_query_server_filter.py +90 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_compare.py +35 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_filters.py +10 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_get_first.py +6 -0
- affinity_sdk-2.0.3/tests/test_list_entries_server_filter.py +270 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_teams_users_feedback.py +38 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tools/api_coverage.json +14 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tools/openapi_snapshots/2024-01-01.json +2 -2
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tools/openapi_snapshots/2026-07-15.json +2 -2
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tools/openapi_snapshots/2026-09-17.json +112 -2
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tools/search_golden.py +45 -5
- affinity_sdk-2.0.2/affinity/cli/list_search.py +0 -92
- affinity_sdk-2.0.2/mcp/.mcp-bash/VERSION +0 -1
- affinity_sdk-2.0.2/mcp/.mcp-bash/vendor.json +0 -1
- affinity_sdk-2.0.2/mcp/VERSION +0 -1
- affinity_sdk-2.0.2/tests/cowork/cassettes/no-key-skill-path.cassette.json +0 -395
- affinity_sdk-2.0.2/tests/cowork/cassettes/status-history.cassette.json +0 -469
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.claude-plugin/marketplace.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.cursor/rules/test-data-privacy.mdc +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.editorconfig +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.env.example +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.gitattributes +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/CODEOWNERS +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/PULL_REQUEST_TEMPLATE/cli_command.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/PULL_REQUEST_TEMPLATE/release.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/dependabot.yml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/labels.yml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/mcp-release-install-footer.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/pull_request_template.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/release-install-footer.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/workflows/ci.yml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/workflows/cowork-replay.yml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/workflows/docs.yml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/workflows/labels.yml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/workflows/linkcheck.yml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/workflows/openapi-validation.yml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/workflows/plugin-build.yml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/workflows/plugin-release.yml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/workflows/release-detect.yml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/workflows/release.yml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.github/workflows/version-check.yml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.gitignore +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/.pre-commit-config.yaml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/AGENTS.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/CODE_OF_CONDUCT.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/CONTRIBUTING.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/LICENSE +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/README.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/SECURITY.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/VERSIONING.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/_internal/__init__.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/_internal/keyfile.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/api_versions.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/__init__.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/__main__.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/_update_worker.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/click_compat.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/__init__.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/_entity_field.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/_entity_files_dump.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/_entity_files_read.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/_entity_files_upload.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/_list_entry_fields.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/_org_reads.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/_search_common.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/_v1_parsing.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/completion_cmd.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/config_cmds.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/discovery_cmds.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/entry_cmds.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/field_cmds.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/file_cmds.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/file_url_cmd.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/interaction_cmds.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/note_cmds.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/opportunity_cmds.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/org_read_cmds.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/relationship_strength_cmds.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/reminder_cmds.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/resolve_url_cmd.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/session_cmds.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/task_cmds.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/transcript_cmds.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/version_cmd.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/webhook_cmds.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/commands/whoami_cmd.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/config.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/confirm.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/constants.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/context.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/csv_utils.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/date_utils.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/decorators.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/errors.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/field_utils.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/formatters.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/help_json.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/interaction_utils.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/logging.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/main.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/mcp_limits.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/options.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/paths.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/progress.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/query/__init__.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/query/aggregates.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/query/cursor.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/query/exceptions.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/query/output.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/query/parser.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/query/progress.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/query/schema.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/render.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/resolve.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/resolvers.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/results.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/runner.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/serialization.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/session_cache.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/types.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/cli/update_check.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/client.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/clients/__init__.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/clients/http.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/clients/pipeline.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/criteria.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/downloads.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/field_resolve_utils.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/field_resolver.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/filters.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/hooks.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/inbound_webhooks.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/models/__init__.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/models/discovery.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/models/entities.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/models/interactions_v2.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/models/pagination.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/models/rate_limit_snapshot.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/models/relationships_v2.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/models/search.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/models/secondary.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/models/transcripts.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/models/types.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/policies.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/progress.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/py.typed +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/__init__.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/_discovery.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/_dropdown_options.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/_field_listing.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/_field_updates.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/_note_html.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/_org_reads.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/_search.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/_single_field.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/_v2_filters.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/companies.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/opportunities.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/persons.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/rate_limits.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/reminders.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/search.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/tasks.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/transcripts.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/v1_only.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/services/webhooks.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/affinity/types.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/codecov.yml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/cli-development-guide.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/ai-integrations/index.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/changelog.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/cli/index.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/cli/scripting.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/cli-reference.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/examples.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/getting-started.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/glossary.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/affinity-official-mcp-comparison.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/api-versions-and-routing.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/api-versions.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/authentication.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/claude-code-plugins.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/configuration.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/csv-export.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/datetime-handling.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/debugging-hooks.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/errors-and-retries.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/field-types-and-values.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/field-values.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/ids-and-types.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/migrating-to-2.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/models.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/opportunity-associations.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/pagination.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/performance.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/query-command.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/rate-limits.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/sync-vs-async.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/guides/webhooks.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/index.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/mcp/index.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/client.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/exceptions.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/filters.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/models.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/query-language.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/services/companies.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/services/lists.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/services/opportunities.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/services/persons.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/services/reminders.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/services/tasks.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/services/teams-users.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/services/v1/auth.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/services/v1/field-values.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/services/v1/fields.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/services/v1/files.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/services/v1/interactions.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/services/v1/notes.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/services/v1/relationships.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/services/webhooks.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/reference/types.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/docs/public/troubleshooting.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/examples/advanced_usage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/examples/async_lifecycle.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/examples/basic_usage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/examples/filter_builder.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/examples/hooks_debugging.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/examples/list_management.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/examples/resolve_helpers.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/examples/task_polling.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.claude-plugin/.gitignore +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.claude-plugin/skills/affinity-mcp-workflows/SKILL.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.claude-plugin/skills/pipeline-history/SKILL.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.claude-plugin/skills/query-language/SKILL.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.claude-plugin/skills/query-language/references/filter-operators.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.claude-plugin/skills/query-language/references/include-expand.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.claude-plugin/skills/query-language/references/output-formats.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.claude-plugin/skills/query-language/references/quantifiers.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.claude-plugin/xaffinity-mcp.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.gitignore +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/bin/mcp-bash +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/handlers/completion.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/handlers/lifecycle.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/handlers/logging.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/handlers/ping.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/handlers/prompts.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/handlers/resources.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/handlers/roots.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/handlers/tools.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/auth.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/capabilities.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/cli/common.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/cli/health.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/completion.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/elicitation.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/file_read.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/handler_helpers.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/hash.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/ids.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/io.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/json.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/lock.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/logging.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/meta_env.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/paginate.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/path.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/policy.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/progress-passthrough.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/progress.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/prompts.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/registry.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/require.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/resource_content.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/resource_match.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/resource_providers.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/resources.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/roots.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/rpc.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/runtime.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/spec.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/timeout.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/tools_policy.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/ui-templates.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/ui.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/uri.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/lib/validate.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/providers/echo.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/providers/file.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/providers/git.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/providers/https.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/providers/ui.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/sdk/tool-error.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/sdk/tool-sdk.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.mcp-bash/sdk/ui-sdk.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.registry/prompts.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.registry/resource-templates.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.registry/resources.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/.registry/ui-resources.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/Makefile +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/README.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/completions/entity-name.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/completions/list-name.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/completions/status-value.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/docs/DEBUGGING.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/icon.svg +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/lib/cache.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/lib/cli-detect.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/lib/cli-gateway.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/lib/common.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/lib/entity-types.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/lib/field-resolution.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/mcp-publisher.lock +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/mcpb.conf +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/prompts/change-status/change-status.completion.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/prompts/change-status/change-status.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/prompts/change-status/change-status.txt +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/prompts/interaction-brief/interaction-brief.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/prompts/interaction-brief/interaction-brief.txt +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/prompts/log-call/log-call.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/prompts/log-call/log-call.txt +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/prompts/log-interaction-and-update-workflow/log-interaction-and-update-workflow.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/prompts/log-interaction-and-update-workflow/log-interaction-and-update-workflow.txt +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/prompts/log-message/log-message.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/prompts/log-message/log-message.txt +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/prompts/pipeline-review/pipeline-review.completion.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/prompts/pipeline-review/pipeline-review.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/prompts/pipeline-review/pipeline-review.txt +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/prompts/prepare-briefing/prepare-briefing.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/prompts/prepare-briefing/prepare-briefing.txt +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/prompts/warm-intro/warm-intro.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/prompts/warm-intro/warm-intro.txt +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/providers/xaffinity.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/data-model/data-model.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/data-model/data-model.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/field-catalogs/field-catalogs.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/field-catalogs/field-catalogs.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/interaction-enums/interaction-enums.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/interaction-enums/interaction-enums.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/me/me.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/me/me.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/me-person-id/me-person-id.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/me-person-id/me-person-id.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/query-guide/query-guide.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/query-guide/query-guide.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/saved-views/saved-views.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/saved-views/saved-views.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/workflow-config/workflow-config.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/workflow-config/workflow-config.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/workflows-guide/workflows-guide.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/workflows-guide/workflows-guide.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/xaffinity-field-catalogs.completion.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/xaffinity-saved-views.completion.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/resources/xaffinity-workflow-config.completion.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/server.d/env.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/server.d/health-checks.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/server.d/policy.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/server.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/test/README.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/test/run.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/test/test_env_update_check.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/test-progress.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/tools/discover-commands/tool.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/tools/discover-commands/tool.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/tools/execute-read-command/tool.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/tools/execute-read-command/tool.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/tools/execute-write-command/tool.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/tools/execute-write-command/tool.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/tools/get-entity-dossier/tool.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/tools/get-entity-dossier/tool.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/tools/get-file-url/tool.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/tools/get-file-url/tool.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/tools/query/tool.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/tools/read-xaffinity-resource/tool.meta.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/tools/read-xaffinity-resource/tool.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/user-config.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/xaffinity-mcp-env.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mcp/xaffinity-mcp.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/mkdocs.yml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/plugins/affinity-sdk/.claude-plugin/skills/affinity-python-sdk/references/services.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/plugins/xaffinity-cli/.claude-plugin/plugin.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/plugins/xaffinity-cli/hooks/guard-env-read.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/plugins/xaffinity-cli/hooks/hooks.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/plugins/xaffinity-cli/hooks/pre-xaffinity.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/plugins/xaffinity-cli/hooks/session-setup.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/plugins/xaffinity-cli/skills/xaffinity-cli-usage/SKILL.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/plugins/xaffinity-cli/skills/xaffinity-cli-usage/references/files-and-writes.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/plugins/xaffinity-cli/skills/xaffinity-cli-usage/references/filtering.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/plugins/xaffinity-cli/skills/xaffinity-cli-usage/references/history-and-activity.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/plugins/xaffinity-cli/skills/xaffinity-cli-usage/references/interactions.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/plugins/xaffinity-cli/skills/xaffinity-cli-usage/references/query-guide.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/plugins/xaffinity-cli/skills/xaffinity-cli-usage/references/setup.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cli/query/test_cursor.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cli/test_company_create_dedup.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cli/test_company_ls_filter_rejection.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cli/test_formatters.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cli/test_output_formats_integration.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cli/test_person_create_dedup.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cli/test_person_ls_filter_rejection.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cli/test_query_filter_rejection.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cli/test_query_list_entry_relationships.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cli/test_query_list_export_parity.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cli/test_query_output_flattening.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cli/test_query_output_truncation.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cli/test_query_unreplied.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/conftest.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/.cowork-redact.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/README.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/fixtures/workdir-key/README.txt +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/fixtures/workdir-key/xaffinity-test-key +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/fixtures/workdir-nokey/README.txt +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/image/Dockerfile +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/image/fakeaff/build_fixtures.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/image/fakeaff/fixtures.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/image/fakeaff/server.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/image/xaffinity-wrapper.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/privacy-allow.txt +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/record.sh +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/scenarios/company-lists-status.yaml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/scenarios/hook-blocks-without-key.yaml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/scenarios/no-key-skill-path.yaml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/scenarios/status-history.yaml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/sessions/no-key.yaml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/cowork/sessions/with-key.yaml +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/field_write_mocks.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/fixtures/openapi/mini_openapi.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/fixtures/webhooks/field_value_updated.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/fixtures/webhooks/list_entry_created.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/fixtures/webhooks/organization_created.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/fixtures/webhooks/organization_merged.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/fixtures/webhooks/person_created.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/fixtures/webhooks/unknown_event.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/integration/README.md +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/integration/conftest.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/integration/setup_query_parity_data.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/integration/test_cli_writes.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/integration/test_query_parity_integration.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/integration/test_sdk_reads.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/integration/test_sdk_writes.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/integration/test_sdk_writes_runner.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/mcp/__init__.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/mcp/test_query_cursor.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/skill-evals/xaffinity-cli-usage-evals/evals.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/spec_guard.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_affinity_client_wrapper_additional_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_api_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_api_key_file_formats.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_api_key_resolution.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_api_version.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_api_version_per_operation.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_async_file_download_streaming.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_batch_get.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_api_version.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_column_limiting.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_command_context.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_company_cmds_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_company_get.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_config_cmds.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_context_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_csv_export.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_date_params.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_date_utils.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_datetime_formatting.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_datetime_header_offsets.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_domain_linkification.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_dotenv_resolution.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_dropdown_field_writes.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_enriched_field_writes.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_entity_field_writes.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_entity_files_dump.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_entry_field.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_entry_field_batch_writes.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_entry_field_multi_value_limits.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_error_hints.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_error_rendering.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_field_history.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_field_history_bulk.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_field_options_and_transcripts.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_field_utils.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_field_utils_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_field_utils_set_phase.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_field_value_humanization.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_file_url.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_files_ls.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_files_read.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_get_interaction_dates.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_help_json.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_interaction_date_chunking.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_interaction_utils.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_json_safety.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_list_cmds_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_list_expand_interactions.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_list_export_entity_scope.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_list_export_field_refresh.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_list_export_truncation_meta.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_missing_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_multi_value_truncation_warnings.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_no_network_commands.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_notes_writes.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_opportunity_cmds.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_opportunity_cmds_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_pager_logic.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_person_cmds_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_person_get.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_plugin_manifest.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_pre_xaffinity_hook.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_progress_manager.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_query_aggregates.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_query_cmd.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_query_cmd_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_query_dates.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_query_executor.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_query_expand_interactions.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_query_filters.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_query_integration.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_query_parser.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_query_planner.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_query_safety.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_query_schema.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_rate_limit_rendering.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_rate_limit_throttle.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_reminders.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_render_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_resolve_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_resolve_url_parsing.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_result_summary.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_safe_field_writes.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_search.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_serialization.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_session_cache.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_session_setup_hook.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_set_phase_atomicity.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_types.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_unreplied.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_update_check.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_v1_only_cmds.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_v1_parsing.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_webhooks.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_cli_write_ops.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_client.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_companies_create_dedup.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_companies_filter_rejection.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_duplicates_connections.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_env_helpers.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_exceptions_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_field_resolver.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_field_service_caching.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_field_types_c4.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_field_value_type.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_field_values_get_value.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_file_batch_get.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_file_download_streaming.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_file_list_batch.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_file_upload_helpers_and_validation.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_files_upload_returning_files.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_files_v2.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_generate_mcp_command_registry.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_get_fields_filter_includes.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_get_fields_pagination.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_hidden_field_values.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_hooks.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_http_client_additional_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_http_client_remaining_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_inbound_webhooks.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_integration_smoke.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_interaction_date_validation.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_interactions_and_files_regressions.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_isodatetime.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_list_entry_membership_and_opportunities.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_list_term.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_lists_v2_writes.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_mcp_bundle_env_and_completions.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_mcp_cli_version_check.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_mcp_entity_dossier.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_mcp_gateway_limits.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_mcp_history_bulk_gateway.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_mcp_limits.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_mcp_query_cli_error.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_mcp_read_truncation.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_mcp_resource_scripts.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_models.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_models_v2_2026_fields.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_multi_value_limits.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_new_features.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_notes_v2.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_notes_v2_writes.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_opportunities_filter_rejection.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_org_reads.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_pagination_iterators.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_persons_create_dedup.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_persons_filter_rejection.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_public_api.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_query_pushdown.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_rate_limit_v2.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_raw_pipeline_hook_events.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_readonly_factory.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_reminder_list_batch.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_reminders_v2.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_requirements_additional.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_sdk_interaction_dates.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_search.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_search_v2.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_service_get_parameter_combinations.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_services_companies_coverage_gaps.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_services_dropdown_options.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_services_entity_field_updates.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_services_field_value_changes.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_services_list_entries_filter_paths.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_services_lists_additional_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_services_lists_coverage_gaps.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_services_opportunities_additional_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_services_opportunities_coverage_gaps.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_services_opportunities_tasks_additional_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_services_persons_companies_additional_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_services_rate_limits_coverage_gaps.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_services_v1_only_async.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_services_v1_only_coverage_gaps.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_single_field_v2.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_small_modules_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_spec_guard.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_transport_and_readonly_mode.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_v1_association_parsing.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_v1_only_services_additional_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_v1_only_services_more_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_v2_request_bodies.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_validate_openapi_tool.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/test_webhooks_v2.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/unit/__init__.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tests/unit/test_exceptions.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tools/__init__.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tools/api_coverage.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tools/api_coverage_v1_snapshot.json +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tools/check_cli_patterns.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tools/generate_mcp_command_registry.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tools/generate_release_notes.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tools/generate_requirements_mapping.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tools/sync_mcp_bundle_env.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tools/sync_mcp_registry.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tools/sync_mcp_version.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tools/validate_mcp_registry_metadata.py +0 -0
- {affinity_sdk-2.0.2 → affinity_sdk-2.0.3}/tools/validate_openapi_models.py +0 -0
|
@@ -7,6 +7,65 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.0.3] - 2026-10-13
|
|
11
|
+
|
|
12
|
+
**Exception to semantic versioning** (the maintainer's call, as 2.0.2): some results change
|
|
13
|
+
(`list(filter=...)` pages, the local sort's order, date comparisons in filters). The
|
|
14
|
+
`### Changed` entries say how.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- **SDK list-entry filters go through Affinity's search:**
|
|
19
|
+
`lists.entries(id).list/pages/all/iter/get_first(filter=...)` send the part of the filter
|
|
20
|
+
Affinity's search can answer and check the whole filter locally, as `list export --filter`
|
|
21
|
+
does. Only when no part can go there (or the search is refused) is every entry read; that
|
|
22
|
+
case emits the new `ClientSideFilterWarning` (a `UserWarning`).
|
|
23
|
+
- **`list(filter=...)` returns the matches of one page read**, possibly fewer than `limit` (or
|
|
24
|
+
none), with a `next_cursor` that carries the filter: follow it, or use `iter()` for every
|
|
25
|
+
match. Before, it filtered only the first page and had no way to continue.
|
|
26
|
+
- **Filtered-read cursors are `xsearch2:`** (the filter travels as data, so multi-word field
|
|
27
|
+
names and dates round-trip) in the SDK, `list export`, and `company ls` / `person ls`;
|
|
28
|
+
2.0.2's `xsearch1:` cursors still work.
|
|
29
|
+
- **Field values can be named by id in text filters** (`F.field("field-123")`,
|
|
30
|
+
`--filter 'field-123 = …'`), as `query` allows `fields.<id>`.
|
|
31
|
+
- **`query`'s local sort follows Affinity's order** (when Affinity can't sort, e.g. an
|
|
32
|
+
unsortable field or a refused search): ranked dropdowns sort by rank (not by text),
|
|
33
|
+
multi-value fields by their first item, and text case-insensitively ("apple" before
|
|
34
|
+
"Zeta"); empty values stay last in both directions. Fields named only in `orderBy` are now
|
|
35
|
+
fetched (a local sort used to see them empty). Grouped rows (`groupBy`) sort by their keys
|
|
36
|
+
as before.
|
|
37
|
+
- **Quantifier queries (`all`/`none`/`exists`) on companies and persons no longer always need
|
|
38
|
+
`--max-records`**: their `where` must test a field, which Affinity's search applies first;
|
|
39
|
+
when it matches at most 100 records the query runs, otherwise it stops and names the count.
|
|
40
|
+
`--dry-run` gives a number of API calls instead of "UNBOUNDED" (with `--max-records` too),
|
|
41
|
+
which also fixes the MCP query tool's timeout for these queries (it fell back to 120 s).
|
|
42
|
+
Validation errors found while the query runs exit 2 with `validation_error`, like those
|
|
43
|
+
found while planning.
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
|
|
47
|
+
- **`server_filter=False`** on the list-entry `list/pages/all/iter/get_first` reads every entry
|
|
48
|
+
and filters locally (to compare or debug), and **`affinity.ClientSideFilterWarning`**.
|
|
49
|
+
- **More filters go to Affinity's search** (verified live): `starts_word` on single-company
|
|
50
|
+
fields, and `is_not_null` / `is_null` on location fields.
|
|
51
|
+
|
|
52
|
+
- **A team's members and accessible lists, every page** (V2, beta; API 2026-09-17+):
|
|
53
|
+
`teams.list|iter_members(team_id)` and `teams.list|iter_accessible_lists(team_id)`; CLI
|
|
54
|
+
`team members <id>` and `team lists <id>` (`team get` shows only the first 10).
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
|
|
58
|
+
- **Dates compare as dates in `>`, `>=`, `<`, `<=` and between**, everywhere filters run
|
|
59
|
+
(`query`, `--filter`, `--expand-filter`, `company ls` / `person ls --filter`, and the SDK's
|
|
60
|
+
`affinity.filters`): relative dates (`-30d`, `today`) and ISO dates are compared with the
|
|
61
|
+
field's timestamp in UTC. Before, the text filter compared them as text, so
|
|
62
|
+
`Close > "-30d"` matched every dated entry. Equality stays textual (`= "today"` matches the
|
|
63
|
+
word). Timestamps with `Z` or long fractions parse on Python 3.10 too.
|
|
64
|
+
- **`list export --field X --filter "Y = …"`** returned no rows: the filter's fields are now
|
|
65
|
+
fetched even when `--field` leaves them out of the output.
|
|
66
|
+
- **`Team` reads `updatedAt`** (`updated_at`), and accepts the `membersPreview` /
|
|
67
|
+
`accessibleListsPreview` blocks under their own names too.
|
|
68
|
+
|
|
10
69
|
## [2.0.2] - 2026-10-13
|
|
11
70
|
|
|
12
71
|
### Changed (breaking)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: affinity-sdk
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.3
|
|
4
4
|
Summary: A modern, strongly-typed Python SDK for the Affinity CRM API
|
|
5
5
|
Project-URL: Homepage, https://github.com/yaniv-golan/affinity-sdk
|
|
6
6
|
Project-URL: Documentation, https://yaniv-golan.github.io/affinity-sdk/latest/
|
|
@@ -47,6 +47,7 @@ from .exceptions import (
|
|
|
47
47
|
ApiVersionTooOldError,
|
|
48
48
|
AuthenticationError,
|
|
49
49
|
AuthorizationError,
|
|
50
|
+
ClientSideFilterWarning,
|
|
50
51
|
CompanyMergedError,
|
|
51
52
|
CompanyNotFoundError,
|
|
52
53
|
ConfigurationError,
|
|
@@ -108,6 +109,7 @@ __all__ = [
|
|
|
108
109
|
"AsyncAffinity",
|
|
109
110
|
# Exceptions
|
|
110
111
|
"AffinityError",
|
|
112
|
+
"ClientSideFilterWarning",
|
|
111
113
|
"AuthenticationError",
|
|
112
114
|
"AuthorizationError",
|
|
113
115
|
"NotFoundError",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Relative date parsing for query WHERE clauses.
|
|
2
2
|
|
|
3
3
|
This module provides parsing for relative date strings like "-30d", "today", etc.
|
|
4
|
-
|
|
4
|
+
Private module: not part of the public SDK API (the CLI and the SDK share it).
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
@@ -113,17 +113,31 @@ def parse_date_value(value: str) -> datetime | None:
|
|
|
113
113
|
pass
|
|
114
114
|
|
|
115
115
|
# Try ISO date
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
116
|
+
return parse_iso_datetime(value)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
_FRACTION = re.compile(r"(\.\d+)")
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def parse_iso_datetime(value: object) -> datetime | None:
|
|
123
|
+
"""An ISO 8601 date or date-time (``YYYY-MM-DD``, ``...THH:MM:SS[.fraction][Z|±HH:MM]``) as a
|
|
124
|
+
datetime, or ``None``. Tolerant of what Python 3.10's ``fromisoformat`` rejects: ``Z`` and
|
|
125
|
+
fractions that aren't 3 or 6 digits."""
|
|
126
|
+
if not isinstance(value, str) or not ISO_DATE_PATTERN.match(value.strip()):
|
|
127
|
+
return None
|
|
128
|
+
text = value.strip()
|
|
129
|
+
if text.endswith(("Z", "z")):
|
|
130
|
+
text = text[:-1] + "+00:00"
|
|
131
|
+
text = _FRACTION.sub(lambda m: (m.group(1) + "000000")[:7], text, count=1)
|
|
132
|
+
try:
|
|
133
|
+
return datetime.fromisoformat(text)
|
|
134
|
+
except ValueError:
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def as_utc(value: datetime) -> datetime:
|
|
139
|
+
"""``value`` with a time zone: naive values are taken as UTC."""
|
|
140
|
+
return value if value.tzinfo is not None else value.replace(tzinfo=timezone.utc)
|
|
127
141
|
|
|
128
142
|
|
|
129
143
|
def days_since(date: datetime, *, now: datetime | None = None) -> int:
|
{affinity_sdk-2.0.2/affinity/cli/query → affinity_sdk-2.0.3/affinity/_filtering}/filter_text.py
RENAMED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
language written two ways; this turns the first into the second, so both go through the same
|
|
5
5
|
server-side split (:mod:`.pushdown`). The text filter still runs locally exactly as before.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Private module: not part of the public SDK API (the CLI and the SDK share it).
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
|
-
from
|
|
13
|
-
from
|
|
12
|
+
from ..compare import map_operator
|
|
13
|
+
from ..filters import (
|
|
14
14
|
AndExpression,
|
|
15
15
|
FieldComparison,
|
|
16
16
|
FilterExpression,
|
|
@@ -18,7 +18,7 @@ from ...filters import (
|
|
|
18
18
|
OrExpression,
|
|
19
19
|
RawToken,
|
|
20
20
|
)
|
|
21
|
-
from .
|
|
21
|
+
from .where import WhereClause
|
|
22
22
|
|
|
23
23
|
# Row-level keys a list entry filter sees besides its fields (they aren't list fields).
|
|
24
24
|
ROW_KEYS = frozenset(
|
{affinity_sdk-2.0.2/affinity/cli/query → affinity_sdk-2.0.3/affinity/_filtering}/pushdown.py
RENAMED
|
@@ -9,7 +9,7 @@ A mapping is used only when it is in :data:`VERIFIED` — confirmed against the
|
|
|
9
9
|
``tools/search_golden.py`` (server count == local count on real data). Unverified mappings stay
|
|
10
10
|
local.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Private module: not part of the public SDK API (the CLI and the SDK share it).
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
15
|
from __future__ import annotations
|
|
@@ -21,11 +21,11 @@ from dataclasses import dataclass, field
|
|
|
21
21
|
from datetime import date, datetime, timedelta
|
|
22
22
|
from typing import Any
|
|
23
23
|
|
|
24
|
-
from
|
|
25
|
-
from
|
|
26
|
-
from
|
|
24
|
+
from ..compare import compare_values
|
|
25
|
+
from ..criteria import Condition, FilterGroup, filter_value_type
|
|
26
|
+
from ..models.entities import DropdownOption, FieldMetadata
|
|
27
27
|
from .dates import parse_date_value
|
|
28
|
-
from .
|
|
28
|
+
from .where import WhereClause
|
|
29
29
|
|
|
30
30
|
# (filter value type, server operator) pairs confirmed by tools/search_golden.py: no record the
|
|
31
31
|
# local filter keeps was missing from the server's answer (5 lists, about 3,750 entries,
|
|
@@ -78,6 +78,12 @@ VERIFIED: frozenset[tuple[str, str]] = frozenset(
|
|
|
78
78
|
("dropdown-multi", "has-all-of"),
|
|
79
79
|
("dropdown-multi", "has-none-of"),
|
|
80
80
|
("number", "is-not-equal-to"),
|
|
81
|
+
# Fourth run (lists with single-company and location fields), 2026-10-13
|
|
82
|
+
("company", "term"),
|
|
83
|
+
("company", "is-not-empty"),
|
|
84
|
+
("location", "is-not-empty"),
|
|
85
|
+
("location-multi", "is-empty"),
|
|
86
|
+
("location-multi", "is-not-empty"),
|
|
81
87
|
}
|
|
82
88
|
)
|
|
83
89
|
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"""Filtering list entries, companies and persons through Affinity's search, shared by the SDK
|
|
2
|
+
(``lists.entries(id).list/pages/all/iter(filter=...)``) and the CLI (``list export --filter``,
|
|
3
|
+
``company ls`` / ``person ls --filter``).
|
|
4
|
+
|
|
5
|
+
- :func:`plan_filter` splits a text filter (or :class:`~affinity.filters.FilterExpression`)
|
|
6
|
+
into the search body Affinity can answer and the rest, which the caller re-checks locally.
|
|
7
|
+
- Next-page cursors carry the filter, so a cursor alone resumes a filtered read:
|
|
8
|
+
``xsearch2:`` + base64 JSON ``{"k": "search"|"local", "u": next page URL, "b": search body,
|
|
9
|
+
"f": filter tree}``. ``k="search"`` re-sends ``b`` to ``u`` (a search's next page needs its
|
|
10
|
+
body); ``k="local"`` reads ``u`` (a plain page) and filters it locally. ``xsearch1:`` cursors
|
|
11
|
+
(filter as text, always a search) from 2.0.2 still decode.
|
|
12
|
+
- :class:`SearchCache` keeps fields, dropdown options and refused searches per list for a while.
|
|
13
|
+
|
|
14
|
+
Private module: not part of the public SDK API.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import base64
|
|
20
|
+
import json
|
|
21
|
+
import threading
|
|
22
|
+
import time
|
|
23
|
+
from collections.abc import Callable, Iterable, Sequence
|
|
24
|
+
from dataclasses import dataclass, field
|
|
25
|
+
from datetime import date, datetime
|
|
26
|
+
from typing import Any, Literal
|
|
27
|
+
from urllib.parse import urlparse
|
|
28
|
+
|
|
29
|
+
from ..criteria import SearchCriteria
|
|
30
|
+
from ..filters import (
|
|
31
|
+
AndExpression,
|
|
32
|
+
FieldComparison,
|
|
33
|
+
FilterExpression,
|
|
34
|
+
NotExpression,
|
|
35
|
+
OrExpression,
|
|
36
|
+
RawFilter,
|
|
37
|
+
RawToken,
|
|
38
|
+
parse,
|
|
39
|
+
)
|
|
40
|
+
from ..models.entities import DropdownOption, FieldMetadata
|
|
41
|
+
from .filter_text import ROW_KEYS, where_from_filter
|
|
42
|
+
from .pushdown import fields_index, split_where
|
|
43
|
+
from .where import WhereClause
|
|
44
|
+
|
|
45
|
+
CURSOR_V2 = "xsearch2:"
|
|
46
|
+
CURSOR_V1 = "xsearch1:"
|
|
47
|
+
CURSOR_PREFIXES = (CURSOR_V2, CURSOR_V1)
|
|
48
|
+
CACHE_TTL_SECONDS = 300.0
|
|
49
|
+
|
|
50
|
+
CursorKind = Literal["search", "local"]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# --- plan ---------------------------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class FilterPlan:
|
|
58
|
+
"""What Affinity's search applies of a filter (``body``; ``None``: nothing) and the notes."""
|
|
59
|
+
|
|
60
|
+
body: dict[str, Any] | None
|
|
61
|
+
field_ids: list[str] = field(default_factory=list) # fields the filter tests
|
|
62
|
+
pushed: list[str] = field(default_factory=list)
|
|
63
|
+
local: list[str] = field(default_factory=list)
|
|
64
|
+
caveats: list[str] = field(default_factory=list)
|
|
65
|
+
errors: list[str] = field(default_factory=list)
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def complete(self) -> bool:
|
|
69
|
+
"""Every condition went to the server."""
|
|
70
|
+
return self.body is not None and not self.local
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def reject_raw_filters(expr: FilterExpression) -> None:
|
|
74
|
+
"""``RawFilter`` can't be evaluated locally; refuse it anywhere in the tree."""
|
|
75
|
+
if isinstance(expr, RawFilter):
|
|
76
|
+
raise ValueError(
|
|
77
|
+
"A raw filter string can't filter list entries, companies or persons: "
|
|
78
|
+
"use a parsed filter (affinity.filters.parse or the F builder)"
|
|
79
|
+
)
|
|
80
|
+
for child in _children(expr):
|
|
81
|
+
reject_raw_filters(child)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _children(expr: FilterExpression) -> list[FilterExpression]:
|
|
85
|
+
if isinstance(expr, (AndExpression, OrExpression)):
|
|
86
|
+
return [expr.left, expr.right]
|
|
87
|
+
if isinstance(expr, NotExpression):
|
|
88
|
+
return [expr.expr]
|
|
89
|
+
return []
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _field_paths(where: WhereClause) -> set[str]:
|
|
93
|
+
paths: set[str] = set()
|
|
94
|
+
if where.path and where.path.startswith("fields."):
|
|
95
|
+
paths.add(where.path.removeprefix("fields."))
|
|
96
|
+
for child in [*(where.and_ or []), *(where.or_ or [])]:
|
|
97
|
+
paths |= _field_paths(child)
|
|
98
|
+
if where.not_ is not None:
|
|
99
|
+
paths |= _field_paths(where.not_)
|
|
100
|
+
return paths
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def referenced_field_names(expr: FilterExpression, row_keys: frozenset[str] = ROW_KEYS) -> set[str]:
|
|
104
|
+
"""The field names (or ids) a filter tests; record attributes (``row_keys``) excluded."""
|
|
105
|
+
return _field_paths(where_from_filter(expr, row_keys))
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def plan_filter(
|
|
109
|
+
expr: FilterExpression,
|
|
110
|
+
fields: Sequence[FieldMetadata],
|
|
111
|
+
options_for: Callable[[FieldMetadata], Sequence[DropdownOption]],
|
|
112
|
+
*,
|
|
113
|
+
row_keys: frozenset[str] = ROW_KEYS,
|
|
114
|
+
enriched: str = "or-empty",
|
|
115
|
+
) -> FilterPlan:
|
|
116
|
+
"""Split ``expr`` for these fields (pure: ``options_for`` supplies dropdown options)."""
|
|
117
|
+
where = where_from_filter(expr, row_keys)
|
|
118
|
+
split = split_where(where, fields, options_for, enriched=enriched)
|
|
119
|
+
index = fields_index(fields)
|
|
120
|
+
ids: list[str] = []
|
|
121
|
+
for name in sorted(_field_paths(where)):
|
|
122
|
+
meta = index.get(name) or index.get(name.lower())
|
|
123
|
+
if meta is not None and str(meta.id) not in ids:
|
|
124
|
+
ids.append(str(meta.id))
|
|
125
|
+
body = (
|
|
126
|
+
SearchCriteria(where=split.where, term=split.term, term_fields=split.term_fields).to_body()
|
|
127
|
+
if split.server_side
|
|
128
|
+
else None
|
|
129
|
+
)
|
|
130
|
+
return FilterPlan(
|
|
131
|
+
body=body,
|
|
132
|
+
field_ids=ids,
|
|
133
|
+
pushed=split.pushed,
|
|
134
|
+
local=split.local,
|
|
135
|
+
caveats=split.caveats,
|
|
136
|
+
errors=split.errors,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def dropdown_fields(expr: FilterExpression, fields: Sequence[FieldMetadata]) -> list[FieldMetadata]:
|
|
141
|
+
"""The dropdown fields a filter tests (their options are needed to plan it)."""
|
|
142
|
+
index = fields_index(fields)
|
|
143
|
+
out: list[FieldMetadata] = []
|
|
144
|
+
for name in sorted(referenced_field_names(expr)):
|
|
145
|
+
meta = index.get(name) or index.get(name.lower())
|
|
146
|
+
value_type = str(getattr(meta.value_type, "value", meta.value_type)) if meta else ""
|
|
147
|
+
if meta is not None and value_type in ("dropdown", "ranked-dropdown", "dropdown-multi"):
|
|
148
|
+
out.append(meta)
|
|
149
|
+
return out
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
# --- filter tree (cursor payload) ---------------------------------------------------------------
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _value_to_json(value: Any) -> Any:
|
|
156
|
+
if isinstance(value, RawToken):
|
|
157
|
+
return {"t": "raw", "v": value.token}
|
|
158
|
+
if isinstance(value, datetime):
|
|
159
|
+
return {"t": "datetime", "v": value.isoformat()}
|
|
160
|
+
if isinstance(value, date):
|
|
161
|
+
return {"t": "date", "v": value.isoformat()}
|
|
162
|
+
if isinstance(value, (list, tuple)):
|
|
163
|
+
return {"t": "list", "v": [_value_to_json(v) for v in value]}
|
|
164
|
+
if value is None or isinstance(value, (str, bool, int, float)):
|
|
165
|
+
return {"t": "value", "v": value}
|
|
166
|
+
raise ValueError(f"Can't put a {type(value).__name__} filter value in a cursor")
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _value_from_json(obj: Any) -> Any:
|
|
170
|
+
if not isinstance(obj, dict) or set(obj) != {"t", "v"}:
|
|
171
|
+
raise ValueError("bad value")
|
|
172
|
+
tag, v = obj["t"], obj["v"]
|
|
173
|
+
if tag == "raw":
|
|
174
|
+
if v != "*":
|
|
175
|
+
raise ValueError("bad raw token")
|
|
176
|
+
return RawToken("*")
|
|
177
|
+
if tag == "datetime" and isinstance(v, str):
|
|
178
|
+
return datetime.fromisoformat(v)
|
|
179
|
+
if tag == "date" and isinstance(v, str):
|
|
180
|
+
return date.fromisoformat(v)
|
|
181
|
+
if tag == "list" and isinstance(v, list):
|
|
182
|
+
return [_value_from_json(x) for x in v]
|
|
183
|
+
if tag == "value" and (v is None or isinstance(v, (str, bool, int, float))):
|
|
184
|
+
return v
|
|
185
|
+
raise ValueError("bad value")
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def filter_to_json(expr: FilterExpression) -> Any:
|
|
189
|
+
"""A filter expression as JSON data (round-trips, unlike ``to_string``)."""
|
|
190
|
+
if isinstance(expr, FieldComparison):
|
|
191
|
+
return {
|
|
192
|
+
"cmp": [expr.field_name, expr.operator, _value_to_json(expr.value)],
|
|
193
|
+
}
|
|
194
|
+
if isinstance(expr, AndExpression):
|
|
195
|
+
return {"and": [filter_to_json(expr.left), filter_to_json(expr.right)]}
|
|
196
|
+
if isinstance(expr, OrExpression):
|
|
197
|
+
return {"or": [filter_to_json(expr.left), filter_to_json(expr.right)]}
|
|
198
|
+
if isinstance(expr, NotExpression):
|
|
199
|
+
return {"not": filter_to_json(expr.expr)}
|
|
200
|
+
raise ValueError(f"Can't put a {type(expr).__name__} in a cursor")
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def filter_from_json(obj: Any, depth: int = 0) -> FilterExpression:
|
|
204
|
+
"""The filter expression :func:`filter_to_json` made; ``ValueError`` for anything else."""
|
|
205
|
+
if depth > 64 or not isinstance(obj, dict) or len(obj) != 1:
|
|
206
|
+
raise ValueError("bad filter")
|
|
207
|
+
((key, value),) = obj.items()
|
|
208
|
+
if key == "cmp" and isinstance(value, list) and len(value) == 3:
|
|
209
|
+
name, operator, raw = value
|
|
210
|
+
if not isinstance(name, str) or not isinstance(operator, str):
|
|
211
|
+
raise ValueError("bad comparison")
|
|
212
|
+
return FieldComparison(name, operator, _value_from_json(raw))
|
|
213
|
+
if key in ("and", "or") and isinstance(value, list) and len(value) == 2:
|
|
214
|
+
left, right = (filter_from_json(v, depth + 1) for v in value)
|
|
215
|
+
return AndExpression(left, right) if key == "and" else OrExpression(left, right)
|
|
216
|
+
if key == "not":
|
|
217
|
+
return NotExpression(filter_from_json(value, depth + 1))
|
|
218
|
+
raise ValueError("bad filter")
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
# --- cursors ------------------------------------------------------------------------------------
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@dataclass(frozen=True)
|
|
225
|
+
class SearchCursor:
|
|
226
|
+
"""A decoded filtered-read cursor."""
|
|
227
|
+
|
|
228
|
+
kind: CursorKind
|
|
229
|
+
url: str
|
|
230
|
+
body: dict[str, Any] | None
|
|
231
|
+
filter: FilterExpression
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def encode_cursor(
|
|
235
|
+
kind: CursorKind, url: str, body: dict[str, Any] | None, expr: FilterExpression
|
|
236
|
+
) -> str:
|
|
237
|
+
payload = {"k": kind, "u": url, "b": body, "f": filter_to_json(expr)}
|
|
238
|
+
raw = json.dumps(payload, separators=(",", ":")).encode()
|
|
239
|
+
return CURSOR_V2 + base64.urlsafe_b64encode(raw).decode().rstrip("=")
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def is_filter_cursor(cursor: str | None) -> bool:
|
|
243
|
+
return cursor is not None and cursor.startswith(CURSOR_PREFIXES)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def decode_cursor(cursor: str, *, path_suffixes: Iterable[str]) -> SearchCursor | None:
|
|
247
|
+
"""The filtered-read cursor in ``cursor`` (``None`` when it's a plain page URL). The
|
|
248
|
+
cursor is user input: anything malformed, or a cursor for another kind of record (its URL
|
|
249
|
+
path must end with one of ``path_suffixes``), raises ``ValueError``."""
|
|
250
|
+
if not is_filter_cursor(cursor):
|
|
251
|
+
return None
|
|
252
|
+
prefix = CURSOR_V2 if cursor.startswith(CURSOR_V2) else CURSOR_V1
|
|
253
|
+
encoded = cursor.removeprefix(prefix)
|
|
254
|
+
try:
|
|
255
|
+
data = json.loads(base64.urlsafe_b64decode(encoded + "=" * (-len(encoded) % 4)))
|
|
256
|
+
except (ValueError, TypeError) as e:
|
|
257
|
+
raise ValueError("This cursor is damaged: start the read again without it") from e
|
|
258
|
+
if not isinstance(data, dict) or not isinstance(data.get("u"), str):
|
|
259
|
+
raise ValueError("This cursor is damaged: start the read again without it")
|
|
260
|
+
url = data["u"]
|
|
261
|
+
if not any(urlparse(url).path.endswith(s) for s in path_suffixes):
|
|
262
|
+
raise ValueError("This cursor belongs to a different list or kind of record")
|
|
263
|
+
if prefix == CURSOR_V1:
|
|
264
|
+
body, text = data.get("b"), data.get("f")
|
|
265
|
+
if not isinstance(body, dict) or not isinstance(text, str):
|
|
266
|
+
raise ValueError("This cursor is damaged: start the read again without it")
|
|
267
|
+
return SearchCursor("search", url, body, parse(text))
|
|
268
|
+
kind, body = data.get("k"), data.get("b")
|
|
269
|
+
if kind not in ("search", "local") or (kind == "search" and not isinstance(body, dict)):
|
|
270
|
+
raise ValueError("This cursor is damaged: start the read again without it")
|
|
271
|
+
return SearchCursor(
|
|
272
|
+
kind, url, body if kind == "search" else None, filter_from_json(data.get("f"))
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
# --- cache --------------------------------------------------------------------------------------
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class SearchCache:
|
|
280
|
+
"""Per-client cache for planning searches: a list's fields (with filterability), dropdown
|
|
281
|
+
options, and lists whose search was refused (403 / 404 / unsupported). Entries expire
|
|
282
|
+
after ``ttl`` seconds; races only cost a duplicate fetch."""
|
|
283
|
+
|
|
284
|
+
def __init__(self, ttl: float = CACHE_TTL_SECONDS) -> None:
|
|
285
|
+
self._ttl = ttl
|
|
286
|
+
self._data: dict[tuple[str, ...], tuple[float, Any]] = {}
|
|
287
|
+
self._lock = threading.Lock()
|
|
288
|
+
|
|
289
|
+
def get(self, key: tuple[str, ...]) -> Any | None:
|
|
290
|
+
with self._lock:
|
|
291
|
+
hit = self._data.get(key)
|
|
292
|
+
if hit is None or hit[0] < time.monotonic():
|
|
293
|
+
self._data.pop(key, None)
|
|
294
|
+
return None
|
|
295
|
+
return hit[1]
|
|
296
|
+
|
|
297
|
+
def put(self, key: tuple[str, ...], value: Any) -> None:
|
|
298
|
+
with self._lock:
|
|
299
|
+
self._data[key] = (time.monotonic() + self._ttl, value)
|
|
300
|
+
|
|
301
|
+
def drop_scope(self, scope: str) -> None:
|
|
302
|
+
"""Forget everything cached for one list (or entity type), e.g. after a 400."""
|
|
303
|
+
with self._lock:
|
|
304
|
+
for key in [k for k in self._data if k[1:2] == (scope,)]:
|
|
305
|
+
del self._data[key]
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""WHERE clauses: the filter language's structured form (``query``'s JSON ``where``; the
|
|
2
|
+
``--filter`` text converts to it). Shared by the SDK's server-side filtering and the CLI.
|
|
3
|
+
|
|
4
|
+
Private module: not part of the public SDK API.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any, Literal
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
12
|
+
|
|
13
|
+
__all__ = ["ExistsClause", "FilterCondition", "QuantifierClause", "WhereClause"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class FilterCondition(BaseModel):
|
|
17
|
+
"""A single filter condition.
|
|
18
|
+
|
|
19
|
+
Examples:
|
|
20
|
+
{"path": "email", "op": "contains", "value": "@acme.com"}
|
|
21
|
+
{"path": "createdAt", "op": "gt", "value": "-30d"}
|
|
22
|
+
{"path": "companies._count", "op": "gte", "value": 2}
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
model_config = ConfigDict(extra="forbid")
|
|
26
|
+
|
|
27
|
+
path: str | None = None
|
|
28
|
+
expr: dict[str, Any] | None = None
|
|
29
|
+
op: Literal[
|
|
30
|
+
"eq",
|
|
31
|
+
"neq",
|
|
32
|
+
"gt",
|
|
33
|
+
"gte",
|
|
34
|
+
"lt",
|
|
35
|
+
"lte",
|
|
36
|
+
"contains",
|
|
37
|
+
"starts_with",
|
|
38
|
+
"in",
|
|
39
|
+
"between",
|
|
40
|
+
"is_null",
|
|
41
|
+
"is_not_null",
|
|
42
|
+
"contains_any",
|
|
43
|
+
"contains_all",
|
|
44
|
+
"starts_word",
|
|
45
|
+
]
|
|
46
|
+
value: Any = None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class QuantifierClause(BaseModel):
|
|
50
|
+
"""Quantifier clause for 'all' or 'none' predicates.
|
|
51
|
+
|
|
52
|
+
Examples:
|
|
53
|
+
{"all": {"path": "interactions", "where": {"path": "type", "op": "eq", "value": "MEETING"}}}
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
model_config = ConfigDict(extra="forbid")
|
|
57
|
+
|
|
58
|
+
path: str
|
|
59
|
+
where: WhereClause
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ExistsClause(BaseModel):
|
|
63
|
+
"""EXISTS subquery clause.
|
|
64
|
+
|
|
65
|
+
Examples:
|
|
66
|
+
{"exists": {"from": "interactions", "via": "personId", "where": {...}}}
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
model_config = ConfigDict(extra="forbid")
|
|
70
|
+
|
|
71
|
+
from_: str = Field(..., alias="from")
|
|
72
|
+
via: str | None = None
|
|
73
|
+
where: WhereClause | None = None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class WhereClause(BaseModel):
|
|
77
|
+
"""WHERE clause supporting compound conditions.
|
|
78
|
+
|
|
79
|
+
Can be:
|
|
80
|
+
- A single condition: {"path": "x", "op": "eq", "value": "y"}
|
|
81
|
+
- Compound: {"and": [...]} or {"or": [...]}
|
|
82
|
+
- Negation: {"not": {...}}
|
|
83
|
+
- Quantifiers: {"all": {...}} or {"none": {...}}
|
|
84
|
+
- Existence: {"exists": {...}}
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
model_config = ConfigDict(extra="forbid", populate_by_name=True)
|
|
88
|
+
|
|
89
|
+
# Single condition fields
|
|
90
|
+
path: str | None = None
|
|
91
|
+
expr: dict[str, Any] | None = None
|
|
92
|
+
op: str | None = None
|
|
93
|
+
value: Any = None
|
|
94
|
+
|
|
95
|
+
# Compound conditions
|
|
96
|
+
and_: list[WhereClause] | None = Field(None, alias="and")
|
|
97
|
+
or_: list[WhereClause] | None = Field(None, alias="or")
|
|
98
|
+
not_: WhereClause | None = Field(None, alias="not")
|
|
99
|
+
|
|
100
|
+
# Quantifiers
|
|
101
|
+
all_: QuantifierClause | None = Field(None, alias="all")
|
|
102
|
+
none_: QuantifierClause | None = Field(None, alias="none")
|
|
103
|
+
|
|
104
|
+
# Existence
|
|
105
|
+
exists_: ExistsClause | None = Field(None, alias="exists")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
WhereClause.model_rebuild()
|
|
@@ -27,7 +27,7 @@ from ..csv_utils import write_csv_to_stdout
|
|
|
27
27
|
from ..decorators import category, destructive, progress_capable
|
|
28
28
|
from ..entity_search import COMPANY_KEYS, filtered_ls
|
|
29
29
|
from ..errors import CLIError
|
|
30
|
-
from ..list_search import
|
|
30
|
+
from ..list_search import is_filter_cursor
|
|
31
31
|
from ..mcp_limits import apply_mcp_limits
|
|
32
32
|
from ..options import csv_output_options, csv_suboption_callback, output_options
|
|
33
33
|
from ..progress import ProgressManager, ProgressSettings
|
|
@@ -433,7 +433,7 @@ def company_ls(
|
|
|
433
433
|
)
|
|
434
434
|
|
|
435
435
|
# --filter (or a cursor from a filtered run): Affinity's search filters on its side
|
|
436
|
-
if filter_expr is not None or (cursor
|
|
436
|
+
if filter_expr is not None or is_filter_cursor(cursor):
|
|
437
437
|
found, next_token = filtered_ls(
|
|
438
438
|
client.companies,
|
|
439
439
|
entity="company",
|