footprinter-cli 1.0.4__tar.gz → 1.0.5__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.
- {footprinter_cli-1.0.4/footprinter_cli.egg-info → footprinter_cli-1.0.5}/PKG-INFO +8 -10
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/README.md +7 -9
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/access_stamper.py +17 -14
- footprinter_cli-1.0.5/footprinter/api/__main__.py +5 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/api/entities.py +0 -2
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/api/server.py +43 -1
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/bundled/config.example.yaml +0 -12
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/__init__.py +20 -15
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/_common.py +94 -34
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/_policy_helpers.py +151 -101
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/_vectorize_stage.py +10 -3
- footprinter_cli-1.0.5/footprinter/cli/add.py +813 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/delete.py +4 -4
- footprinter_cli-1.0.5/footprinter/cli/diagnostics.py +148 -0
- footprinter_cli-1.0.5/footprinter/cli/doctor.py +410 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/ingest.py +21 -324
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/mcp_setup.py +11 -107
- footprinter_cli-1.0.5/footprinter/cli/permission_cmd.py +577 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/setup.py +53 -392
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/status.py +34 -3
- footprinter_cli-1.0.5/footprinter/cli/update.py +884 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/view.py +221 -7
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/browser.py +32 -12
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/chats.py +29 -8
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/clients.py +35 -26
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/emails.py +14 -8
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/files.py +34 -62
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/folders.py +40 -15
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/messages.py +18 -9
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/policies.py +3 -3
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/projects.py +74 -129
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/search.py +44 -31
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/status.py +39 -9
- footprinter_cli-1.0.5/footprinter/ingest/adapters/chat.py +120 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/adapters/local_files.py +1 -1
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/adapters/local_folders.py +6 -6
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/adapters/protocol.py +1 -1
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/chat_indexer.py +2 -2
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/chat_parsers/__init__.py +2 -1
- footprinter_cli-1.0.5/footprinter/ingest/chat_parsers/claude_code_parser.py +165 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/db/schema.py +212 -78
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/file_indexer.py +15 -13
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/file_scanner.py +4 -4
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/folder_indexer.py +3 -3
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/orchestrator.py +2 -7
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/processing.py +30 -9
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/scan_summary.py +1 -1
- footprinter_cli-1.0.5/footprinter/ingest/status.py +133 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/vector_ops.py +14 -14
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/resources/discoverability.py +1 -1
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/tools/navigation.py +0 -8
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/semantic/embeddings.py +1 -1
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/semantic/vector_store.py +2 -2
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/access_service.py +14 -14
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/chat_service.py +4 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/client_service.py +4 -9
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/ingest_service.py +1 -1
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/project_service.py +14 -32
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/semantic_service.py +10 -10
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/visit_service.py +5 -1
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/visibility.py +40 -40
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5/footprinter_cli.egg-info}/PKG-INFO +8 -10
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter_cli.egg-info/SOURCES.txt +8 -5
- footprinter_cli-1.0.5/footprinter_cli.egg-info/entry_points.txt +4 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/pyproject.toml +3 -1
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_access_control_bypasses.py +7 -7
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_access_control_docs.py +2 -2
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_access_recalculate.py +146 -146
- footprinter_cli-1.0.5/tests/test_access_source_provenance.py +263 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_e2e_install.py +48 -65
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_edit_recalculate.py +16 -16
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_files_surface.py +3 -3
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_inherit_resolution.py +13 -13
- footprinter_cli-1.0.5/tests/test_no_issue_ids.py +53 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_pip_install_e2e.py +4 -6
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_resolver.py +17 -10
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_security_layer.py +87 -87
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_security_permissions.py +25 -25
- footprinter_cli-1.0.4/footprinter/cli/api_cmd.py +0 -61
- footprinter_cli-1.0.4/footprinter/cli/data.py +0 -879
- footprinter_cli-1.0.4/footprinter/cli/doctor.py +0 -251
- footprinter_cli-1.0.4/footprinter/cli/mcp_cmd.py +0 -469
- footprinter_cli-1.0.4/footprinter/cli/upsert.py +0 -1087
- footprinter_cli-1.0.4/footprinter/cli/vectorize.py +0 -215
- footprinter_cli-1.0.4/footprinter/ingest/adapters/chat.py +0 -57
- footprinter_cli-1.0.4/footprinter/ingest/status.py +0 -347
- footprinter_cli-1.0.4/footprinter_cli.egg-info/entry_points.txt +0 -2
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/LICENSE +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/__init__.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/api/__init__.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/api/db.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/api/search.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/api/semantic.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/api/status.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/bundled/__init__.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/bundled/patterns/context_patterns.yaml +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/bundled/patterns/extensions.yaml +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/bundled/patterns/filename_patterns.yaml +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/bundled/patterns/mime_mappings.yaml +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/bundled/patterns/salesforce_rules.yaml +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/bundled/patterns/security_patterns.yaml +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/__main__.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/_prompt.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/connect.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/search.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/cli/uninstall.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/connectors/__init__.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/connectors/config_utils.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/__init__.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/protocols.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/sql_utils.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db/uploads.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/db_base.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/__init__.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/adapters/__init__.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/adapters/browser.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/adapters/ingest.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/browser_indexer.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/chat_parsers/chatgpt_parser.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/chat_parsers/claude_parser.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/content_extractors.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/database.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/db/__init__.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/db/connector_schema.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/full_content_extractor.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/pipe_runner.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/registry.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/ingest/run_record.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/__init__.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/__main__.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/db.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/errors.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/resources/__init__.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/resources/context.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/server.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/tools/__init__.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/tools/read.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/tools/search.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/tools/semantic.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/mcp/tools/status.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/paths.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/permissions.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/semantic/__init__.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/semantic/chunking.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/semantic/hybrid_search.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/__init__.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/content_service.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/email_service.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/file_service.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/folder_service.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/includes.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/roles.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/search_service.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/services/status_service.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/source_registry.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/utils/__init__.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/utils/exceptions.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/utils/extraction.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/utils/hash_utils.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/utils/logging_config.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/utils/mime.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/utils/paths.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/utils/text.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter/utils/time.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter_cli.egg-info/dependency_links.txt +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter_cli.egg-info/requires.txt +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/footprinter_cli.egg-info/top_level.txt +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/setup.cfg +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_build_status_filter.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_bundled.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_db_base.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_e2e_pipeline.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_examples.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_files_rename.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_logging.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_no_project_root.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_package_init.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_paths_no_test_marker.py +0 -0
- {footprinter_cli-1.0.4 → footprinter_cli-1.0.5}/tests/test_prompt_safety.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: footprinter-cli
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.5
|
|
4
4
|
Summary: A local context layer for your files, browser history, chats, and email — searchable, user-owned, MCP-served.
|
|
5
5
|
Author: SwellCity Group
|
|
6
6
|
License: MIT
|
|
@@ -75,7 +75,7 @@ Or install with **pipx** directly:
|
|
|
75
75
|
pipx install footprinter-cli
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
Either method
|
|
78
|
+
Either method installs three commands: `fp` (the CLI and indexing pipeline), `fp-mcp` (the MCP server for AI agents), and `fp-api` (the HTTP API). Optional extras add more:
|
|
79
79
|
|
|
80
80
|
| Extra | What it adds |
|
|
81
81
|
|-------|-------------|
|
|
@@ -160,14 +160,12 @@ All commands use the `fp` entry point.
|
|
|
160
160
|
| `fp status` | System health and data counts |
|
|
161
161
|
| `fp search` | Search across all indexed sources |
|
|
162
162
|
| `fp connect` | Manage optional integrations |
|
|
163
|
-
| `fp
|
|
164
|
-
| `fp api` | Start the HTTP API server |
|
|
163
|
+
| `fp permission` | Manage access policies (visibility, permissions) |
|
|
165
164
|
| `fp view` | Browse indexed data (files, folders, projects, clients, chats, emails, visits) |
|
|
166
|
-
| `fp
|
|
167
|
-
| `fp
|
|
165
|
+
| `fp add` | Create new entity records or import from CSV |
|
|
166
|
+
| `fp update` | Update existing records by ID — status, assignments, metadata |
|
|
168
167
|
| `fp delete` | Hard-delete a super entity (irreversible) |
|
|
169
|
-
| `fp
|
|
170
|
-
| `fp doctor` | Post-install health check (Python version, install location, FDA, MCP wiring) |
|
|
168
|
+
| `fp doctor` | Post-install health check (Python version, platform, FDA, MCP wiring) |
|
|
171
169
|
| `fp uninstall` | Remove Footprinter — MCP entry, user data, package |
|
|
172
170
|
|
|
173
171
|
Run `fp <command> --help` for full usage.
|
|
@@ -176,7 +174,7 @@ Run `fp <command> --help` for full usage.
|
|
|
176
174
|
|
|
177
175
|
Single-process CLI with optional MCP server. SQLite database. No containers, no cloud, no accounts.
|
|
178
176
|
|
|
179
|
-
Sources are scanned into SQLite with bidirectional links connecting local files to remote backups via content hash matching. Embeddings are generated at ingest time for semantic search. The MCP server exposes indexed data with two-layer access control (visibility +
|
|
177
|
+
Sources are scanned into SQLite with bidirectional links connecting local files to remote backups via content hash matching. Embeddings are generated at ingest time for semantic search. The MCP server exposes indexed data with two-layer access control (visibility + access) — you decide what agents can see.
|
|
180
178
|
|
|
181
179
|
## Documentation
|
|
182
180
|
|
|
@@ -194,7 +192,7 @@ Bug fixes, documentation, and tests welcome. For new features or architectural c
|
|
|
194
192
|
|
|
195
193
|
```bash
|
|
196
194
|
git clone https://github.com/harringjohn/footprinter-cli.git
|
|
197
|
-
cd footprinter
|
|
195
|
+
cd footprinter-cli
|
|
198
196
|
python3 -m venv venv
|
|
199
197
|
./venv/bin/pip install -e ".[dev]"
|
|
200
198
|
```
|
|
@@ -21,7 +21,7 @@ Or install with **pipx** directly:
|
|
|
21
21
|
pipx install footprinter-cli
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
Either method
|
|
24
|
+
Either method installs three commands: `fp` (the CLI and indexing pipeline), `fp-mcp` (the MCP server for AI agents), and `fp-api` (the HTTP API). Optional extras add more:
|
|
25
25
|
|
|
26
26
|
| Extra | What it adds |
|
|
27
27
|
|-------|-------------|
|
|
@@ -106,14 +106,12 @@ All commands use the `fp` entry point.
|
|
|
106
106
|
| `fp status` | System health and data counts |
|
|
107
107
|
| `fp search` | Search across all indexed sources |
|
|
108
108
|
| `fp connect` | Manage optional integrations |
|
|
109
|
-
| `fp
|
|
110
|
-
| `fp api` | Start the HTTP API server |
|
|
109
|
+
| `fp permission` | Manage access policies (visibility, permissions) |
|
|
111
110
|
| `fp view` | Browse indexed data (files, folders, projects, clients, chats, emails, visits) |
|
|
112
|
-
| `fp
|
|
113
|
-
| `fp
|
|
111
|
+
| `fp add` | Create new entity records or import from CSV |
|
|
112
|
+
| `fp update` | Update existing records by ID — status, assignments, metadata |
|
|
114
113
|
| `fp delete` | Hard-delete a super entity (irreversible) |
|
|
115
|
-
| `fp
|
|
116
|
-
| `fp doctor` | Post-install health check (Python version, install location, FDA, MCP wiring) |
|
|
114
|
+
| `fp doctor` | Post-install health check (Python version, platform, FDA, MCP wiring) |
|
|
117
115
|
| `fp uninstall` | Remove Footprinter — MCP entry, user data, package |
|
|
118
116
|
|
|
119
117
|
Run `fp <command> --help` for full usage.
|
|
@@ -122,7 +120,7 @@ Run `fp <command> --help` for full usage.
|
|
|
122
120
|
|
|
123
121
|
Single-process CLI with optional MCP server. SQLite database. No containers, no cloud, no accounts.
|
|
124
122
|
|
|
125
|
-
Sources are scanned into SQLite with bidirectional links connecting local files to remote backups via content hash matching. Embeddings are generated at ingest time for semantic search. The MCP server exposes indexed data with two-layer access control (visibility +
|
|
123
|
+
Sources are scanned into SQLite with bidirectional links connecting local files to remote backups via content hash matching. Embeddings are generated at ingest time for semantic search. The MCP server exposes indexed data with two-layer access control (visibility + access) — you decide what agents can see.
|
|
126
124
|
|
|
127
125
|
## Documentation
|
|
128
126
|
|
|
@@ -140,7 +138,7 @@ Bug fixes, documentation, and tests welcome. For new features or architectural c
|
|
|
140
138
|
|
|
141
139
|
```bash
|
|
142
140
|
git clone https://github.com/harringjohn/footprinter-cli.git
|
|
143
|
-
cd footprinter
|
|
141
|
+
cd footprinter-cli
|
|
144
142
|
python3 -m venv venv
|
|
145
143
|
./venv/bin/pip install -e ".[dev]"
|
|
146
144
|
```
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Maps a policy scope (e.g. "global", "project:3", "folder:~/Work/") to affected
|
|
4
4
|
entity rows, calls the existing batch resolve functions, and writes resolved
|
|
5
|
-
values back to
|
|
5
|
+
values back to visibility / access columns.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
@@ -45,8 +45,8 @@ def _is_inherit_source(source: str) -> bool:
|
|
|
45
45
|
# ---------------------------------------------------------------------------
|
|
46
46
|
# Each entry describes an entity type's table and capabilities.
|
|
47
47
|
# table: SQL table name
|
|
48
|
-
# has_visibility: has
|
|
49
|
-
# has_permissions: has
|
|
48
|
+
# has_visibility: has visibility column
|
|
49
|
+
# has_permissions: has access column
|
|
50
50
|
# has_status: has status column (filter WHERE status = 'listed')
|
|
51
51
|
# has_project_id: has project_id FK
|
|
52
52
|
# has_client_id: has client_id FK
|
|
@@ -102,7 +102,7 @@ ENTITY_META: dict[str, dict[str, Any]] = {
|
|
|
102
102
|
"has_project_id": False,
|
|
103
103
|
"has_client_id": True,
|
|
104
104
|
"has_account": False,
|
|
105
|
-
"path_column":
|
|
105
|
+
"path_column": None,
|
|
106
106
|
},
|
|
107
107
|
"client": {
|
|
108
108
|
"table": "clients",
|
|
@@ -305,31 +305,34 @@ def _get_ids_for_scope(conn: sqlite3.Connection, scope: str) -> dict[str, list[i
|
|
|
305
305
|
|
|
306
306
|
|
|
307
307
|
def _write_back_visibility(conn: sqlite3.Connection, entity_type: str, results: dict[int, tuple]) -> None:
|
|
308
|
-
"""Batch UPDATE
|
|
308
|
+
"""Batch UPDATE visibility and visibility_source from resolve results.
|
|
309
309
|
|
|
310
310
|
Entities whose visibility comes from the global policy or the hardcoded
|
|
311
|
-
baseline are written as ``'inherit'``
|
|
312
|
-
|
|
311
|
+
baseline are written as ``'inherit'`` with ``visibility_source = NULL``.
|
|
312
|
+
Entities with a specific policy get the resolved value and source scope.
|
|
313
313
|
"""
|
|
314
314
|
table = ENTITY_META[entity_type]["table"]
|
|
315
315
|
conn.executemany(
|
|
316
|
-
f"UPDATE {table} SET
|
|
317
|
-
[
|
|
316
|
+
f"UPDATE {table} SET visibility = ?, visibility_source = ? WHERE id = ?",
|
|
317
|
+
[
|
|
318
|
+
("inherit", None, eid) if _is_inherit_source(source) else (state, source, eid)
|
|
319
|
+
for eid, (state, source) in results.items()
|
|
320
|
+
],
|
|
318
321
|
)
|
|
319
322
|
|
|
320
323
|
|
|
321
324
|
def _write_back_permissions(conn: sqlite3.Connection, entity_type: str, results: dict[int, tuple]) -> None:
|
|
322
|
-
"""Batch UPDATE
|
|
325
|
+
"""Batch UPDATE access and access_source from resolve results.
|
|
323
326
|
|
|
324
327
|
Entities whose permission comes from the global policy or the hardcoded
|
|
325
|
-
baseline are written as ``'inherit'``
|
|
326
|
-
|
|
328
|
+
baseline are written as ``'inherit'`` with ``access_source = NULL``.
|
|
329
|
+
Entities with a specific policy get the resolved value and source scope.
|
|
327
330
|
"""
|
|
328
331
|
table = ENTITY_META[entity_type]["table"]
|
|
329
332
|
conn.executemany(
|
|
330
|
-
f"UPDATE {table} SET
|
|
333
|
+
f"UPDATE {table} SET access = ?, access_source = ? WHERE id = ?",
|
|
331
334
|
[
|
|
332
|
-
("inherit" if _is_inherit_source(source) else ("allow" if allowed else "deny"
|
|
335
|
+
("inherit", None, eid) if _is_inherit_source(source) else ("allow" if allowed else "deny", source, eid)
|
|
333
336
|
for eid, (allowed, source) in results.items()
|
|
334
337
|
],
|
|
335
338
|
)
|
|
@@ -137,7 +137,6 @@ def list_projects(
|
|
|
137
137
|
include: Optional[str] = Query(None, description="Comma-separated includes"),
|
|
138
138
|
status: Optional[str] = Query(None, description="Comma-separated status filter"),
|
|
139
139
|
client: Optional[str] = None,
|
|
140
|
-
project_type: Optional[str] = None,
|
|
141
140
|
limit: int = Query(50, ge=1, le=MAX_LIMIT),
|
|
142
141
|
page: int = 1,
|
|
143
142
|
):
|
|
@@ -149,7 +148,6 @@ def list_projects(
|
|
|
149
148
|
include=include_list,
|
|
150
149
|
status=status_list,
|
|
151
150
|
client=client,
|
|
152
|
-
project_type=project_type,
|
|
153
151
|
limit=limit,
|
|
154
152
|
page=page,
|
|
155
153
|
)
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
"""Footprinter HTTP API — FastAPI app factory and server entry point."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
|
|
3
8
|
from fastapi import FastAPI, Request
|
|
4
9
|
from fastapi.responses import JSONResponse
|
|
5
10
|
|
|
6
11
|
from footprinter.utils.exceptions import DatabaseNotInitializedError
|
|
7
12
|
|
|
13
|
+
_LOOPBACK_HOSTS = {"127.0.0.1", "localhost", "::1"}
|
|
14
|
+
|
|
8
15
|
|
|
9
16
|
def create_app() -> "FastAPI":
|
|
10
17
|
"""Create and configure the FastAPI application.
|
|
@@ -62,5 +69,40 @@ def main(host: str = "127.0.0.1", port: int = 8000) -> None:
|
|
|
62
69
|
uvicorn.run(app, host=host, port=port)
|
|
63
70
|
|
|
64
71
|
|
|
72
|
+
def cli(argv: list[str] | None = None) -> None:
|
|
73
|
+
"""Console_script entry point — parse CLI args, validate host, start server."""
|
|
74
|
+
parser = argparse.ArgumentParser(
|
|
75
|
+
prog="fp-api",
|
|
76
|
+
description="Start the Footprinter HTTP API server.",
|
|
77
|
+
)
|
|
78
|
+
parser.add_argument("--host", default="127.0.0.1", help="Host to bind (default: 127.0.0.1)")
|
|
79
|
+
parser.add_argument("--port", type=int, default=8000, help="Port to bind (default: 8000)")
|
|
80
|
+
parser.add_argument(
|
|
81
|
+
"--allow-insecure-bind",
|
|
82
|
+
action="store_true",
|
|
83
|
+
help="Allow binding to non-loopback interfaces (no authentication!).",
|
|
84
|
+
)
|
|
85
|
+
args = parser.parse_args(argv)
|
|
86
|
+
|
|
87
|
+
if args.host not in _LOOPBACK_HOSTS:
|
|
88
|
+
if not args.allow_insecure_bind:
|
|
89
|
+
print(
|
|
90
|
+
f"error: refusing to bind to non-loopback host {args.host!r}.\n"
|
|
91
|
+
"The Footprinter HTTP API has no authentication; binding outside "
|
|
92
|
+
"loopback exposes indexed data to anyone on the network.\n"
|
|
93
|
+
"Pass --allow-insecure-bind to override.",
|
|
94
|
+
file=sys.stderr,
|
|
95
|
+
)
|
|
96
|
+
raise SystemExit(2)
|
|
97
|
+
print(
|
|
98
|
+
f"WARNING: binding to {args.host} — the HTTP API has no authentication. "
|
|
99
|
+
"Anyone reachable on this network can read indexed files, emails, "
|
|
100
|
+
"chats, and browser history.",
|
|
101
|
+
file=sys.stderr,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
main(host=args.host, port=args.port)
|
|
105
|
+
|
|
106
|
+
|
|
65
107
|
if __name__ == "__main__":
|
|
66
|
-
|
|
108
|
+
cli()
|
|
@@ -81,9 +81,6 @@ indexing:
|
|
|
81
81
|
max_file_size_mb: 50 # MB; 0 = no size limit. 50 is generous for prose/docs.
|
|
82
82
|
lookback_days: 14 # Browser history window (days back to index)
|
|
83
83
|
content_snippets: false # Extract file/email content previews for keyword search
|
|
84
|
-
# `fp ingest --preview` (FPR-1723) — pre-scan summary tuning
|
|
85
|
-
preview_top_n: 10 # rows shown for top files / top directories
|
|
86
|
-
preview_size_threshold_mb: 50 # files at or above this size are flagged as outliers
|
|
87
84
|
|
|
88
85
|
# Semantic search — stores content as embeddings in a local ChromaDB database
|
|
89
86
|
# Enables finding files and chats by meaning, not just keywords
|
|
@@ -163,12 +160,3 @@ source_seeds:
|
|
|
163
160
|
account: null
|
|
164
161
|
label: "Chat"
|
|
165
162
|
icon: message
|
|
166
|
-
|
|
167
|
-
# Display labels for the group/project hierarchy
|
|
168
|
-
# Customize these to match your organization's terminology
|
|
169
|
-
domain:
|
|
170
|
-
labels:
|
|
171
|
-
group_singular: "Client"
|
|
172
|
-
group_plural: "Clients"
|
|
173
|
-
project_singular: "Project"
|
|
174
|
-
project_plural: "Projects"
|
|
@@ -39,7 +39,6 @@ def main(argv=None) -> None:
|
|
|
39
39
|
epilog=(
|
|
40
40
|
"getting started:\n"
|
|
41
41
|
" fp setup Run the configuration wizard\n"
|
|
42
|
-
" fp setup --check Validate existing configuration\n"
|
|
43
42
|
" fp connect list Show available data source connectors\n"
|
|
44
43
|
"\n"
|
|
45
44
|
"data commands:\n"
|
|
@@ -57,13 +56,23 @@ def main(argv=None) -> None:
|
|
|
57
56
|
" fp view emails List indexed emails\n"
|
|
58
57
|
" fp view visits List browser history\n"
|
|
59
58
|
"\n"
|
|
60
|
-
"
|
|
61
|
-
" fp
|
|
62
|
-
" fp
|
|
63
|
-
" fp
|
|
59
|
+
"manage records:\n"
|
|
60
|
+
" fp add client --name Acme Create a new entity record\n"
|
|
61
|
+
" fp update client 5 --name X Update an existing record by ID\n"
|
|
62
|
+
" fp update file 42 --project-id 3 Assign a file to a project\n"
|
|
63
|
+
" fp update files data.csv Bulk update from CSV\n"
|
|
64
|
+
"\n"
|
|
65
|
+
"access control:\n"
|
|
66
|
+
" fp permission list Show all configured access policies\n"
|
|
67
|
+
" fp permission set Set visibility and/or access for a scope\n"
|
|
68
|
+
" fp permission check Check access resolution for a target\n"
|
|
69
|
+
" fp permission reset Remove policy (fall back to inheritance)\n"
|
|
70
|
+
" fp permission recalculate Re-resolve access stamps from the policy chain\n"
|
|
64
71
|
"\n"
|
|
65
72
|
"diagnostics:\n"
|
|
66
73
|
" fp doctor Check installation health\n"
|
|
74
|
+
" fp doctor search Rebuild FTS search indexes\n"
|
|
75
|
+
" fp doctor semantic Rebuild vector store\n"
|
|
67
76
|
"\n"
|
|
68
77
|
"cleanup:\n"
|
|
69
78
|
" fp uninstall Remove Footprinter (MCP entry, data, package)\n"
|
|
@@ -81,35 +90,31 @@ def main(argv=None) -> None:
|
|
|
81
90
|
subparsers.required = False
|
|
82
91
|
|
|
83
92
|
from footprinter.cli import (
|
|
84
|
-
|
|
93
|
+
add,
|
|
85
94
|
connect,
|
|
86
|
-
data,
|
|
87
95
|
delete,
|
|
88
96
|
doctor,
|
|
89
97
|
ingest,
|
|
90
|
-
|
|
98
|
+
permission_cmd,
|
|
91
99
|
search,
|
|
92
100
|
setup,
|
|
93
101
|
status,
|
|
94
102
|
uninstall,
|
|
95
|
-
|
|
96
|
-
vectorize,
|
|
103
|
+
update,
|
|
97
104
|
view,
|
|
98
105
|
)
|
|
99
106
|
|
|
100
107
|
for mod in [
|
|
108
|
+
add,
|
|
101
109
|
ingest,
|
|
102
|
-
|
|
103
|
-
api_cmd,
|
|
110
|
+
permission_cmd,
|
|
104
111
|
status,
|
|
105
112
|
search,
|
|
106
113
|
setup,
|
|
107
114
|
connect,
|
|
108
115
|
view,
|
|
109
|
-
|
|
110
|
-
data,
|
|
116
|
+
update,
|
|
111
117
|
delete,
|
|
112
|
-
vectorize,
|
|
113
118
|
uninstall,
|
|
114
119
|
doctor,
|
|
115
120
|
]:
|
|
@@ -14,6 +14,8 @@ from typing import Optional, Union
|
|
|
14
14
|
|
|
15
15
|
from rich.console import Console
|
|
16
16
|
|
|
17
|
+
from footprinter.db.clients import VALID_STATUSES as VALID_CLIENT_STATUSES
|
|
18
|
+
from footprinter.db.projects import VALID_STATUSES as VALID_PROJECT_STATUSES
|
|
17
19
|
from footprinter.services import access_service as _access
|
|
18
20
|
from footprinter.services.access_service import (
|
|
19
21
|
resolve_inherit_permission,
|
|
@@ -51,8 +53,13 @@ C_DIM = "dim"
|
|
|
51
53
|
|
|
52
54
|
VALID_STATUSES = frozenset({"listed", "unlisted", "removed"})
|
|
53
55
|
|
|
56
|
+
VALID_STATUSES_BY_ENTITY: dict[str, frozenset[str]] = {
|
|
57
|
+
"client": VALID_CLIENT_STATUSES,
|
|
58
|
+
"project": VALID_PROJECT_STATUSES,
|
|
59
|
+
}
|
|
60
|
+
|
|
54
61
|
ALLOWED_TABLES = frozenset({"clients", "projects"})
|
|
55
|
-
ALLOWED_COLUMNS = frozenset({"name"
|
|
62
|
+
ALLOWED_COLUMNS = frozenset({"name"})
|
|
56
63
|
|
|
57
64
|
|
|
58
65
|
# ---------------------------------------------------------------------------
|
|
@@ -157,6 +164,16 @@ def add_csv_flag(parser) -> None:
|
|
|
157
164
|
)
|
|
158
165
|
|
|
159
166
|
|
|
167
|
+
def add_template_flag(parser) -> None:
|
|
168
|
+
"""Add a ``--template`` flag to an argparse parser."""
|
|
169
|
+
parser.add_argument(
|
|
170
|
+
"--template",
|
|
171
|
+
action="store_true",
|
|
172
|
+
default=False,
|
|
173
|
+
help="Output an import-compatible CSV template with example data",
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
160
177
|
# ---------------------------------------------------------------------------
|
|
161
178
|
# Output helpers
|
|
162
179
|
# ---------------------------------------------------------------------------
|
|
@@ -263,46 +280,80 @@ def enrich_verbose_access(
|
|
|
263
280
|
*,
|
|
264
281
|
id_key: str = "id",
|
|
265
282
|
) -> None:
|
|
266
|
-
"""Annotate rows in-place with access
|
|
283
|
+
"""Annotate rows in-place with resolved access fields.
|
|
267
284
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
``
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
Three cases based on the ``mcp_read`` key in each row dict:
|
|
275
|
-
|
|
276
|
-
* **Key absent** (folders, visits): access = "—", source = "—"
|
|
277
|
-
* **Key is None** (truly missing): fails closed, source = "default"
|
|
278
|
-
* **Key is "inherit"**: resolved via global policy (source = "global")
|
|
279
|
-
or baseline (source = "baseline") depending on whether ``load_globals``
|
|
280
|
-
has been called
|
|
281
|
-
* **Key has a real value**: access from value, source = "cached"
|
|
285
|
+
Pops raw ``visibility`` and ``access`` stamped columns, resolves
|
|
286
|
+
``inherit`` values, and writes a six-field access block:
|
|
287
|
+
``visibility_raw``, ``access_raw``, ``visibility``, ``access``,
|
|
288
|
+
``access_source``, ``visibility_source``. Internal provenance columns
|
|
289
|
+
(``visibility_source``, ``access_source``) are consumed then removed.
|
|
282
290
|
|
|
283
291
|
No-op if *rows* is empty.
|
|
284
292
|
"""
|
|
285
293
|
if not rows:
|
|
286
294
|
return
|
|
287
295
|
for r in rows:
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
296
|
+
raw_vis = r.pop("visibility", None)
|
|
297
|
+
access_present = "access" in r
|
|
298
|
+
raw_access = r.pop("access", None)
|
|
299
|
+
read_source = r.pop("access_source", None)
|
|
300
|
+
view_source = r.pop("visibility_source", None)
|
|
301
|
+
|
|
302
|
+
if not access_present:
|
|
303
|
+
resolved_access = "—"
|
|
304
|
+
access_source = "—"
|
|
305
|
+
elif raw_access not in (None, "inherit"):
|
|
306
|
+
resolved_access = "allow" if raw_access == "allow" else "deny"
|
|
307
|
+
access_source = read_source if read_source else "cached"
|
|
294
308
|
else:
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
r["access_source"] = "global" if _access.is_global_policy_loaded() else "baseline"
|
|
309
|
+
resolved_access = resolve_inherit_permission(raw_access)
|
|
310
|
+
if raw_access == "inherit":
|
|
311
|
+
access_source = "global" if _access.is_global_policy_loaded() else "baseline"
|
|
299
312
|
else:
|
|
300
|
-
|
|
301
|
-
|
|
313
|
+
access_source = "default"
|
|
314
|
+
|
|
315
|
+
if raw_vis not in (None, "inherit"):
|
|
316
|
+
visibility_source = view_source if view_source else "cached"
|
|
317
|
+
elif raw_vis == "inherit":
|
|
318
|
+
visibility_source = "global" if _access.is_global_policy_loaded() else "baseline"
|
|
319
|
+
else:
|
|
320
|
+
visibility_source = "default"
|
|
321
|
+
|
|
322
|
+
r["visibility_raw"] = raw_vis
|
|
323
|
+
r["access_raw"] = raw_access
|
|
324
|
+
r["visibility"] = resolve_inherit_visibility(raw_vis)
|
|
325
|
+
r["access"] = resolved_access
|
|
326
|
+
r["access_source"] = access_source
|
|
327
|
+
r["visibility_source"] = visibility_source
|
|
302
328
|
|
|
303
329
|
|
|
304
330
|
def verbose_access_cells(row: dict) -> list[str]:
|
|
305
|
-
"""Return [
|
|
331
|
+
"""Return [vis_raw, access_raw, visibility, access, source, vis_source] with Rich color markup."""
|
|
332
|
+
vis_colors = {"full": "green", "opaque": "yellow", "hidden": "red"}
|
|
333
|
+
|
|
334
|
+
raw_vis = row.get("visibility_raw")
|
|
335
|
+
if raw_vis is None:
|
|
336
|
+
vis_raw_cell = "[dim]—[/dim]"
|
|
337
|
+
elif raw_vis == "inherit":
|
|
338
|
+
vis_raw_cell = "[dim]inherit[/dim]"
|
|
339
|
+
else:
|
|
340
|
+
vc = vis_colors.get(raw_vis, "white")
|
|
341
|
+
vis_raw_cell = f"[{vc}]{raw_vis}[/{vc}]"
|
|
342
|
+
|
|
343
|
+
raw_access = row.get("access_raw")
|
|
344
|
+
if raw_access is None:
|
|
345
|
+
access_raw_cell = "[dim]—[/dim]"
|
|
346
|
+
elif raw_access == "inherit":
|
|
347
|
+
access_raw_cell = "[dim]inherit[/dim]"
|
|
348
|
+
elif raw_access == "allow":
|
|
349
|
+
access_raw_cell = "[green]allow[/green]"
|
|
350
|
+
else:
|
|
351
|
+
access_raw_cell = "[red]deny[/red]"
|
|
352
|
+
|
|
353
|
+
visibility = row.get("visibility", "opaque")
|
|
354
|
+
vis_color = vis_colors.get(visibility, "white")
|
|
355
|
+
vis_cell = f"[{vis_color}]{visibility}[/{vis_color}]"
|
|
356
|
+
|
|
306
357
|
access = row.get("access", "deny")
|
|
307
358
|
if access == "—":
|
|
308
359
|
access_cell = "[dim]—[/dim]"
|
|
@@ -311,12 +362,21 @@ def verbose_access_cells(row: dict) -> list[str]:
|
|
|
311
362
|
else:
|
|
312
363
|
access_cell = "[red]deny[/red]"
|
|
313
364
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
365
|
+
source = row.get("access_source")
|
|
366
|
+
if source is None or source == "—":
|
|
367
|
+
source_cell = "[dim]—[/dim]"
|
|
368
|
+
else:
|
|
369
|
+
source_cell = source
|
|
370
|
+
|
|
371
|
+
vis_source = row.get("visibility_source")
|
|
372
|
+
if vis_source is None or vis_source == "—":
|
|
373
|
+
vis_source_cell = "[dim]—[/dim]"
|
|
374
|
+
elif vis_source == source:
|
|
375
|
+
vis_source_cell = "[dim]≡[/dim]"
|
|
376
|
+
else:
|
|
377
|
+
vis_source_cell = vis_source
|
|
318
378
|
|
|
319
|
-
return [access_cell,
|
|
379
|
+
return [vis_raw_cell, access_raw_cell, vis_cell, access_cell, source_cell, vis_source_cell]
|
|
320
380
|
|
|
321
381
|
|
|
322
382
|
def format_size(size_bytes: int) -> str:
|