agentirc-cli 3.0.1__tar.gz → 3.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/CHANGELOG.md +8 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/CLAUDE.md +1 -1
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/PKG-INFO +1 -1
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/cli.py +67 -12
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/server/ircd.py +20 -6
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/pyproject.toml +1 -1
- agentirc_cli-3.0.2/tests/test_wait_for_port.py +89 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/uv.lock +1 -1
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/.claude/skills/pr-review/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/.claude/skills/run-tests/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/.claude/skills/run-tests/scripts/test.sh +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/.flake8 +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/.github/workflows/pages.yml +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/.github/workflows/publish.yml +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/.github/workflows/security-checks.yml +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/.github/workflows/tests.yml +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/.gitignore +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/.markdownlint-cli2.yaml +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/.pr_agent.toml +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/.pre-commit-config.yaml +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/.pylintrc +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/CNAME +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/Gemfile +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/Gemfile.lock +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/LICENSE +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/README.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/SECURITY.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/_config.yml +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/_sass/color_schemes/anthropic.scss +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/_sass/custom/custom.scss +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/__main__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/bots/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/bots/bot.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/bots/bot_manager.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/bots/config.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/bots/http_listener.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/bots/template_engine.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/bots/virtual_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/acp/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/acp/agent_runner.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/acp/config.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/acp/daemon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/acp/ipc.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/acp/irc_transport.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/acp/message_buffer.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/acp/skill/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/acp/skill/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/acp/skill/irc_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/acp/socket_server.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/acp/supervisor.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/acp/webhook.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/claude/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/claude/__main__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/claude/agent_runner.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/claude/config.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/claude/daemon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/claude/ipc.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/claude/irc_transport.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/claude/message_buffer.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/claude/skill/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/claude/skill/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/claude/skill/irc_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/claude/socket_server.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/claude/supervisor.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/claude/webhook.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/codex/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/codex/agent_runner.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/codex/config.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/codex/daemon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/codex/ipc.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/codex/irc_transport.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/codex/message_buffer.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/codex/skill/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/codex/skill/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/codex/skill/irc_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/codex/socket_server.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/codex/supervisor.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/codex/webhook.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/copilot/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/copilot/agent_runner.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/copilot/config.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/copilot/daemon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/copilot/ipc.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/copilot/irc_transport.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/copilot/message_buffer.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/copilot/skill/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/copilot/skill/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/copilot/skill/irc_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/copilot/socket_server.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/copilot/supervisor.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/clients/copilot/webhook.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/console/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/console/app.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/console/client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/console/commands.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/console/widgets/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/console/widgets/chat.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/console/widgets/info_panel.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/console/widgets/sidebar.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/credentials.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/learn_prompt.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/mesh_config.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/observer.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/overview/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/overview/collector.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/overview/model.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/overview/renderer_text.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/overview/renderer_web.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/overview/web/style.css +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/persistence.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/pidfile.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/protocol/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/protocol/commands.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/protocol/extensions/federation.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/protocol/extensions/history.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/protocol/extensions/icons.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/protocol/extensions/rooms.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/protocol/extensions/tags.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/protocol/extensions/threads.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/protocol/message.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/protocol/protocol-index.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/protocol/replies.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/server/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/server/__main__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/server/channel.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/server/client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/server/config.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/server/remote_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/server/room_store.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/server/rooms_util.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/server/server_link.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/server/skill.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/server/skills/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/server/skills/history.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/server/skills/icon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/server/skills/rooms.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/server/skills/threads.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/server/thread_store.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/culture/skills/culture/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/agent-lifecycle.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/agentic-self-learn.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/architecture/agent-client.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/architecture/agent-harness-spec.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/architecture/design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/architecture/harness-conformance.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/architecture/index.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/architecture/layer1-core-irc.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/architecture/layer2-attention.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/architecture/layer3-skills.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/architecture/layer4-federation.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/architecture/layer5-agent-harness.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/architecture/server-architecture.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/architecture/threads.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/acp/overview.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/claude/configuration.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/claude/context-management.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/claude/irc-tools.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/claude/overview.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/claude/setup.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/claude/supervisor.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/claude/webhooks.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/codex/configuration.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/codex/context-management.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/codex/irc-tools.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/codex/overview.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/codex/setup.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/codex/supervisor.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/codex/webhooks.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/copilot/configuration.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/copilot/context-management.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/copilot/irc-tools.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/copilot/overview.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/copilot/setup.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/copilot/supervisor.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/clients/copilot/webhooks.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/culture-cli.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/getting-started.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/index.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/operations/SECURITY.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/operations/bots.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/operations/ci.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/operations/cli.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/operations/docs-site.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/operations/index.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/operations/ops-tooling.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/operations/overview.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/operations/publishing.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/resources/github-copilot-sdk-instructions.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/rooms.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/plans/2026-03-19-layer1-core-irc.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/plans/2026-03-21-layer5-agent-harness.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/plans/2026-03-30-overview.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/plans/2026-03-30-rooms-management.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/plans/2026-04-02-conversation-threads.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/plans/2026-04-02-ops-tooling.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/plans/2026-04-04-culture-rename.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/plans/2026-04-05-docs-speak-culture.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/plans/2026-04-06-console-chat.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/specs/2026-03-19-agentirc-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/specs/2026-03-21-layer5-agent-harness-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/specs/2026-03-30-overview-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/specs/2026-03-30-rooms-management-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/specs/2026-04-02-conversation-threads-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/specs/2026-04-02-ops-tooling-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/specs/2026-04-03-bots-webhooks-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/specs/2026-04-04-culture-rename-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/specs/2026-04-05-docs-speak-culture-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/specs/2026-04-05-lifecycle-reframe-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/superpowers/specs/2026-04-06-console-chat-design.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/use-cases/01-pair-programming.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/use-cases/02-code-review-ensemble.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/use-cases/03-cross-server-delegation.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/use-cases/04-knowledge-propagation.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/use-cases/05-the-observer.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/use-cases/06-cross-server-ops.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/use-cases/07-supervisor-intervention.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/use-cases/08-apps-as-agents.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/use-cases/09-research-swarm.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/use-cases/10-agent-lifecycle.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/use-cases-index.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/docs/what-is-culture.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/packages/agent-harness/README.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/packages/agent-harness/config.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/packages/agent-harness/daemon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/packages/agent-harness/ipc.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/packages/agent-harness/irc_transport.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/packages/agent-harness/message_buffer.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/packages/agent-harness/skill/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/packages/agent-harness/skill/irc_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/packages/agent-harness/socket_server.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/packages/agent-harness/webhook.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/plugins/claude-code/.claude-plugin/plugin.json +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/plugins/claude-code/skills/culture/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/plugins/claude-code/skills/irc/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/plugins/codex/skills/culture-irc/SKILL.md +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/sonar-project.properties +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/__init__.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/conftest.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_acp_daemon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_agent_runner.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_bot.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_bot_config.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_bot_manager.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_bots_integration.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_channel.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_codex_daemon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_connection.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_console_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_console_commands.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_console_connection.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_console_icons.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_console_integration.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_copilot_daemon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_daemon.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_daemon_config.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_daemon_ipc.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_discovery.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_federation.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_history.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_http_listener.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_integration_layer5.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_ipc.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_irc_transport.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_link_reconnect.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_mentions.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_mesh_config.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_message.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_message_buffer.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_messaging.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_modes.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_overview_cli.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_overview_collector.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_overview_model.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_overview_renderer.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_overview_web.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_persistence.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_pidfile.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_room_persistence.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_rooms.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_rooms_federation.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_rooms_integration.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_server_icon_skill.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_setup_update_cli.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_skill_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_skills.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_socket_server.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_supervisor.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_template_engine.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_thread_buffer.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_threads.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_virtual_client.py +0 -0
- {agentirc_cli-3.0.1 → agentirc_cli-3.0.2}/tests/test_webhook.py +0 -0
|
@@ -4,6 +4,14 @@ 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
|
+
## [3.0.2] - 2026-04-06
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Server startup readiness — culture server start now waits for port to accept connections before returning
|
|
13
|
+
- Added startup phase logging to server log for diagnosing slow starts
|
|
14
|
+
|
|
7
15
|
## [3.0.1] - 2026-04-06
|
|
8
16
|
|
|
9
17
|
|
|
@@ -42,7 +42,7 @@ When implementing features, write a corresponding markdown doc in `docs/` descri
|
|
|
42
42
|
|
|
43
43
|
## Testing
|
|
44
44
|
|
|
45
|
-
- `pytest` + `pytest-asyncio`
|
|
45
|
+
- `pytest` + `pytest-asyncio`, always run with `-n auto` for parallel execution
|
|
46
46
|
- No mocks for the server — tests spin up real server instances on random ports with real TCP connections
|
|
47
47
|
- Validate each layer with real IRC clients (weechat/irssi)
|
|
48
48
|
|
|
@@ -29,6 +29,7 @@ import logging
|
|
|
29
29
|
import os
|
|
30
30
|
import shutil
|
|
31
31
|
import signal
|
|
32
|
+
import socket
|
|
32
33
|
import subprocess
|
|
33
34
|
import sys
|
|
34
35
|
import time
|
|
@@ -493,6 +494,39 @@ def _cmd_server(args: argparse.Namespace) -> None:
|
|
|
493
494
|
print(f"Default server set to '{args.name}'")
|
|
494
495
|
|
|
495
496
|
|
|
497
|
+
def _wait_for_port(
|
|
498
|
+
host: str,
|
|
499
|
+
port: int,
|
|
500
|
+
pid: int,
|
|
501
|
+
timeout: float = 30,
|
|
502
|
+
) -> tuple[bool, str]:
|
|
503
|
+
"""Poll *host*:*port* until a TCP connect succeeds or *timeout* expires.
|
|
504
|
+
|
|
505
|
+
Returns ``(True, "")`` on success, or ``(False, reason)`` on failure.
|
|
506
|
+
Checks that *pid* is still alive on every iteration so we fail fast if
|
|
507
|
+
the child crashes (e.g. because the port was already in use).
|
|
508
|
+
"""
|
|
509
|
+
check_host = "127.0.0.1" if host == "0.0.0.0" else host
|
|
510
|
+
deadline = time.monotonic() + timeout
|
|
511
|
+
while time.monotonic() < deadline:
|
|
512
|
+
if not is_process_alive(pid):
|
|
513
|
+
return False, "failed to start"
|
|
514
|
+
try:
|
|
515
|
+
s = socket.create_connection((check_host, port), timeout=0.5)
|
|
516
|
+
s.close()
|
|
517
|
+
except OSError:
|
|
518
|
+
time.sleep(0.2)
|
|
519
|
+
continue
|
|
520
|
+
# Port responded — give the child a moment, then confirm it's
|
|
521
|
+
# still alive (guards against connecting to a *stale* listener
|
|
522
|
+
# on the same port while our child crashes).
|
|
523
|
+
time.sleep(0.1)
|
|
524
|
+
if not is_process_alive(pid):
|
|
525
|
+
return False, "failed to start"
|
|
526
|
+
return True, ""
|
|
527
|
+
return False, "started but not yet accepting connections"
|
|
528
|
+
|
|
529
|
+
|
|
496
530
|
def _server_start(args: argparse.Namespace) -> None:
|
|
497
531
|
pid_name = f"server-{args.name}"
|
|
498
532
|
|
|
@@ -532,19 +566,33 @@ def _server_start(args: argparse.Namespace) -> None:
|
|
|
532
566
|
# Fork to daemonize
|
|
533
567
|
pid = os.fork()
|
|
534
568
|
if pid > 0:
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
write_default_server(args.name)
|
|
569
|
+
log_hint = f"{LOG_DIR}/server-{args.name}.log"
|
|
570
|
+
|
|
571
|
+
if args.port == 0:
|
|
572
|
+
# Ephemeral port — can't probe; fall back to process-alive check
|
|
573
|
+
time.sleep(0.5)
|
|
574
|
+
if not is_process_alive(pid):
|
|
575
|
+
print(f"Server '{args.name}' failed to start", file=sys.stderr)
|
|
576
|
+
print(f" Check logs: {log_hint}", file=sys.stderr)
|
|
577
|
+
sys.exit(1)
|
|
545
578
|
else:
|
|
546
|
-
|
|
547
|
-
|
|
579
|
+
ok, err = _wait_for_port(args.host, args.port, pid, timeout=30)
|
|
580
|
+
if not ok:
|
|
581
|
+
print(
|
|
582
|
+
f"Server '{args.name}' {err}",
|
|
583
|
+
file=sys.stderr,
|
|
584
|
+
)
|
|
585
|
+
print(f" Check logs: {log_hint}", file=sys.stderr)
|
|
586
|
+
sys.exit(1)
|
|
587
|
+
|
|
588
|
+
print(f"Server '{args.name}' started (PID {pid})")
|
|
589
|
+
print(f" Listening on {args.host}:{args.port}")
|
|
590
|
+
print(f" Logs: {log_hint}")
|
|
591
|
+
# Auto-set default server if none is set
|
|
592
|
+
from culture.pidfile import read_default_server, write_default_server
|
|
593
|
+
|
|
594
|
+
if read_default_server() is None:
|
|
595
|
+
write_default_server(args.name)
|
|
548
596
|
return
|
|
549
597
|
|
|
550
598
|
# Child: detach from parent session
|
|
@@ -562,6 +610,13 @@ def _server_start(args: argparse.Namespace) -> None:
|
|
|
562
610
|
os.dup2(devnull, 0)
|
|
563
611
|
os.close(devnull)
|
|
564
612
|
|
|
613
|
+
# Reconfigure logging so handlers write to the redirected stderr (log file)
|
|
614
|
+
logging.basicConfig(
|
|
615
|
+
level=logging.INFO,
|
|
616
|
+
format="%(asctime)s %(name)s %(levelname)s %(message)s",
|
|
617
|
+
force=True,
|
|
618
|
+
)
|
|
619
|
+
|
|
565
620
|
write_pid(pid_name, os.getpid())
|
|
566
621
|
|
|
567
622
|
try:
|
|
@@ -10,6 +10,8 @@ from culture.server.channel import Channel
|
|
|
10
10
|
from culture.server.config import ServerConfig
|
|
11
11
|
from culture.server.skill import Event, Skill
|
|
12
12
|
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
13
15
|
if TYPE_CHECKING:
|
|
14
16
|
from culture.bots.virtual_client import VirtualClient
|
|
15
17
|
from culture.server.client import Client
|
|
@@ -41,16 +43,25 @@ class IRCd:
|
|
|
41
43
|
self.bot_manager = None # set in start() if webhook_port configured
|
|
42
44
|
|
|
43
45
|
async def start(self) -> None:
|
|
46
|
+
logger.info("Registering default skills...")
|
|
44
47
|
await self._register_default_skills()
|
|
48
|
+
|
|
49
|
+
logger.info("Restoring persistent rooms...")
|
|
45
50
|
self._restore_persistent_rooms()
|
|
46
51
|
|
|
47
52
|
# Initialize bot manager and webhook HTTP listener
|
|
48
53
|
from culture.bots.bot_manager import BotManager
|
|
49
54
|
from culture.bots.http_listener import HttpListener
|
|
50
55
|
|
|
56
|
+
logger.info("Loading bots...")
|
|
51
57
|
self.bot_manager = BotManager(self)
|
|
52
58
|
await self.bot_manager.load_bots()
|
|
53
59
|
|
|
60
|
+
logger.info(
|
|
61
|
+
"Binding IRC socket on %s:%d...",
|
|
62
|
+
self.config.host,
|
|
63
|
+
self.config.port,
|
|
64
|
+
)
|
|
54
65
|
self._server = await asyncio.start_server(
|
|
55
66
|
self._handle_connection,
|
|
56
67
|
self.config.host,
|
|
@@ -63,16 +74,22 @@ class IRCd:
|
|
|
63
74
|
self.config.webhook_port,
|
|
64
75
|
)
|
|
65
76
|
try:
|
|
77
|
+
logger.info(
|
|
78
|
+
"Starting webhook listener on port %d...",
|
|
79
|
+
self.config.webhook_port,
|
|
80
|
+
)
|
|
66
81
|
await self._http_listener.start()
|
|
67
82
|
except OSError:
|
|
68
83
|
# Port unavailable (e.g. in tests using port 0 that got
|
|
69
84
|
# assigned an in-use ephemeral port). Non-fatal — bots
|
|
70
85
|
# still work, just without the HTTP endpoint.
|
|
71
|
-
|
|
86
|
+
logger.warning(
|
|
72
87
|
"Could not start webhook listener on port %d",
|
|
73
88
|
self.config.webhook_port,
|
|
74
89
|
)
|
|
75
90
|
|
|
91
|
+
logger.info("Server ready")
|
|
92
|
+
|
|
76
93
|
async def _register_default_skills(self) -> None:
|
|
77
94
|
from culture.server.skills.history import HistorySkill
|
|
78
95
|
from culture.server.skills.icon import IconSkill
|
|
@@ -101,9 +118,7 @@ class IRCd:
|
|
|
101
118
|
try:
|
|
102
119
|
await skill.on_event(event)
|
|
103
120
|
except Exception:
|
|
104
|
-
|
|
105
|
-
"Skill %s failed on event %s", skill.name, event.type
|
|
106
|
-
)
|
|
121
|
+
logger.exception("Skill %s failed on event %s", skill.name, event.type)
|
|
107
122
|
|
|
108
123
|
# Relay to linked peers — only relay locally-originated events
|
|
109
124
|
# (no mesh routing; scope is direct peers only)
|
|
@@ -112,7 +127,7 @@ class IRCd:
|
|
|
112
127
|
try:
|
|
113
128
|
await link.relay_event(event)
|
|
114
129
|
except Exception:
|
|
115
|
-
|
|
130
|
+
logger.exception("Failed to relay event to %s", peer_name)
|
|
116
131
|
|
|
117
132
|
def get_skill_for_command(self, command: str) -> Skill | None:
|
|
118
133
|
for skill in self.skills:
|
|
@@ -190,7 +205,6 @@ class IRCd:
|
|
|
190
205
|
|
|
191
206
|
async def _retry_link_loop(self, peer_name: str, link_config, state: dict) -> None:
|
|
192
207
|
"""Retry connecting to a peer with exponential backoff."""
|
|
193
|
-
logger = logging.getLogger(__name__)
|
|
194
208
|
try:
|
|
195
209
|
while True:
|
|
196
210
|
await asyncio.sleep(state["delay"])
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Tests for the _wait_for_port readiness helper."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import socket
|
|
6
|
+
import threading
|
|
7
|
+
import time
|
|
8
|
+
|
|
9
|
+
from culture.cli import _wait_for_port
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _start_listener(port_holder: list, delay: float = 0) -> socket.socket:
|
|
13
|
+
"""Start a TCP listener, optionally after *delay* seconds.
|
|
14
|
+
|
|
15
|
+
Stores the assigned port in *port_holder[0]*.
|
|
16
|
+
"""
|
|
17
|
+
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
18
|
+
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
19
|
+
|
|
20
|
+
def _bind():
|
|
21
|
+
if delay:
|
|
22
|
+
time.sleep(delay)
|
|
23
|
+
srv.bind(("127.0.0.1", 0))
|
|
24
|
+
srv.listen(1)
|
|
25
|
+
port_holder.append(srv.getsockname()[1])
|
|
26
|
+
|
|
27
|
+
t = threading.Thread(target=_bind, daemon=True)
|
|
28
|
+
t.start()
|
|
29
|
+
t.join(timeout=delay + 2)
|
|
30
|
+
return srv
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_success_immediate():
|
|
34
|
+
"""Port already open — should return immediately."""
|
|
35
|
+
port_holder: list[int] = []
|
|
36
|
+
srv = _start_listener(port_holder)
|
|
37
|
+
try:
|
|
38
|
+
ok, err = _wait_for_port(
|
|
39
|
+
"127.0.0.1",
|
|
40
|
+
port_holder[0],
|
|
41
|
+
# Use our own PID (always alive)
|
|
42
|
+
pid=threading.current_thread().native_id or 1,
|
|
43
|
+
timeout=5,
|
|
44
|
+
)
|
|
45
|
+
assert ok
|
|
46
|
+
assert err == ""
|
|
47
|
+
finally:
|
|
48
|
+
srv.close()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_timeout_no_listener():
|
|
52
|
+
"""No listener — should time out."""
|
|
53
|
+
# Bind then close to get a free port that nothing listens on
|
|
54
|
+
tmp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
55
|
+
tmp.bind(("127.0.0.1", 0))
|
|
56
|
+
port = tmp.getsockname()[1]
|
|
57
|
+
tmp.close()
|
|
58
|
+
|
|
59
|
+
import os
|
|
60
|
+
|
|
61
|
+
ok, err = _wait_for_port("127.0.0.1", port, pid=os.getpid(), timeout=1)
|
|
62
|
+
assert not ok
|
|
63
|
+
assert "not yet accepting connections" in err
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_process_dies():
|
|
67
|
+
"""Dead PID — should fail fast."""
|
|
68
|
+
import subprocess
|
|
69
|
+
|
|
70
|
+
# Spawn a process that exits immediately to get a dead PID
|
|
71
|
+
p = subprocess.Popen(["true"])
|
|
72
|
+
p.wait()
|
|
73
|
+
|
|
74
|
+
ok, err = _wait_for_port("127.0.0.1", 1, pid=p.pid, timeout=5)
|
|
75
|
+
assert not ok
|
|
76
|
+
assert "failed to start" in err
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_host_0000_uses_localhost():
|
|
80
|
+
"""0.0.0.0 host should probe 127.0.0.1."""
|
|
81
|
+
port_holder: list[int] = []
|
|
82
|
+
srv = _start_listener(port_holder)
|
|
83
|
+
try:
|
|
84
|
+
import os
|
|
85
|
+
|
|
86
|
+
ok, _ = _wait_for_port("0.0.0.0", port_holder[0], pid=os.getpid(), timeout=5)
|
|
87
|
+
assert ok
|
|
88
|
+
finally:
|
|
89
|
+
srv.close()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|