firekeep-client 1.3.0__tar.gz → 1.4.2__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.
- {firekeep_client-1.3.0/firekeep_client.egg-info → firekeep_client-1.4.2}/PKG-INFO +13 -9
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/README.md +12 -8
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/__init__.py +1 -1
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/adapters/base.py +33 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/gateway.py +75 -4
- {firekeep_client-1.3.0 → firekeep_client-1.4.2/firekeep_client.egg-info}/PKG-INFO +13 -9
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client.egg-info/SOURCES.txt +1 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/pyproject.toml +1 -1
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_gateway.py +2 -0
- firekeep_client-1.4.2/tests/test_gateway_toolset.py +153 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_package.py +1 -1
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/LICENSE +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/NOTICE +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/adapters/__init__.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/adapters/claude.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/adapters/claude_desktop.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/adapters/codex.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/adapters/generic.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/adapters/kiro.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/adapters/opencode.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/autoupdate.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/backups.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/cli.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/config_write.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/connect.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/contract/__init__.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/contract/matrix.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/decision/__init__.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/decision/board.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/decision/server.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/dexes.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/docdexsync.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/hooklog.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/hooks/__init__.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/hooks/__main__.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/hooks/_git.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/hooks/_mcp.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/hooks/destructive.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/hooks/post_tool.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/hooks/pre_tool.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/hooks/precompact.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/hooks/prompt.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/hooks/runbooks.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/hooks/session_end.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/hooks/session_start.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/hooks/stop.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/join.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/joincode.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/maildexsync.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/migrate.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/nightshift.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/pathenv.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/promptrecall.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/resolver.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/serverinit.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/shim.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/sidecar.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/signing.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/state.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/stdio.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/symdexindex.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/transport.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/updater.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/wizard.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client/worktree_snapshot.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client.egg-info/dependency_links.txt +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client.egg-info/entry_points.txt +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client.egg-info/requires.txt +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client.egg-info/top_level.txt +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/setup.cfg +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_attribution_headers.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_autoupdate.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_bash_layer_retired.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_bootstrap_docdex.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_bootstrap_maildex.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_bootstrap_ps1.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_bootstrap_reinstall.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_bootstrap_sh.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_bootstrap_symdex.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_bootstrap_venv_provisioning.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_ci_publishes_symdex.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_cli.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_cli_backup.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_cli_dex.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_cli_docdex.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_cli_doctor.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_cli_doctor_generic.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_cli_entry.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_cli_generic_install.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_cli_install.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_cli_maildex.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_cli_onboarding.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_cli_personal.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_cli_uninstall.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_cli_uninstall_generic.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_cli_update.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_config_migration.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_connect.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_decision_board_html.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_decision_bypass.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_decision_registration.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_decision_server.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_dexes.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_docdexsync.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_docs_reference_client_kit.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_doctor_credential_expiry.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_doctor_embeddings.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_doctor_no_server.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_e2e_bootstrap.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_generic_config.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_hooklog.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_import_boundary.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_init_self_enroll.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_init_stale_bundle.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_join.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_join_config_write.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_joincode.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_kit_smoke.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_maildexsync.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_make_release.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_memory_instructions.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_migration_entrypoints.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_nightshift.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_pathenv.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_promptrecall.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_resolver.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_resolver_bypass.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_resolver_single_connection.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_runbook_match_parity.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_runbook_state.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_scratch_ttl.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_serverinit.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_shim_bridge.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_shim_bypass.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_shim_fail_loud.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_shim_identity.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_shim_recovery.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_shim_skeleton.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_sidecar.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_sidecar_robustness.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_signing.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_state.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_stdio_encoding.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_symdexindex.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_transport.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_transport_sse.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_twin_removed.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_updater.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_wizard.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_wizard_generic.py +0 -0
- {firekeep_client-1.3.0 → firekeep_client-1.4.2}/tests/test_worktree_snapshot.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: firekeep-client
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.2
|
|
4
4
|
Summary: Client for Firekeep, the self-hosted operating layer for connected AI agents.
|
|
5
5
|
License-Expression: LicenseRef-Firekeep-BUSL-1.1
|
|
6
6
|
Project-URL: Homepage, https://firekeep.ai
|
|
@@ -32,7 +32,8 @@ operating layer for connected AI agents**. It carries durable knowledge,
|
|
|
32
32
|
working context, procedures, coordination, and replayable evidence across
|
|
33
33
|
sessions, models, machines, and teammates. Agent-facing MCP tools are exposed
|
|
34
34
|
through one local `firekeep` gateway. Shipped adapters configure Claude Code,
|
|
35
|
-
|
|
35
|
+
Claude Desktop (auto-detected when the app's config dir exists), Codex, Kiro,
|
|
36
|
+
and OpenCode; other MCP clients can connect through the generic
|
|
36
37
|
configuration path, without hook-driven lifecycle automation.
|
|
37
38
|
|
|
38
39
|
mcp-name: io.github.kapella-hub/firekeep
|
|
@@ -44,13 +45,16 @@ aggregates your team server's services (memory, sessions, environment,
|
|
|
44
45
|
coordination) plus the Decision Board and whichever **dexes** — the domain
|
|
45
46
|
indexes the Keep understands — you have registered, the lifecycle hook cores,
|
|
46
47
|
the runtime adapters, and the `firekeep` CLI (`install`, `join`, `doctor`,
|
|
47
|
-
`update`, `dex`, `docdex`).
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
`firekeep-docdex` (folders of documents indexed into the Keep's corpus)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
`update`, `dex`, `docdex`, `maildex`).
|
|
49
|
+
|
|
50
|
+
Three dexes ship today: `firekeep-symdex` (code intelligence),
|
|
51
|
+
`firekeep-docdex` (folders of documents indexed into the Keep's corpus), and
|
|
52
|
+
`firekeep-maildex` (email over read-only IMAP, always member-private,
|
|
53
|
+
registered with `firekeep maildex add`). The managed installer always installs
|
|
54
|
+
all three wheels; `firekeep dex list|add|remove` decides which of them
|
|
55
|
+
actually run. Symdex and docdex are registered by default — since client 1.2.0
|
|
56
|
+
an absent registry is seeded with both (default-on), `firekeep dex remove` is
|
|
57
|
+
the off-switch, and removals stick across updates.
|
|
54
58
|
|
|
55
59
|
**Firekeep is self-hosted — this package needs a server to talk to.** A person
|
|
56
60
|
or team runs its own server, with per-key authentication. Current server images
|
|
@@ -5,7 +5,8 @@ operating layer for connected AI agents**. It carries durable knowledge,
|
|
|
5
5
|
working context, procedures, coordination, and replayable evidence across
|
|
6
6
|
sessions, models, machines, and teammates. Agent-facing MCP tools are exposed
|
|
7
7
|
through one local `firekeep` gateway. Shipped adapters configure Claude Code,
|
|
8
|
-
|
|
8
|
+
Claude Desktop (auto-detected when the app's config dir exists), Codex, Kiro,
|
|
9
|
+
and OpenCode; other MCP clients can connect through the generic
|
|
9
10
|
configuration path, without hook-driven lifecycle automation.
|
|
10
11
|
|
|
11
12
|
mcp-name: io.github.kapella-hub/firekeep
|
|
@@ -17,13 +18,16 @@ aggregates your team server's services (memory, sessions, environment,
|
|
|
17
18
|
coordination) plus the Decision Board and whichever **dexes** — the domain
|
|
18
19
|
indexes the Keep understands — you have registered, the lifecycle hook cores,
|
|
19
20
|
the runtime adapters, and the `firekeep` CLI (`install`, `join`, `doctor`,
|
|
20
|
-
`update`, `dex`, `docdex`).
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
`firekeep-docdex` (folders of documents indexed into the Keep's corpus)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
`update`, `dex`, `docdex`, `maildex`).
|
|
22
|
+
|
|
23
|
+
Three dexes ship today: `firekeep-symdex` (code intelligence),
|
|
24
|
+
`firekeep-docdex` (folders of documents indexed into the Keep's corpus), and
|
|
25
|
+
`firekeep-maildex` (email over read-only IMAP, always member-private,
|
|
26
|
+
registered with `firekeep maildex add`). The managed installer always installs
|
|
27
|
+
all three wheels; `firekeep dex list|add|remove` decides which of them
|
|
28
|
+
actually run. Symdex and docdex are registered by default — since client 1.2.0
|
|
29
|
+
an absent registry is seeded with both (default-on), `firekeep dex remove` is
|
|
30
|
+
the off-switch, and removals stick across updates.
|
|
27
31
|
|
|
28
32
|
**Firekeep is self-hosted — this package needs a server to talk to.** A person
|
|
29
33
|
or team runs its own server, with per-key authentication. Current server images
|
|
@@ -516,6 +516,36 @@ instead of asking inline. If it returns pending, keep calling decision_board_che
|
|
|
516
516
|
until the human answers.
|
|
517
517
|
"""
|
|
518
518
|
|
|
519
|
+
# The handshake text for the `chat` toolset (gateway.py, FIREKEEP_TOOLSET=chat —
|
|
520
|
+
# the ChatGPT tunnel surface). NOT derived from MCP_SERVER_INSTRUCTIONS by
|
|
521
|
+
# replace(): that text instructs agents to call vault_retrieve, vault_store,
|
|
522
|
+
# skill_create, action_before and decision_board — none of which the chat preset
|
|
523
|
+
# serves, and an instruction to call a tool that errors is worse than no
|
|
524
|
+
# instruction. This variant may only name tools the preset carries;
|
|
525
|
+
# test_gateway_toolset pins that mechanically (every tool-shaped name mentioned
|
|
526
|
+
# here must be in CHAT_TOOLSET).
|
|
527
|
+
CHAT_INSTRUCTIONS = """\
|
|
528
|
+
Firekeep — persistent team memory, reachable from this chat.
|
|
529
|
+
|
|
530
|
+
Recall BEFORE answering, and treat not knowing as the trigger: if the user names
|
|
531
|
+
a host, IP, path, service or convention you cannot name from the current
|
|
532
|
+
conversation ("my VPS", "our server"), or uses history words ("again", "still",
|
|
533
|
+
"last time", "how did we"), call memory_recall(task=<their request>) first.
|
|
534
|
+
Never claim you don't know about the user's own systems before calling it once.
|
|
535
|
+
For operational or repeated-failure tasks, also check skill_recall(task).
|
|
536
|
+
|
|
537
|
+
Working on something across turns? ctx_start_session(goal) opens a session — its
|
|
538
|
+
response includes prior art: what the team already built or decided near your
|
|
539
|
+
goal. ctx_update as you go, ctx_complete_session(outcome) when done.
|
|
540
|
+
|
|
541
|
+
memory_learn the moment something worth keeping is decided or fixed (include
|
|
542
|
+
what failed first). Never store secrets — no passwords, tokens or keys in
|
|
543
|
+
memory.
|
|
544
|
+
|
|
545
|
+
When recalled knowledge shaped what you DID, call memory_feedback(memory_ids,
|
|
546
|
+
useful) with a one-line comment — only for knowledge you acted on.
|
|
547
|
+
"""
|
|
548
|
+
|
|
519
549
|
|
|
520
550
|
def _hash12(text: str) -> str:
|
|
521
551
|
"""sha256(text utf-8), first 12 hex chars — the contract's hash shape."""
|
|
@@ -529,6 +559,9 @@ def _hash12(text: str) -> str:
|
|
|
529
559
|
# covers the handshake text served fresh from the running wheel every session.
|
|
530
560
|
RENDERED_INSTRUCTIONS_HASH = _hash12(FIREKEEP_INSTRUCTIONS)
|
|
531
561
|
GATEWAY_INSTRUCTIONS_HASH = _hash12(GATEWAY_INSTRUCTIONS)
|
|
562
|
+
# Same contract for the chat-toolset handshake: a session's recorded
|
|
563
|
+
# serverInfo.version names exactly which instruction text it received.
|
|
564
|
+
CHAT_INSTRUCTIONS_HASH = _hash12(CHAT_INSTRUCTIONS)
|
|
532
565
|
# Same definition for the hook-free text. Doctor compares a rendered block
|
|
533
566
|
# against the hash for ITS runtime — a generic block checked against the four's
|
|
534
567
|
# hash would read "edited" forever.
|
|
@@ -22,7 +22,12 @@ from pathlib import Path
|
|
|
22
22
|
from typing import Any
|
|
23
23
|
|
|
24
24
|
from firekeep_client import dexes
|
|
25
|
-
from firekeep_client.adapters.base import
|
|
25
|
+
from firekeep_client.adapters.base import (
|
|
26
|
+
CHAT_INSTRUCTIONS,
|
|
27
|
+
CHAT_INSTRUCTIONS_HASH,
|
|
28
|
+
GATEWAY_INSTRUCTIONS,
|
|
29
|
+
GATEWAY_INSTRUCTIONS_HASH,
|
|
30
|
+
)
|
|
26
31
|
from firekeep_client.stdio import force_utf8_stdio, pin_import_paths
|
|
27
32
|
|
|
28
33
|
|
|
@@ -43,6 +48,56 @@ STATUS_TOOL = {
|
|
|
43
48
|
}
|
|
44
49
|
|
|
45
50
|
|
|
51
|
+
# Named toolsets (FIREKEEP_TOOLSET). A toolset narrows the gateway to a curated
|
|
52
|
+
# surface for hosts where the full ~90 tools are wrong — the ChatGPT tunnel is
|
|
53
|
+
# the founding case: a consumer chat surface, prompt-injection-rich, that still
|
|
54
|
+
# deserves recall, sessions (prior art rides ctx_start_session) and the one
|
|
55
|
+
# write that makes chat valuable (memory_learn — the poisoning risk is carried
|
|
56
|
+
# by replay attribution, runtime: chatgpt, not prevented). Excluded outright:
|
|
57
|
+
# vault, corpus ingest, relay, backup, dex/code tools.
|
|
58
|
+
TOOLSET_PRESETS: dict[str, frozenset[str]] = {
|
|
59
|
+
"chat": frozenset({
|
|
60
|
+
"memory_recall", "memory_learn", "memory_feedback",
|
|
61
|
+
"skill_recall", "skill_list",
|
|
62
|
+
"ctx_start_session", "ctx_update", "ctx_complete_session",
|
|
63
|
+
"ctx_abandon_session", "ctx_list_sessions", "ctx_resume_session",
|
|
64
|
+
"ctx_get_shadow",
|
|
65
|
+
}),
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# A preset that narrows the tool surface also narrows the handshake text: the
|
|
69
|
+
# default GATEWAY_INSTRUCTIONS instructs agents to call vault_retrieve and
|
|
70
|
+
# decision_board, which the chat preset does not serve. An explicit
|
|
71
|
+
# FIREKEEP_TOOLS_ALLOW keeps the default text — the operator overrode the
|
|
72
|
+
# preset and owns the mismatch.
|
|
73
|
+
_PRESET_INSTRUCTIONS: dict[str, tuple[str, str]] = {
|
|
74
|
+
"chat": (CHAT_INSTRUCTIONS, CHAT_INSTRUCTIONS_HASH),
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _active_toolset() -> tuple[str | None, frozenset[str] | None]:
|
|
79
|
+
"""(label, allowlist) from the environment.
|
|
80
|
+
|
|
81
|
+
FIREKEEP_TOOLS_ALLOW (explicit comma-list) wins over FIREKEEP_TOOLSET.
|
|
82
|
+
An UNKNOWN preset refuses to start rather than falling back to the full
|
|
83
|
+
surface: this gateway may sit behind a tunnel reachable from a consumer
|
|
84
|
+
chat host, and a typo must fail closed, not open ~90 tools."""
|
|
85
|
+
allow = os.environ.get("FIREKEEP_TOOLS_ALLOW", "").strip()
|
|
86
|
+
if allow:
|
|
87
|
+
names = frozenset(n.strip() for n in allow.split(",") if n.strip())
|
|
88
|
+
return "allowlist", names
|
|
89
|
+
preset = os.environ.get("FIREKEEP_TOOLSET", "").strip()
|
|
90
|
+
if not preset:
|
|
91
|
+
return None, None
|
|
92
|
+
if preset not in TOOLSET_PRESETS:
|
|
93
|
+
raise SystemExit(
|
|
94
|
+
f"firekeep gateway: unknown FIREKEEP_TOOLSET {preset!r} "
|
|
95
|
+
f"(valid: {', '.join(sorted(TOOLSET_PRESETS))}); refusing to start "
|
|
96
|
+
"rather than serve the full tool surface"
|
|
97
|
+
)
|
|
98
|
+
return preset, TOOLSET_PRESETS[preset]
|
|
99
|
+
|
|
100
|
+
|
|
46
101
|
def _console_script(name: str) -> str:
|
|
47
102
|
suffix = ".exe" if os.name == "nt" else ""
|
|
48
103
|
filename = f"{name}{suffix}"
|
|
@@ -210,11 +265,15 @@ class Gateway:
|
|
|
210
265
|
]
|
|
211
266
|
self.protocol_version = "2025-03-26"
|
|
212
267
|
self.routes: dict[str, Backend] = {}
|
|
268
|
+
# Fail-closed at construction: an unknown preset never serves a request.
|
|
269
|
+
self.toolset_label, self.toolset = _active_toolset()
|
|
270
|
+
self.tools_filtered = 0
|
|
213
271
|
|
|
214
272
|
def discover(self) -> list[dict[str, Any]]:
|
|
215
273
|
with ThreadPoolExecutor(max_workers=len(self.backends)) as pool:
|
|
216
274
|
list(pool.map(lambda backend: backend.discover(self.protocol_version), self.backends))
|
|
217
275
|
self.routes = {}
|
|
276
|
+
self.tools_filtered = 0
|
|
218
277
|
tools = [STATUS_TOOL]
|
|
219
278
|
for backend in self.backends:
|
|
220
279
|
for tool in backend.tools:
|
|
@@ -222,6 +281,12 @@ class Gateway:
|
|
|
222
281
|
if name in self.routes or name == STATUS_TOOL["name"]:
|
|
223
282
|
backend.state = f"degraded: duplicate tool name {name}"
|
|
224
283
|
continue
|
|
284
|
+
# Toolset filter at the ROUTING layer: an excluded tool never
|
|
285
|
+
# enters self.routes, so it is invisible in tools/list AND a
|
|
286
|
+
# tools/call for it returns -32601 — enforcement, not decoration.
|
|
287
|
+
if self.toolset is not None and name not in self.toolset:
|
|
288
|
+
self.tools_filtered += 1
|
|
289
|
+
continue
|
|
225
290
|
self.routes[name] = backend
|
|
226
291
|
tools.append(tool)
|
|
227
292
|
return tools
|
|
@@ -231,6 +296,9 @@ class Gateway:
|
|
|
231
296
|
"backends": {backend.name: backend.state for backend in self.backends},
|
|
232
297
|
"tool_count": len(self.routes),
|
|
233
298
|
"plan_filtering": False,
|
|
299
|
+
# Disclosure: a narrowed surface must say so. null when unfiltered.
|
|
300
|
+
"toolset": self.toolset_label,
|
|
301
|
+
"tools_filtered": self.tools_filtered,
|
|
234
302
|
}
|
|
235
303
|
|
|
236
304
|
def handle(self, message: dict[str, Any]) -> dict[str, Any] | None:
|
|
@@ -241,6 +309,9 @@ class Gateway:
|
|
|
241
309
|
return None
|
|
242
310
|
if method == "initialize":
|
|
243
311
|
self.protocol_version = params.get("protocolVersion") or self.protocol_version
|
|
312
|
+
instructions, instructions_hash = _PRESET_INSTRUCTIONS.get(
|
|
313
|
+
self.toolset_label or "", (GATEWAY_INSTRUCTIONS, GATEWAY_INSTRUCTIONS_HASH)
|
|
314
|
+
)
|
|
244
315
|
return {
|
|
245
316
|
"jsonrpc": "2.0",
|
|
246
317
|
"id": request_id,
|
|
@@ -250,9 +321,9 @@ class Gateway:
|
|
|
250
321
|
# version = the handshake text's content hash, so a session's
|
|
251
322
|
# recorded serverInfo names exactly which instruction text it
|
|
252
323
|
# received (round-2 measurement contract) — the hardcoded "1"
|
|
253
|
-
# said nothing.
|
|
254
|
-
"serverInfo": {"name": "firekeep", "version":
|
|
255
|
-
"instructions":
|
|
324
|
+
# said nothing. A preset that swaps the text swaps the hash.
|
|
325
|
+
"serverInfo": {"name": "firekeep", "version": instructions_hash},
|
|
326
|
+
"instructions": instructions,
|
|
256
327
|
},
|
|
257
328
|
}
|
|
258
329
|
if method == "ping":
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: firekeep-client
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.2
|
|
4
4
|
Summary: Client for Firekeep, the self-hosted operating layer for connected AI agents.
|
|
5
5
|
License-Expression: LicenseRef-Firekeep-BUSL-1.1
|
|
6
6
|
Project-URL: Homepage, https://firekeep.ai
|
|
@@ -32,7 +32,8 @@ operating layer for connected AI agents**. It carries durable knowledge,
|
|
|
32
32
|
working context, procedures, coordination, and replayable evidence across
|
|
33
33
|
sessions, models, machines, and teammates. Agent-facing MCP tools are exposed
|
|
34
34
|
through one local `firekeep` gateway. Shipped adapters configure Claude Code,
|
|
35
|
-
|
|
35
|
+
Claude Desktop (auto-detected when the app's config dir exists), Codex, Kiro,
|
|
36
|
+
and OpenCode; other MCP clients can connect through the generic
|
|
36
37
|
configuration path, without hook-driven lifecycle automation.
|
|
37
38
|
|
|
38
39
|
mcp-name: io.github.kapella-hub/firekeep
|
|
@@ -44,13 +45,16 @@ aggregates your team server's services (memory, sessions, environment,
|
|
|
44
45
|
coordination) plus the Decision Board and whichever **dexes** — the domain
|
|
45
46
|
indexes the Keep understands — you have registered, the lifecycle hook cores,
|
|
46
47
|
the runtime adapters, and the `firekeep` CLI (`install`, `join`, `doctor`,
|
|
47
|
-
`update`, `dex`, `docdex`).
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
`firekeep-docdex` (folders of documents indexed into the Keep's corpus)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
`update`, `dex`, `docdex`, `maildex`).
|
|
49
|
+
|
|
50
|
+
Three dexes ship today: `firekeep-symdex` (code intelligence),
|
|
51
|
+
`firekeep-docdex` (folders of documents indexed into the Keep's corpus), and
|
|
52
|
+
`firekeep-maildex` (email over read-only IMAP, always member-private,
|
|
53
|
+
registered with `firekeep maildex add`). The managed installer always installs
|
|
54
|
+
all three wheels; `firekeep dex list|add|remove` decides which of them
|
|
55
|
+
actually run. Symdex and docdex are registered by default — since client 1.2.0
|
|
56
|
+
an absent registry is seeded with both (default-on), `firekeep dex remove` is
|
|
57
|
+
the off-switch, and removals stick across updates.
|
|
54
58
|
|
|
55
59
|
**Firekeep is self-hosted — this package needs a server to talk to.** A person
|
|
56
60
|
or team runs its own server, with per-key authentication. Current server images
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "firekeep-client"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.4.2"
|
|
8
8
|
description = "Client for Firekeep, the self-hosted operating layer for connected AI agents."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""FIREKEEP_TOOLSET / FIREKEEP_TOOLS_ALLOW — the gateway's curated surfaces.
|
|
2
|
+
|
|
3
|
+
The invariants:
|
|
4
|
+
- filtering happens at the ROUTING layer: an excluded tool is invisible in
|
|
5
|
+
tools/list AND uncallable (-32601), never decoratively hidden;
|
|
6
|
+
- an unknown preset fails CLOSED (refuses to start) — this gateway can sit
|
|
7
|
+
behind a tunnel reachable from a consumer chat host, and a typo must not
|
|
8
|
+
open the full ~90-tool surface;
|
|
9
|
+
- unset env is byte-identical to today (pinned by test_gateway.py's exact
|
|
10
|
+
status assertion plus the unfiltered case here);
|
|
11
|
+
- the narrowing is disclosed via firekeep_gateway_status;
|
|
12
|
+
- a preset that narrows the tools narrows the handshake text with it, and
|
|
13
|
+
the chat instructions may only name tools the chat preset serves.
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import re
|
|
18
|
+
|
|
19
|
+
import pytest
|
|
20
|
+
|
|
21
|
+
from firekeep_client.adapters.base import (
|
|
22
|
+
CHAT_INSTRUCTIONS,
|
|
23
|
+
CHAT_INSTRUCTIONS_HASH,
|
|
24
|
+
GATEWAY_INSTRUCTIONS_HASH,
|
|
25
|
+
)
|
|
26
|
+
from firekeep_client.gateway import STATUS_TOOL, TOOLSET_PRESETS, Gateway, _active_toolset
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class FakeBackend:
|
|
30
|
+
def __init__(self, name, tools):
|
|
31
|
+
self.name = name
|
|
32
|
+
self.tools = tools
|
|
33
|
+
self.state = "not checked"
|
|
34
|
+
|
|
35
|
+
def discover(self, protocol_version):
|
|
36
|
+
self.state = f"ready ({len(self.tools)} tools)"
|
|
37
|
+
|
|
38
|
+
def request(self, method, params, **kwargs):
|
|
39
|
+
return {"jsonrpc": "2.0", "id": "up", "result": {"content": []}}
|
|
40
|
+
|
|
41
|
+
def close(self):
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _tool(name):
|
|
46
|
+
return {"name": name, "inputSchema": {}}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _gateway(monkeypatch, **env):
|
|
50
|
+
for key in ("FIREKEEP_TOOLSET", "FIREKEEP_TOOLS_ALLOW"):
|
|
51
|
+
monkeypatch.delenv(key, raising=False)
|
|
52
|
+
for key, value in env.items():
|
|
53
|
+
monkeypatch.setenv(key, value)
|
|
54
|
+
gateway = Gateway()
|
|
55
|
+
gateway.backends = [
|
|
56
|
+
FakeBackend("cortex", [_tool("memory_recall"), _tool("memory_learn"),
|
|
57
|
+
_tool("vault_retrieve"), _tool("corpus_ingest")]),
|
|
58
|
+
FakeBackend("bridge", [_tool("ctx_start_session"), _tool("ctx_update")]),
|
|
59
|
+
FakeBackend("relay", [_tool("relay_broadcast")]),
|
|
60
|
+
]
|
|
61
|
+
return gateway
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_chat_preset_filters_list_and_routes(monkeypatch):
|
|
65
|
+
gateway = _gateway(monkeypatch, FIREKEEP_TOOLSET="chat")
|
|
66
|
+
reply = gateway.handle({"jsonrpc": "2.0", "id": 1, "method": "tools/list"})
|
|
67
|
+
names = {tool["name"] for tool in reply["result"]["tools"]}
|
|
68
|
+
assert names == {STATUS_TOOL["name"], "memory_recall", "memory_learn",
|
|
69
|
+
"ctx_start_session", "ctx_update"}
|
|
70
|
+
# Routing-layer enforcement: the excluded tool is UNCALLABLE, and the
|
|
71
|
+
# handler's re-discover retry must not resurrect it.
|
|
72
|
+
call = gateway.handle({"jsonrpc": "2.0", "id": 2, "method": "tools/call",
|
|
73
|
+
"params": {"name": "vault_retrieve", "arguments": {}}})
|
|
74
|
+
assert call["error"]["code"] == -32601
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_allowlist_overrides_preset(monkeypatch):
|
|
78
|
+
gateway = _gateway(monkeypatch, FIREKEEP_TOOLSET="chat",
|
|
79
|
+
FIREKEEP_TOOLS_ALLOW="vault_retrieve, relay_broadcast")
|
|
80
|
+
reply = gateway.handle({"jsonrpc": "2.0", "id": 1, "method": "tools/list"})
|
|
81
|
+
names = {tool["name"] for tool in reply["result"]["tools"]}
|
|
82
|
+
assert names == {STATUS_TOOL["name"], "vault_retrieve", "relay_broadcast"}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_unset_env_serves_everything(monkeypatch):
|
|
86
|
+
gateway = _gateway(monkeypatch)
|
|
87
|
+
reply = gateway.handle({"jsonrpc": "2.0", "id": 1, "method": "tools/list"})
|
|
88
|
+
assert len(reply["result"]["tools"]) == 8 # 7 fakes + status tool
|
|
89
|
+
assert gateway.status()["toolset"] is None
|
|
90
|
+
assert gateway.status()["tools_filtered"] == 0
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_unknown_preset_fails_closed(monkeypatch):
|
|
94
|
+
monkeypatch.delenv("FIREKEEP_TOOLS_ALLOW", raising=False)
|
|
95
|
+
monkeypatch.setenv("FIREKEEP_TOOLSET", "chta")
|
|
96
|
+
with pytest.raises(SystemExit) as exc:
|
|
97
|
+
_active_toolset()
|
|
98
|
+
assert "chta" in str(exc.value)
|
|
99
|
+
assert "refusing" in str(exc.value)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def test_status_discloses_narrowing(monkeypatch):
|
|
103
|
+
gateway = _gateway(monkeypatch, FIREKEEP_TOOLSET="chat")
|
|
104
|
+
gateway.discover()
|
|
105
|
+
status = gateway.status()
|
|
106
|
+
assert status["toolset"] == "chat"
|
|
107
|
+
assert status["tools_filtered"] == 3 # vault_retrieve, corpus_ingest, relay_broadcast
|
|
108
|
+
assert status["tool_count"] == 4
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_status_tool_is_never_filtered(monkeypatch):
|
|
112
|
+
gateway = _gateway(monkeypatch, FIREKEEP_TOOLS_ALLOW="memory_recall")
|
|
113
|
+
reply = gateway.handle({"jsonrpc": "2.0", "id": 1, "method": "tools/list"})
|
|
114
|
+
assert STATUS_TOOL["name"] in {tool["name"] for tool in reply["result"]["tools"]}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_chat_preset_swaps_handshake_text_and_hash(monkeypatch):
|
|
118
|
+
gateway = _gateway(monkeypatch, FIREKEEP_TOOLSET="chat")
|
|
119
|
+
reply = gateway.handle({"jsonrpc": "2.0", "id": 1, "method": "initialize",
|
|
120
|
+
"params": {"protocolVersion": "2025-03-26"}})
|
|
121
|
+
result = reply["result"]
|
|
122
|
+
assert result["instructions"] == CHAT_INSTRUCTIONS
|
|
123
|
+
assert result["serverInfo"]["version"] == CHAT_INSTRUCTIONS_HASH
|
|
124
|
+
assert "decision_board" not in result["instructions"]
|
|
125
|
+
assert "vault" not in result["instructions"]
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_allowlist_keeps_default_handshake(monkeypatch):
|
|
129
|
+
# The operator overrode the preset; the default text stays and they own
|
|
130
|
+
# any tool/instruction mismatch.
|
|
131
|
+
gateway = _gateway(monkeypatch, FIREKEEP_TOOLS_ALLOW="memory_recall")
|
|
132
|
+
reply = gateway.handle({"jsonrpc": "2.0", "id": 1, "method": "initialize",
|
|
133
|
+
"params": {"protocolVersion": "2025-03-26"}})
|
|
134
|
+
assert reply["result"]["serverInfo"]["version"] == GATEWAY_INSTRUCTIONS_HASH
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def test_chat_instructions_only_name_preset_tools():
|
|
138
|
+
"""Mechanical honesty: every tool-shaped name the chat handshake mentions
|
|
139
|
+
must be a tool the chat preset actually serves — an instruction to call a
|
|
140
|
+
tool that errors is worse than no instruction."""
|
|
141
|
+
mentioned = set(re.findall(r"\b[a-z]+_[a-z_]+\b", CHAT_INSTRUCTIONS))
|
|
142
|
+
non_tools = {"memory_ids"} # a parameter name, not a tool
|
|
143
|
+
assert mentioned - non_tools <= TOOLSET_PRESETS["chat"]
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_chat_preset_is_exactly_the_decided_surface():
|
|
147
|
+
assert TOOLSET_PRESETS["chat"] == {
|
|
148
|
+
"memory_recall", "memory_learn", "memory_feedback",
|
|
149
|
+
"skill_recall", "skill_list",
|
|
150
|
+
"ctx_start_session", "ctx_update", "ctx_complete_session",
|
|
151
|
+
"ctx_abandon_session", "ctx_list_sessions", "ctx_resume_session",
|
|
152
|
+
"ctx_get_shadow",
|
|
153
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{firekeep_client-1.3.0 → firekeep_client-1.4.2}/firekeep_client.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|