agentirc-cli 4.3.2__tar.gz → 4.3.4__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.
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/CHANGELOG.md +15 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/PKG-INFO +1 -1
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/bots/bot_manager.py +5 -3
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/cli/agent.py +14 -16
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/cli/bot.py +12 -12
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/cli/channel.py +2 -1
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/cli/mesh.py +7 -11
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/cli/server.py +22 -15
- agentirc_cli-4.3.4/culture/cli/shared/constants.py +16 -0
- agentirc_cli-4.3.4/culture/cli/shared/display.py +131 -0
- agentirc_cli-4.3.4/culture/cli/shared/ipc.py +68 -0
- agentirc_cli-4.3.4/culture/cli/shared/mesh.py +102 -0
- agentirc_cli-4.3.4/culture/cli/shared/process.py +144 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/cli/skills.py +18 -10
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/acp/agent_runner.py +46 -36
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/acp/daemon.py +31 -22
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/acp/skill/irc_client.py +98 -57
- {agentirc_cli-4.3.2/culture/clients/claude → agentirc_cli-4.3.4/culture/clients/acp}/supervisor.py +8 -4
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/claude/agent_runner.py +14 -6
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/claude/daemon.py +9 -6
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/claude/skill/irc_client.py +98 -57
- {agentirc_cli-4.3.2/culture/clients/acp → agentirc_cli-4.3.4/culture/clients/claude}/supervisor.py +8 -4
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/codex/agent_runner.py +41 -31
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/codex/daemon.py +31 -22
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/codex/skill/irc_client.py +98 -57
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/codex/supervisor.py +22 -14
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/copilot/agent_runner.py +26 -17
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/copilot/daemon.py +31 -22
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/copilot/skill/irc_client.py +98 -57
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/copilot/supervisor.py +25 -15
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/observer.py +55 -90
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/overview/collector.py +64 -41
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/overview/renderer_text.py +67 -39
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/overview/renderer_web.py +22 -14
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/persistence.py +34 -19
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/protocol/replies.py +8 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/server/client.py +166 -168
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/server/ircd.py +16 -11
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/server/server_link.py +125 -133
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/server/skills/history.py +3 -3
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/server/skills/rooms.py +57 -37
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/server/skills/threads.py +15 -13
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/packages/agent-harness/skill/irc_client.py +98 -57
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/pyproject.toml +1 -1
- agentirc_cli-4.3.4/tests/__init__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/uv.lock +1 -1
- agentirc_cli-4.3.2/culture/cli/_helpers.py +0 -456
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/.claude/skills/pr-review/SKILL.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/.claude/skills/run-tests/SKILL.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/.flake8 +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/.github/workflows/pages.yml +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/.github/workflows/publish.yml +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/.github/workflows/security-checks.yml +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/.github/workflows/tests.yml +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/.gitignore +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/.markdownlint-cli2.yaml +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/.pr_agent.toml +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/.pre-commit-config.yaml +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/.pylintrc +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/CLAUDE.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/CNAME +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/Gemfile +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/Gemfile.lock +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/LICENSE +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/README.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/SECURITY.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/_config.yml +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/_sass/color_schemes/anthropic.scss +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/_sass/custom/custom.scss +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/__init__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/__main__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/aio.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/bots/__init__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/bots/bot.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/bots/config.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/bots/http_listener.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/bots/template_engine.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/bots/virtual_client.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/cli/__init__.py +0 -0
- {agentirc_cli-4.3.2/culture/clients → agentirc_cli-4.3.4/culture/cli/shared}/__init__.py +0 -0
- {agentirc_cli-4.3.2/culture/clients/acp → agentirc_cli-4.3.4/culture/clients}/__init__.py +0 -0
- {agentirc_cli-4.3.2/culture/clients/acp/skill → agentirc_cli-4.3.4/culture/clients/acp}/__init__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/acp/config.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/acp/ipc.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/acp/irc_transport.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/acp/message_buffer.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/acp/skill/SKILL.md +0 -0
- {agentirc_cli-4.3.2/culture/clients/claude → agentirc_cli-4.3.4/culture/clients/acp/skill}/__init__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/acp/socket_server.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/acp/webhook.py +0 -0
- {agentirc_cli-4.3.2/culture/clients/claude/skill → agentirc_cli-4.3.4/culture/clients/claude}/__init__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/claude/__main__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/claude/config.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/claude/ipc.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/claude/irc_transport.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/claude/message_buffer.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/claude/skill/SKILL.md +0 -0
- {agentirc_cli-4.3.2/culture/clients/codex → agentirc_cli-4.3.4/culture/clients/claude/skill}/__init__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/claude/socket_server.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/claude/webhook.py +0 -0
- {agentirc_cli-4.3.2/culture/clients/codex/skill → agentirc_cli-4.3.4/culture/clients/codex}/__init__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/codex/config.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/codex/ipc.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/codex/irc_transport.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/codex/message_buffer.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/codex/skill/SKILL.md +0 -0
- {agentirc_cli-4.3.2/culture/clients/copilot → agentirc_cli-4.3.4/culture/clients/codex/skill}/__init__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/codex/socket_server.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/codex/webhook.py +0 -0
- {agentirc_cli-4.3.2/culture/clients/copilot/skill → agentirc_cli-4.3.4/culture/clients/copilot}/__init__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/copilot/config.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/copilot/ipc.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/copilot/irc_transport.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/copilot/message_buffer.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/copilot/skill/SKILL.md +0 -0
- {agentirc_cli-4.3.2/culture/protocol → agentirc_cli-4.3.4/culture/clients/copilot/skill}/__init__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/copilot/socket_server.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/clients/copilot/webhook.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/console/__init__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/console/app.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/console/client.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/console/commands.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/console/widgets/__init__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/console/widgets/chat.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/console/widgets/info_panel.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/console/widgets/sidebar.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/credentials.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/learn_prompt.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/mesh_config.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/overview/__init__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/overview/model.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/overview/web/style.css +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/pidfile.py +0 -0
- {agentirc_cli-4.3.2/culture/server → agentirc_cli-4.3.4/culture/protocol}/__init__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/protocol/commands.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/protocol/extensions/federation.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/protocol/extensions/history.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/protocol/extensions/icons.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/protocol/extensions/rooms.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/protocol/extensions/tags.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/protocol/extensions/threads.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/protocol/message.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/protocol/protocol-index.md +0 -0
- {agentirc_cli-4.3.2/culture/server/skills → agentirc_cli-4.3.4/culture/server}/__init__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/server/__main__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/server/channel.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/server/config.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/server/remote_client.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/server/room_store.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/server/rooms_util.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/server/skill.py +0 -0
- {agentirc_cli-4.3.2/tests → agentirc_cli-4.3.4/culture/server/skills}/__init__.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/server/skills/icon.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/server/thread_store.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/culture/skills/culture/SKILL.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/agent-lifecycle.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/agentic-self-learn.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/architecture/agent-client.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/architecture/agent-harness-spec.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/architecture/design.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/architecture/harness-conformance.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/architecture/index.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/architecture/layer1-core-irc.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/architecture/layer2-attention.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/architecture/layer3-skills.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/architecture/layer4-federation.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/architecture/layer5-agent-harness.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/architecture/server-architecture.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/architecture/threads.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/channel-polling.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/acp/overview.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/claude/configuration.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/claude/context-management.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/claude/irc-tools.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/claude/overview.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/claude/setup.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/claude/supervisor.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/claude/webhooks.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/codex/configuration.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/codex/context-management.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/codex/irc-tools.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/codex/overview.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/codex/setup.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/codex/supervisor.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/codex/webhooks.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/copilot/configuration.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/copilot/context-management.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/copilot/irc-tools.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/copilot/overview.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/copilot/setup.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/copilot/supervisor.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/clients/copilot/webhooks.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/culture-cli.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/getting-started.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/index.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/operations/SECURITY.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/operations/bots.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/operations/ci.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/operations/cli.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/operations/docs-site.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/operations/index.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/operations/ops-tooling.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/operations/overview.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/operations/publishing.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/resources/github-copilot-sdk-instructions.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/rooms.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/server-rename.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/specs/2026-04-06-cli-reorganization-design.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/superpowers/specs/2026-04-07-entity-archiving-design.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/use-cases/01-pair-programming.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/use-cases/02-code-review-ensemble.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/use-cases/03-cross-server-delegation.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/use-cases/04-knowledge-propagation.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/use-cases/05-the-observer.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/use-cases/06-cross-server-ops.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/use-cases/07-supervisor-intervention.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/use-cases/08-apps-as-agents.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/use-cases/09-research-swarm.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/use-cases/10-agent-lifecycle.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/use-cases-index.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/docs/what-is-culture.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/packages/agent-harness/README.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/packages/agent-harness/config.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/packages/agent-harness/daemon.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/packages/agent-harness/ipc.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/packages/agent-harness/irc_transport.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/packages/agent-harness/message_buffer.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/packages/agent-harness/skill/SKILL.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/packages/agent-harness/socket_server.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/packages/agent-harness/webhook.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/plugins/claude-code/skills/culture/SKILL.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/plugins/claude-code/skills/irc/SKILL.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/sonar-project.properties +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/conftest.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_acp_daemon.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_agent_runner.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_archive.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_bot.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_bot_config.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_bot_manager.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_bots_integration.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_channel.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_codex_daemon.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_connection.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_console_client.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_console_commands.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_console_connection.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_console_icons.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_console_integration.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_copilot_daemon.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_daemon.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_daemon_config.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_daemon_ipc.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_discovery.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_federation.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_history.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_http_listener.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_integration_layer5.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_ipc.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_irc_transport.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_link_reconnect.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_mention_alias.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_mention_target_cleanup.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_mentions.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_mesh_config.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_message.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_message_buffer.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_messaging.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_modes.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_overview_cli.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_overview_collector.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_overview_model.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_overview_renderer.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_overview_web.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_persistence.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_pidfile.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_poll_loop.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_room_persistence.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_rooms.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_rooms_federation.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_rooms_integration.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_server_icon_skill.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_setup_update_cli.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_skill_client.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_skills.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_socket_server.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_supervisor.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_template_engine.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_thread_buffer.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_threads.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_virtual_client.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_wait_for_port.py +0 -0
- {agentirc_cli-4.3.2 → agentirc_cli-4.3.4}/tests/test_webhook.py +0 -0
|
@@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
Format follows [Keep a Changelog](https://keepachangelog.com/).
|
|
6
6
|
|
|
7
|
+
## [4.3.4] - 2026-04-07
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Extract duplicated string literals into named constants (SonarCloud S1192)
|
|
13
|
+
- Refactor cli/_helpers.py into modular cli/shared/ package (constants, ipc, process, mesh, display)
|
|
14
|
+
|
|
15
|
+
## [4.3.3] - 2026-04-07
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Reduced cognitive complexity in 40 functions across 25 files to meet SonarCloud threshold (≤15)
|
|
21
|
+
|
|
7
22
|
## [4.3.2] - 2026-04-07
|
|
8
23
|
|
|
9
24
|
|
|
@@ -13,6 +13,8 @@ if TYPE_CHECKING:
|
|
|
13
13
|
|
|
14
14
|
logger = logging.getLogger(__name__)
|
|
15
15
|
|
|
16
|
+
BOT_CONFIG_FILE = "bot.yaml"
|
|
17
|
+
|
|
16
18
|
|
|
17
19
|
class BotManager:
|
|
18
20
|
"""Loads, starts, stops, and dispatches webhooks to bots."""
|
|
@@ -27,7 +29,7 @@ class BotManager:
|
|
|
27
29
|
return
|
|
28
30
|
|
|
29
31
|
for bot_dir in sorted(BOTS_DIR.iterdir()):
|
|
30
|
-
yaml_path = bot_dir /
|
|
32
|
+
yaml_path = bot_dir / BOT_CONFIG_FILE
|
|
31
33
|
if not yaml_path.is_file():
|
|
32
34
|
continue
|
|
33
35
|
try:
|
|
@@ -45,7 +47,7 @@ class BotManager:
|
|
|
45
47
|
async def create_bot(self, config: BotConfig) -> Bot:
|
|
46
48
|
"""Create a new bot: write config to disk and start it."""
|
|
47
49
|
bot_dir = BOTS_DIR / config.name
|
|
48
|
-
save_bot_config(bot_dir /
|
|
50
|
+
save_bot_config(bot_dir / BOT_CONFIG_FILE, config)
|
|
49
51
|
|
|
50
52
|
bot = Bot(config, self.server)
|
|
51
53
|
self.bots[config.name] = bot
|
|
@@ -57,7 +59,7 @@ class BotManager:
|
|
|
57
59
|
bot = self.bots.get(name)
|
|
58
60
|
if not bot:
|
|
59
61
|
# Try loading from disk
|
|
60
|
-
yaml_path = BOTS_DIR / name /
|
|
62
|
+
yaml_path = BOTS_DIR / name / BOT_CONFIG_FILE
|
|
61
63
|
if not yaml_path.is_file():
|
|
62
64
|
raise ValueError(f"Bot {name!r} not found")
|
|
63
65
|
config = load_bot_config(yaml_path)
|
|
@@ -27,18 +27,16 @@ from culture.pidfile import (
|
|
|
27
27
|
write_pid,
|
|
28
28
|
)
|
|
29
29
|
|
|
30
|
-
from .
|
|
30
|
+
from .shared.constants import (
|
|
31
31
|
_CONFIG_HELP,
|
|
32
|
+
DEFAULT_CHANNEL,
|
|
32
33
|
DEFAULT_CONFIG,
|
|
33
34
|
LOG_DIR,
|
|
34
|
-
|
|
35
|
-
get_observer,
|
|
36
|
-
ipc_request,
|
|
37
|
-
print_agent_detail,
|
|
38
|
-
print_agents_overview,
|
|
39
|
-
print_bot_listing,
|
|
40
|
-
stop_agent,
|
|
35
|
+
NO_AGENTS_MSG,
|
|
41
36
|
)
|
|
37
|
+
from .shared.display import print_agent_detail, print_agents_overview, print_bot_listing
|
|
38
|
+
from .shared.ipc import agent_socket_path, get_observer, ipc_request
|
|
39
|
+
from .shared.process import stop_agent
|
|
42
40
|
|
|
43
41
|
logger = logging.getLogger("culture")
|
|
44
42
|
|
|
@@ -212,7 +210,7 @@ def _create_agent_config(args: argparse.Namespace, full_nick: str) -> AgentConfi
|
|
|
212
210
|
nick=full_nick,
|
|
213
211
|
agent="codex",
|
|
214
212
|
directory=os.getcwd(),
|
|
215
|
-
channels=[
|
|
213
|
+
channels=[DEFAULT_CHANNEL],
|
|
216
214
|
)
|
|
217
215
|
if args.agent == "copilot":
|
|
218
216
|
from culture.clients.copilot.config import AgentConfig as CopilotAgentConfig
|
|
@@ -221,7 +219,7 @@ def _create_agent_config(args: argparse.Namespace, full_nick: str) -> AgentConfi
|
|
|
221
219
|
nick=full_nick,
|
|
222
220
|
agent="copilot",
|
|
223
221
|
directory=os.getcwd(),
|
|
224
|
-
channels=[
|
|
222
|
+
channels=[DEFAULT_CHANNEL],
|
|
225
223
|
)
|
|
226
224
|
if args.agent == "acp":
|
|
227
225
|
import json as _json
|
|
@@ -246,13 +244,13 @@ def _create_agent_config(args: argparse.Namespace, full_nick: str) -> AgentConfi
|
|
|
246
244
|
agent="acp",
|
|
247
245
|
acp_command=acp_cmd,
|
|
248
246
|
directory=os.getcwd(),
|
|
249
|
-
channels=[
|
|
247
|
+
channels=[DEFAULT_CHANNEL],
|
|
250
248
|
)
|
|
251
249
|
return AgentConfig(
|
|
252
250
|
nick=full_nick,
|
|
253
251
|
agent=args.agent,
|
|
254
252
|
directory=os.getcwd(),
|
|
255
|
-
channels=[
|
|
253
|
+
channels=[DEFAULT_CHANNEL],
|
|
256
254
|
)
|
|
257
255
|
|
|
258
256
|
|
|
@@ -365,7 +363,7 @@ def _resolve_agents_to_start(config, args) -> list:
|
|
|
365
363
|
agents = _resolve_auto(config)
|
|
366
364
|
|
|
367
365
|
if not agents:
|
|
368
|
-
print(
|
|
366
|
+
print(NO_AGENTS_MSG, file=sys.stderr)
|
|
369
367
|
sys.exit(1)
|
|
370
368
|
return agents
|
|
371
369
|
|
|
@@ -541,7 +539,7 @@ def _cmd_stop(args: argparse.Namespace) -> None:
|
|
|
541
539
|
if len(config.agents) == 1:
|
|
542
540
|
agents = config.agents
|
|
543
541
|
elif len(config.agents) == 0:
|
|
544
|
-
print(
|
|
542
|
+
print(NO_AGENTS_MSG, file=sys.stderr)
|
|
545
543
|
sys.exit(1)
|
|
546
544
|
else:
|
|
547
545
|
print(
|
|
@@ -574,14 +572,14 @@ def _no_agents_message(config, show_all: bool) -> str:
|
|
|
574
572
|
archived_count = sum(1 for a in config.agents if a.archived)
|
|
575
573
|
if archived_count:
|
|
576
574
|
return f"No active agents ({archived_count} archived, use --all to show)"
|
|
577
|
-
return
|
|
575
|
+
return NO_AGENTS_MSG
|
|
578
576
|
|
|
579
577
|
|
|
580
578
|
def _cmd_status(args: argparse.Namespace) -> None:
|
|
581
579
|
config = load_config_or_default(args.config)
|
|
582
580
|
|
|
583
581
|
if not config.agents:
|
|
584
|
-
print(
|
|
582
|
+
print(NO_AGENTS_MSG)
|
|
585
583
|
return
|
|
586
584
|
|
|
587
585
|
if args.nick:
|
|
@@ -8,7 +8,7 @@ import time
|
|
|
8
8
|
|
|
9
9
|
from culture.clients.claude.config import load_config_or_default
|
|
10
10
|
|
|
11
|
-
from .
|
|
11
|
+
from .shared.constants import _BOT_NAME_HELP, _CONFIG_HELP, BOT_CONFIG_FILE, DEFAULT_CONFIG
|
|
12
12
|
|
|
13
13
|
NAME = "bot"
|
|
14
14
|
|
|
@@ -31,11 +31,11 @@ def register(subparsers: argparse._SubParsersAction) -> None:
|
|
|
31
31
|
bot_create.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
32
32
|
|
|
33
33
|
bot_start = bot_sub.add_parser("start", help="Start a bot")
|
|
34
|
-
bot_start.add_argument("name", help=
|
|
34
|
+
bot_start.add_argument("name", help=_BOT_NAME_HELP)
|
|
35
35
|
bot_start.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
36
36
|
|
|
37
37
|
bot_stop = bot_sub.add_parser("stop", help="Stop a bot")
|
|
38
|
-
bot_stop.add_argument("name", help=
|
|
38
|
+
bot_stop.add_argument("name", help=_BOT_NAME_HELP)
|
|
39
39
|
bot_stop.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
40
40
|
|
|
41
41
|
bot_list = bot_sub.add_parser("list", help="List bots")
|
|
@@ -43,7 +43,7 @@ def register(subparsers: argparse._SubParsersAction) -> None:
|
|
|
43
43
|
bot_list.add_argument("--all", action="store_true", help="Include archived bots")
|
|
44
44
|
|
|
45
45
|
bot_inspect = bot_sub.add_parser("inspect", help="Show bot details")
|
|
46
|
-
bot_inspect.add_argument("name", help=
|
|
46
|
+
bot_inspect.add_argument("name", help=_BOT_NAME_HELP)
|
|
47
47
|
bot_inspect.add_argument("--config", default=DEFAULT_CONFIG, help=_CONFIG_HELP)
|
|
48
48
|
|
|
49
49
|
bot_archive = bot_sub.add_parser("archive", help="Archive a bot")
|
|
@@ -113,11 +113,11 @@ def _bot_create(args: argparse.Namespace) -> None:
|
|
|
113
113
|
)
|
|
114
114
|
|
|
115
115
|
bot_dir = BOTS_DIR / name
|
|
116
|
-
if (bot_dir /
|
|
116
|
+
if (bot_dir / BOT_CONFIG_FILE).exists():
|
|
117
117
|
print(f"Bot '{name}' already exists at {bot_dir}", file=sys.stderr)
|
|
118
118
|
sys.exit(1)
|
|
119
119
|
|
|
120
|
-
save_bot_config(bot_dir /
|
|
120
|
+
save_bot_config(bot_dir / BOT_CONFIG_FILE, bot_config)
|
|
121
121
|
print(f"Bot '{name}' created at {bot_dir}")
|
|
122
122
|
print(f" Owner: {args.owner}")
|
|
123
123
|
print(f" Trigger: {args.trigger}")
|
|
@@ -132,7 +132,7 @@ def _bot_start(args: argparse.Namespace) -> None:
|
|
|
132
132
|
from culture.bots.config import BOTS_DIR
|
|
133
133
|
|
|
134
134
|
bot_dir = BOTS_DIR / args.name
|
|
135
|
-
if not (bot_dir /
|
|
135
|
+
if not (bot_dir / BOT_CONFIG_FILE).exists():
|
|
136
136
|
print(f"Bot '{args.name}' not found at {bot_dir}", file=sys.stderr)
|
|
137
137
|
sys.exit(1)
|
|
138
138
|
|
|
@@ -144,7 +144,7 @@ def _bot_stop(args: argparse.Namespace) -> None:
|
|
|
144
144
|
from culture.bots.config import BOTS_DIR
|
|
145
145
|
|
|
146
146
|
bot_dir = BOTS_DIR / args.name
|
|
147
|
-
if not (bot_dir /
|
|
147
|
+
if not (bot_dir / BOT_CONFIG_FILE).exists():
|
|
148
148
|
print(f"Bot '{args.name}' not found at {bot_dir}", file=sys.stderr)
|
|
149
149
|
sys.exit(1)
|
|
150
150
|
|
|
@@ -161,7 +161,7 @@ def _load_and_filter_bots(args) -> list:
|
|
|
161
161
|
show_all = getattr(args, "all", False)
|
|
162
162
|
bots = []
|
|
163
163
|
for bot_dir in sorted(BOTS_DIR.iterdir()):
|
|
164
|
-
yaml_path = bot_dir /
|
|
164
|
+
yaml_path = bot_dir / BOT_CONFIG_FILE
|
|
165
165
|
if not yaml_path.is_file():
|
|
166
166
|
continue
|
|
167
167
|
try:
|
|
@@ -203,7 +203,7 @@ def _bot_inspect(args: argparse.Namespace) -> None:
|
|
|
203
203
|
from culture.bots.config import BOTS_DIR, load_bot_config
|
|
204
204
|
|
|
205
205
|
bot_dir = BOTS_DIR / args.name
|
|
206
|
-
yaml_path = bot_dir /
|
|
206
|
+
yaml_path = bot_dir / BOT_CONFIG_FILE
|
|
207
207
|
if not yaml_path.is_file():
|
|
208
208
|
print(f"Bot '{args.name}' not found at {bot_dir}", file=sys.stderr)
|
|
209
209
|
sys.exit(1)
|
|
@@ -245,7 +245,7 @@ def _bot_archive(args: argparse.Namespace) -> None:
|
|
|
245
245
|
from culture.bots.config import BOTS_DIR, load_bot_config, save_bot_config
|
|
246
246
|
|
|
247
247
|
bot_dir = BOTS_DIR / args.name
|
|
248
|
-
yaml_path = bot_dir /
|
|
248
|
+
yaml_path = bot_dir / BOT_CONFIG_FILE
|
|
249
249
|
if not yaml_path.is_file():
|
|
250
250
|
print(f"Bot '{args.name}' not found at {bot_dir}", file=sys.stderr)
|
|
251
251
|
sys.exit(1)
|
|
@@ -270,7 +270,7 @@ def _bot_unarchive(args: argparse.Namespace) -> None:
|
|
|
270
270
|
from culture.bots.config import BOTS_DIR, load_bot_config, save_bot_config
|
|
271
271
|
|
|
272
272
|
bot_dir = BOTS_DIR / args.name
|
|
273
|
-
yaml_path = bot_dir /
|
|
273
|
+
yaml_path = bot_dir / BOT_CONFIG_FILE
|
|
274
274
|
if not yaml_path.is_file():
|
|
275
275
|
print(f"Bot '{args.name}' not found at {bot_dir}", file=sys.stderr)
|
|
276
276
|
sys.exit(1)
|
|
@@ -17,13 +17,9 @@ from culture.clients.claude.config import (
|
|
|
17
17
|
)
|
|
18
18
|
from culture.pidfile import is_process_alive, read_pid
|
|
19
19
|
|
|
20
|
-
from .
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
generate_mesh_from_agents,
|
|
24
|
-
server_stop_by_name,
|
|
25
|
-
stop_agent,
|
|
26
|
-
)
|
|
20
|
+
from .shared.constants import AGENTS_YAML, CULTURE_DIR, DEFAULT_CONFIG
|
|
21
|
+
from .shared.mesh import build_server_start_cmd, generate_mesh_from_agents
|
|
22
|
+
from .shared.process import server_stop_by_name, stop_agent
|
|
27
23
|
|
|
28
24
|
logger = logging.getLogger("culture")
|
|
29
25
|
|
|
@@ -313,7 +309,7 @@ def _generate_agent_configs(mesh, server_name: str) -> None:
|
|
|
313
309
|
|
|
314
310
|
for workdir, agents in workdir_agents.items():
|
|
315
311
|
os.makedirs(workdir, exist_ok=True)
|
|
316
|
-
config_path = os.path.join(workdir,
|
|
312
|
+
config_path = os.path.join(workdir, CULTURE_DIR, AGENTS_YAML)
|
|
317
313
|
os.makedirs(os.path.dirname(config_path), exist_ok=True)
|
|
318
314
|
|
|
319
315
|
agent_configs = []
|
|
@@ -348,7 +344,7 @@ def _install_mesh_services(mesh, server_name: str, culture_bin: str, config_path
|
|
|
348
344
|
for agent in mesh.agents:
|
|
349
345
|
full_nick = f"{server_name}-{agent.nick}"
|
|
350
346
|
workdir = os.path.expanduser(agent.workdir)
|
|
351
|
-
agent_config_path = os.path.join(workdir,
|
|
347
|
+
agent_config_path = os.path.join(workdir, CULTURE_DIR, AGENTS_YAML)
|
|
352
348
|
agent_cmd = [
|
|
353
349
|
culture_bin,
|
|
354
350
|
"agent",
|
|
@@ -502,7 +498,7 @@ def _restart_mesh_services(
|
|
|
502
498
|
for agent in mesh.agents:
|
|
503
499
|
full_nick = f"{server_name}-{agent.nick}"
|
|
504
500
|
workdir = os.path.expanduser(agent.workdir)
|
|
505
|
-
agent_config_path = os.path.join(workdir,
|
|
501
|
+
agent_config_path = os.path.join(workdir, CULTURE_DIR, AGENTS_YAML)
|
|
506
502
|
agent_cmd = [
|
|
507
503
|
culture_bin,
|
|
508
504
|
"agent",
|
|
@@ -549,7 +545,7 @@ def _restart_mesh_services(
|
|
|
549
545
|
print(f" Restarting {agent_svc}...")
|
|
550
546
|
if not restart_service(agent_svc):
|
|
551
547
|
workdir = os.path.expanduser(agent.workdir)
|
|
552
|
-
agent_config_path = os.path.join(workdir,
|
|
548
|
+
agent_config_path = os.path.join(workdir, CULTURE_DIR, AGENTS_YAML)
|
|
553
549
|
subprocess.run(
|
|
554
550
|
[culture_bin, "agent", "start", full_nick, "--config", agent_config_path],
|
|
555
551
|
check=False,
|
|
@@ -19,7 +19,14 @@ from culture.pidfile import (
|
|
|
19
19
|
write_pid,
|
|
20
20
|
)
|
|
21
21
|
|
|
22
|
-
from .
|
|
22
|
+
from .shared.constants import (
|
|
23
|
+
_CONFIG_HELP,
|
|
24
|
+
_SERVER_NAME_HELP,
|
|
25
|
+
BOT_CONFIG_FILE,
|
|
26
|
+
DEFAULT_CONFIG,
|
|
27
|
+
LOG_DIR,
|
|
28
|
+
)
|
|
29
|
+
from .shared.mesh import parse_link, resolve_links_from_mesh
|
|
23
30
|
|
|
24
31
|
logger = logging.getLogger("culture")
|
|
25
32
|
|
|
@@ -31,7 +38,7 @@ def register(subparsers: argparse._SubParsersAction) -> None:
|
|
|
31
38
|
server_sub = server_parser.add_subparsers(dest="server_command")
|
|
32
39
|
|
|
33
40
|
srv_start = server_sub.add_parser("start", help="Start the IRC server daemon")
|
|
34
|
-
srv_start.add_argument("--name", default="culture", help=
|
|
41
|
+
srv_start.add_argument("--name", default="culture", help=_SERVER_NAME_HELP)
|
|
35
42
|
srv_start.add_argument("--host", default="0.0.0.0", help="Listen address")
|
|
36
43
|
srv_start.add_argument("--port", type=int, default=6667, help="Listen port")
|
|
37
44
|
srv_start.add_argument(
|
|
@@ -59,10 +66,10 @@ def register(subparsers: argparse._SubParsersAction) -> None:
|
|
|
59
66
|
)
|
|
60
67
|
|
|
61
68
|
srv_stop = server_sub.add_parser("stop", help="Stop the IRC server daemon")
|
|
62
|
-
srv_stop.add_argument("--name", default="culture", help=
|
|
69
|
+
srv_stop.add_argument("--name", default="culture", help=_SERVER_NAME_HELP)
|
|
63
70
|
|
|
64
71
|
srv_status = server_sub.add_parser("status", help="Check server daemon status")
|
|
65
|
-
srv_status.add_argument("--name", default="culture", help=
|
|
72
|
+
srv_status.add_argument("--name", default="culture", help=_SERVER_NAME_HELP)
|
|
66
73
|
|
|
67
74
|
srv_default = server_sub.add_parser("default", help="Set default server")
|
|
68
75
|
srv_default.add_argument("name", help="Server name to set as default")
|
|
@@ -73,29 +80,29 @@ def register(subparsers: argparse._SubParsersAction) -> None:
|
|
|
73
80
|
srv_rename.add_argument("new_name", help="New server name")
|
|
74
81
|
srv_rename.add_argument(
|
|
75
82
|
"--config",
|
|
76
|
-
default=
|
|
77
|
-
help=
|
|
83
|
+
default=DEFAULT_CONFIG,
|
|
84
|
+
help=_CONFIG_HELP,
|
|
78
85
|
)
|
|
79
86
|
|
|
80
87
|
srv_archive = server_sub.add_parser(
|
|
81
88
|
"archive", help="Archive the server and all its agents/bots"
|
|
82
89
|
)
|
|
83
|
-
srv_archive.add_argument("--name", default="culture", help=
|
|
90
|
+
srv_archive.add_argument("--name", default="culture", help=_SERVER_NAME_HELP)
|
|
84
91
|
srv_archive.add_argument("--reason", default="", help="Reason for archiving")
|
|
85
92
|
srv_archive.add_argument(
|
|
86
93
|
"--config",
|
|
87
|
-
default=
|
|
88
|
-
help=
|
|
94
|
+
default=DEFAULT_CONFIG,
|
|
95
|
+
help=_CONFIG_HELP,
|
|
89
96
|
)
|
|
90
97
|
|
|
91
98
|
srv_unarchive = server_sub.add_parser(
|
|
92
99
|
"unarchive", help="Restore an archived server and all its agents/bots"
|
|
93
100
|
)
|
|
94
|
-
srv_unarchive.add_argument("--name", default="culture", help=
|
|
101
|
+
srv_unarchive.add_argument("--name", default="culture", help=_SERVER_NAME_HELP)
|
|
95
102
|
srv_unarchive.add_argument(
|
|
96
103
|
"--config",
|
|
97
|
-
default=
|
|
98
|
-
help=
|
|
104
|
+
default=DEFAULT_CONFIG,
|
|
105
|
+
help=_CONFIG_HELP,
|
|
99
106
|
)
|
|
100
107
|
|
|
101
108
|
|
|
@@ -256,7 +263,7 @@ def _server_start(args: argparse.Namespace) -> None:
|
|
|
256
263
|
# Check if server is archived
|
|
257
264
|
from culture.clients.claude.config import load_config_or_default
|
|
258
265
|
|
|
259
|
-
config_path = getattr(args, "config",
|
|
266
|
+
config_path = getattr(args, "config", DEFAULT_CONFIG)
|
|
260
267
|
config = load_config_or_default(config_path)
|
|
261
268
|
if config.server.name == args.name and config.server.archived:
|
|
262
269
|
print(
|
|
@@ -439,7 +446,7 @@ def _set_bots_archive_state(agent_nicks: set, *, archive: bool, reason: str = ""
|
|
|
439
446
|
return changed
|
|
440
447
|
today = time.strftime("%Y-%m-%d")
|
|
441
448
|
for bot_dir in BOTS_DIR.iterdir():
|
|
442
|
-
yaml_path = bot_dir /
|
|
449
|
+
yaml_path = bot_dir / BOT_CONFIG_FILE
|
|
443
450
|
if not yaml_path.is_file():
|
|
444
451
|
continue
|
|
445
452
|
try:
|
|
@@ -486,7 +493,7 @@ def _server_archive(args: argparse.Namespace) -> None:
|
|
|
486
493
|
_server_stop(args)
|
|
487
494
|
|
|
488
495
|
# Stop all running agents
|
|
489
|
-
from culture.cli.
|
|
496
|
+
from culture.cli.shared.process import stop_agent
|
|
490
497
|
|
|
491
498
|
for agent in config.agents:
|
|
492
499
|
agent_pid = read_pid(f"agent-{agent.nick}")
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Shared constants for culture CLI modules."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
DEFAULT_CONFIG = os.path.expanduser("~/.culture/agents.yaml")
|
|
6
|
+
LOG_DIR = os.path.expanduser("~/.culture/logs")
|
|
7
|
+
|
|
8
|
+
_CONFIG_HELP = "Config file path"
|
|
9
|
+
_SERVER_NAME_HELP = "Server name"
|
|
10
|
+
_BOT_NAME_HELP = "Bot name"
|
|
11
|
+
|
|
12
|
+
BOT_CONFIG_FILE = "bot.yaml"
|
|
13
|
+
DEFAULT_CHANNEL = "#general"
|
|
14
|
+
NO_AGENTS_MSG = "No agents configured"
|
|
15
|
+
CULTURE_DIR = ".culture"
|
|
16
|
+
AGENTS_YAML = "agents.yaml"
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Agent and bot status display helpers for culture CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import asyncio
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
|
|
10
|
+
from culture.pidfile import is_process_alive, read_pid, remove_pid
|
|
11
|
+
|
|
12
|
+
from .constants import BOT_CONFIG_FILE
|
|
13
|
+
from .ipc import agent_socket_path, ipc_request
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger("culture")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def agent_process_status(agent) -> tuple[str, int | None]:
|
|
19
|
+
"""Return (status_str, pid_or_none) for an agent."""
|
|
20
|
+
pid_name = f"agent-{agent.nick}"
|
|
21
|
+
pid = read_pid(pid_name)
|
|
22
|
+
if pid and is_process_alive(pid):
|
|
23
|
+
socket_path = agent_socket_path(agent.nick)
|
|
24
|
+
if os.path.exists(socket_path):
|
|
25
|
+
return "running", pid
|
|
26
|
+
return "starting", pid
|
|
27
|
+
if pid:
|
|
28
|
+
remove_pid(pid_name)
|
|
29
|
+
return "stopped", None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def print_agent_detail(agent, config_path: str, args: argparse.Namespace) -> None:
|
|
33
|
+
"""Print detailed status for a single agent, including live IPC activity query."""
|
|
34
|
+
status, pid = agent_process_status(agent)
|
|
35
|
+
print(agent.nick)
|
|
36
|
+
print(f" Status: {status}")
|
|
37
|
+
print(f" PID: {pid or '-'}")
|
|
38
|
+
|
|
39
|
+
if status == "running":
|
|
40
|
+
resp = asyncio.run(ipc_request(agent_socket_path(agent.nick), "status", query=True))
|
|
41
|
+
if resp and resp.get("ok"):
|
|
42
|
+
data = resp.get("data", {})
|
|
43
|
+
print(f" Activity: {data.get('description', 'nothing')}")
|
|
44
|
+
print(f" Turns: {data.get('turn_count', 0)}")
|
|
45
|
+
print(f" Paused: {'yes' if data.get('paused') else 'no'}")
|
|
46
|
+
else:
|
|
47
|
+
print(" Activity: unknown (daemon may need restart)")
|
|
48
|
+
else:
|
|
49
|
+
print(" Activity: -")
|
|
50
|
+
|
|
51
|
+
channels = agent.channels if isinstance(agent.channels, list) else []
|
|
52
|
+
print(f" Directory: {agent.directory}")
|
|
53
|
+
print(f" Backend: {agent.agent}")
|
|
54
|
+
print(f" Channels: {', '.join(channels)}")
|
|
55
|
+
print(f" Model: {agent.model}")
|
|
56
|
+
print(f" Config: {config_path}")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _format_agent_status(base_status: str, archived: bool, show_archived_marker: bool) -> str:
|
|
60
|
+
"""Format the display status string for an agent."""
|
|
61
|
+
if not archived:
|
|
62
|
+
return base_status
|
|
63
|
+
if show_archived_marker:
|
|
64
|
+
return f"{base_status} (archived)"
|
|
65
|
+
if base_status == "stopped":
|
|
66
|
+
return "archived"
|
|
67
|
+
return base_status
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _fetch_agent_activity(agent) -> str:
|
|
71
|
+
"""Fetch activity description from a running agent via IPC."""
|
|
72
|
+
resp = asyncio.run(ipc_request(agent_socket_path(agent.nick), "status"))
|
|
73
|
+
if resp and resp.get("ok"):
|
|
74
|
+
return resp.get("data", {}).get("description", "nothing")
|
|
75
|
+
return "-"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def print_agents_overview(
|
|
79
|
+
agents: list, show_activity: bool, show_archived_marker: bool = False
|
|
80
|
+
) -> None:
|
|
81
|
+
"""Print a table of all agents with status, PID, and optionally activity."""
|
|
82
|
+
if show_activity:
|
|
83
|
+
print(f"{'NICK':<30} {'STATUS':<12} {'PID':<10} {'ACTIVITY'}")
|
|
84
|
+
print("-" * 72)
|
|
85
|
+
else:
|
|
86
|
+
print(f"{'NICK':<30} {'STATUS':<12} {'PID':<10}")
|
|
87
|
+
print("-" * 52)
|
|
88
|
+
|
|
89
|
+
for agent in agents:
|
|
90
|
+
base_status, pid = agent_process_status(agent)
|
|
91
|
+
status = _format_agent_status(
|
|
92
|
+
base_status, getattr(agent, "archived", False), show_archived_marker
|
|
93
|
+
)
|
|
94
|
+
activity = (
|
|
95
|
+
_fetch_agent_activity(agent) if show_activity and base_status == "running" else "-"
|
|
96
|
+
)
|
|
97
|
+
if show_activity:
|
|
98
|
+
print(f"{agent.nick:<30} {status:<12} {str(pid or '-'):<10} {activity}")
|
|
99
|
+
else:
|
|
100
|
+
print(f"{agent.nick:<30} {status:<12} {str(pid or '-'):<10}")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _load_bot_configs() -> list:
|
|
104
|
+
"""Load all valid bot configs from the bots directory."""
|
|
105
|
+
from culture.bots.config import BOTS_DIR, load_bot_config
|
|
106
|
+
|
|
107
|
+
if not BOTS_DIR.is_dir():
|
|
108
|
+
return []
|
|
109
|
+
configs = []
|
|
110
|
+
for bot_dir in sorted(BOTS_DIR.iterdir()):
|
|
111
|
+
yaml_path = bot_dir / BOT_CONFIG_FILE
|
|
112
|
+
if not yaml_path.is_file():
|
|
113
|
+
continue
|
|
114
|
+
try:
|
|
115
|
+
configs.append(load_bot_config(yaml_path))
|
|
116
|
+
except Exception as exc:
|
|
117
|
+
logger.warning("Failed to load bot config %s: %s", yaml_path, exc)
|
|
118
|
+
return configs
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def print_bot_listing() -> None:
|
|
122
|
+
"""Print a table of configured bots (if any exist)."""
|
|
123
|
+
bot_configs = _load_bot_configs()
|
|
124
|
+
if not bot_configs:
|
|
125
|
+
return
|
|
126
|
+
print()
|
|
127
|
+
print(f"{'BOT':<30} {'TRIGGER':<12} {'CHANNELS'}")
|
|
128
|
+
print("-" * 60)
|
|
129
|
+
for bc in bot_configs:
|
|
130
|
+
channels = ", ".join(bc.channels) if bc.channels else "-"
|
|
131
|
+
print(f"{bc.name:<30} {bc.trigger_type:<12} {channels}")
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""IPC and observer helpers for culture CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
from culture.clients.claude.config import load_config_or_default
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def agent_socket_path(nick: str) -> str:
|
|
12
|
+
return os.path.join(
|
|
13
|
+
os.environ.get("XDG_RUNTIME_DIR", "/tmp"),
|
|
14
|
+
f"culture-{nick}.sock",
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def ipc_request(socket_path: str, msg_type: str, **kwargs) -> dict | None:
|
|
19
|
+
"""Send an IPC request via Unix socket and return the response."""
|
|
20
|
+
from culture.clients.claude.ipc import decode_message, encode_message, make_request
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
reader, writer = await asyncio.wait_for(
|
|
24
|
+
asyncio.open_unix_connection(socket_path),
|
|
25
|
+
timeout=3.0,
|
|
26
|
+
)
|
|
27
|
+
except (ConnectionRefusedError, FileNotFoundError, OSError):
|
|
28
|
+
return None
|
|
29
|
+
try:
|
|
30
|
+
req = make_request(msg_type, **kwargs)
|
|
31
|
+
writer.write(encode_message(req))
|
|
32
|
+
await writer.drain()
|
|
33
|
+
loop = asyncio.get_running_loop()
|
|
34
|
+
deadline = loop.time() + 3.0
|
|
35
|
+
while True:
|
|
36
|
+
remaining = deadline - loop.time()
|
|
37
|
+
if remaining <= 0:
|
|
38
|
+
return None
|
|
39
|
+
data = await asyncio.wait_for(reader.readline(), timeout=remaining)
|
|
40
|
+
msg = decode_message(data)
|
|
41
|
+
if msg and msg.get("type") == "response":
|
|
42
|
+
return msg
|
|
43
|
+
except (asyncio.TimeoutError, ConnectionError, BrokenPipeError, OSError):
|
|
44
|
+
return None
|
|
45
|
+
finally:
|
|
46
|
+
writer.close()
|
|
47
|
+
try:
|
|
48
|
+
await writer.wait_closed()
|
|
49
|
+
except (ConnectionError, BrokenPipeError, OSError):
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
async def ipc_shutdown(socket_path: str) -> bool:
|
|
54
|
+
"""Send a shutdown command via Unix socket IPC."""
|
|
55
|
+
resp = await ipc_request(socket_path, "shutdown")
|
|
56
|
+
return resp is not None and resp.get("ok", False)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def get_observer(config_path: str):
|
|
60
|
+
"""Create an IRCObserver from the config file."""
|
|
61
|
+
from culture.observer import IRCObserver
|
|
62
|
+
|
|
63
|
+
config = load_config_or_default(config_path)
|
|
64
|
+
return IRCObserver(
|
|
65
|
+
host=config.server.host,
|
|
66
|
+
port=config.server.port,
|
|
67
|
+
server_name=config.server.name,
|
|
68
|
+
)
|