kanibako-cli 1.6.0.dev31__tar.gz → 1.6.0.dev36__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.dev31/src/kanibako_cli.egg-info → kanibako_cli-1.6.0.dev36}/PKG-INFO +2 -1
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/pyproject.toml +2 -2
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/__init__.py +1 -1
- kanibako_cli-1.6.0.dev36/src/kanibako/agent_defaults.py +183 -0
- kanibako_cli-1.6.0.dev36/src/kanibako/agent_representation.py +152 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/cli.py +47 -2
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/agent_cmd.py +18 -2
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/box/_parser.py +2 -2
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/flags.py +11 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/helper_cmd.py +2 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/image.py +121 -16
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/install.py +11 -9
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/setup_cmd.py +10 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/start.py +722 -672
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/workset_cmd.py +107 -35
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/config.py +10 -207
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/config_interface.py +198 -2
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/container.py +151 -40
- kanibako_cli-1.6.0.dev36/src/kanibako/core_defaults.py +302 -0
- kanibako_cli-1.6.0.dev36/src/kanibako/data/core-defaults.yaml +195 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/freshness.py +1 -1
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/helper_client.py +2 -1
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/helper_listener.py +52 -24
- kanibako_cli-1.6.0.dev36/src/kanibako/image_sharing.py +287 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/paths.py +7 -3
- kanibako_cli-1.6.0.dev36/src/kanibako/settings_assemble.py +318 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/settings_categories.py +74 -45
- kanibako_cli-1.6.0.dev36/src/kanibako/settings_configset.py +417 -0
- kanibako_cli-1.6.0.dev36/src/kanibako/settings_expand.py +401 -0
- kanibako_cli-1.6.0.dev36/src/kanibako/settings_launch.py +734 -0
- kanibako_cli-1.6.0.dev36/src/kanibako/settings_merge.py +302 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/settings_resolve.py +128 -94
- kanibako_cli-1.6.0.dev36/src/kanibako/settings_store.py +321 -0
- kanibako_cli-1.6.0.dev36/src/kanibako/settings_views.py +466 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/targets/base.py +16 -8
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/templates.py +67 -50
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36/src/kanibako_cli.egg-info}/PKG-INFO +2 -1
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako_cli.egg-info/SOURCES.txt +21 -5
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako_cli.egg-info/requires.txt +1 -0
- kanibako_cli-1.6.0.dev36/tests/test_agent_representation.py +339 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_agent_resolution.py +39 -18
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_cli.py +111 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_config.py +19 -111
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_config_interface.py +131 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_container.py +212 -3
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_container_extended.py +73 -23
- kanibako_cli-1.6.0.dev36/tests/test_defaults_golden.py +389 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_error_messages.py +3 -3
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_freshness.py +5 -1
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_helper_client.py +2 -2
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_helper_cmd.py +1 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_helper_listener.py +144 -23
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_image_sharing.py +302 -27
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_resource_scoping.py +39 -11
- kanibako_cli-1.6.0.dev36/tests/test_settings_assemble.py +511 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_settings_categories.py +335 -45
- kanibako_cli-1.6.0.dev36/tests/test_settings_configset.py +440 -0
- kanibako_cli-1.6.0.dev36/tests/test_settings_expand.py +465 -0
- kanibako_cli-1.6.0.dev36/tests/test_settings_launch.py +354 -0
- kanibako_cli-1.6.0.dev36/tests/test_settings_launch_equivalence.py +478 -0
- kanibako_cli-1.6.0.dev36/tests/test_settings_merge.py +470 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_settings_resolve.py +56 -1
- kanibako_cli-1.6.0.dev36/tests/test_settings_store.py +463 -0
- kanibako_cli-1.6.0.dev36/tests/test_settings_views.py +375 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_setup_cmd.py +24 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_snapshots.py +106 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_templates.py +95 -81
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_workset.py +5 -4
- kanibako_cli-1.6.0.dev31/src/kanibako/image_sharing.py +0 -133
- kanibako_cli-1.6.0.dev31/src/kanibako/settings_seeds.py +0 -81
- kanibako_cli-1.6.0.dev31/src/kanibako/settings_shares.py +0 -77
- kanibako_cli-1.6.0.dev31/tests/test_settings_loader.py +0 -229
- kanibako_cli-1.6.0.dev31/tests/test_settings_seeds.py +0 -352
- kanibako_cli-1.6.0.dev31/tests/test_settings_shares.py +0 -388
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/LICENSE.md +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/README.md +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/setup.cfg +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/__main__.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/_atomic.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/agent_config.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/auth_browser.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/auth_parser.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/baseline.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/box_identity.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/box_seed.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/browser_sidecar.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/browser_state.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/bun_sea.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/channels.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/__init__.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/archive.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/baseline_cmd.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/box/__init__.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/box/_duplicate.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/box/_lifecycle.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/clean.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/diagnose.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/fork_cmd.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/restore.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/stop.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/system_cmd.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/upgrade.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/commands/vault_cmd.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/config_io.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/containerfiles.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/containers/Containerfile.template-android +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/containers/Containerfile.template-dotnet +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/containers/Containerfile.template-js +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/containers/Containerfile.template-jvm +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/containers/Containerfile.template-systems +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/containers/__init__.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/containers/tmux.conf +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/data/__init__.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/data/image-baseline.yaml +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/data/templates/base/INSTRUCTIONS.md +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/deprecation.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/errors.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/git.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/helpers.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/hygiene.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/import_reconcile.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/install_method.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/log.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/names.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/plugins/__init__.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/py.typed +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/registry.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/registry_store.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/rig_bundle.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/rig_meta.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/rig_registry.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/rig_resolve.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/rig_source.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/scripts/__init__.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/scripts/helper-init.sh +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/scripts/kanibako-entry +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/shellenv.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/shells.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/snapshots.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/targets/__init__.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/targets/assembly.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/targets/credsync.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/targets/no_agent.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/templates_image.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/tweakcc.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/tweakcc_cache.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/utils.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako/workset.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako_cli.egg-info/dependency_links.txt +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako_cli.egg-info/entry_points.txt +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/src/kanibako_cli.egg-info/top_level.txt +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_agent_config.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_archive_restore_integration.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_atomic.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_auth_browser.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_auth_parser.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_baseline.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_box_identity.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_box_seed.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_browser_sidecar.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_browser_state.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_bun_sea.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_channels.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_cli_exitcodes.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_comms.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_container_integration.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_containerfiles.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_credentials.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_deprecations.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_env_cmd.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_fork.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_git.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_helpers.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_hygiene.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_import.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_init_cmd.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_install_integration.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_lifecycle_integration.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_log.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_names.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_paths.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_paths_extended.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_registry.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_registry_store.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_resolve_box_target.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_rig_bundle.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_rig_meta.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_rig_registry.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_rig_resolve.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_rig_source.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_safety_invariants.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_shellenv.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_shells.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_standalone_paths.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_start_integration.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_status.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_system_paths.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_templates_image.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_tweakcc.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_utils.py +0 -0
- {kanibako_cli-1.6.0.dev31 → kanibako_cli-1.6.0.dev36}/tests/test_workset_paths.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kanibako-cli
|
|
3
|
-
Version: 1.6.0.
|
|
3
|
+
Version: 1.6.0.dev36
|
|
4
4
|
Summary: Run AI coding agents in rootless containers with per-project isolation
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
License-File: LICENSE.md
|
|
@@ -13,4 +13,5 @@ Requires-Dist: pytest-cov; extra == "dev"
|
|
|
13
13
|
Requires-Dist: mypy; extra == "dev"
|
|
14
14
|
Requires-Dist: ruff; extra == "dev"
|
|
15
15
|
Requires-Dist: bump2version; extra == "dev"
|
|
16
|
+
Requires-Dist: pexpect>=4.8; extra == "dev"
|
|
16
17
|
Dynamic: license-file
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "kanibako-cli"
|
|
7
|
-
version = "1.6.0.
|
|
7
|
+
version = "1.6.0.dev36"
|
|
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"]
|
|
@@ -16,7 +16,7 @@ kanibako = "kanibako.cli:main"
|
|
|
16
16
|
no_agent = "kanibako.targets.no_agent:NoAgentTarget"
|
|
17
17
|
|
|
18
18
|
[project.optional-dependencies]
|
|
19
|
-
dev = ["pytest>=7.0", "pytest-cov", "mypy", "ruff", "bump2version"]
|
|
19
|
+
dev = ["pytest>=7.0", "pytest-cov", "mypy", "ruff", "bump2version", "pexpect>=4.8"]
|
|
20
20
|
|
|
21
21
|
[tool.setuptools.packages.find]
|
|
22
22
|
where = ["src"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""kanibako: Run AI coding agents in rootless containers with per-project isolation."""
|
|
2
2
|
|
|
3
|
-
__version__ = "1.6.0.
|
|
3
|
+
__version__ = "1.6.0.dev36"
|
|
4
4
|
|
|
5
5
|
# The oldest build version whose ``setup`` run is still considered current. The
|
|
6
6
|
# setup-completion gate (W1) re-nudges the user to re-run ``kanibako setup`` when
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""Per-agent plugin defaults — thin reader of each plugin's shipped defaults file.
|
|
2
|
+
|
|
3
|
+
Each agent plugin package (``kanibako.plugins.claude`` / ``…goose`` / ``…codex``)
|
|
4
|
+
owns ONE declarative defaults file (``<agent>-defaults.yaml``) holding that
|
|
5
|
+
agent's complete DEFAULT-SET — its :class:`~kanibako.targets.base.PluginDescriptor`
|
|
6
|
+
data (bindings, launch grammar, settings, container env, cred files, …) plus its
|
|
7
|
+
AGENT-scope ``default_shares`` — in the specced structured form. This mirrors how
|
|
8
|
+
the system/core defaults ship (:mod:`kanibako.core_defaults` /
|
|
9
|
+
``kanibako/data/core-defaults.yaml``) and how containerfiles/templates ship via
|
|
10
|
+
:mod:`importlib.resources`.
|
|
11
|
+
|
|
12
|
+
This module is the THIN reader those plugins call from their ``descriptor`` /
|
|
13
|
+
``default_shares`` properties. It builds the descriptor from the file so the
|
|
14
|
+
in-code descriptor is no longer hand-written per plugin.
|
|
15
|
+
|
|
16
|
+
Split (documented in each YAML header too):
|
|
17
|
+
|
|
18
|
+
* DECLARATIVE — everything in the file: the binding ORIGIN selectors (which
|
|
19
|
+
detected install field supplies the host source — a declarative string, NOT a
|
|
20
|
+
probed value), box-side destinations, the mode/operations grammar, settings
|
|
21
|
+
routing, container env, cred-file lifecycle, host_prep/init_dirs, and the
|
|
22
|
+
agent-share box_dests. Box-side destinations under the guest home are written
|
|
23
|
+
as ``$GUEST_HOME`` EXPRESSIONS (e.g. ``$GUEST_HOME/.local/bin/claude``) that
|
|
24
|
+
this loader expands from the single :data:`~kanibako.settings_resolve.GUEST_HOME`
|
|
25
|
+
constant — no ``/home/agent`` literal in the file.
|
|
26
|
+
* CODE-RESOLVED — the CRITICAL/runtime-probed HOST binary paths stay in the
|
|
27
|
+
plugin's ``detect()`` (the contract-path constants + npm/ELF resolution). The
|
|
28
|
+
file only names, declaratively, WHICH detected field (``binary`` / ``launcher`` /
|
|
29
|
+
``install_dir``) each binding's host source comes from (its ``origin``); the
|
|
30
|
+
actual probed path is filled in later by ``descriptor_mounts`` from the
|
|
31
|
+
:class:`~kanibako.targets.base.AgentInstall`.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
import importlib.resources
|
|
37
|
+
from pathlib import Path
|
|
38
|
+
from typing import Any
|
|
39
|
+
|
|
40
|
+
import yaml
|
|
41
|
+
|
|
42
|
+
from kanibako.settings_resolve import GUEST_HOME
|
|
43
|
+
from kanibako.targets.base import (
|
|
44
|
+
BindDefault,
|
|
45
|
+
BindKind,
|
|
46
|
+
Binding,
|
|
47
|
+
BindScope,
|
|
48
|
+
Cadence,
|
|
49
|
+
Channel,
|
|
50
|
+
CredFileSpec,
|
|
51
|
+
HostSrcOrigin,
|
|
52
|
+
Operation,
|
|
53
|
+
PluginDescriptor,
|
|
54
|
+
SafeBypass,
|
|
55
|
+
SettingArg,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _expand(value: str) -> str:
|
|
60
|
+
"""Expand a ``$GUEST_HOME`` prefix in a box-side path expression.
|
|
61
|
+
|
|
62
|
+
The defaults files write every in-box destination as a ``$GUEST_HOME``
|
|
63
|
+
expression so the guest-home literal lives in exactly one place (the
|
|
64
|
+
:data:`~kanibako.settings_resolve.GUEST_HOME` constant, single SoT). Only the
|
|
65
|
+
leading ``$GUEST_HOME`` token is substituted; the rest is left verbatim.
|
|
66
|
+
"""
|
|
67
|
+
if value.startswith("$GUEST_HOME"):
|
|
68
|
+
return GUEST_HOME + value[len("$GUEST_HOME"):]
|
|
69
|
+
return value
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _load_doc(package: str, filename: str) -> dict[str, Any]:
|
|
73
|
+
"""Read and parse a plugin's bundled defaults file."""
|
|
74
|
+
ref = importlib.resources.files(package).joinpath(filename)
|
|
75
|
+
raw = yaml.safe_load(Path(str(ref)).read_text()) or {}
|
|
76
|
+
if not isinstance(raw, dict):
|
|
77
|
+
return {}
|
|
78
|
+
return raw
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _build_binding(entry: dict[str, Any]) -> Binding:
|
|
82
|
+
"""Build one :class:`Binding` from a declarative file entry.
|
|
83
|
+
|
|
84
|
+
``origin`` names the detected install field that supplies the host source
|
|
85
|
+
(``binary`` / ``launcher`` / ``install_dir`` / ``literal``); ``box_dest`` is a
|
|
86
|
+
``$GUEST_HOME`` expression expanded here. ``kind`` / ``scope`` map to their
|
|
87
|
+
enums; ``ro`` defaults to True.
|
|
88
|
+
"""
|
|
89
|
+
literal = entry.get("literal_src")
|
|
90
|
+
return Binding(
|
|
91
|
+
key=entry["key"],
|
|
92
|
+
origin=HostSrcOrigin(entry["origin"]),
|
|
93
|
+
box_dest=_expand(entry["box_dest"]),
|
|
94
|
+
kind=BindKind(entry["kind"]),
|
|
95
|
+
scope=BindScope(entry["scope"]),
|
|
96
|
+
ro=bool(entry.get("ro", True)),
|
|
97
|
+
literal_src=Path(literal) if literal is not None else None,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _build_safe_bypass(raw: dict[str, Any] | None) -> SafeBypass | None:
|
|
102
|
+
"""Build the :class:`SafeBypass` toggle from its declarative block (or None)."""
|
|
103
|
+
if not raw:
|
|
104
|
+
return None
|
|
105
|
+
return SafeBypass(
|
|
106
|
+
channel=Channel(raw["channel"]),
|
|
107
|
+
flag=tuple(raw.get("flag", ())),
|
|
108
|
+
env_var=raw.get("env_var", ""),
|
|
109
|
+
env_value=raw.get("env_value", ""),
|
|
110
|
+
secure_env_value=raw.get("secure_env_value", ""),
|
|
111
|
+
secure_flag=tuple(raw.get("secure_flag", ())),
|
|
112
|
+
setting_key=raw.get("setting_key", ""),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _build_setting_arg(entry: dict[str, Any]) -> SettingArg:
|
|
117
|
+
return SettingArg(
|
|
118
|
+
setting_key=entry["setting_key"],
|
|
119
|
+
channel=Channel(entry["channel"]),
|
|
120
|
+
flag=tuple(entry.get("flag", ())),
|
|
121
|
+
env_var=entry.get("env_var", ""),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _build_cred_file(entry: dict[str, Any]) -> CredFileSpec:
|
|
126
|
+
return CredFileSpec(
|
|
127
|
+
home_rel=entry["home_rel"],
|
|
128
|
+
host_rel=entry["host_rel"],
|
|
129
|
+
cadence=Cadence(entry.get("cadence", "sync")),
|
|
130
|
+
mtime_gate=bool(entry.get("mtime_gate", True)),
|
|
131
|
+
filtered=bool(entry.get("filtered", False)),
|
|
132
|
+
is_dir=bool(entry.get("is_dir", False)),
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def load_descriptor(package: str, filename: str) -> PluginDescriptor:
|
|
137
|
+
"""Build a plugin's :class:`PluginDescriptor` from its shipped defaults file.
|
|
138
|
+
|
|
139
|
+
*package* is the plugin's import package (e.g. ``"kanibako.plugins.claude"``)
|
|
140
|
+
and *filename* its declarative defaults file (e.g. ``"claude-defaults.yaml"``).
|
|
141
|
+
The returned descriptor is byte-for-byte equivalent to the former hand-written
|
|
142
|
+
one — box_dest ``$GUEST_HOME`` expressions are expanded and every enum field
|
|
143
|
+
is mapped from its string name.
|
|
144
|
+
"""
|
|
145
|
+
doc = _load_doc(package, filename)
|
|
146
|
+
desc = doc.get("descriptor", {})
|
|
147
|
+
|
|
148
|
+
return PluginDescriptor(
|
|
149
|
+
command=tuple(desc["command"]),
|
|
150
|
+
bindings=tuple(_build_binding(b) for b in desc.get("bindings", [])),
|
|
151
|
+
mode={k: tuple(v) for k, v in desc.get("mode", {}).items()},
|
|
152
|
+
operations={
|
|
153
|
+
k: Operation(tuple(v["fragment"]))
|
|
154
|
+
for k, v in desc.get("operations", {}).items()
|
|
155
|
+
},
|
|
156
|
+
safe_bypass=_build_safe_bypass(desc.get("safe_bypass")),
|
|
157
|
+
settings=tuple(_build_setting_arg(s) for s in desc.get("settings", [])),
|
|
158
|
+
container_env=dict(desc.get("container_env", {})),
|
|
159
|
+
cred_files=tuple(_build_cred_file(c) for c in desc.get("cred_files", [])),
|
|
160
|
+
host_prep=bool(desc.get("host_prep", False)),
|
|
161
|
+
init_dirs=tuple(desc.get("init_dirs", ())),
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def load_shares(package: str, filename: str) -> dict[str, BindDefault]:
|
|
166
|
+
"""Build a plugin's AGENT-scope ``default_shares`` map from its defaults file.
|
|
167
|
+
|
|
168
|
+
Each entry maps a scoped category key (``agent.shared.<name>``) to a STRUCTURED
|
|
169
|
+
bind pair ``(host_src, box_dest)`` (spec §2a — a tuple, NOT a colon-joined
|
|
170
|
+
string). The relative ``host_src`` joins under the per-agent store root in the
|
|
171
|
+
category resolver; ``box_dest`` is a ``$GUEST_HOME`` expression expanded here.
|
|
172
|
+
Returns ``{}`` when the file declares no shares.
|
|
173
|
+
"""
|
|
174
|
+
shares: dict[str, BindDefault] = {}
|
|
175
|
+
for entry in _load_doc(package, filename).get("shares", []):
|
|
176
|
+
options = entry.get("options")
|
|
177
|
+
if options is not None:
|
|
178
|
+
shares[entry["key"]] = (
|
|
179
|
+
entry["host_src"], _expand(entry["box_dest"]), options,
|
|
180
|
+
)
|
|
181
|
+
else:
|
|
182
|
+
shares[entry["key"]] = (entry["host_src"], _expand(entry["box_dest"]))
|
|
183
|
+
return shares
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Agent descriptor → KeyStore representation (block 7a — PURE, ALONGSIDE).
|
|
2
|
+
|
|
3
|
+
This module represents a plugin's per-agent DESCRIPTOR delivery binds as
|
|
4
|
+
agent-level category-default entries in the resolved-keyspace
|
|
5
|
+
:class:`~kanibako.settings_store.KeyStore`, so agent binary/launcher/share
|
|
6
|
+
delivery flows through the ONE category keyspace (the single-route invariant),
|
|
7
|
+
NOT a parallel descriptor mount route.
|
|
8
|
+
|
|
9
|
+
It is item-0's hard half: each :class:`~kanibako.targets.base.Binding` in a
|
|
10
|
+
:class:`~kanibako.targets.base.PluginDescriptor` becomes an
|
|
11
|
+
``agent.<name>.bindings.{ro,rw}.<key> = Bind(host_src, box_dest, opts)`` leaf under
|
|
12
|
+
the descriptor's OWN agent name ``<name>`` (``install.name``; S27) — the §2d
|
|
13
|
+
``agent.<agent>.bindings.*`` key form, NOT a bare ``agent`` token (§0 L21) —
|
|
14
|
+
mirroring :func:`~kanibako.targets.assembly.resolve_binding_source`'s
|
|
15
|
+
origin→host_src resolution.
|
|
16
|
+
|
|
17
|
+
PURE + ALONGSIDE (the block-7a boundaries)
|
|
18
|
+
------------------------------------------
|
|
19
|
+
* **Imported, single-source resolution.** host_src is resolved by IMPORTING
|
|
20
|
+
:func:`~kanibako.targets.assembly.resolve_binding_source` — the origin→path
|
|
21
|
+
logic is NOT re-implemented here.
|
|
22
|
+
* **No override application (S26).** ``resolve_binding_source`` is called with
|
|
23
|
+
``override=""`` so this emits the origin DEFAULT only. A user repoint now comes
|
|
24
|
+
from a HIGHER cascade level (``box`` / ``workset`` / ``agent.<active>``), merged
|
|
25
|
+
by block 2b — NOT baked into the agent default here.
|
|
26
|
+
* **No existence check (S26).** This function NEVER touches the filesystem (no
|
|
27
|
+
``Path.exists()``); the ``AGENT_CRITICAL`` must-exist safe-fail is a CONSUMER
|
|
28
|
+
concern (the block-7b mount/reconcile step), not representation. Feed it a
|
|
29
|
+
fixture install whose paths do not exist and it still represents them.
|
|
30
|
+
* **No expansion (§6a / spec §0).** ``@``-refs / ``$XDG`` / ``~`` are left RAW —
|
|
31
|
+
expansion is block 3. ``box_dest`` is carried VERBATIM: the descriptor loader
|
|
32
|
+
(:func:`kanibako.agent_defaults._build_binding`) has ALREADY expanded the
|
|
33
|
+
``$GUEST_HOME`` box constant at load, so the :class:`Binding` this function
|
|
34
|
+
receives is post-expansion; re-expanding would be wrong. A LITERAL-origin raw
|
|
35
|
+
``@``/``$XDG``/``~`` ``box_dest`` therefore stays raw for free (§6a).
|
|
36
|
+
* **Build ALONGSIDE.** Nothing here is wired into the launch path; block 7b swaps
|
|
37
|
+
``descriptor_mounts`` onto this representation. ``assembly.py`` /
|
|
38
|
+
``agent_defaults.py`` / ``start.py`` are UNTOUCHED.
|
|
39
|
+
|
|
40
|
+
Representation rules (the heart of the block — brief §3)
|
|
41
|
+
--------------------------------------------------------
|
|
42
|
+
For each :class:`Binding` in ``descriptor.bindings`` (order preserved):
|
|
43
|
+
|
|
44
|
+
* **host_src** = ``resolve_binding_source(binding, install, override="")`` (the
|
|
45
|
+
origin-resolved ``LAUNCHER`` / ``INSTALL_DIR`` / ``BINARY`` / ``LITERAL`` path),
|
|
46
|
+
stored as ``str(host_src)`` (``Bind.host`` is ``str``, S1).
|
|
47
|
+
* **box_dest** = ``binding.box_dest`` VERBATIM (see above).
|
|
48
|
+
* **opts** = ``"ro"`` if ``binding.ro`` else ``None``. ``None`` (NOT ``""``) means
|
|
49
|
+
"no per-entry mount-options override" — ``Bind.opts`` defaults to ``None`` (S1)
|
|
50
|
+
and reconcile falls back to the category default for an rw bind. This is the
|
|
51
|
+
``Bind`` convention, distinct from ``descriptor_mounts``'s ``""`` (an argv-mount
|
|
52
|
+
detail, not the stored shape).
|
|
53
|
+
* **key path** = the descriptor's OWN agent name ``<name>`` (``install.name``;
|
|
54
|
+
S27): ``agent.<name>.bindings.ro.<key>`` or ``agent.<name>.bindings.rw.<key>``
|
|
55
|
+
per ``binding.ro``. The agent NAME is IN the key path (the §2d
|
|
56
|
+
``agent.<agent>.*`` form, NOT a bare ``agent`` token, §0 L21), so this partial
|
|
57
|
+
merges BY NAME with 2a's discriminated ``agent.<active-name>.*`` level and any
|
|
58
|
+
higher-scope ``agent.<name>.*`` override (S8 / block 2b).
|
|
59
|
+
|
|
60
|
+
None-origin rule (S27, recorded here)
|
|
61
|
+
-------------------------------------
|
|
62
|
+
When ``resolve_binding_source`` returns ``None`` (an unresolvable origin — e.g.
|
|
63
|
+
a ``LITERAL`` binding with no ``literal_src``, or a detection field the install
|
|
64
|
+
left unset), the entry is **OMITTED**. This mirrors the AGENT best-effort skip in
|
|
65
|
+
:func:`~kanibako.targets.assembly.descriptor_mounts` and keeps the tier-2 typed
|
|
66
|
+
accessor honest: ``bindings`` exposes ``Mapping[str, Bind]`` (NOT ``Bind | None``)
|
|
67
|
+
ONLY because build omits absent/None binds (design §5/§6e) — emitting a
|
|
68
|
+
``None``-host bind would be a lie a consumer crashes on.
|
|
69
|
+
|
|
70
|
+
Authority: spec ``settings-keyspace-1.6.0-target.md`` §2d L356–378
|
|
71
|
+
(``agent.<agent>.bindings.{ro,rw}.<key>`` — the ONLY agent key form; §0 L21
|
|
72
|
+
forbids a bare ``agent.<key>``) / §2a (binding REPRESENTATION);
|
|
73
|
+
``~/vault/rw/keystore-design.md`` §2 (binds are ``Bind``) / §6a (raw refs). SEAMS
|
|
74
|
+
S1/S2/S3/S7/S8/S9 + S26/S27.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
from __future__ import annotations
|
|
78
|
+
|
|
79
|
+
from typing import TYPE_CHECKING
|
|
80
|
+
|
|
81
|
+
from kanibako.settings_store import Bind, KeyStore
|
|
82
|
+
from kanibako.targets.assembly import resolve_binding_source
|
|
83
|
+
|
|
84
|
+
if TYPE_CHECKING:
|
|
85
|
+
from kanibako.targets.base import AgentInstall, PluginDescriptor
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def agent_default_partial(
|
|
89
|
+
descriptor: PluginDescriptor,
|
|
90
|
+
install: AgentInstall,
|
|
91
|
+
) -> KeyStore:
|
|
92
|
+
"""Represent a descriptor's delivery bindings as an agent-level KeyStore partial.
|
|
93
|
+
|
|
94
|
+
Returns a :class:`~kanibako.settings_store.KeyStore` partial rooted at
|
|
95
|
+
``agent.<name>`` where ``<name>`` is the descriptor's OWN agent (``install.name``;
|
|
96
|
+
S27 / §2d), holding each resolvable :class:`~kanibako.targets.base.Binding` as a
|
|
97
|
+
``agent.<name>.bindings.{ro,rw}.<key> = Bind(host_src, box_dest, opts)`` leaf —
|
|
98
|
+
mirroring :func:`~kanibako.targets.assembly.resolve_binding_source` with NO
|
|
99
|
+
override and NO existence check (S26). See the module docstring for the full
|
|
100
|
+
rules and the None-origin OMIT contract.
|
|
101
|
+
|
|
102
|
+
The partial nests ``agent.<name>.bindings`` with ``ro`` / ``rw`` sub-tables; a
|
|
103
|
+
sub-table is present only if at least one binding lands in it, ``bindings`` is
|
|
104
|
+
present only if at least one binding resolved, and the result is ALWAYS a
|
|
105
|
+
``KeyStore`` rooted at ``agent.<name>`` (empty ``agent.<name>`` node when no
|
|
106
|
+
binding resolves) so it merges by NAME with the 2a agent levels (S8).
|
|
107
|
+
Descriptor order is preserved.
|
|
108
|
+
|
|
109
|
+
PURE: no filesystem access, no mutation of *descriptor* / *install*.
|
|
110
|
+
"""
|
|
111
|
+
# Build the ro/rw sub-tables locally, then attach only the non-empty ones so
|
|
112
|
+
# the partial shape stays minimal (an empty `ro`/`rw`/`bindings` node would be
|
|
113
|
+
# an absent-vs-present-empty distinction the merge need not carry).
|
|
114
|
+
ro_binds = KeyStore()
|
|
115
|
+
rw_binds = KeyStore()
|
|
116
|
+
|
|
117
|
+
for binding in descriptor.bindings:
|
|
118
|
+
src = resolve_binding_source(binding, install, override="")
|
|
119
|
+
if src is None:
|
|
120
|
+
# None-origin → OMIT (S27); matches the AGENT best-effort skip and
|
|
121
|
+
# keeps tier-2 `Mapping[str, Bind]` honest (design §5/§6e).
|
|
122
|
+
continue
|
|
123
|
+
opts = "ro" if binding.ro else None
|
|
124
|
+
bind = Bind(str(src), binding.box_dest, opts)
|
|
125
|
+
# `binding.key` is the stable override key; place it under ro/rw by `ro`.
|
|
126
|
+
if binding.ro:
|
|
127
|
+
ro_binds[binding.key] = bind
|
|
128
|
+
else:
|
|
129
|
+
rw_binds[binding.key] = bind
|
|
130
|
+
|
|
131
|
+
bindings = KeyStore()
|
|
132
|
+
if dict.__len__(ro_binds):
|
|
133
|
+
bindings["ro"] = ro_binds
|
|
134
|
+
if dict.__len__(rw_binds):
|
|
135
|
+
bindings["rw"] = rw_binds
|
|
136
|
+
|
|
137
|
+
# Root under the descriptor's OWN agent name (§2d ``agent.<agent>.*``): the
|
|
138
|
+
# claude descriptor's binds land at ``agent.claude.bindings.*``. The agent NAME
|
|
139
|
+
# is part of the KEY PATH (NOT a bare ``agent`` token, §0 L21) — so this partial
|
|
140
|
+
# merges BY NAME with 2a's discriminated ``agent.<active>.*`` level and any
|
|
141
|
+
# higher-scope ``agent.<name>.*`` override (block 2b).
|
|
142
|
+
name = install.name
|
|
143
|
+
agent_sub = KeyStore()
|
|
144
|
+
if dict.__len__(bindings):
|
|
145
|
+
agent_sub["bindings"] = bindings
|
|
146
|
+
|
|
147
|
+
agent = KeyStore()
|
|
148
|
+
agent[name] = agent_sub
|
|
149
|
+
|
|
150
|
+
partial = KeyStore()
|
|
151
|
+
partial["agent"] = agent
|
|
152
|
+
return partial
|
|
@@ -184,6 +184,44 @@ _SUBCOMMANDS = {
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
|
|
187
|
+
def _normalize_command(effective: list[str]) -> list[str]:
|
|
188
|
+
"""Reorder argv so a leading global-style flag doesn't swallow a subcommand.
|
|
189
|
+
|
|
190
|
+
The dispatcher's fallback rule ``effective[0] not in _SUBCOMMANDS -> prepend
|
|
191
|
+
"start"`` treats a LEADING flag (e.g. ``--agent goose shell``) as a bare
|
|
192
|
+
``start`` with that flag — turning ``--agent goose shell`` into ``start
|
|
193
|
+
shell`` with ``project="shell"``, which both launches the wrong thing and
|
|
194
|
+
fires the Gate-1 setup nudge.
|
|
195
|
+
|
|
196
|
+
To honour the blanket-flag design (``kanibako --agent X <subcommand>`` ==
|
|
197
|
+
``kanibako <subcommand> --agent X``), when ``effective[0]`` is an option
|
|
198
|
+
(starts with ``-``) and a KNOWN subcommand appears later, move the FIRST such
|
|
199
|
+
subcommand token to the front, preserving the relative order of everything
|
|
200
|
+
else. ``--agent goose shell`` -> ``["shell", "--agent", "goose"]``.
|
|
201
|
+
|
|
202
|
+
Heuristic (kept simple, matches the documented design where flags normally
|
|
203
|
+
follow the subcommand): scan for the first token that is a ``_SUBCOMMANDS``
|
|
204
|
+
member. A flag VALUE that happens to equal a subcommand name (e.g.
|
|
205
|
+
``--box shell start`` where a box is literally named "shell") would be
|
|
206
|
+
matched as the subcommand; this is an accepted edge case.
|
|
207
|
+
|
|
208
|
+
The genuinely-no-subcommand cases are left untouched here so the caller's
|
|
209
|
+
existing ``prepend "start"`` rule still handles them: ``kanibako myproject``
|
|
210
|
+
(bare positional) and ``kanibako -A`` / ``kanibako -N`` (leading flags, no
|
|
211
|
+
subcommand) both fall through unchanged.
|
|
212
|
+
"""
|
|
213
|
+
if not effective or not effective[0].startswith("-"):
|
|
214
|
+
return effective
|
|
215
|
+
sub_idx = next(
|
|
216
|
+
(i for i, tok in enumerate(effective) if tok in _SUBCOMMANDS),
|
|
217
|
+
None,
|
|
218
|
+
)
|
|
219
|
+
if sub_idx is None:
|
|
220
|
+
return effective
|
|
221
|
+
sub = effective[sub_idx]
|
|
222
|
+
return [sub] + effective[:sub_idx] + effective[sub_idx + 1:]
|
|
223
|
+
|
|
224
|
+
|
|
187
225
|
def _ensure_initialized() -> None:
|
|
188
226
|
"""Ensure kanibako is initialized (create config + data dirs on first run)."""
|
|
189
227
|
from kanibako.config import (
|
|
@@ -256,8 +294,9 @@ def _ensure_initialized() -> None:
|
|
|
256
294
|
# CURATED templates instead. The content ships as static package data and
|
|
257
295
|
# is COPIED here into the runtime template dirs (@system.base_template +
|
|
258
296
|
# @system.agents/<agent>/template), create-if-absent so user edits survive
|
|
259
|
-
# an upgrade. The layered seed-once
|
|
260
|
-
# then copies them into each new box
|
|
297
|
+
# an upgrade. The layered seed-once stage+seed
|
|
298
|
+
# (templates.stage_and_seed_templates) then copies them into each new box
|
|
299
|
+
# home at creation.
|
|
261
300
|
from kanibako.paths import load_std_paths
|
|
262
301
|
from kanibako.templates import install_packaged_templates
|
|
263
302
|
|
|
@@ -339,6 +378,12 @@ def main(argv: list[str] | None = None) -> None:
|
|
|
339
378
|
print(f"kanibako {__version__}")
|
|
340
379
|
sys.exit(0)
|
|
341
380
|
else:
|
|
381
|
+
# Reorder a leading global-style flag ahead of a later subcommand so
|
|
382
|
+
# `kanibako --agent goose shell` dispatches as `shell` (not `start` with
|
|
383
|
+
# project="shell"). Must run BEFORE the prepend-"start" fallback and the
|
|
384
|
+
# `--` split below.
|
|
385
|
+
effective = _normalize_command(effective)
|
|
386
|
+
|
|
342
387
|
# If the first arg isn't a known subcommand, default to "start".
|
|
343
388
|
if not effective or effective[0] not in _SUBCOMMANDS:
|
|
344
389
|
effective = ["start"] + effective
|
|
@@ -433,9 +433,25 @@ def run_reauth(args: argparse.Namespace) -> int:
|
|
|
433
433
|
return 1
|
|
434
434
|
|
|
435
435
|
if target.check_auth():
|
|
436
|
-
# Sync refreshed credentials to the project shell directory
|
|
436
|
+
# Sync refreshed credentials to the project shell directory. Mirror the
|
|
437
|
+
# start.py gate exactly: descriptor-bearing targets route their cred
|
|
438
|
+
# refresh through the credsync engine (descriptor.cred_files); only legacy
|
|
439
|
+
# (desc is None) targets fall back to the per-plugin refresh hook. An
|
|
440
|
+
# ungated target.refresh_credentials here would push a descriptor agent
|
|
441
|
+
# (e.g. goose) down its legacy path / bespoke copy.
|
|
437
442
|
if proj.group_auth:
|
|
438
|
-
|
|
443
|
+
from pathlib import Path
|
|
444
|
+
|
|
445
|
+
from kanibako.targets import credsync
|
|
446
|
+
|
|
447
|
+
desc = target.descriptor
|
|
448
|
+
if desc is not None:
|
|
449
|
+
credsync.refresh_cred_files(
|
|
450
|
+
desc, target, host_home=Path.home(),
|
|
451
|
+
project_home=proj.shell_path, group_auth=proj.group_auth,
|
|
452
|
+
)
|
|
453
|
+
else:
|
|
454
|
+
target.refresh_credentials(proj.shell_path)
|
|
439
455
|
print(f"{target.display_name}: authenticated.", file=sys.stderr)
|
|
440
456
|
return 0
|
|
441
457
|
|
|
@@ -1226,7 +1226,7 @@ def run_config(args: argparse.Namespace) -> int:
|
|
|
1226
1226
|
from kanibako.targets import resolve_target
|
|
1227
1227
|
from kanibako.commands.start import (
|
|
1228
1228
|
_build_config_env,
|
|
1229
|
-
|
|
1229
|
+
_effective_behavior_for_display,
|
|
1230
1230
|
)
|
|
1231
1231
|
merged = load_merged_config(
|
|
1232
1232
|
config_file, project_toml if project_toml.exists() else None,
|
|
@@ -1257,7 +1257,7 @@ def run_config(args: argparse.Namespace) -> int:
|
|
|
1257
1257
|
else:
|
|
1258
1258
|
agent_cfg = None
|
|
1259
1259
|
if target is not None and agent_cfg is not None:
|
|
1260
|
-
agent_state =
|
|
1260
|
+
agent_state = _effective_behavior_for_display(
|
|
1261
1261
|
target, agent_cfg, project_toml,
|
|
1262
1262
|
global_config_path=std.settings,
|
|
1263
1263
|
workset_config_path=workset_path,
|
|
@@ -249,6 +249,17 @@ def check_flag_relevance(args: argparse.Namespace) -> None:
|
|
|
249
249
|
# the excluded commands entirely. Only a real string value counts as "set"
|
|
250
250
|
# (a non-str sentinel from a MagicMock test stub is ignored).
|
|
251
251
|
if isinstance(agent, str) and key not in _AGENT_FLAG_EXCLUDE:
|
|
252
|
+
# `shell` (and `box shell`) is a shell — it never launches an agent, so
|
|
253
|
+
# `--agent` is meaningless rather than wrong. Product decision: IGNORE
|
|
254
|
+
# it with a clear note (don't hard-error), then proceed to open a plain
|
|
255
|
+
# shell. run_shell never reads args.agent, so the value is dropped here.
|
|
256
|
+
if key in ("shell", "box shell"):
|
|
257
|
+
print(
|
|
258
|
+
"Note: --agent is ignored for 'shell' "
|
|
259
|
+
"(a shell never launches an agent).",
|
|
260
|
+
file=sys.stderr,
|
|
261
|
+
)
|
|
262
|
+
return
|
|
252
263
|
if key not in AGENT_FLAG_COMMANDS:
|
|
253
264
|
raise FlagRelevanceError(
|
|
254
265
|
f"--agent is not valid for '{key}'. It applies to agent "
|
|
@@ -370,6 +370,7 @@ def run_stop(args: argparse.Namespace) -> int:
|
|
|
370
370
|
send_request(_socket_path(), {
|
|
371
371
|
"action": "stop",
|
|
372
372
|
"container_name": container_name,
|
|
373
|
+
"helper_num": helper_num,
|
|
373
374
|
})
|
|
374
375
|
except Exception:
|
|
375
376
|
pass # Best-effort stop
|
|
@@ -399,6 +400,7 @@ def run_cleanup(args: argparse.Namespace) -> int:
|
|
|
399
400
|
send_request(_socket_path(), {
|
|
400
401
|
"action": "stop",
|
|
401
402
|
"container_name": container_name,
|
|
403
|
+
"helper_num": helper_num,
|
|
402
404
|
})
|
|
403
405
|
except Exception:
|
|
404
406
|
pass
|