ocode 0.1.0__py3-none-any.whl

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.
ocode/__init__.py ADDED
@@ -0,0 +1,26 @@
1
+ from .agent import Agent
2
+ from .client import OpenCodeClient
3
+ from .config import OpenCodeConfig
4
+ from .exceptions import (
5
+ MCPReadinessError,
6
+ OpenCodeError,
7
+ OpenCodeHTTPError,
8
+ SessionNotFoundError,
9
+ TaskTimeoutError,
10
+ ToolNotFoundError,
11
+ )
12
+ from .orchestrator import MultiAgentRunner, SingleAgentRunner
13
+
14
+ __all__ = [
15
+ "Agent",
16
+ "MCPReadinessError",
17
+ "MultiAgentRunner",
18
+ "OpenCodeClient",
19
+ "OpenCodeConfig",
20
+ "OpenCodeError",
21
+ "OpenCodeHTTPError",
22
+ "SessionNotFoundError",
23
+ "SingleAgentRunner",
24
+ "TaskTimeoutError",
25
+ "ToolNotFoundError",
26
+ ]
ocode/agent.py ADDED
@@ -0,0 +1,137 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import asdict, is_dataclass
4
+ from typing import Any
5
+
6
+ from .client import OpenCodeClient
7
+ from .config import OpenCodeConfig
8
+ from .managers import MCPManager, SessionManager, ToolManager
9
+ from .models import ExecutionMetadata, ExecutionResult, StreamEvent
10
+ from .orchestrator import EventStreamer, SingleAgentRunner
11
+
12
+
13
+ class Agent:
14
+ def __init__(
15
+ self,
16
+ name: str = "OpenCodeAgent",
17
+ system_prompt: str | None = None,
18
+ *,
19
+ agent: str | None = None,
20
+ model: str | dict[str, Any] | None = None,
21
+ tools: list[str] | None = None,
22
+ mcp_servers: list[str] | None = None,
23
+ project_path: str | None = None,
24
+ base_url: str | None = None,
25
+ streaming: bool | None = None,
26
+ verbose: bool | None = None,
27
+ ):
28
+ config = OpenCodeConfig.from_env()
29
+ if project_path is not None:
30
+ config.project_path = project_path
31
+ if base_url is not None:
32
+ config.base_url = base_url
33
+ if streaming is not None:
34
+ config.streaming = streaming
35
+ if verbose is not None:
36
+ config.verbose = verbose
37
+ if model is not None:
38
+ config.default_model = model
39
+ if agent is not None:
40
+ config.default_agent = agent
41
+ if tools is not None:
42
+ config.default_tools = tools
43
+
44
+ self.name = name
45
+ self.system_prompt = system_prompt
46
+ self.default_mcp_servers = mcp_servers or []
47
+ self.config = config
48
+ self.client = OpenCodeClient(config=config)
49
+ self.session_manager = SessionManager(self.client)
50
+ self.tool_manager = ToolManager(self.client)
51
+ self.mcp_manager = MCPManager(self.client)
52
+ self.runner = SingleAgentRunner(
53
+ client=self.client,
54
+ config=self.config,
55
+ session_manager=self.session_manager,
56
+ tool_manager=self.tool_manager,
57
+ mcp_manager=self.mcp_manager,
58
+ )
59
+ self.event_streamer = EventStreamer(self.client)
60
+ self.session_id: str | None = None
61
+
62
+ def close(self) -> None:
63
+ self.client.close()
64
+
65
+ def set_model(self, model: str | dict[str, Any] | None) -> None:
66
+ self.config.default_model = model
67
+
68
+ def list_models(self, include_details: bool = False) -> list[Any]:
69
+ return self.client.list_available_models(include_details=include_details)
70
+
71
+ def run(
72
+ self,
73
+ user_prompt: str,
74
+ *,
75
+ agent: str | None = None,
76
+ model: str | dict[str, Any] | None = None,
77
+ tools: list[str] | None = None,
78
+ mcp_servers: list[str] | None = None,
79
+ session_id: str | None = None,
80
+ return_result: bool = False,
81
+ ) -> str | ExecutionResult:
82
+ result = self.runner.run(
83
+ user_prompt,
84
+ agent=agent,
85
+ model=model,
86
+ tools=tools,
87
+ mcp_servers=mcp_servers or self.default_mcp_servers or None,
88
+ session_id=session_id or self.session_id,
89
+ title=self.name,
90
+ system=self.system_prompt,
91
+ )
92
+ self.session_id = result.metadata.session_id
93
+ return result if return_result else result.output
94
+
95
+ def run_async(
96
+ self,
97
+ user_prompt: str,
98
+ *,
99
+ agent: str | None = None,
100
+ model: str | dict[str, Any] | None = None,
101
+ tools: list[str] | None = None,
102
+ session_id: str | None = None,
103
+ ) -> ExecutionMetadata:
104
+ metadata = self.runner.run_async(
105
+ user_prompt,
106
+ agent=agent,
107
+ model=model,
108
+ tools=tools,
109
+ session_id=session_id or self.session_id,
110
+ title=self.name,
111
+ )
112
+ self.session_id = metadata.session_id
113
+ return metadata
114
+
115
+ def run_shell(
116
+ self,
117
+ command: str,
118
+ *,
119
+ agent: str | None = None,
120
+ model: str | dict[str, Any] | None = None,
121
+ return_result: bool = False,
122
+ ) -> str | ExecutionResult:
123
+ if not self.session_id:
124
+ self.session_id = self.client.create_session(title=self.name).id
125
+ result = self.runner.run_shell(command, session_id=self.session_id, agent=agent, model=model)
126
+ return result if return_result else result.output
127
+
128
+ def stream(self, *, limit: int = 10) -> list[StreamEvent]:
129
+ return self.event_streamer.stream(limit=limit)
130
+
131
+ @staticmethod
132
+ def to_dict(value: Any) -> Any:
133
+ if isinstance(value, list):
134
+ return [Agent.to_dict(item) for item in value]
135
+ if is_dataclass(value):
136
+ return asdict(value)
137
+ return value
ocode/cli.py ADDED
@@ -0,0 +1,208 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ from dataclasses import asdict, is_dataclass
5
+ import json
6
+ from typing import Any
7
+
8
+ from .client import OpenCodeClient
9
+ from .config import OpenCodeConfig
10
+ from .logging import configure_logging
11
+ from .managers import MCPManager, SessionManager, ToolManager
12
+ from .orchestrator import EventStreamer, MultiAgentRunner, SingleAgentRunner
13
+
14
+
15
+ def _build_runtime(config: OpenCodeConfig) -> dict[str, Any]:
16
+ client = OpenCodeClient(config)
17
+ session_manager = SessionManager(client)
18
+ tool_manager = ToolManager(client)
19
+ mcp_manager = MCPManager(client)
20
+ return {
21
+ "client": client,
22
+ "single": SingleAgentRunner(client=client, config=config, session_manager=session_manager, tool_manager=tool_manager, mcp_manager=mcp_manager),
23
+ "multi": MultiAgentRunner(client=client, config=config, session_manager=session_manager, tool_manager=tool_manager, mcp_manager=mcp_manager),
24
+ "session_manager": session_manager,
25
+ "tool_manager": tool_manager,
26
+ "mcp_manager": mcp_manager,
27
+ "event_streamer": EventStreamer(client),
28
+ }
29
+
30
+
31
+ def _to_json(payload: Any) -> str:
32
+ def _default(obj: Any) -> Any:
33
+ if is_dataclass(obj):
34
+ return asdict(obj)
35
+ return str(obj)
36
+ return json.dumps(payload, indent=2, default=_default)
37
+
38
+
39
+ def _print_json(payload: Any) -> None:
40
+ print(_to_json(payload))
41
+
42
+
43
+ def build_parser() -> argparse.ArgumentParser:
44
+ parser = argparse.ArgumentParser(
45
+ prog="opencode-agent",
46
+ description="Python orchestration CLI for the OpenCode Agent API.",
47
+ )
48
+ parser.add_argument("--base-url", help="Override OpenCode base URL")
49
+ parser.add_argument("--project-path", help="Override OpenCode project path")
50
+ parser.add_argument("--verbose", action="store_true", help="Enable verbose logging")
51
+ parser.add_argument("--json", dest="output_json", action="store_true", help="Output raw JSON instead of plain text")
52
+ subparsers = parser.add_subparsers(dest="command", required=True)
53
+
54
+ run_parser = subparsers.add_parser("run", help="Run a single-agent task")
55
+ run_parser.add_argument("prompt")
56
+ run_parser.add_argument("--agent")
57
+ run_parser.add_argument("--model")
58
+ run_parser.add_argument("--session-id")
59
+ run_parser.add_argument("--title")
60
+ run_parser.add_argument("--tool", action="append", default=[])
61
+ run_parser.add_argument("--mcp", action="append", default=[])
62
+ run_parser.add_argument("--stream", action="store_true")
63
+ run_parser.add_argument("--async", dest="async_mode", action="store_true")
64
+
65
+ orchestration_parser = subparsers.add_parser("orchestrate", help="Run a multi-agent task")
66
+ orchestration_parser.add_argument("prompt")
67
+ orchestration_parser.add_argument("--strategy", default="plan-explore-build-review")
68
+ orchestration_parser.add_argument("--session-id")
69
+ orchestration_parser.add_argument("--title")
70
+ orchestration_parser.add_argument("--tool", action="append", default=[])
71
+ orchestration_parser.add_argument("--mcp", action="append", default=[])
72
+ orchestration_parser.add_argument("--parallel", action="store_true")
73
+
74
+ sessions_parser = subparsers.add_parser("sessions", help="Inspect sessions")
75
+ sessions_subparsers = sessions_parser.add_subparsers(dest="sessions_command", required=True)
76
+ sessions_subparsers.add_parser("list", help="List sessions")
77
+ status_parser = sessions_subparsers.add_parser("status", help="Show session status")
78
+ status_parser.add_argument("session_id", nargs="?")
79
+ abort_parser = sessions_subparsers.add_parser("abort", help="Abort a session")
80
+ abort_parser.add_argument("session_id")
81
+
82
+ tool_parser = subparsers.add_parser("tools", help="Inspect tools")
83
+ tool_parser.add_argument("--provider")
84
+ tool_parser.add_argument("--model")
85
+ tool_parser.add_argument("--ids", action="store_true")
86
+
87
+ mcp_parser = subparsers.add_parser("mcp", help="Inspect or register MCP servers")
88
+ mcp_subparsers = mcp_parser.add_subparsers(dest="mcp_command", required=True)
89
+ mcp_subparsers.add_parser("list", help="List MCP servers")
90
+ register_parser = mcp_subparsers.add_parser("register", help="Register an MCP server")
91
+ register_parser.add_argument("name")
92
+ register_parser.add_argument("config_json")
93
+
94
+ shell_parser = subparsers.add_parser("shell", help="Run a shell command inside a session")
95
+ shell_parser.add_argument("session_id")
96
+ shell_parser.add_argument("command_text")
97
+ shell_parser.add_argument("--agent")
98
+ shell_parser.add_argument("--model")
99
+
100
+ stream_parser = subparsers.add_parser("stream", help="Read SSE events")
101
+ stream_parser.add_argument("--limit", type=int, default=10)
102
+
103
+ return parser
104
+
105
+
106
+ def main(argv: list[str] | None = None) -> int:
107
+ parser = build_parser()
108
+ args = parser.parse_args(argv)
109
+ config = OpenCodeConfig.from_env()
110
+ if args.base_url:
111
+ config.base_url = args.base_url
112
+ if args.project_path:
113
+ config.project_path = args.project_path
114
+ if args.verbose:
115
+ config.verbose = True
116
+ configure_logging(config.verbose)
117
+ runtime = _build_runtime(config)
118
+ client: OpenCodeClient = runtime["client"]
119
+ output_json: bool = args.output_json
120
+ try:
121
+ if args.command == "run":
122
+ if args.async_mode:
123
+ result = runtime["single"].run_async(
124
+ args.prompt,
125
+ agent=args.agent,
126
+ model=args.model,
127
+ session_id=args.session_id,
128
+ title=args.title,
129
+ tools=args.tool or None,
130
+ )
131
+ # async just returns metadata — always show as JSON
132
+ _print_json(result)
133
+ return 0
134
+ result = runtime["single"].run(
135
+ args.prompt,
136
+ agent=args.agent,
137
+ model=args.model,
138
+ session_id=args.session_id,
139
+ title=args.title,
140
+ tools=args.tool or None,
141
+ mcp_servers=args.mcp or None,
142
+ streaming=args.stream,
143
+ )
144
+ if output_json:
145
+ _print_json({"output": result.output, "metadata": result.metadata})
146
+ else:
147
+ print(result.output)
148
+ return 0
149
+
150
+ if args.command == "orchestrate":
151
+ result = runtime["multi"].run(
152
+ args.prompt,
153
+ strategy=args.strategy,
154
+ session_id=args.session_id,
155
+ title=args.title,
156
+ tools=args.tool or None,
157
+ mcp_servers=args.mcp or None,
158
+ parallel=args.parallel,
159
+ )
160
+ if output_json:
161
+ _print_json(result)
162
+ else:
163
+ print(result.output)
164
+ return 0
165
+
166
+ if args.command == "sessions":
167
+ if args.sessions_command == "list":
168
+ _print_json(client.list_sessions())
169
+ return 0
170
+ if args.sessions_command == "status":
171
+ status = client.get_session_status()
172
+ if args.session_id:
173
+ _print_json(status.get(args.session_id))
174
+ else:
175
+ _print_json(status)
176
+ return 0
177
+ if args.sessions_command == "abort":
178
+ client.abort_session(args.session_id)
179
+ print(f"aborted {args.session_id}")
180
+ return 0
181
+
182
+ if args.command == "tools":
183
+ _print_json(client.list_tool_ids() if args.ids else client.list_tools(provider=args.provider, model=args.model))
184
+ return 0
185
+
186
+ if args.command == "mcp":
187
+ if args.mcp_command == "list":
188
+ _print_json(runtime["mcp_manager"].list_servers())
189
+ return 0
190
+ if args.mcp_command == "register":
191
+ _print_json(runtime["mcp_manager"].register(args.name, json.loads(args.config_json)))
192
+ return 0
193
+
194
+ if args.command == "shell":
195
+ result = runtime["single"].run_shell(args.command_text, session_id=args.session_id, agent=args.agent, model=args.model)
196
+ if output_json:
197
+ _print_json(result)
198
+ else:
199
+ print(result.output)
200
+ return 0
201
+
202
+ if args.command == "stream":
203
+ _print_json(runtime["event_streamer"].stream(limit=args.limit))
204
+ return 0
205
+
206
+ return 1
207
+ finally:
208
+ client.close()