"""Deterministic in-memory ContextForge-compatible target.""" from __future__ import annotations import json from copy import deepcopy from typing import Any from uuid import UUID, uuid4 import httpx class FakeContextForge: """Fake REST gateway for offline adapter and reconciliation tests.""" def __init__(self) -> None: self.gateways: dict[str, dict[str, Any]] = {} self.tools: dict[str, list[dict[str, Any]]] = {} self.prompts: dict[str, list[dict[str, Any]]] = {} self.servers: dict[str, dict[str, Any]] = {} self.requests: list[tuple[str, str, dict[str, str]]] = [] self.fail_next: int | None = None _gateway_tools = { "itworx-stitch": ( "stitch.project.list", "stitch.project.get", "stitch.project.create", "stitch.screen.list", "stitch.screen.get", "stitch.screen.generate", "stitch.screen.edit", "stitch.screen.variants", ), "itworx-knowledge": ( "knowledge.document.search", "knowledge.document.get", "knowledge.source.list", "knowledge.index.status", "knowledge.adr.list", "knowledge.project.summary", ), "itworx-gitea": ( "gitea.capabilities.get", "gitea.connection.check", "gitea.selftest.run", "gitea.repository.list", "gitea.repository.get", "gitea.repository.context", "gitea.repository.health", "gitea.branchprotection.get", "gitea.branchprotection.preview", "gitea.branch.list", "gitea.commit.list", "gitea.commit.get", "gitea.ref.compare", "gitea.release.list", "gitea.issue.list", "gitea.issue.search", "gitea.code.search", "search", "fetch", "gitea.file.read", "gitea.tree.list", "gitea.changeset.preview", "gitea.validation.get", "gitea.validation.failure.get", "gitea.validation.list", "gitea.change.status", "gitea.change.review", "gitea.change.reconcile", "gitea.workspace.status", "gitea.portfolio.health", "gitea.portfolio.governance.preview", "gitea.portfolio.governance.verify", "gitea.orchestration.plan", "gitea.pullrequest.list", "gitea.pullrequest.get", "gitea.pullrequest.feedback", "gitea.commitstatus.list", "gitea.repository.create", "gitea.branchprotection.ensure", "gitea.portfolio.governance.apply", "gitea.branch.create", "gitea.issue.create", "gitea.issue.update", "gitea.issue.comment.create", "gitea.file.create", "gitea.file.update", "gitea.file.delete", "gitea.pullrequest.create", "gitea.pullrequest.update", "gitea.pullrequest.close", "gitea.pullrequest.validation.ensure", "gitea.pullrequest.sync", "gitea.pullrequest.merge", "gitea.branch.delete", "gitea.changeset.apply", "gitea.validation.run", "gitea.workspace.run", "gitea.orchestration.start", ), "itworx-unraid": ( "unraid.host.status", "unraid.storage.list", "unraid.network.list", "unraid.container.list", "unraid.container.inspect", "unraid.container.health", "unraid.container.logs", "unraid.project.summary", ), "itworx-mobilityops": ( "mobilityops.operations.summary", "mobilityops.attention.list", "mobilityops.vehicle.get", "mobilityops.knowledge.search", ), "itworx-projectbrain": ( "projectbrain.project.list", "projectbrain.repository.list", "projectbrain.portfolio.get", "projectbrain.portfolio.repository.list", "projectbrain.portfolio.repository.reconcile", "projectbrain.portfolio.repository.exclusion.set", "projectbrain.repository.search", "projectbrain.repository.fetch", "projectbrain.context.get", "projectbrain.workspace.render", "projectbrain.modules.get", "projectbrain.brainstorm.save", "projectbrain.knowledge.save", "projectbrain.documentation.save", "projectbrain.memory.save", "projectbrain.contextpack.create", "projectbrain.feedback.record", "projectbrain.delivery.list", "projectbrain.delivery.get", "projectbrain.delivery.create", "projectbrain.delivery.update", "projectbrain.repository.tree", "projectbrain.repository.file", "projectbrain.repository.branches", "projectbrain.repository.pullrequests", "projectbrain.delivery.start", "projectbrain.delivery.file.write", "projectbrain.delivery.file.delete", "projectbrain.delivery.changeset.apply", "projectbrain.delivery.validation.record", "projectbrain.delivery.validation.runs", "projectbrain.delivery.validation.sync", "projectbrain.delivery.complete", "projectbrain.delivery.merge", "projectbrain.analysis.start", "projectbrain.analysis.list", "projectbrain.analysis.status", "projectbrain.analysis.results", "projectbrain.analysis.cancel", "projectbrain.analysis.render", "projectbrain.finding.list", "projectbrain.finding.get", "projectbrain.finding.issue.sync", "projectbrain.finding.investigation.start", "projectbrain.finding.investigation.get", "projectbrain.finding.investigation.save", "projectbrain.finding.suppress", "projectbrain.finding.delivery.create", "projectbrain.finding.verify", "projectbrain.autonomy.projects.list", "projectbrain.autonomy.executor.heartbeat", "projectbrain.autonomy.state.get", "projectbrain.autonomy.configure", "projectbrain.autonomy.roadmap.import", "projectbrain.autonomy.resume", "projectbrain.autonomy.work.claim", "projectbrain.autonomy.work.report", ), "itworx-appops": ( "server.summary.get", "server.application.list", "server.container.list", "server.safety.findings", "server.action.list", "server.operation.preview", "server.audit.search", "server.knowledge.query", "server.operation.request", "server.operation.status", "server.operation.execute", "server.job.status", "server.job.cancel", "server.ci.runners.status", "server.project.list", "server.project.reconcile", "server.deployment.preflight", "server.deployment.capabilities", "server.deployment.enrollment.preflight", "server.deployment.target.enroll", "server.deployment.enrollment.status", "server.deployment.apply", "server.deployment.status", "server.deployment.verify", "server.deployment.rollback", ), "itworx-opencode": ( "opencode.capabilities.get", "opencode.models.list", "opencode.workspace.ensure", "opencode.job.preview", "opencode.job.start", "opencode.jobs.list", "opencode.job.status", "opencode.job.result", "opencode.job.cancel", "opencode.delivery.control.repositories.list", "opencode.delivery.control.plan", "opencode.delivery.control.start", "opencode.delivery.control.jobs.list", "opencode.delivery.control.status", "opencode.delivery.control.result", "opencode.delivery.control.retry", "opencode.delivery.control.cancel", ), "itworx-opencode-readonly": ( "opencode.analysis.capabilities.get", "opencode.analysis.models.list", "opencode.analysis.start", "opencode.analysis.jobs.list", "opencode.analysis.status", "opencode.analysis.result", "opencode.delivery.repositories.list", "opencode.delivery.jobs.list", "opencode.delivery.status", "opencode.delivery.result", ), } _gateway_prompts = { "itworx-knowledge": ("project-context-pack", "pre-change-impact-review"), "itworx-gitea": ( "repository-engineer", "repository-change-review", "repository-maintenance", "portfolio-engineer", "release-readiness", ), "itworx-unraid": ("production-incident-triage", "deployment-health-review"), "itworx-mobilityops": ("fleet-operations-morning-brief",), } @staticmethod def _tool_payload(tool_name: str) -> dict[str, Any]: payload: dict[str, Any] = {"id": str(uuid4()), "name": tool_name} if tool_name == "gitea.repository.create": payload["inputSchema"] = { "type": "object", "properties": { field: {"type": field_type} for field, field_type in ( ("name", "string"), ("description", "string"), ("private", "boolean"), ("autoInit", "boolean"), ("defaultBranch", "string"), ) }, "required": ["name", "description", "private", "autoInit", "defaultBranch"], } payload["inputSchema"]["properties"]["autoInit"]["const"] = True return payload def transport(self) -> httpx.MockTransport: return httpx.MockTransport(self._handle) def _response(self, request: httpx.Request, status: int, payload: Any) -> httpx.Response: return httpx.Response(status, json=payload, request=request) def _handle(self, request: httpx.Request) -> httpx.Response: self.requests.append( ( request.method, request.url.path, { key: "" if key.lower() == "authorization" else value for key, value in request.headers.items() }, ) ) if self.fail_next is not None: status = self.fail_next self.fail_next = None return self._response(request, status, {"error": "simulated"}) path = request.url.path if path.startswith("/v1/"): path = path[3:] if request.method == "GET" and path == "/health": return self._response(request, 200, {"status": "healthy", "version": "1.0.6"}) if request.method == "GET" and path == "/ready": return self._response(request, 200, {"status": "ready"}) if request.method == "GET" and path == "/version": return self._response(request, 200, {"version": "1.0.6"}) if path == "/gateways": if request.method == "GET": return self._response( request, 200, {"gateways": list(self.gateways.values()), "nextCursor": None} ) if request.method == "POST": payload = json.loads(request.content) name = str(payload["name"]) if any(item["name"] == name for item in self.gateways.values()): return self._response(request, 409, {"error": "conflict"}) gateway = { "id": f"gateway-{uuid4().hex}", "name": name, "url": payload["url"], "description": payload.get("description", ""), "transport": payload.get("transport", "STREAMABLEHTTP"), "enabled": payload.get("enabled", True), "status": "active", "passthrough_headers": payload.get("passthrough_headers", []), } self.gateways[gateway["id"]] = gateway self.tools[gateway["id"]] = [ self._tool_payload(tool_name) for tool_name in self._gateway_tools.get(name, ()) ] self.prompts[gateway["id"]] = [ {"id": str(uuid4()), "name": prompt_name} for prompt_name in self._gateway_prompts.get(name, ()) ] return self._response(request, 201, deepcopy(gateway)) if path == "/tools" and request.method == "GET": gateway_id = request.url.params.get("gateway_id", "") return self._response(request, 200, deepcopy(self.tools.get(gateway_id, []))) if path == "/prompts" and request.method == "GET": gateway_id = request.url.params.get("gateway_id", "") return self._response(request, 200, deepcopy(self.prompts.get(gateway_id, []))) if path == "/servers": if request.method == "GET": return self._response(request, 200, deepcopy(list(self.servers.values()))) if request.method == "POST": payload = json.loads(request.content) payload = payload.get("server", payload) external_id = UUID(str(payload["id"])).hex if payload.get("id") else uuid4().hex server = { "id": external_id, "name": payload["name"], "associated_tool_ids": payload.get("associated_tools", []), "associated_prompt_ids": payload.get("associated_prompts", []), "enabled": True, "visibility": payload.get("visibility", "private"), } self.servers[external_id] = server return self._response(request, 201, deepcopy(server)) parts = path.strip("/").split("/") if len(parts) == 2 and parts[0] == "tools" and request.method == "PUT": payload = json.loads(request.content) for gateway_tools in self.tools.values(): tool = next((item for item in gateway_tools if item.get("id") == parts[1]), None) if tool is not None: if "customName" in payload: tool["customName"] = payload["customName"] return self._response(request, 200, deepcopy(tool)) return self._response(request, 404, {"error": "not_found"}) if len(parts) >= 2 and parts[0] == "gateways": stored = self.gateways.get(parts[1]) if stored is None: return self._response(request, 404, {"error": "not_found"}) if len(parts) == 3 and parts[2] == "test" and request.method == "GET": return self._response(request, 200, {"reachable": True, "status": "healthy"}) if len(parts) == 4 and parts[2:] == ["tools", "refresh"] and request.method == "POST": old_tools = len(self.tools.get(parts[1], [])) old_prompts = len(self.prompts.get(parts[1], [])) self.tools[parts[1]] = [ self._tool_payload(tool_name) for tool_name in self._gateway_tools.get(str(stored["name"]), ()) ] self.prompts[parts[1]] = [ {"id": str(uuid4()), "name": prompt_name} for prompt_name in self._gateway_prompts.get(str(stored["name"]), ()) ] return self._response( request, 200, { "success": True, "tools_added": max(0, len(self.tools[parts[1]]) - old_tools), "tools_removed": 0, "prompts_added": max(0, len(self.prompts[parts[1]]) - old_prompts), "prompts_removed": 0, }, ) if request.method == "GET": return self._response(request, 200, deepcopy(stored)) if request.method == "PUT": payload = json.loads(request.content) stored.update( { "name": payload["name"], "url": payload["url"], "description": payload.get("description", ""), "transport": payload.get("transport", "STREAMABLEHTTP"), "enabled": payload.get("enabled", True), "status": "active" if payload.get("enabled", True) else "inactive", "passthrough_headers": payload.get("passthrough_headers", []), } ) return self._response(request, 200, deepcopy(stored)) if request.method == "DELETE": del self.gateways[parts[1]] return self._response(request, 204, {}) if len(parts) == 2 and parts[0] == "servers": stored_server = self.servers.get(parts[1]) if stored_server is None: return self._response(request, 404, {"error": "not_found"}) if request.method == "PUT": payload = json.loads(request.content) stored_server.update( { "name": payload["name"], "associated_tool_ids": payload.get("associated_tools", []), "associated_prompt_ids": payload.get("associated_prompts", []), "visibility": payload.get("visibility", "private"), } ) return self._response(request, 200, deepcopy(stored_server)) return self._response(request, 404, {"error": "not_found"})