stcode 0.0.1a1__tar.gz → 0.0.1a2__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.
- {stcode-0.0.1a1 → stcode-0.0.1a2}/PKG-INFO +1 -1
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/architecture/configuration.md +4 -4
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/architecture/daemon.md +14 -6
- {stcode-0.0.1a1 → stcode-0.0.1a2}/pyproject.toml +1 -1
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/main.py +3 -1
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/services/client.py +7 -1
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/configs.py +5 -1
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/daemon/server.py +4 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode.example.toml +4 -4
- stcode-0.0.1a2/tests/integration/transport_test.py +96 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/uv.lock +1 -1
- {stcode-0.0.1a1 → stcode-0.0.1a2}/.dockerignore +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/.gitignore +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/CLAUDE.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/Dockerfile +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/LICENSE +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/README.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/architecture/agent-loop.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/architecture/cli.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/architecture/harness.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/architecture/index.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/architecture/mcp.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/architecture/providers.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/architecture/session.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/architecture/supervisor.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/architecture/team.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/architecture/tracing.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/contributing/releasing.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/contributing/testing.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/contributing/workflow.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/decisions/0001-tool-search.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/decisions/0002-file-and-image-input.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/decisions/0003-what-the-tui-owns.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/decisions/0004-what-a-remote-client-may-change.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/decisions/0005-an-agent-is-a-config-file.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/decisions/0006-cli-and-core-are-separate-programs.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/decisions/0007-cli-layers.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/decisions/index.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/getting-started/configuration.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/getting-started/installation.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/getting-started/quickstart.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/guide/approval-modes.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/guide/mcp.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/guide/repl.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/guide/sessions.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/guide/shapes.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/guide/skills.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/guide/tools.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/guide/tracing.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/guide/tui.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/index.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/sdk/custom-tools.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/sdk/daemon-protocol.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/sdk/embedding.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/sdk/streaming.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/teams/roles.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/teams/setup.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/docs/teams/workflow.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/examples/agents/ba.toml +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/examples/agents/backend-dev.toml +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/examples/agents/devops.toml +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/examples/agents/frontend-dev.toml +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/mkdocs.yml +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/pytest.ini +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/__init__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/__init__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/bootstrap.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/labels.py +0 -0
- {stcode-0.0.1a1/stcode/cli/ui/screens/chat → stcode-0.0.1a2/stcode/cli/logic}/__init__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/logic/commands.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/logic/completion.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/logic/connection.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/logic/requests.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/logic/sessions.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/logic/settings.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/logic/state.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/logic/workspace.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/models.py +0 -0
- {stcode-0.0.1a1/stcode/cli/ui/screens → stcode-0.0.1a2/stcode/cli/services}/__init__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/services/launcher.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/services/paths.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/services/preferences.py +0 -0
- {stcode-0.0.1a1/stcode/cli/ui/components → stcode-0.0.1a2/stcode/cli/ui}/__init__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/app.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/app.tcss +0 -0
- {stcode-0.0.1a1/stcode/cli/ui → stcode-0.0.1a2/stcode/cli/ui/components}/__init__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/components/banner.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/components/cards.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/components/prompt.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/components/status.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/components/transcript/__init__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/components/transcript/entries.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/components/transcript/formatting.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/components/transcript/view.py +0 -0
- {stcode-0.0.1a1/stcode/cli/services → stcode-0.0.1a2/stcode/cli/ui/screens}/__init__.py +0 -0
- {stcode-0.0.1a1/stcode/cli/logic → stcode-0.0.1a2/stcode/cli/ui/screens/chat}/__init__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/screens/chat/chat.tcss +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/screens/chat/presenter.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/screens/chat/screen.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/screens/connect.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/screens/connect.tcss +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/screens/dialog.tcss +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/screens/sessions.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/screens/settings.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/screens/settings.tcss +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/screens/trust.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/cli/ui/theme.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/__main__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/agent/__init__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/agent/agent.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/agent/events.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/agent/supervisor.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/agent/task.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/common/__init__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/common/compat.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/common/ids.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/common/tools.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/common/trace.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/common/truncate.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/daemon/__init__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/daemon/autonomy.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/daemon/main.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/daemon/protocol.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/daemon/runner.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/daemon/workspace.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/__init__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/approvals.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/context.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/errors.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/harness.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/mcp.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/outputs.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/prompts/__init__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/prompts/execute.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/prompts/plan.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/prompts/sections.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/skills/__init__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/skills/loader.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/tools/__init__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/tools/base.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/tools/files.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/tools/interact.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/tools/registry.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/tools/repl.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/tools/schema.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/tools/search.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/tools/shell.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/tools/skill.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/tools/todo.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/harness/tools/web.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/providers/__init__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/providers/anthropic_claude.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/providers/base.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/providers/gateway.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/providers/google_gemini.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/providers/openai_gpt.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/providers/registry.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/providers/types.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/repl/__init__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/repl/_worker.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/repl/client.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/session/__init__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/session/session.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/team/__init__.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/team/mailbox.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/stcode/core/team/tools.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/cli/architecture_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/cli/labels_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/cli/logic/completion_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/cli/logic/connection_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/cli/logic/sessions_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/cli/logic/workflows_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/cli/main_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/cli/services/launcher_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/cli/services/preferences_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/cli/ui/components/prompt_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/cli/ui/components/transcript_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/cli/ui/theme_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/conftest.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/core/agent_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/core/common_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/core/configs_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/core/daemon_main_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/core/daemon_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/core/harness_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/core/providers_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/core/repl_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/core/session_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/core/team_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/core/trace_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/core/workspace_discovery_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/driving.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/fakes.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/fixtures/workspace/.agents/skills/db-migrations/SKILL.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/fixtures/workspace/.agents/skills/release-notes/SKILL.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/fixtures/workspace/AGENTS.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/fixtures/workspace/README.md +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/fixtures/workspace/config.toml +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/fixtures/workspace/mcp_server.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/fixtures/workspace/src/app.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/fixtures/workspace/src/util.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/integration/conftest.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/integration/conversation_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/integration/trace_live_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/integration/tui_test.py +0 -0
- {stcode-0.0.1a1 → stcode-0.0.1a2}/tests/integration/workspace_test.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: stcode
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.1a2
|
|
4
4
|
Summary: A coding agent that lives in a daemon, and a UI that is a client of it.
|
|
5
5
|
Project-URL: Homepage, https://ming-doan.github.io/stcode/
|
|
6
6
|
Project-URL: Documentation, https://ming-doan.github.io/stcode/docs/
|
|
@@ -127,14 +127,14 @@ project) and `keep`, the number of session files `prune` leaves behind.
|
|
|
127
127
|
|
|
128
128
|
| Key | Default | |
|
|
129
129
|
| --- | --- | --- |
|
|
130
|
-
| `transport` | `"unix"`
|
|
130
|
+
| `transport` | `"unix"` on Unix, `"tcp"` on Windows | `tcp` also works in a container |
|
|
131
131
|
| `socket` | `~/.stcode/daemon.sock` | `unix` only |
|
|
132
132
|
| `host` / `port` | `127.0.0.1` / `7717` | `tcp` only |
|
|
133
133
|
| `max_clients` | *unset* | concurrent connections. **Unset = 1 in a container, unlimited on the host**; `0` is unlimited everywhere |
|
|
134
134
|
|
|
135
|
-
Read by the daemon only. The UI connects to `~/.stcode/daemon.sock`
|
|
136
|
-
|
|
137
|
-
address. → [daemon.md](daemon.md#one-client-in-a-container)
|
|
135
|
+
Read by the daemon only. The UI connects to `~/.stcode/daemon.sock` on Unix or
|
|
136
|
+
`127.0.0.1:7717` over TCP on Windows unless given address flags, and starts the daemon
|
|
137
|
+
on that same address. → [daemon.md](daemon.md#one-client-in-a-container)
|
|
138
138
|
|
|
139
139
|
### `[mcp]`
|
|
140
140
|
|
|
@@ -41,9 +41,10 @@ stcode --daemonless # the UI alone, attached to a daemon elsewhere
|
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
No implementation switches between them. `--headless` replaces the `stcode` process
|
|
44
|
-
with `stcode-daemon`. Plain `stcode` connects to `~/.stcode/daemon.sock`
|
|
45
|
-
its flags name); if nothing answers,
|
|
46
|
-
|
|
44
|
+
with `stcode-daemon`. Plain `stcode` connects to `~/.stcode/daemon.sock` on Unix or
|
|
45
|
+
`127.0.0.1:7717` over TCP on Windows (or the address its flags name); if nothing answers,
|
|
46
|
+
`cli/services/launcher.py` starts `stcode-daemon` on that address as a subprocess,
|
|
47
|
+
logging to `daemon.log` in the stcode home directory, and stops it again when the
|
|
47
48
|
UI exits. A daemon it *found* is left running — quitting is a detach. `stcode --restart`
|
|
48
49
|
sends `shutdown` to whatever is listening first, for a daemon that is wedged or running
|
|
49
50
|
old code.
|
|
@@ -260,17 +261,24 @@ the container is the mitigation.
|
|
|
260
261
|
|
|
261
262
|
```toml
|
|
262
263
|
[daemon]
|
|
263
|
-
transport = "unix"
|
|
264
|
+
# transport = "unix" # default on Unix; "tcp" on Windows
|
|
264
265
|
socket = "~/.stcode/daemon.sock"
|
|
265
266
|
host = "127.0.0.1"
|
|
266
267
|
port = 7717
|
|
267
268
|
```
|
|
268
269
|
|
|
269
|
-
`unix` for solo — no port to collide with, and filesystem permissions are the
|
|
270
|
-
control (the socket is chmod 0600; anyone who can open it can drive an agent with your
|
|
270
|
+
`unix` for solo on Unix — no port to collide with, and filesystem permissions are the
|
|
271
|
+
access control (the socket is chmod 0600; anyone who can open it can drive an agent with your
|
|
271
272
|
privileges). `tcp` for a container, which has no host filesystem to put a socket on. Same
|
|
272
273
|
JSONL framing either way, so a WebSocket adapter later is an adapter, not a protocol.
|
|
273
274
|
|
|
275
|
+
Both the client and daemon default to loopback TCP when asyncio's Unix socket APIs
|
|
276
|
+
are unavailable, as on Windows. Explicitly requesting `unix` on such a platform
|
|
277
|
+
reports an unsupported transport with a hint to use `--transport tcp`.
|
|
278
|
+
On Windows, set provider keys in the daemon's config file or environment: TCP still
|
|
279
|
+
refuses API key updates and protocol shutdown, including `--restart` of a running
|
|
280
|
+
daemon. The launcher can stop a daemon it started when the UI exits.
|
|
281
|
+
|
|
274
282
|
**Transport is configuration, not architecture.** The one thing that reads it is
|
|
275
283
|
`Daemon.local_only`, which decides what only this machine's user may do — send a key,
|
|
276
284
|
stop the daemon. Anything else that needs to know is in the wrong layer.
|
|
@@ -178,7 +178,9 @@ def main(
|
|
|
178
178
|
def _address(
|
|
179
179
|
transport: str | None, socket: str | None, host: str | None, port: int | None
|
|
180
180
|
) -> Address:
|
|
181
|
-
address = Address(
|
|
181
|
+
address = Address()
|
|
182
|
+
if transport:
|
|
183
|
+
address.transport = transport
|
|
182
184
|
if socket:
|
|
183
185
|
address.socket = Path(socket).expanduser()
|
|
184
186
|
address.host = host or address.host
|
|
@@ -44,7 +44,9 @@ rather than hung on."""
|
|
|
44
44
|
class Address:
|
|
45
45
|
"""Where a daemon listens."""
|
|
46
46
|
|
|
47
|
-
transport: str =
|
|
47
|
+
transport: str = field(
|
|
48
|
+
default_factory=lambda: "unix" if hasattr(asyncio, "open_unix_connection") else "tcp"
|
|
49
|
+
)
|
|
48
50
|
socket: Path = field(default_factory=lambda: stcode_home() / "daemon.sock")
|
|
49
51
|
host: str = "127.0.0.1"
|
|
50
52
|
port: int = 7717
|
|
@@ -91,6 +93,10 @@ class DaemonClient:
|
|
|
91
93
|
"""Open the transport. `OSError` when nothing is listening — the caller's cue to
|
|
92
94
|
start a daemon or ask for another address, not an error to hide."""
|
|
93
95
|
if address.transport == "unix":
|
|
96
|
+
if not hasattr(asyncio, "open_unix_connection"):
|
|
97
|
+
raise OSError(
|
|
98
|
+
"Unix sockets are unavailable on this platform; use --transport tcp."
|
|
99
|
+
)
|
|
94
100
|
reader, writer = await asyncio.open_unix_connection(
|
|
95
101
|
str(address.socket.expanduser()), limit=STREAM_LIMIT
|
|
96
102
|
)
|
|
@@ -17,6 +17,7 @@ A leaf: imports nothing from `stcode`, so anything in `core/` may import it.
|
|
|
17
17
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
|
+
import asyncio
|
|
20
21
|
import os
|
|
21
22
|
import re
|
|
22
23
|
from pathlib import Path
|
|
@@ -103,13 +104,16 @@ class DaemonConfig(BaseModel):
|
|
|
103
104
|
"""Where the daemon listens.
|
|
104
105
|
Args:
|
|
105
106
|
transport: `unix` — socket via `socket` path, `tcp` — socket via `host` and `port`.
|
|
107
|
+
Defaults to `tcp` when asyncio has no Unix socket support (Windows).
|
|
106
108
|
socket: Path to the Unix socket file. Ignored if `transport` is `tcp`.
|
|
107
109
|
host: Hostname to bind to. Ignored if `transport` is `unix`.
|
|
108
110
|
port: Port to bind to. Ignored if `transport` is `unix`.
|
|
109
111
|
max_clients: How many clients may be connected at once. 0 = unlimited. Unset =
|
|
110
112
|
1 in a container, unlimited on the host.
|
|
111
113
|
"""
|
|
112
|
-
transport: Literal["unix", "tcp"] =
|
|
114
|
+
transport: Literal["unix", "tcp"] = Field(
|
|
115
|
+
default_factory=lambda: "unix" if hasattr(asyncio, "start_unix_server") else "tcp"
|
|
116
|
+
)
|
|
113
117
|
socket: Path = Field(default_factory=lambda: home_dir() / "daemon.sock")
|
|
114
118
|
host: str = "127.0.0.1"
|
|
115
119
|
port: int = 7717
|
|
@@ -155,6 +155,10 @@ class Daemon:
|
|
|
155
155
|
|
|
156
156
|
settings = self.config.daemon
|
|
157
157
|
if settings.transport == "unix":
|
|
158
|
+
if not hasattr(asyncio, "start_unix_server"):
|
|
159
|
+
raise OSError(
|
|
160
|
+
"Unix sockets are unavailable on this platform; use --transport tcp."
|
|
161
|
+
)
|
|
158
162
|
path = socket_path(settings.socket)
|
|
159
163
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
160
164
|
_clear_stale_socket(path)
|
|
@@ -63,11 +63,11 @@ difficulty = "high"
|
|
|
63
63
|
# dir = "./.stcode/sessions"
|
|
64
64
|
# keep = 100
|
|
65
65
|
|
|
66
|
-
# Where the daemon listens. `unix` on
|
|
67
|
-
# The UI does not read this: it
|
|
68
|
-
#
|
|
66
|
+
# Where the daemon listens. Default: `unix` on Unix, loopback `tcp` on Windows.
|
|
67
|
+
# Use `tcp` inside a container. The UI does not read this: it uses the same platform
|
|
68
|
+
# default unless given --transport / --socket / --host / --port flags.
|
|
69
69
|
[daemon]
|
|
70
|
-
transport = "unix"
|
|
70
|
+
# transport = "unix" # use "tcp" on Windows or inside a container
|
|
71
71
|
# socket = "~/.stcode/daemon.sock"
|
|
72
72
|
# host = "0.0.0.0" # when transport = "tcp"
|
|
73
73
|
# port = 7717
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Platform transport defaults must agree without requiring Unix-only asyncio APIs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
from driving import asynctest
|
|
10
|
+
|
|
11
|
+
from stcode.cli.main import _address
|
|
12
|
+
from stcode.cli.services.client import Address, DaemonClient
|
|
13
|
+
from stcode.core.configs import Config, load_config
|
|
14
|
+
from stcode.core.daemon import Daemon
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.fixture
|
|
18
|
+
def without_unix_sockets(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
19
|
+
"""Match Windows' asyncio surface while still using real TCP sockets."""
|
|
20
|
+
monkeypatch.delattr(asyncio, "open_unix_connection", raising=False)
|
|
21
|
+
monkeypatch.delattr(asyncio, "start_unix_server", raising=False)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.mark.skipif(
|
|
25
|
+
not hasattr(asyncio, "open_unix_connection")
|
|
26
|
+
or not hasattr(asyncio, "start_unix_server"),
|
|
27
|
+
reason="Unix socket APIs are unavailable",
|
|
28
|
+
)
|
|
29
|
+
def test_unix_remains_the_default_where_supported() -> None:
|
|
30
|
+
assert Address().transport == "unix"
|
|
31
|
+
assert _address(None, None, None, None).transport == "unix"
|
|
32
|
+
assert Config().daemon.transport == "unix"
|
|
33
|
+
assert _address("tcp", None, "127.0.0.1", 9000).daemon_args() == [
|
|
34
|
+
"--transport", "tcp", "--host", "127.0.0.1", "--port", "9000",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@asynctest
|
|
39
|
+
async def test_defaults_connect_over_tcp_without_unix_apis(
|
|
40
|
+
tmp_path: Path, without_unix_sockets: None,
|
|
41
|
+
) -> None:
|
|
42
|
+
address = _address(None, None, None, None)
|
|
43
|
+
assert address.transport == Address().transport == "tcp"
|
|
44
|
+
assert str(address) == "127.0.0.1:7717"
|
|
45
|
+
assert address.daemon_args() == [
|
|
46
|
+
"--transport", "tcp", "--host", "127.0.0.1", "--port", "7717",
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
path = tmp_path / "config.toml"
|
|
50
|
+
config = load_config(
|
|
51
|
+
path,
|
|
52
|
+
create_if_missing=True,
|
|
53
|
+
overrides={"session.dir": str(tmp_path / "sessions"), "daemon.port": 0},
|
|
54
|
+
)
|
|
55
|
+
assert config.daemon.transport == "tcp"
|
|
56
|
+
assert load_config(path).daemon.transport == "tcp"
|
|
57
|
+
assert config.daemon.host == address.host
|
|
58
|
+
|
|
59
|
+
async with Daemon(config) as daemon:
|
|
60
|
+
assert daemon._server is not None
|
|
61
|
+
address.port = daemon._server.sockets[0].getsockname()[1]
|
|
62
|
+
async with await DaemonClient.connect(address) as client:
|
|
63
|
+
frame = await client.get_config()
|
|
64
|
+
assert frame["path"] == str(path)
|
|
65
|
+
assert frame["keys_editable"] is False
|
|
66
|
+
assert await client.sessions() == []
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@asynctest
|
|
70
|
+
async def test_explicit_unix_client_reports_how_to_use_tcp(
|
|
71
|
+
tmp_path: Path, without_unix_sockets: None,
|
|
72
|
+
) -> None:
|
|
73
|
+
address = _address("unix", str(tmp_path / "d.sock"), None, None)
|
|
74
|
+
assert address.transport == "unix"
|
|
75
|
+
with pytest.raises(OSError, match="Unix sockets.*--transport tcp"):
|
|
76
|
+
await DaemonClient.connect(address)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@asynctest
|
|
80
|
+
async def test_explicit_unix_daemon_reports_how_to_use_tcp(
|
|
81
|
+
tmp_path: Path, without_unix_sockets: None,
|
|
82
|
+
) -> None:
|
|
83
|
+
config = load_config(
|
|
84
|
+
tmp_path / "config.toml",
|
|
85
|
+
create_if_missing=True,
|
|
86
|
+
overrides={
|
|
87
|
+
"daemon.transport": "unix",
|
|
88
|
+
"daemon.socket": str(tmp_path / "d.sock"),
|
|
89
|
+
"session.dir": str(tmp_path / "sessions"),
|
|
90
|
+
},
|
|
91
|
+
)
|
|
92
|
+
assert config.daemon.transport == "unix"
|
|
93
|
+
with pytest.raises(OSError, match="Unix sockets.*--transport tcp"):
|
|
94
|
+
async with Daemon(config):
|
|
95
|
+
pass
|
|
96
|
+
assert not config.daemon.socket.exists()
|
|
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
|
|
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
|
{stcode-0.0.1a1 → stcode-0.0.1a2}/tests/fixtures/workspace/.agents/skills/db-migrations/SKILL.md
RENAMED
|
File without changes
|
{stcode-0.0.1a1 → stcode-0.0.1a2}/tests/fixtures/workspace/.agents/skills/release-notes/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|