kanibako-cli 1.6.0.dev36__tar.gz → 1.7.0rc0__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.
- {kanibako_cli-1.6.0.dev36/src/kanibako_cli.egg-info → kanibako_cli-1.7.0rc0}/PKG-INFO +1 -1
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/README.md +31 -30
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/pyproject.toml +1 -1
- kanibako_cli-1.7.0rc0/src/kanibako/__init__.py +25 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/agent_config.py +14 -5
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/agent_defaults.py +1 -0
- kanibako_cli-1.7.0rc0/src/kanibako/agent_ref.py +168 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/agent_representation.py +23 -9
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/channels.py +13 -13
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/cli.py +42 -31
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/agent_cmd.py +197 -49
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/box/__init__.py +7 -3
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/box/_duplicate.py +5 -6
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/box/_lifecycle.py +24 -24
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/box/_parser.py +277 -72
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/clean.py +9 -9
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/diagnose.py +64 -2
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/flags.py +4 -1
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/install.py +11 -15
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/setup_cmd.py +2 -2
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/start.py +901 -178
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/stop.py +30 -5
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/system_cmd.py +112 -29
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/workset_cmd.py +167 -135
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/config.py +157 -141
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/config_interface.py +502 -122
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/config_io.py +1 -1
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/core_defaults.py +31 -4
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/data/core-defaults.yaml +74 -2
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/helper_listener.py +13 -4
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/helpers.py +2 -2
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/import_reconcile.py +178 -38
- kanibako_cli-1.7.0rc0/src/kanibako/journal.py +168 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/names.py +104 -36
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/paths.py +329 -177
- kanibako_cli-1.7.0rc0/src/kanibako/registry_store.py +194 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/rig_registry.py +9 -7
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/settings_assemble.py +18 -20
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/settings_categories.py +19 -13
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/settings_configset.py +64 -81
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/settings_expand.py +124 -10
- kanibako_cli-1.7.0rc0/src/kanibako/settings_launch.py +1586 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/settings_merge.py +28 -93
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/settings_resolve.py +20 -2
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/settings_views.py +99 -6
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/shells.py +6 -4
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/targets/__init__.py +4 -4
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/targets/base.py +4 -2
- kanibako_cli-1.7.0rc0/src/kanibako/targets/credsync.py +452 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/templates.py +12 -12
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/workset.py +90 -82
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0/src/kanibako_cli.egg-info}/PKG-INFO +1 -1
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako_cli.egg-info/SOURCES.txt +6 -2
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_agent_config.py +41 -0
- kanibako_cli-1.7.0rc0/tests/test_agent_ref.py +211 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_agent_representation.py +53 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_agent_resolution.py +106 -14
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_channels.py +2 -2
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_cli.py +276 -50
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_cli_exitcodes.py +16 -8
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_comms.py +15 -17
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_config.py +295 -163
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_config_interface.py +537 -56
- kanibako_cli-1.7.0rc0/tests/test_create_recovery.py +506 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_env_cmd.py +6 -6
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_fork.py +4 -3
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_helper_cmd.py +1 -1
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_helper_listener.py +13 -3
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_image_sharing.py +3 -3
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_import.py +36 -41
- kanibako_cli-1.7.0rc0/tests/test_import_recovery.py +646 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_init_cmd.py +25 -52
- kanibako_cli-1.7.0rc0/tests/test_journal.py +147 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_lifecycle_integration.py +3 -3
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_names.py +210 -128
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_paths.py +19 -58
- kanibako_cli-1.7.0rc0/tests/test_registry_store.py +268 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_resolve_box_target.py +3 -3
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_resource_scoping.py +3 -3
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_rig_registry.py +1 -1
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_safety_invariants.py +2 -2
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_settings_assemble.py +25 -38
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_settings_categories.py +290 -29
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_settings_configset.py +185 -56
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_settings_expand.py +167 -0
- kanibako_cli-1.7.0rc0/tests/test_settings_launch.py +1144 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_settings_launch_equivalence.py +68 -10
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_settings_merge.py +31 -102
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_settings_views.py +1 -4
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_shells.py +7 -1
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_standalone_paths.py +2 -2
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_system_paths.py +247 -120
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_workset.py +49 -101
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_workset_paths.py +0 -79
- kanibako_cli-1.6.0.dev36/src/kanibako/__init__.py +0 -11
- kanibako_cli-1.6.0.dev36/src/kanibako/box_seed.py +0 -84
- kanibako_cli-1.6.0.dev36/src/kanibako/registry_store.py +0 -285
- kanibako_cli-1.6.0.dev36/src/kanibako/settings_launch.py +0 -734
- kanibako_cli-1.6.0.dev36/src/kanibako/targets/credsync.py +0 -208
- kanibako_cli-1.6.0.dev36/tests/test_box_seed.py +0 -137
- kanibako_cli-1.6.0.dev36/tests/test_registry_store.py +0 -238
- kanibako_cli-1.6.0.dev36/tests/test_settings_launch.py +0 -354
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/LICENSE.md +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/setup.cfg +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/__main__.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/_atomic.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/auth_browser.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/auth_parser.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/baseline.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/box_identity.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/browser_sidecar.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/browser_state.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/bun_sea.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/__init__.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/archive.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/baseline_cmd.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/fork_cmd.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/helper_cmd.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/image.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/restore.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/upgrade.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/commands/vault_cmd.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/container.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/containerfiles.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/containers/Containerfile.template-android +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/containers/Containerfile.template-dotnet +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/containers/Containerfile.template-js +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/containers/Containerfile.template-jvm +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/containers/Containerfile.template-systems +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/containers/__init__.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/containers/tmux.conf +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/data/__init__.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/data/image-baseline.yaml +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/data/templates/base/INSTRUCTIONS.md +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/deprecation.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/errors.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/freshness.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/git.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/helper_client.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/hygiene.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/image_sharing.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/install_method.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/log.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/plugins/__init__.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/py.typed +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/registry.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/rig_bundle.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/rig_meta.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/rig_resolve.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/rig_source.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/scripts/__init__.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/scripts/helper-init.sh +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/scripts/kanibako-entry +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/settings_store.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/shellenv.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/snapshots.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/targets/assembly.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/targets/no_agent.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/templates_image.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/tweakcc.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/tweakcc_cache.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako/utils.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako_cli.egg-info/dependency_links.txt +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako_cli.egg-info/entry_points.txt +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako_cli.egg-info/requires.txt +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/src/kanibako_cli.egg-info/top_level.txt +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_archive_restore_integration.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_atomic.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_auth_browser.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_auth_parser.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_baseline.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_box_identity.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_browser_sidecar.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_browser_state.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_bun_sea.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_container.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_container_extended.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_container_integration.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_containerfiles.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_credentials.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_defaults_golden.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_deprecations.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_error_messages.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_freshness.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_git.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_helper_client.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_helpers.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_hygiene.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_install_integration.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_log.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_paths_extended.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_registry.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_rig_bundle.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_rig_meta.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_rig_resolve.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_rig_source.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_settings_resolve.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_settings_store.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_setup_cmd.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_shellenv.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_snapshots.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_start_integration.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_status.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_templates.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_templates_image.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_tweakcc.py +0 -0
- {kanibako_cli-1.6.0.dev36 → kanibako_cli-1.7.0rc0}/tests/test_utils.py +0 -0
|
@@ -39,12 +39,12 @@ by the `kanibako` meta-package); other agents can be added as plugins.
|
|
|
39
39
|
- **Vault snapshots** -- per-box read-only and read-write shared
|
|
40
40
|
directories with smart snapshot strategy detection (reflink, hardlink,
|
|
41
41
|
or tar.xz depending on filesystem)
|
|
42
|
-
- **Shell customization** -- per-box environment variables (`box
|
|
42
|
+
- **Shell customization** -- per-box environment variables (`box set
|
|
43
43
|
env.*`), drop-in init scripts (`shell.d/`), and layered seed-once home
|
|
44
44
|
templates
|
|
45
45
|
- **Agent configuration** -- per-agent YAML config with default args, state
|
|
46
46
|
knobs, env vars, and shared caches; per-box setting overrides via
|
|
47
|
-
`box
|
|
47
|
+
`box set`
|
|
48
48
|
- **Shared caches** -- global download caches (pip, cargo, npm, etc.)
|
|
49
49
|
shared across boxes; agent-level caches via the agent config
|
|
50
50
|
- **Plugin system** -- agent-agnostic core (`kanibako-cli`); Claude/Codex/Goose
|
|
@@ -220,7 +220,7 @@ shortcuts for common operations:
|
|
|
220
220
|
| `box list` / `box ls` | List projects (`--all`, `--orphan`, `-q`) |
|
|
221
221
|
| `box info` / `box inspect` | Project details (mode, paths, lock, rig) |
|
|
222
222
|
| `box rm` / `box delete` | Remove project (`--purge` deletes metadata, `--force` skips confirm) |
|
|
223
|
-
| `box
|
|
223
|
+
| `box set` / `box get` / `box show` / `box reset` | View or modify project configuration |
|
|
224
224
|
| `box diagnose [project]` | Check project box health |
|
|
225
225
|
|
|
226
226
|
**Relocation & conversion:**
|
|
@@ -265,7 +265,7 @@ shortcuts for common operations:
|
|
|
265
265
|
| `workset list` / `workset ls` | List working sets (`-q`) |
|
|
266
266
|
| `workset info` / `workset inspect` | Working set details |
|
|
267
267
|
| `workset rm` / `workset delete` | Remove working set (`--purge`, `--force`) |
|
|
268
|
-
| `workset
|
|
268
|
+
| `workset set` / `workset get` / `workset show` / `workset reset` | View or modify workset configuration (use `workset set default <key>=<value>` for default-workset defaults) |
|
|
269
269
|
| `workset connect <workset> [source]` | Add project to working set (`--name`) |
|
|
270
270
|
| `workset disconnect <workset> <project>` | Remove project from working set (`--force`) |
|
|
271
271
|
|
|
@@ -275,7 +275,7 @@ shortcuts for common operations:
|
|
|
275
275
|
|------------|-------------|
|
|
276
276
|
| `agent list` / `agent ls` | List configured agents (`-q`) |
|
|
277
277
|
| `agent info` / `agent inspect` | Agent configuration details |
|
|
278
|
-
| `agent
|
|
278
|
+
| `agent set` / `agent get` / `agent show` / `agent reset` | View or modify agent configuration |
|
|
279
279
|
| `agent reauth [project]` | Refresh credentials |
|
|
280
280
|
|
|
281
281
|
### `box helper` / `box fork` Subcommands
|
|
@@ -300,7 +300,7 @@ The runtime helper and fork verbs (formerly under `crab`) now live under `box`:
|
|
|
300
300
|
| Subcommand | Description |
|
|
301
301
|
|------------|-------------|
|
|
302
302
|
| `system info` / `system inspect` | System details (version, runtime, paths) |
|
|
303
|
-
| `system
|
|
303
|
+
| `system set` / `system get` / `system show` / `system reset` | View or modify global configuration |
|
|
304
304
|
| `system upgrade` | Self-update (`--check` for dry run) |
|
|
305
305
|
| `system diagnose` | Check system health (runtime, images, agents, storage) |
|
|
306
306
|
|
|
@@ -349,7 +349,7 @@ error, not a silent no-op).
|
|
|
349
349
|
#### `--box`: operate on any box
|
|
350
350
|
|
|
351
351
|
`--box` substitutes for being in the box's directory: `kanibako stop --box myproj`,
|
|
352
|
-
`kanibako box
|
|
352
|
+
`kanibako box set --box myproj model=opus`. The value is a **box name (resolved
|
|
353
353
|
first) or a path**. It is the *subject* the command acts on, and stays orthogonal to
|
|
354
354
|
the `move`/`convert` *destination* group, so they coexist:
|
|
355
355
|
|
|
@@ -408,7 +408,7 @@ confirm. Non-TTY runs (CI / headless) skip the prompt gracefully.
|
|
|
408
408
|
nudge to run `setup` if it has never been run, then proceed.
|
|
409
409
|
|
|
410
410
|
Because `system.*` keys are **file-only** (see [Configuration](#configuration)),
|
|
411
|
-
the default agent is *not* settable via `kanibako system
|
|
411
|
+
the default agent is *not* settable via `kanibako system set` -- use `setup` or
|
|
412
412
|
edit `global/settings.yaml` directly.
|
|
413
413
|
|
|
414
414
|
## Project Modes
|
|
@@ -431,11 +431,11 @@ and run `kanibako`, and the box joins the primary workset.
|
|
|
431
431
|
|
|
432
432
|
The primary workset is addressable through the same `workset` commands as named
|
|
433
433
|
worksets (workset name token `__PRIMARY__`), so primary-workset settings use the
|
|
434
|
-
ordinary `workset
|
|
434
|
+
ordinary `workset set default` mechanism (see [Configuration](#configuration)):
|
|
435
435
|
|
|
436
436
|
```bash
|
|
437
|
-
kanibako workset
|
|
438
|
-
kanibako workset
|
|
437
|
+
kanibako workset set default model=opus # default for ALL primary-mode boxes
|
|
438
|
+
kanibako workset set default group_auth=false # distinct credentials by default
|
|
439
439
|
```
|
|
440
440
|
|
|
441
441
|
The names `__PRIMARY__` / `__STANDALONE__` (and legacy `default`) are reserved
|
|
@@ -629,9 +629,9 @@ container:
|
|
|
629
629
|
|
|
630
630
|
```bash
|
|
631
631
|
# Persistent (stored in project config)
|
|
632
|
-
kanibako box
|
|
633
|
-
kanibako system
|
|
634
|
-
kanibako box
|
|
632
|
+
kanibako box set env.EDITOR=vim # project-level
|
|
633
|
+
kanibako system set env.EDITOR=nano # global (all projects)
|
|
634
|
+
kanibako box get env.EDITOR # show one value
|
|
635
635
|
|
|
636
636
|
# Per-run (not persisted)
|
|
637
637
|
kanibako start -e EDITOR=vim -e DEBUG=1
|
|
@@ -644,7 +644,7 @@ Project env vars override global ones.
|
|
|
644
644
|
The shell prompt is controlled by the `KANIBAKO_PS1` environment variable:
|
|
645
645
|
|
|
646
646
|
```bash
|
|
647
|
-
kanibako box
|
|
647
|
+
kanibako box set env.KANIBAKO_PS1="(myproject) \u:\w\$ "
|
|
648
648
|
```
|
|
649
649
|
|
|
650
650
|
### Init scripts
|
|
@@ -702,8 +702,8 @@ Manage agent settings via the CLI:
|
|
|
702
702
|
|
|
703
703
|
```bash
|
|
704
704
|
kanibako agent list # list configured agents
|
|
705
|
-
kanibako agent
|
|
706
|
-
kanibako agent
|
|
705
|
+
kanibako agent get claude model # show the agent's model
|
|
706
|
+
kanibako agent set claude model=sonnet # set agent-level default
|
|
707
707
|
```
|
|
708
708
|
|
|
709
709
|
## Home Templates
|
|
@@ -822,7 +822,7 @@ guide.
|
|
|
822
822
|
pip install kanibako-target-myagent
|
|
823
823
|
|
|
824
824
|
# Use a specific target
|
|
825
|
-
kanibako box
|
|
825
|
+
kanibako box set box.agent=myagent
|
|
826
826
|
kanibako start
|
|
827
827
|
|
|
828
828
|
# (`crab_name` is gone -- select the agent via box.agent)
|
|
@@ -846,29 +846,30 @@ CLI flag > settings_required > box > workset > agent.<agent> > system > settings
|
|
|
846
846
|
**file-only** -- the CLI reads and shows them but refuses to set them (edit the
|
|
847
847
|
file directly); `setup` and programmatic writers still write them.
|
|
848
848
|
|
|
849
|
-
All settings levels share
|
|
849
|
+
All settings levels share the same four verbs -- `set` / `get` / `show` /
|
|
850
|
+
`reset`:
|
|
850
851
|
|
|
851
852
|
```bash
|
|
852
853
|
# Box level
|
|
853
|
-
kanibako box
|
|
854
|
-
kanibako box
|
|
855
|
-
kanibako box
|
|
856
|
-
kanibako box
|
|
857
|
-
kanibako box
|
|
854
|
+
kanibako box show # show box overrides
|
|
855
|
+
kanibako box show --effective # show resolved values (inherited + overrides)
|
|
856
|
+
kanibako box get model # get one key
|
|
857
|
+
kanibako box set model=sonnet # set one key
|
|
858
|
+
kanibako box reset model # remove override, back to default
|
|
858
859
|
|
|
859
860
|
# Workset level (group defaults inherited by member boxes)
|
|
860
|
-
kanibako workset
|
|
861
|
-
kanibako workset
|
|
861
|
+
kanibako workset set <workset> model=opus
|
|
862
|
+
kanibako workset set default model=opus # primary-workset default
|
|
862
863
|
|
|
863
864
|
# Agent level (defaults for all boxes using this agent)
|
|
864
|
-
kanibako agent
|
|
865
|
+
kanibako agent set claude model=opus
|
|
865
866
|
|
|
866
867
|
# System level (global settings defaults)
|
|
867
|
-
kanibako system
|
|
868
|
-
kanibako system
|
|
868
|
+
kanibako system set model=opus
|
|
869
|
+
kanibako system reset --all # reset all global settings
|
|
869
870
|
```
|
|
870
871
|
|
|
871
|
-
`kanibako system
|
|
872
|
+
`kanibako system set` sets **non-`system.`** settings only. `system.*`-prefixed
|
|
872
873
|
keys (layout paths AND `system.default_agent`) are **file-only**: the CLI shows them
|
|
873
874
|
but refuses to set/reset them, pointing you at the config file. Set the default agent
|
|
874
875
|
with `kanibako setup`; edit structural paths in `~/.config/kanibako.yaml` directly.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "kanibako-cli"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.7.0rc0"
|
|
8
8
|
description = "Run AI coding agents in rootless containers with per-project isolation"
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
10
|
dependencies = ["argcomplete>=3.0", "PyYAML>=6.0", "packaging>=20.0"]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""kanibako: Run AI coding agents in rootless containers with per-project isolation."""
|
|
2
|
+
|
|
3
|
+
__version__ = "1.7.0rc0"
|
|
4
|
+
|
|
5
|
+
# Two-tier setup/config compatibility constants for the 5-band setup-completion
|
|
6
|
+
# gate (design: ``plans/2026-06-23-setup-version-tiers-NEXT.md``). Both are
|
|
7
|
+
# compared against the recorded ``system.setup_completed`` marker by BASE version
|
|
8
|
+
# (PEP 440 via ``packaging.version``), so a dev/rc build of the same base reads
|
|
9
|
+
# as the released base, not "from the future".
|
|
10
|
+
#
|
|
11
|
+
# * ``SETUP_BCV`` — backward-compatible version: the OLDEST setup/config version
|
|
12
|
+
# this build can function with AT ALL. Below it, the build can no longer
|
|
13
|
+
# auto-fill the gaps → the gate ERRORS (rc1) and the user must re-run setup.
|
|
14
|
+
# Bump only when a setup change can no longer be auto-filled.
|
|
15
|
+
# * ``SETUP_FCV`` — forward-compatible version: the oldest version whose setup is
|
|
16
|
+
# COMPLETELY compatible (nothing new since). At or above it (but below the
|
|
17
|
+
# running build) the gate SILENTLY bumps the marker forward. Between BCV and
|
|
18
|
+
# FCV the gate NUDGES (non-blocking) to re-run setup. Bump whenever setup adds
|
|
19
|
+
# a new feature/case.
|
|
20
|
+
#
|
|
21
|
+
# Invariant: ``SETUP_BCV <= SETUP_FCV <= __version__`` (base versions). Most
|
|
22
|
+
# releases change setup in no way → bump NEITHER → existing configs land
|
|
23
|
+
# ``>= FCV`` → silent.
|
|
24
|
+
SETUP_BCV = "1.6.0"
|
|
25
|
+
SETUP_FCV = "1.7.0"
|
|
@@ -16,15 +16,19 @@ class AgentConfig:
|
|
|
16
16
|
"""Per-agent configuration loaded from an agent YAML file.
|
|
17
17
|
|
|
18
18
|
Sections:
|
|
19
|
-
agent
|
|
20
|
-
|
|
21
|
-
env
|
|
19
|
+
agent — identity (name, run_args) plus agent-state knobs
|
|
20
|
+
(model, access, start_mode, autonomous, …)
|
|
21
|
+
env — raw env vars injected into container (VAR -> value)
|
|
22
|
+
env_file — env-from-file pointers (VAR -> host PATH): at launch the
|
|
23
|
+
file's contents become the env var's VALUE (secret stays in
|
|
24
|
+
the host file, only the path is stored — spec §2d).
|
|
22
25
|
"""
|
|
23
26
|
|
|
24
27
|
name: str = ""
|
|
25
28
|
run_args: list[str] = field(default_factory=list)
|
|
26
29
|
state: dict[str, str] = field(default_factory=dict)
|
|
27
30
|
env: dict[str, str] = field(default_factory=dict)
|
|
31
|
+
env_file: dict[str, str] = field(default_factory=dict)
|
|
28
32
|
tweakcc: dict = field(default_factory=dict)
|
|
29
33
|
|
|
30
34
|
|
|
@@ -34,10 +38,10 @@ def agents_dir(data_path: Path, paths_agents: str = "agents") -> Path:
|
|
|
34
38
|
|
|
35
39
|
|
|
36
40
|
def agent_settings_path(agents_root: Path, agent_id: str) -> Path:
|
|
37
|
-
"""Return ``@agent.<agent>.
|
|
41
|
+
"""Return ``@meta.agent.<agent>.settings`` for *agent_id*.
|
|
38
42
|
|
|
39
43
|
The per-agent SETTINGS cascade file lives INSIDE the per-agent store dir
|
|
40
|
-
(``@agent.<agent>.
|
|
44
|
+
(``@meta.agent.<agent>.path`` = ``agents/<agent>/``) as ``settings.yaml``
|
|
41
45
|
— NOT the old sibling ``agents/<agent>.yaml`` file (D-2026-06-22). This
|
|
42
46
|
parallels the per-agent template dir ``agents/<agent>/template`` and the
|
|
43
47
|
Part-3 ``agents/<agent>/{plugins,cache}`` stores.
|
|
@@ -76,6 +80,10 @@ def load_agent_config(path: Path) -> AgentConfig:
|
|
|
76
80
|
k: str(v) for k, v in agent_sec.items() if k not in IDENTITY_KEYS
|
|
77
81
|
}
|
|
78
82
|
cfg.env = {k: str(v) for k, v in data.get("env", {}).items()}
|
|
83
|
+
# env_file: VAR -> host PATH pointer (the token file). Stored as a plain
|
|
84
|
+
# string path; the file's CONTENTS (the secret) are never persisted here —
|
|
85
|
+
# they are read into the container env only at launch (spec §2d).
|
|
86
|
+
cfg.env_file = {k: str(v) for k, v in data.get("env_file", {}).items()}
|
|
79
87
|
cfg.tweakcc = dict(data.get("tweakcc", {}))
|
|
80
88
|
|
|
81
89
|
return cfg
|
|
@@ -93,6 +101,7 @@ def write_agent_config(path: Path, cfg: AgentConfig) -> None:
|
|
|
93
101
|
data: dict = {
|
|
94
102
|
"agent": agent_sec,
|
|
95
103
|
"env": dict(cfg.env),
|
|
104
|
+
"env_file": dict(cfg.env_file),
|
|
96
105
|
"tweakcc": dict(cfg.tweakcc),
|
|
97
106
|
}
|
|
98
107
|
# The settings file lives inside the per-agent store dir
|
|
@@ -159,6 +159,7 @@ def load_descriptor(package: str, filename: str) -> PluginDescriptor:
|
|
|
159
159
|
cred_files=tuple(_build_cred_file(c) for c in desc.get("cred_files", [])),
|
|
160
160
|
host_prep=bool(desc.get("host_prep", False)),
|
|
161
161
|
init_dirs=tuple(desc.get("init_dirs", ())),
|
|
162
|
+
auth_share_support=bool(desc.get("auth_share_support", False)),
|
|
162
163
|
)
|
|
163
164
|
|
|
164
165
|
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""Agent-ref parsing: the ``persona+harness`` selection grammar (rider MVP).
|
|
2
|
+
|
|
3
|
+
An ``--agent`` value is a *ref* that names an agent as ``persona+harness`` —
|
|
4
|
+
for example ``navigator+claude``. This module is the single, dependency-light,
|
|
5
|
+
pure parser for that grammar; every ref source (explicit ``--agent``, box /
|
|
6
|
+
workset / system settings) is normalised through here on the way into the
|
|
7
|
+
resolver so the rest of the code deals only in *node-names*.
|
|
8
|
+
|
|
9
|
+
Terminology (design SOT ``plans/2026-06-24-agent-variant-DESIGN.md``):
|
|
10
|
+
|
|
11
|
+
* **harness** — the agent runtime/plugin, right of the separator (``claude``).
|
|
12
|
+
Resolves the target/plugin (``resolve_target`` / ``discover_targets`` key).
|
|
13
|
+
* **persona** — the identity, left of the separator (``navigator``).
|
|
14
|
+
* **node-name** — the canonical fused form ``persona℘harness`` (``navigator℘claude``).
|
|
15
|
+
This is the on-disk ``agents/<node>/`` dir and the keyspace ``agent.<node>.*``
|
|
16
|
+
slot. It is what ``resolve_agent`` returns and what ``KANIBAKO_AGENT`` stamps.
|
|
17
|
+
|
|
18
|
+
Separators: the human-typable ``+`` (U+002B) and the canonical ``℘`` (U+2118,
|
|
19
|
+
SCRIPT CAPITAL P — "persona"). Both are accepted on input; the node-name always
|
|
20
|
+
canonicalises the separator to ``℘``. Only the FIRST separator splits (a persona
|
|
21
|
+
segment may not itself contain a separator; the harness segment is whatever
|
|
22
|
+
follows, verbatim, and is likewise separator-checked).
|
|
23
|
+
|
|
24
|
+
Backward compatibility (LOAD-BEARING): a bare ref with no separator (``claude``)
|
|
25
|
+
parses to ``(raw, raw)`` — node == harness == the bare name — so every bare path
|
|
26
|
+
is byte-for-byte identical to pre-rider behaviour.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
from kanibako.errors import ConfigError
|
|
32
|
+
|
|
33
|
+
#: The canonical persona/harness separator (U+2118 SCRIPT CAPITAL P).
|
|
34
|
+
CANONICAL_SEP = "℘" # ℘
|
|
35
|
+
#: The human-typable separator (U+002B PLUS SIGN).
|
|
36
|
+
PLUS_SEP = "+"
|
|
37
|
+
#: Both accepted separators.
|
|
38
|
+
SEPARATORS = (PLUS_SEP, CANONICAL_SEP)
|
|
39
|
+
|
|
40
|
+
#: Characters permitted in a persona OR harness segment (fs- and key-safe).
|
|
41
|
+
#: Alphanumeric plus ``-``, ``.``, ``_`` — NO separators, NO path/whitespace.
|
|
42
|
+
_SAFE_EXTRA = frozenset("-._")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _is_segment_safe(segment: str) -> bool:
|
|
46
|
+
"""A non-empty segment of only ``[A-Za-z0-9]`` plus ``-`` / ``.`` / ``_``."""
|
|
47
|
+
if not segment:
|
|
48
|
+
return False
|
|
49
|
+
return all(ch.isalnum() or ch in _SAFE_EXTRA for ch in segment)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _first_sep_index(raw: str) -> int:
|
|
53
|
+
"""Index of the FIRST separator in *raw*, or ``-1`` if none."""
|
|
54
|
+
indices = [raw.find(sep) for sep in SEPARATORS]
|
|
55
|
+
present = [i for i in indices if i != -1]
|
|
56
|
+
return min(present) if present else -1
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def parse_agent_ref(raw: str) -> tuple[str, str]:
|
|
60
|
+
"""Parse an agent ref into ``(node, harness)``.
|
|
61
|
+
|
|
62
|
+
* Bare (no separator): ``"claude"`` -> ``("claude", "claude")`` — node and
|
|
63
|
+
harness are the same, preserving backward compatibility.
|
|
64
|
+
* Composite: ``"navigator+claude"`` -> ``("navigator℘claude", "claude")`` —
|
|
65
|
+
the persona (left) and harness (right) are validated and re-joined with
|
|
66
|
+
the canonical ``℘`` separator to form the node-name.
|
|
67
|
+
|
|
68
|
+
Splits on the FIRST separator only; a second separator in EITHER segment is
|
|
69
|
+
rejected (a segment must be fs/key-safe: alnum plus ``-``/``.``/``_``).
|
|
70
|
+
|
|
71
|
+
:raises ConfigError: on empty input, an empty persona/harness segment, or a
|
|
72
|
+
segment containing an illegal character (incl. a stray separator).
|
|
73
|
+
"""
|
|
74
|
+
if not isinstance(raw, str):
|
|
75
|
+
raise ConfigError(
|
|
76
|
+
f"agent ref must be a string, got {type(raw).__name__}: {raw!r}"
|
|
77
|
+
)
|
|
78
|
+
ref = raw.strip()
|
|
79
|
+
if not ref:
|
|
80
|
+
raise ConfigError("agent ref is empty")
|
|
81
|
+
|
|
82
|
+
idx = _first_sep_index(ref)
|
|
83
|
+
if idx == -1:
|
|
84
|
+
# Bare: node == harness == the whole (validated) name.
|
|
85
|
+
if not _is_segment_safe(ref):
|
|
86
|
+
raise ConfigError(
|
|
87
|
+
f"invalid agent name '{ref}': names may contain only letters, "
|
|
88
|
+
f"digits, '-', '.', '_'"
|
|
89
|
+
)
|
|
90
|
+
return ref, ref
|
|
91
|
+
|
|
92
|
+
persona = ref[:idx]
|
|
93
|
+
harness = ref[idx + 1 :]
|
|
94
|
+
|
|
95
|
+
if not _is_segment_safe(persona):
|
|
96
|
+
raise ConfigError(
|
|
97
|
+
f"invalid agent ref '{raw}': the persona segment '{persona}' must be "
|
|
98
|
+
f"non-empty and contain only letters, digits, '-', '.', '_' "
|
|
99
|
+
f"(no separator)"
|
|
100
|
+
)
|
|
101
|
+
if not _is_segment_safe(harness):
|
|
102
|
+
raise ConfigError(
|
|
103
|
+
f"invalid agent ref '{raw}': the harness segment '{harness}' must be "
|
|
104
|
+
f"non-empty and contain only letters, digits, '-', '.', '_' "
|
|
105
|
+
f"(no separator)"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
node = f"{persona}{CANONICAL_SEP}{harness}"
|
|
109
|
+
return node, harness
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def harness_of(node: str) -> str:
|
|
113
|
+
"""Return the harness (part right of ``℘``) of a *node*-name.
|
|
114
|
+
|
|
115
|
+
A bare node with no separator IS its own harness (``"claude" -> "claude"``).
|
|
116
|
+
Only the canonical ``℘`` is recognised here — callers pass node-names, which
|
|
117
|
+
are always canonicalised. ``+`` is deliberately NOT split here so a literal
|
|
118
|
+
``+`` in an already-canonical node (there should be none) is not mistaken for
|
|
119
|
+
a separator; use :func:`parse_agent_ref` / :func:`canonicalize_agent_ref` for
|
|
120
|
+
raw, possibly-``+`` input.
|
|
121
|
+
"""
|
|
122
|
+
_, _, harness = node.rpartition(CANONICAL_SEP)
|
|
123
|
+
return harness if harness else node
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def with_harness(node: str, harness: str) -> str:
|
|
127
|
+
"""Return *node* with its harness segment REPLACED by *harness*.
|
|
128
|
+
|
|
129
|
+
Preserves the persona (left of ``℘``) while swapping the harness — used when
|
|
130
|
+
the actually-RESOLVED target differs from the requested harness (e.g. the
|
|
131
|
+
``NoAgentTarget`` fallback when a named agent's binary is absent), so the
|
|
132
|
+
on-disk ``agents/<node>/`` dir + keyspace slot follow the real target.
|
|
133
|
+
|
|
134
|
+
* bare node (``"claude"``, harness ``"claude"``) -> ``"claude"`` (unchanged);
|
|
135
|
+
* bare node, fallback harness (``"claude"``, ``"no_agent"``) -> ``"no_agent"``;
|
|
136
|
+
* rider node (``"navigator℘claude"``, ``"claude"``) -> ``"navigator℘claude"``;
|
|
137
|
+
* rider node, fallback (``"navigator℘claude"``, ``"no_agent"``) ->
|
|
138
|
+
``"navigator℘no_agent"`` (persona kept, harness swapped).
|
|
139
|
+
"""
|
|
140
|
+
persona, sep, _ = node.rpartition(CANONICAL_SEP)
|
|
141
|
+
if not sep:
|
|
142
|
+
# Bare node: it IS just a harness — replace wholesale.
|
|
143
|
+
return harness
|
|
144
|
+
return f"{persona}{CANONICAL_SEP}{harness}"
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def display_agent_ref(node: str) -> str:
|
|
148
|
+
"""Return the USER-FACING form of a *node*-name (``℘`` -> ``+``).
|
|
149
|
+
|
|
150
|
+
Presentation-only inverse of :func:`canonicalize_agent_ref`: internal storage,
|
|
151
|
+
keyspace, on-disk paths, and ``KANIBAKO_AGENT`` all keep the canonical ``℘``
|
|
152
|
+
form; whenever an agent ref is rendered to the user (error text, ``crab``/``box``
|
|
153
|
+
listings, session labels, log lines) it is shown with the typable ``+``.
|
|
154
|
+
|
|
155
|
+
Bare names contain no ``℘`` -> returned unchanged (existing output byte-identical).
|
|
156
|
+
Does NOT validate: it is a pure display swap tolerant of any string.
|
|
157
|
+
"""
|
|
158
|
+
return node.replace(CANONICAL_SEP, PLUS_SEP)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def canonicalize_agent_ref(raw: str) -> str:
|
|
162
|
+
"""Return the canonical *node*-name for a raw ref (``+`` -> ``℘``).
|
|
163
|
+
|
|
164
|
+
Idempotent: a node that is already canonical (or bare) returns unchanged.
|
|
165
|
+
Validates via :func:`parse_agent_ref`, so a malformed ref raises ``ConfigError``.
|
|
166
|
+
"""
|
|
167
|
+
node, _ = parse_agent_ref(raw)
|
|
168
|
+
return node
|
|
@@ -88,17 +88,29 @@ if TYPE_CHECKING:
|
|
|
88
88
|
def agent_default_partial(
|
|
89
89
|
descriptor: PluginDescriptor,
|
|
90
90
|
install: AgentInstall,
|
|
91
|
+
node_name: str | None = None,
|
|
91
92
|
) -> KeyStore:
|
|
92
93
|
"""Represent a descriptor's delivery bindings as an agent-level KeyStore partial.
|
|
93
94
|
|
|
94
95
|
Returns a :class:`~kanibako.settings_store.KeyStore` partial rooted at
|
|
95
|
-
``agent.<
|
|
96
|
-
|
|
97
|
-
``agent.<
|
|
96
|
+
``agent.<node>`` where ``<node>`` is the ACTIVE node-name (*node_name*), holding
|
|
97
|
+
each resolvable :class:`~kanibako.targets.base.Binding` as a
|
|
98
|
+
``agent.<node>.bindings.{ro,rw}.<key> = Bind(host_src, box_dest, opts)`` leaf —
|
|
98
99
|
mirroring :func:`~kanibako.targets.assembly.resolve_binding_source` with NO
|
|
99
100
|
override and NO existence check (S26). See the module docstring for the full
|
|
100
101
|
rules and the None-origin OMIT contract.
|
|
101
102
|
|
|
103
|
+
RIDER threading (Block E fix 2a): the read side (``_agent_pick_node``) walks
|
|
104
|
+
``agent.default`` ∪ ``agent.<active_agent>``, where ``<active_agent>`` is the
|
|
105
|
+
resolved NODE-name (``navigator℘claude`` for a rider). The descriptor's
|
|
106
|
+
``install.name`` is the HARNESS (``"claude"``, hardcoded in claude's ``detect()``),
|
|
107
|
+
so rooting the binds under ``install.name`` ORPHANS a rider's AGENT_CRITICAL
|
|
108
|
+
delivery binds at ``agent.claude.*`` (never read → the ``claude`` binary is never
|
|
109
|
+
mounted → the container exits immediately). So the partial roots under the ACTIVE
|
|
110
|
+
node-name (*node_name*). For a BARE agent node==harness=="claude", so the binds
|
|
111
|
+
still land at ``agent.claude.*`` — byte-identical. *node_name* falls back to
|
|
112
|
+
``install.name`` only when a caller omits it (legacy / test convenience).
|
|
113
|
+
|
|
102
114
|
The partial nests ``agent.<name>.bindings`` with ``ro`` / ``rw`` sub-tables; a
|
|
103
115
|
sub-table is present only if at least one binding lands in it, ``bindings`` is
|
|
104
116
|
present only if at least one binding resolved, and the result is ALWAYS a
|
|
@@ -134,12 +146,14 @@ def agent_default_partial(
|
|
|
134
146
|
if dict.__len__(rw_binds):
|
|
135
147
|
bindings["rw"] = rw_binds
|
|
136
148
|
|
|
137
|
-
# Root under the
|
|
138
|
-
#
|
|
139
|
-
#
|
|
140
|
-
#
|
|
141
|
-
#
|
|
142
|
-
|
|
149
|
+
# Root under the ACTIVE node-name (§2d ``agent.<agent>.*``): for a BARE agent the
|
|
150
|
+
# node-name IS the harness (``agent.claude.bindings.*``); for a RIDER it is the
|
|
151
|
+
# composite node (``agent.navigator℘claude.bindings.*``) the read side actually
|
|
152
|
+
# walks (fix 2a). The agent NAME is part of the KEY PATH (NOT a bare ``agent``
|
|
153
|
+
# token, §0 L21) — so this partial merges BY NAME with 2a's discriminated
|
|
154
|
+
# ``agent.<active>.*`` level and any higher-scope ``agent.<node>.*`` override
|
|
155
|
+
# (block 2b), including the node-keyed override bridge.
|
|
156
|
+
name = node_name if node_name is not None else install.name
|
|
143
157
|
agent_sub = KeyStore()
|
|
144
158
|
if dict.__len__(bindings):
|
|
145
159
|
agent_sub["bindings"] = bindings
|
|
@@ -9,17 +9,17 @@ file-seeding live in sub-step 6b.
|
|
|
9
9
|
|
|
10
10
|
Two channel scopes (TARGET §1, §2c, §2f):
|
|
11
11
|
|
|
12
|
-
* **system scope** — five type roots under ``@system.
|
|
12
|
+
* **system scope** — five type roots under ``@system.channelroot`` (commons, chat,
|
|
13
13
|
broadcast, mailboxes, share). The instance-owned types (mailboxes, share) are
|
|
14
14
|
*partitioned* by the workset-name token: ``mailboxes/<ws>`` and ``share/<ws>``
|
|
15
15
|
where ``<ws>`` is ``__PRIMARY__`` | ``<named>`` | ``__STANDALONE__``. These
|
|
16
16
|
partition roots apply to EVERY mode (standalone included).
|
|
17
|
-
* **workset scope** — three type roots under ``@workset.
|
|
17
|
+
* **workset scope** — three type roots under ``@meta.workset.path/channels``
|
|
18
18
|
(commons, chat, share). These exist for the PRIMARY and NAMED modes ONLY;
|
|
19
|
-
standalone has no workset-local channels (its ``workset.
|
|
19
|
+
standalone has no workset-local channels (its ``workset.channelroot`` is
|
|
20
20
|
``<None>`` per the TARGET).
|
|
21
21
|
|
|
22
|
-
The per-instance partition ADDRESSES (``box.
|
|
22
|
+
The per-instance partition ADDRESSES (``meta.box.{inbox,share_global,
|
|
23
23
|
share_workset}``) are this box's own mailbox/share dirs *within* the partitioned
|
|
24
24
|
roots — derived from the workset-name token + the box name.
|
|
25
25
|
|
|
@@ -27,7 +27,7 @@ roots — derived from the workset-name token + the box name.
|
|
|
27
27
|
``ProjectPaths`` (the legacy comms block used only ``proj.name``). Per the
|
|
28
28
|
design-review A8 resolution we derive them HERE from ``proj.mode`` + ``proj.
|
|
29
29
|
group`` rather than widening the resolver's public shape: PRIMARY roots at
|
|
30
|
-
``@
|
|
30
|
+
``@config.primary_workset`` with token ``__PRIMARY__``; NAMED roots at
|
|
31
31
|
``proj.group.root`` with token ``proj.group.name``; STANDALONE roots at
|
|
32
32
|
``proj.metadata_path`` (the root; the workspace is a subdir) with token
|
|
33
33
|
``__STANDALONE__``.
|
|
@@ -68,9 +68,9 @@ class SystemPartition:
|
|
|
68
68
|
class WorksetChannels:
|
|
69
69
|
"""The workset-local channel roots (PRIMARY/NAMED only).
|
|
70
70
|
|
|
71
|
-
``@workset.
|
|
71
|
+
``@workset.channelroot = @meta.workset.path/channels`` with ``commons``,
|
|
72
72
|
``chat`` (+ the reserved ``broadcast.md`` / default ``general.md`` files
|
|
73
|
-
inside it), and ``share`` (per-box subdirs are ``box.
|
|
73
|
+
inside it), and ``share`` (per-box subdirs are ``meta.box.share_workset``).
|
|
74
74
|
"""
|
|
75
75
|
|
|
76
76
|
root: Path
|
|
@@ -83,7 +83,7 @@ class WorksetChannels:
|
|
|
83
83
|
|
|
84
84
|
@dataclass(frozen=True)
|
|
85
85
|
class BoxChannelAddresses:
|
|
86
|
-
"""This box's own partition ADDRESSES (TARGET §2c ``box
|
|
86
|
+
"""This box's own partition ADDRESSES (TARGET §2c ``meta.box.*``).
|
|
87
87
|
|
|
88
88
|
* ``inbox`` == ``@system.channels.mailboxes/<ws>/<box>`` — this box's own
|
|
89
89
|
mailbox dir (also surfaced in-box at ``~/channels/inbox``).
|
|
@@ -126,7 +126,7 @@ def own_partition_dirs(
|
|
|
126
126
|
The lower-level primitive behind :func:`box_channel_addresses` — it takes the
|
|
127
127
|
workset-name token + box name directly (no ``ProjectPaths``), so the lifecycle
|
|
128
128
|
relocation can compute BOTH the OLD and the NEW partition for a box being
|
|
129
|
-
moved/converted. Mirrors :func:`system_partition` + the ``box.
|
|
129
|
+
moved/converted. Mirrors :func:`system_partition` + the ``meta.box.{inbox,
|
|
130
130
|
share_global}`` joins (TARGET §2c).
|
|
131
131
|
"""
|
|
132
132
|
part = system_partition(std, ws_token)
|
|
@@ -162,9 +162,9 @@ def workset_name_token(proj: ProjectPaths) -> str:
|
|
|
162
162
|
|
|
163
163
|
|
|
164
164
|
def workset_root(proj: ProjectPaths, std: StandardPaths) -> Path:
|
|
165
|
-
"""Return ``@workset.
|
|
165
|
+
"""Return ``@meta.workset.path`` for *proj*.
|
|
166
166
|
|
|
167
|
-
PRIMARY → ``@
|
|
167
|
+
PRIMARY → ``@config.primary_workset``; NAMED → the named workset root
|
|
168
168
|
(``proj.group.root``); STANDALONE → the project root itself
|
|
169
169
|
(``proj.metadata_path``, which for standalone IS the root — the workspace is
|
|
170
170
|
a ``workspace/`` subdir under it, so ``project_path`` is not the root).
|
|
@@ -214,7 +214,7 @@ def workset_channel_paths(
|
|
|
214
214
|
"""Derive the WORKSET-local channel roots for *proj* (PRIMARY/NAMED only).
|
|
215
215
|
|
|
216
216
|
Returns ``None`` for standalone (no workset-local channels). Rooted at
|
|
217
|
-
``@workset.
|
|
217
|
+
``@meta.workset.path/channels`` (A3 — a derived helper, NOT new fields on
|
|
218
218
|
``ProjectPaths``).
|
|
219
219
|
"""
|
|
220
220
|
if not has_workset_channels(proj):
|
|
@@ -234,7 +234,7 @@ def workset_channel_paths(
|
|
|
234
234
|
def box_channel_addresses(
|
|
235
235
|
proj: ProjectPaths, std: StandardPaths
|
|
236
236
|
) -> BoxChannelAddresses:
|
|
237
|
-
"""Derive this box's own partition addresses (``box
|
|
237
|
+
"""Derive this box's own partition addresses (``meta.box.*``) for *proj*.
|
|
238
238
|
|
|
239
239
|
``inbox`` / ``share_global`` always resolve (system-scope, every mode);
|
|
240
240
|
``share_workset`` is ``None`` for standalone. *proj.name* is the box name.
|