Initial agentic-os GitOps scaffold; Argo sources point at Gitea
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
commit
d0f2492e3f
|
|
@ -0,0 +1,24 @@
|
|||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
.venv/
|
||||
venv/
|
||||
*.egg-info/
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Node
|
||||
node_modules/
|
||||
.next/
|
||||
out/
|
||||
|
||||
# IDE / OS
|
||||
.idea/
|
||||
.vscode/
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Secrets (never commit)
|
||||
*.pem
|
||||
.env
|
||||
.env.*
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
FROM python:3.11-slim
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 PIP_NO_CACHE_DIR=1
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends curl ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
WORKDIR /opt/agent
|
||||
RUN pip install --no-cache-dir \
|
||||
"langgraph>=0.2.0" \
|
||||
"langchain-core>=0.3.0" \
|
||||
"langchain-openai>=0.2.0" \
|
||||
"litellm>=1.50.0" \
|
||||
"mcp[cli]>=1.2.0" \
|
||||
"psycopg[binary]>=3.2.0" \
|
||||
"langgraph-checkpoint-postgres>=2.0.0"
|
||||
COPY README.agent.txt /opt/agent/README.agent.txt
|
||||
|
|
@ -0,0 +1 @@
|
|||
Agent base image: Python 3.11 with LangGraph, LangChain MCP adapters, LiteLLM client libraries, and Postgres drivers for AsyncPostgresSaver checkpointers.
|
||||
|
|
@ -0,0 +1 @@
|
|||
"""Bernard dev/PR agent (scaffold)."""
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
def main() -> None:
|
||||
raise SystemExit("Bernard agent is not implemented yet.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
FROM python:3.11-slim
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 PIP_NO_CACHE_DIR=1 PYTHONPATH=/app
|
||||
WORKDIR /app
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
COPY requirements.txt /app/requirements.txt
|
||||
RUN pip install --no-cache-dir -r /app/requirements.txt
|
||||
COPY gumbo /app/gumbo
|
||||
CMD ["python", "-m", "gumbo.run"]
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
FROM python:3.11-slim
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 PIP_NO_CACHE_DIR=1 PYTHONPATH=/app
|
||||
WORKDIR /app
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
COPY requirements.txt /app/requirements.txt
|
||||
RUN pip install --no-cache-dir -r /app/requirements.txt
|
||||
COPY gumbo /app/gumbo
|
||||
CMD ["python", "-m", "gumbo.temporal.worker"]
|
||||
|
|
@ -0,0 +1 @@
|
|||
"""Gumbo summarization agent (LangGraph + Postgres checkpointer)."""
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
"""LangGraph definition for Gumbo (load from MCP FS -> summarize via LiteLLM)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import TypedDict
|
||||
|
||||
from langchain_core.messages import HumanMessage, SystemMessage
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
|
||||
from langgraph.graph import END, StateGraph
|
||||
|
||||
from gumbo.mcp_fs import fetch_text_via_mcp
|
||||
|
||||
|
||||
class GumboState(TypedDict):
|
||||
object_key: str
|
||||
source_text: str
|
||||
summary: str
|
||||
|
||||
|
||||
async def _load(state: GumboState) -> dict:
|
||||
text = await fetch_text_via_mcp(state["object_key"])
|
||||
return {"source_text": text}
|
||||
|
||||
|
||||
async def _summarize(state: GumboState) -> dict:
|
||||
llm = ChatOpenAI(
|
||||
model=os.environ.get("GUMBO_LLM_MODEL", "ollama-qwen"),
|
||||
api_key=os.environ["LITELLM_API_KEY"],
|
||||
base_url=os.environ["LITELLM_BASE_URL"],
|
||||
temperature=0.2,
|
||||
)
|
||||
system = SystemMessage(
|
||||
content="You are Gumbo, a precise documentation summarizer. Return a concise markdown summary."
|
||||
)
|
||||
user = HumanMessage(
|
||||
content=f"Summarize the following document:\n\n{state['source_text']}",
|
||||
)
|
||||
resp = await llm.ainvoke([system, user])
|
||||
summary = resp.content if isinstance(resp.content, str) else str(resp.content)
|
||||
return {"summary": summary}
|
||||
|
||||
|
||||
def build_graph_builder() -> StateGraph:
|
||||
builder = StateGraph(GumboState)
|
||||
builder.add_node("load", _load)
|
||||
builder.add_node("summarize", _summarize)
|
||||
builder.set_entry_point("load")
|
||||
builder.add_edge("load", "summarize")
|
||||
builder.add_edge("summarize", END)
|
||||
return builder
|
||||
|
||||
|
||||
async def run_gumbo(object_key: str, thread_id: str, conn_string: str) -> GumboState:
|
||||
builder = build_graph_builder()
|
||||
async with AsyncPostgresSaver.from_conn_string(conn_string) as checkpointer:
|
||||
await checkpointer.setup()
|
||||
graph = builder.compile(checkpointer=checkpointer)
|
||||
return await graph.ainvoke(
|
||||
{"object_key": object_key, "source_text": "", "summary": ""},
|
||||
config={"configurable": {"thread_id": thread_id}},
|
||||
)
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
"""Human-in-the-loop gate for high-risk tool names (Temporal approval pattern).
|
||||
|
||||
Tool names matching ``(push|apply|send|delete)`` should not execute inside the agent
|
||||
process without an explicit Temporal resume signal. In LangGraph, prefer ``interrupt``
|
||||
from ``langgraph.types`` so the checkpoint captures the pending decision; the Temporal
|
||||
worker should treat an ``interrupt`` payload as a workflow wait + external approval API.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
_DANGEROUS = re.compile(r"(push|apply|send|delete)", re.IGNORECASE)
|
||||
|
||||
|
||||
def tool_name_requires_temporal_approval(tool_name: str) -> bool:
|
||||
return bool(_DANGEROUS.search(tool_name))
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
"""Async MCP client helpers for the filesystem (MinIO) server."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
from mcp import ClientSession
|
||||
from mcp.client.sse import sse_client
|
||||
from mcp.types import TextContent
|
||||
|
||||
|
||||
async def fetch_text_via_mcp(object_key: str) -> str:
|
||||
url = os.environ["MCP_FS_SSE_URL"]
|
||||
async with sse_client(url) as (read_stream, write_stream):
|
||||
async with ClientSession(read_stream, write_stream) as session:
|
||||
await session.initialize()
|
||||
result = await session.call_tool("get_object_text", {"key": object_key})
|
||||
if result.isError:
|
||||
raise RuntimeError(result.content)
|
||||
parts: list[str] = []
|
||||
for block in result.content:
|
||||
if isinstance(block, TextContent):
|
||||
parts.append(block.text)
|
||||
return "".join(parts)
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
"""Job entrypoint: run the Gumbo LangGraph once and print the summary to stdout."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
from gumbo.graph import run_gumbo
|
||||
|
||||
|
||||
async def _main() -> int:
|
||||
object_key = os.environ["GUMBO_OBJECT_KEY"]
|
||||
thread_id = os.environ.get("GUMBO_THREAD_ID", "gumbo-default-thread")
|
||||
conn = os.environ["LANGGRAPH_CHECKPOINT_URI"]
|
||||
|
||||
result = await run_gumbo(object_key, thread_id, conn)
|
||||
payload = {"summary": result["summary"], "object_key": object_key, "thread_id": thread_id}
|
||||
sys.stdout.write(json.dumps(payload))
|
||||
sys.stdout.flush()
|
||||
return 0
|
||||
|
||||
|
||||
def main() -> None:
|
||||
raise SystemExit(asyncio.run(_main()))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -0,0 +1 @@
|
|||
"""Temporal workflow/activity entrypoints for Gumbo."""
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
import psycopg
|
||||
from temporalio import activity
|
||||
|
||||
|
||||
@activity.defn
|
||||
async def run_gumbo_langgraph_job(params: dict[str, Any]) -> dict[str, Any]:
|
||||
from gumbo.graph import run_gumbo
|
||||
|
||||
conn = os.environ["LANGGRAPH_CHECKPOINT_URI"]
|
||||
state = await run_gumbo(params["object_key"], params["thread_id"], conn)
|
||||
return {"summary": state["summary"], "object_key": params["object_key"], "thread_id": params["thread_id"]}
|
||||
|
||||
|
||||
@activity.defn
|
||||
async def persist_gumbo_summary(payload: dict[str, Any]) -> None:
|
||||
dsn = os.environ["GUMBO_RESULTS_DSN"]
|
||||
conn = await psycopg.AsyncConnection.connect(dsn)
|
||||
try:
|
||||
async with conn.cursor() as cur:
|
||||
await cur.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS gumbo_summaries (
|
||||
id bigserial PRIMARY KEY,
|
||||
workflow_id text NOT NULL,
|
||||
object_key text NOT NULL,
|
||||
summary text NOT NULL,
|
||||
created_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
"""
|
||||
)
|
||||
await cur.execute(
|
||||
"""
|
||||
INSERT INTO gumbo_summaries (workflow_id, object_key, summary)
|
||||
VALUES (%s, %s, %s);
|
||||
""",
|
||||
(payload["workflow_id"], payload["object_key"], payload["summary"]),
|
||||
)
|
||||
await conn.commit()
|
||||
finally:
|
||||
await conn.close()
|
||||
|
||||
|
||||
@activity.defn
|
||||
async def parse_gumbo_job_logs(job_log_bytes: bytes) -> dict[str, Any]:
|
||||
return json.loads(job_log_bytes.decode("utf-8"))
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
from temporalio.client import Client
|
||||
from temporalio.worker import Worker
|
||||
|
||||
from gumbo.temporal import activities
|
||||
from gumbo.temporal.workflows import GumboApprovalGateWorkflow, GumboSummarizeWorkflow
|
||||
|
||||
|
||||
async def _main() -> None:
|
||||
address = os.environ.get("TEMPORAL_ADDRESS", "temporal-frontend.ai-core.svc.cluster.local:7233")
|
||||
namespace = os.environ.get("TEMPORAL_NAMESPACE", "default")
|
||||
task_queue = os.environ.get("GUMBO_TASK_QUEUE", "gumbo")
|
||||
|
||||
client = await Client.connect(address, namespace=namespace)
|
||||
worker = Worker(
|
||||
client,
|
||||
task_queue=task_queue,
|
||||
workflows=[GumboSummarizeWorkflow, GumboApprovalGateWorkflow],
|
||||
activities=[
|
||||
activities.run_gumbo_langgraph_job,
|
||||
activities.persist_gumbo_summary,
|
||||
activities.parse_gumbo_job_logs,
|
||||
],
|
||||
)
|
||||
await worker.run()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
asyncio.run(_main())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from temporalio import workflow
|
||||
|
||||
with workflow.unsafe.imports_passed_through():
|
||||
from gumbo.temporal.activities import persist_gumbo_summary, run_gumbo_langgraph_job
|
||||
|
||||
|
||||
@workflow.defn
|
||||
class GumboSummarizeWorkflow:
|
||||
"""Orchestrates Gumbo: LangGraph run (MCP + LiteLLM + Postgres checkpoints) then durable write."""
|
||||
|
||||
@workflow.run
|
||||
async def run(self, object_key: str) -> str:
|
||||
wf_id = workflow.info().workflow_id
|
||||
params = {"object_key": object_key, "thread_id": wf_id}
|
||||
out = await workflow.execute_activity(
|
||||
run_gumbo_langgraph_job,
|
||||
params,
|
||||
start_to_close_timeout=timedelta(minutes=30),
|
||||
)
|
||||
await workflow.execute_activity(
|
||||
persist_gumbo_summary,
|
||||
{
|
||||
"workflow_id": wf_id,
|
||||
"object_key": out["object_key"],
|
||||
"summary": out["summary"],
|
||||
},
|
||||
start_to_close_timeout=timedelta(minutes=5),
|
||||
)
|
||||
return out["summary"]
|
||||
|
||||
|
||||
@workflow.defn
|
||||
class GumboApprovalGateWorkflow:
|
||||
"""Example pattern: wait for an external approval signal before continuing."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._approved = False
|
||||
|
||||
@workflow.signal
|
||||
def approve(self) -> None:
|
||||
self._approved = True
|
||||
|
||||
@workflow.run
|
||||
async def run(self) -> str:
|
||||
await workflow.wait_condition(lambda: self._approved)
|
||||
return "approved"
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
langgraph>=0.2.0
|
||||
langgraph-checkpoint-postgres>=2.0.0
|
||||
langchain-core>=0.3.0
|
||||
langchain-openai>=0.2.0
|
||||
mcp[cli]>=1.2.0
|
||||
psycopg[binary]>=3.2.0
|
||||
temporalio>=1.8.0
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
# Template for a Gumbo Kubernetes Job. Replace REPLACE_IMAGE after building agents/gumbo.
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: gumbo-run
|
||||
namespace: ai-agents-gumbo
|
||||
spec:
|
||||
suspend: true
|
||||
ttlSecondsAfterFinished: 86400
|
||||
backoffLimit: 0
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: gumbo-job
|
||||
spec:
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: gumbo
|
||||
image: REPLACE_IMAGE
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: GUMBO_OBJECT_KEY
|
||||
value: "REPLACE_OBJECT_KEY"
|
||||
- name: GUMBO_THREAD_ID
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.uid
|
||||
- name: MCP_FS_SSE_URL
|
||||
value: "http://mcp-filesystem.tools-mcp.svc.cluster.local:8080/sse"
|
||||
- name: LITELLM_BASE_URL
|
||||
value: "http://litellm.ai-core.svc.cluster.local:4000/v1"
|
||||
- name: LITELLM_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gumbo-litellm
|
||||
key: api_key
|
||||
- name: LANGGRAPH_CHECKPOINT_URI
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: gumbo-checkpoint-db
|
||||
key: uri
|
||||
resources:
|
||||
requests:
|
||||
cpu: "1"
|
||||
memory: 2Gi
|
||||
limits:
|
||||
cpu: "4"
|
||||
memory: 8Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: gumbo-litellm
|
||||
namespace: ai-agents-gumbo
|
||||
type: Opaque
|
||||
stringData:
|
||||
api_key: change-me-litellm-master
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: gumbo-checkpoint-db
|
||||
namespace: ai-agents-gumbo
|
||||
type: Opaque
|
||||
stringData:
|
||||
uri: postgresql://agentic_os:change-me@agentic-os-pg-rw.platform-data.svc.cluster.local:5432/gumbo?sslmode=disable
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- gumbo-job-template.yaml
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://agentic-os.local/schemas/agent-message.json",
|
||||
"title": "AgentMessage",
|
||||
"type": "object",
|
||||
"required": ["schema_version", "message_id", "correlation_id", "from_agent", "payload"],
|
||||
"properties": {
|
||||
"schema_version": {
|
||||
"type": "string",
|
||||
"const": "1.0"
|
||||
},
|
||||
"message_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"correlation_id": {
|
||||
"type": "string",
|
||||
"description": "Temporal workflow run id or trace id"
|
||||
},
|
||||
"thread_id": {
|
||||
"type": "string",
|
||||
"description": "LangGraph thread / checkpoint namespace"
|
||||
},
|
||||
"from_agent": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z0-9-]+$"
|
||||
},
|
||||
"to_agent": {
|
||||
"type": ["string", "null"],
|
||||
"pattern": "^[a-z0-9-]+$"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": ["task", "result", "error", "approval_request", "approval_response"]
|
||||
},
|
||||
"payload": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
FROM python:3.11-slim
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 PIP_NO_CACHE_DIR=1
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends curl ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
ARG KUBECTL_VERSION=v1.30.2
|
||||
RUN curl -fsSL "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl" -o /usr/local/bin/kubectl \
|
||||
&& chmod +x /usr/local/bin/kubectl
|
||||
RUN pip install --no-cache-dir \
|
||||
"temporalio>=1.8.0" \
|
||||
"kubernetes>=30.0.0" \
|
||||
"psycopg[binary]>=3.2.0" \
|
||||
"langgraph>=0.2.0" \
|
||||
"langgraph-checkpoint-postgres>=2.0.0" \
|
||||
"langchain-openai>=0.2.0" \
|
||||
"mcp[cli]>=1.2.0"
|
||||
WORKDIR /worker
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- litellm/service.yaml
|
||||
- litellm/deployment.yaml
|
||||
- litellm/configmap.yaml
|
||||
- litellm/stub-secrets.yaml
|
||||
- ollama/service.yaml
|
||||
- ollama/statefulset.yaml
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: litellm-config
|
||||
namespace: ai-core
|
||||
data:
|
||||
config.yaml: |
|
||||
model_list:
|
||||
- model_name: ollama-qwen
|
||||
litellm_params:
|
||||
model: ollama/qwen2.5:14b
|
||||
api_base: http://ollama.ai-core.svc.cluster.local:11434
|
||||
- model_name: ollama-gemma
|
||||
litellm_params:
|
||||
model: ollama/gemma:7b
|
||||
api_base: http://ollama.ai-core.svc.cluster.local:11434
|
||||
router_settings:
|
||||
routing_strategy: simple-shuffle
|
||||
num_retries: 2
|
||||
timeout: 120
|
||||
allowed_fails: 2
|
||||
context_window_fallbacks:
|
||||
- ollama-qwen
|
||||
- ollama-gemma
|
||||
litellm_settings:
|
||||
max_budget: 5
|
||||
budget_duration: 1d
|
||||
# Virtual keys / teams: use $20/day for dev-agent keys in LiteLLM admin or separate deployment profile.
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: litellm
|
||||
namespace: ai-core
|
||||
labels:
|
||||
app.kubernetes.io/name: litellm
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: litellm
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: litellm
|
||||
spec:
|
||||
containers:
|
||||
- name: litellm
|
||||
image: ghcr.io/berriai/litellm:main-v1.55.10
|
||||
args:
|
||||
- "--config"
|
||||
- "/etc/litellm/config.yaml"
|
||||
ports:
|
||||
- containerPort: 4000
|
||||
name: http
|
||||
env:
|
||||
- name: LITELLM_MASTER_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: litellm-master
|
||||
key: LITELLM_MASTER_KEY
|
||||
- name: LANGFUSE_PUBLIC_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: litellm-langfuse
|
||||
key: public_key
|
||||
optional: true
|
||||
- name: LANGFUSE_SECRET_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: litellm-langfuse
|
||||
key: secret_key
|
||||
optional: true
|
||||
- name: LANGFUSE_HOST
|
||||
value: "http://langfuse.observability.svc.cluster.local:3000"
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /etc/litellm
|
||||
readOnly: true
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: "2"
|
||||
memory: 2Gi
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: litellm-config
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: litellm-master-key
|
||||
namespace: ai-core
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: infisical
|
||||
target:
|
||||
name: litellm-master
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: LITELLM_MASTER_KEY
|
||||
remoteRef:
|
||||
key: /agentic-os/litellm
|
||||
property: master_key
|
||||
---
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: litellm-langfuse-keys
|
||||
namespace: ai-core
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: infisical
|
||||
target:
|
||||
name: litellm-langfuse
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: public_key
|
||||
remoteRef:
|
||||
key: /agentic-os/langfuse
|
||||
property: public_key
|
||||
- secretKey: secret_key
|
||||
remoteRef:
|
||||
key: /agentic-os/langfuse
|
||||
property: secret_key
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: litellm
|
||||
namespace: ai-core
|
||||
labels:
|
||||
app.kubernetes.io/name: litellm
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: litellm
|
||||
ports:
|
||||
- name: http
|
||||
port: 4000
|
||||
targetPort: 4000
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# Replace via ExternalSecrets in production. Required keys for the bundled Deployment manifests.
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: litellm-master
|
||||
namespace: ai-core
|
||||
type: Opaque
|
||||
stringData:
|
||||
LITELLM_MASTER_KEY: change-me-litellm-master
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: litellm-langfuse
|
||||
namespace: ai-core
|
||||
type: Opaque
|
||||
stringData:
|
||||
public_key: ""
|
||||
secret_key: ""
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: ollama
|
||||
namespace: ai-core
|
||||
labels:
|
||||
app.kubernetes.io/name: ollama
|
||||
spec:
|
||||
clusterIP: None
|
||||
selector:
|
||||
app.kubernetes.io/name: ollama
|
||||
ports:
|
||||
- name: http
|
||||
port: 11434
|
||||
targetPort: 11434
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: ollama
|
||||
namespace: ai-core
|
||||
labels:
|
||||
app.kubernetes.io/name: ollama
|
||||
spec:
|
||||
serviceName: ollama
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: ollama
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: ollama
|
||||
spec:
|
||||
containers:
|
||||
- name: ollama
|
||||
image: ollama/ollama:0.4.4
|
||||
ports:
|
||||
- containerPort: 11434
|
||||
name: http
|
||||
env:
|
||||
- name: OLLAMA_HOST
|
||||
value: "0.0.0.0"
|
||||
resources:
|
||||
requests:
|
||||
cpu: "2"
|
||||
memory: 4Gi
|
||||
limits:
|
||||
cpu: "8"
|
||||
memory: 16Gi
|
||||
volumeMounts:
|
||||
- name: models
|
||||
mountPath: /root/.ollama
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: models
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 50Gi
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# Tune to match the Temporal Helm chart version in app-temporal.yaml.
|
||||
# Postgres runs in platform-data (CloudNativePG). Inject credentials via ExternalSecret in production.
|
||||
postgresql:
|
||||
enabled: false
|
||||
cassandra:
|
||||
enabled: false
|
||||
schema:
|
||||
createDatabase:
|
||||
enabled: true
|
||||
setup:
|
||||
enabled: true
|
||||
update:
|
||||
enabled: true
|
||||
server:
|
||||
replicaCount: 1
|
||||
config:
|
||||
persistence:
|
||||
defaultStore: default
|
||||
visibilityStore: default
|
||||
numHistoryShards: 4
|
||||
datastores:
|
||||
default:
|
||||
sql:
|
||||
plugin: postgres12
|
||||
databaseName: temporal
|
||||
connectAddr: agentic-os-pg-rw.platform-data.svc.cluster.local:5432
|
||||
connectProtocol: tcp
|
||||
user: agentic_os
|
||||
password: ""
|
||||
maxConns: 20
|
||||
maxIdleConns: 20
|
||||
maxConnLifetime: "1h"
|
||||
tls:
|
||||
enabled: false
|
||||
visibility:
|
||||
sql:
|
||||
plugin: postgres12
|
||||
databaseName: temporal_visibility
|
||||
connectAddr: agentic-os-pg-rw.platform-data.svc.cluster.local:5432
|
||||
connectProtocol: tcp
|
||||
user: agentic_os
|
||||
password: ""
|
||||
maxConns: 20
|
||||
maxIdleConns: 20
|
||||
maxConnLifetime: "1h"
|
||||
tls:
|
||||
enabled: false
|
||||
metrics:
|
||||
enabled: true
|
||||
elasticsearch:
|
||||
enabled: false
|
||||
prometheus:
|
||||
enabled: false
|
||||
grafana:
|
||||
enabled: false
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body style={{ fontFamily: "system-ui", margin: 0, padding: 24 }}>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
export default function Page() {
|
||||
return (
|
||||
<main>
|
||||
<h1>Agentic OS HQ</h1>
|
||||
<p>Next.js shell scaffold. Wire this UI to Temporal and Langfuse in later iterations.</p>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "agentic-os-hq-dashboard",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"next": "14.2.18",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- placeholder.yaml
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: interface-gitops-placeholder
|
||||
namespace: interface
|
||||
data:
|
||||
note: "Replace with Slack bot and HQ dashboard workloads when ready."
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "agentic-os-slack-bot",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@slack/bolt": "^4.1.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import { App } from "@slack/bolt";
|
||||
|
||||
const app = new App({
|
||||
token: process.env.SLACK_BOT_TOKEN,
|
||||
signingSecret: process.env.SLACK_SIGNING_SECRET,
|
||||
socketMode: true,
|
||||
appToken: process.env.SLACK_APP_TOKEN,
|
||||
});
|
||||
|
||||
app.message("ping", async ({ say }) => {
|
||||
await say("Agentic OS slack-bot scaffold is online.");
|
||||
});
|
||||
|
||||
async function main() {
|
||||
await app.start(process.env.PORT ? Number(process.env.PORT) : 3000);
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- langfuse/secrets-stub.yaml
|
||||
- langfuse/deployment.yaml
|
||||
- langfuse/service.yaml
|
||||
- monitoring
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: langfuse-web
|
||||
namespace: observability
|
||||
labels:
|
||||
app.kubernetes.io/name: langfuse
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: langfuse
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: langfuse
|
||||
spec:
|
||||
containers:
|
||||
- name: langfuse
|
||||
image: langfuse/langfuse:2.93.4
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
name: http
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: langfuse-server
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: "2"
|
||||
memory: 2Gi
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
# Optional: swap stub secrets for ExternalSecrets targeting Vault/Infisical.
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: langfuse-server
|
||||
namespace: observability
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: infisical
|
||||
target:
|
||||
name: langfuse-server
|
||||
creationPolicy: Owner
|
||||
dataFrom:
|
||||
- extract:
|
||||
key: /agentic-os/langfuse-server
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: langfuse-server
|
||||
namespace: observability
|
||||
type: Opaque
|
||||
stringData:
|
||||
DATABASE_URL: postgresql://agentic_os:change-me@agentic-os-pg-rw.platform-data.svc.cluster.local:5432/langfuse
|
||||
NEXTAUTH_URL: http://langfuse.observability.svc.cluster.local:3000
|
||||
NEXTAUTH_SECRET: change-me-nextauth-secret-at-least-32-chars!!
|
||||
SALT: change-me-salt-at-least-32-chars!!!!!!
|
||||
ENCRYPTION_KEY: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: langfuse
|
||||
namespace: observability
|
||||
labels:
|
||||
app.kubernetes.io/name: langfuse
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: langfuse
|
||||
ports:
|
||||
- name: http
|
||||
port: 3000
|
||||
targetPort: 3000
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources: []
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
Agentic OS Helm / manifest pins (update together when bumping).
|
||||
Last reviewed: 2026-05-10
|
||||
|
||||
Git remote (Argo CD source):
|
||||
http://192.168.8.248:3000/deepkoluguri/agentic-os.git
|
||||
Optional Argo repo Secret (HTTP / insecure): platform/bootstrap/argocd-gitea-repo-secret.example.yaml
|
||||
|
||||
Argo CD bootstrap (one-time kustomize remote resource):
|
||||
argoproj/argo-cd v3.4.1 manifests/install.yaml
|
||||
File: platform/bootstrap/initial-argocd/kustomization.yaml
|
||||
|
||||
Argo CD Applications (Helm charts):
|
||||
jetstack/cert-manager v1.20.2 app-cert-manager.yaml
|
||||
external-secrets/external-secrets 0.14.4 app-external-secrets.yaml
|
||||
cloudnative-pg/cloudnative-pg 0.24.0 app-cnpg-operator.yaml
|
||||
temporal/temporal 0.55.0 app-temporal.yaml (1.x chart needs values rewrite before bump)
|
||||
prometheus-community/kube-prometheus-stack 84.4.0 app-kube-prometheus.yaml
|
||||
|
||||
Verify locally (no cluster required):
|
||||
pwsh -File scripts/verify-kustomize.ps1
|
||||
Optional remote bootstrap check (fetches Argo install.yaml):
|
||||
pwsh -File scripts/verify-kustomize.ps1 -IncludeRemoteArgoCD
|
||||
|
||||
Helm chart diff (requires helm + repos):
|
||||
helm template ...
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: agents-runtime
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: agentic-os
|
||||
source:
|
||||
repoURL: http://192.168.8.248:3000/deepkoluguri/agentic-os.git
|
||||
targetRevision: main
|
||||
path: agents/k8s
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: ai-agents-gumbo
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: false
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: ai-core
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: agentic-os
|
||||
source:
|
||||
repoURL: http://192.168.8.248:3000/deepkoluguri/agentic-os.git
|
||||
targetRevision: main
|
||||
path: ai-core
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: ai-core
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: cert-manager
|
||||
namespace: argocd
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "-3"
|
||||
spec:
|
||||
project: agentic-os
|
||||
source:
|
||||
chart: cert-manager
|
||||
repoURL: https://charts.jetstack.io
|
||||
targetRevision: v1.20.2
|
||||
helm:
|
||||
values: |
|
||||
installCRDs: true
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: cert-manager
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: cloudnative-pg-operator
|
||||
namespace: argocd
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "-2"
|
||||
spec:
|
||||
project: agentic-os
|
||||
source:
|
||||
chart: cloudnative-pg
|
||||
repoURL: https://cloudnative-pg.github.io/charts
|
||||
targetRevision: 0.24.0
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: cnpg-system
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: external-secrets
|
||||
namespace: argocd
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "-2"
|
||||
spec:
|
||||
project: agentic-os
|
||||
source:
|
||||
chart: external-secrets
|
||||
repoURL: https://charts.external-secrets.io
|
||||
targetRevision: 0.14.4
|
||||
helm:
|
||||
values: |
|
||||
installCRDs: true
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: platform-security
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: interface
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: agentic-os
|
||||
source:
|
||||
repoURL: http://192.168.8.248:3000/deepkoluguri/agentic-os.git
|
||||
targetRevision: main
|
||||
path: interface/k8s
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: interface
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: kube-prometheus-stack
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: agentic-os
|
||||
source:
|
||||
chart: kube-prometheus-stack
|
||||
repoURL: https://prometheus-community.github.io/helm-charts
|
||||
targetRevision: 84.4.0
|
||||
helm:
|
||||
values: |
|
||||
prometheus:
|
||||
prometheusSpec:
|
||||
retention: 15d
|
||||
grafana:
|
||||
enabled: true
|
||||
alertmanager:
|
||||
enabled: true
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: observability
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: false
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: observability
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: agentic-os
|
||||
source:
|
||||
repoURL: http://192.168.8.248:3000/deepkoluguri/agentic-os.git
|
||||
targetRevision: main
|
||||
path: observability
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: observability
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: platform-data
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: agentic-os
|
||||
source:
|
||||
repoURL: http://192.168.8.248:3000/deepkoluguri/agentic-os.git
|
||||
targetRevision: main
|
||||
path: platform/data
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: platform-data
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: platform-networking
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: agentic-os
|
||||
source:
|
||||
repoURL: http://192.168.8.248:3000/deepkoluguri/agentic-os.git
|
||||
targetRevision: main
|
||||
path: platform/networking
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: kube-system
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: platform-security
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: agentic-os
|
||||
source:
|
||||
repoURL: http://192.168.8.248:3000/deepkoluguri/agentic-os.git
|
||||
targetRevision: main
|
||||
path: platform/security
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: platform-security
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: temporal
|
||||
namespace: argocd
|
||||
annotations:
|
||||
agentic-os.io/helm-chart-notes: "Pinned 0.55.x; Temporal chart 1.x uses a different values schema—bump targetRevision only after rewriting ai-core/temporal/helm-values.yaml against that chart."
|
||||
spec:
|
||||
project: agentic-os
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: ai-core
|
||||
sources:
|
||||
- repoURL: https://go.temporal.io/helm-charts
|
||||
chart: temporal
|
||||
targetRevision: 0.55.0
|
||||
helm:
|
||||
valueFiles:
|
||||
- $repo/helm-values.yaml
|
||||
- repoURL: http://192.168.8.248:3000/deepkoluguri/agentic-os.git
|
||||
targetRevision: main
|
||||
ref: repo
|
||||
path: ai-core/temporal
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: false
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: tools-mcp
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: agentic-os
|
||||
source:
|
||||
repoURL: http://192.168.8.248:3000/deepkoluguri/agentic-os.git
|
||||
targetRevision: main
|
||||
path: tools-mcp
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: tools-mcp
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- app-cert-manager.yaml
|
||||
- app-external-secrets.yaml
|
||||
- app-cnpg-operator.yaml
|
||||
- app-platform-networking.yaml
|
||||
- app-platform-security.yaml
|
||||
- app-platform-data.yaml
|
||||
- app-observability.yaml
|
||||
- app-kube-prometheus.yaml
|
||||
- app-ai-core.yaml
|
||||
- app-temporal.yaml
|
||||
- app-tools-mcp.yaml
|
||||
- app-agents-runtime.yaml
|
||||
- app-interface.yaml
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
# Optional: apply manually if Argo CD refuses plain HTTP or your repo is private.
|
||||
# kubectl apply -f platform/bootstrap/argocd-gitea-repo-secret.example.yaml
|
||||
# For private repos, set username + password (Gitea PAT or user password).
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: agentic-os-gitea
|
||||
namespace: argocd
|
||||
labels:
|
||||
argocd.argoproj.io/secret-type: repository
|
||||
stringData:
|
||||
type: git
|
||||
url: http://192.168.8.248:3000/deepkoluguri/agentic-os.git
|
||||
insecure: "true"
|
||||
username: ""
|
||||
password: ""
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# One-time bootstrap: Argo CD must exist before GitOps takes over.
|
||||
# Apply from repo root (after replacing nothing): kubectl apply -k platform/bootstrap/initial-argocd
|
||||
# Pin the tag to a release you have audited (see platform/bootstrap/CHART_PINS.txt).
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: argocd
|
||||
resources:
|
||||
- https://raw.githubusercontent.com/argoproj/argo-cd/v3.4.1/manifests/install.yaml
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- namespaces.yaml
|
||||
- root-app-project.yaml
|
||||
- root-application.yaml
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: argocd
|
||||
labels:
|
||||
pod-security.kubernetes.io/enforce: privileged
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: platform-data
|
||||
labels:
|
||||
agentic-os.io/layer: "data"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: platform-security
|
||||
labels:
|
||||
agentic-os.io/layer: "security"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: ai-core
|
||||
labels:
|
||||
agentic-os.io/layer: "ai-core"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: tools-mcp
|
||||
labels:
|
||||
agentic-os.io/layer: "mcp"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: observability
|
||||
labels:
|
||||
agentic-os.io/layer: "observability"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: ai-agents-gumbo
|
||||
labels:
|
||||
agentic-os.io/ai-agent-namespace: "true"
|
||||
agentic-os.io/agent: "gumbo"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: ai-agents-bernard
|
||||
labels:
|
||||
agentic-os.io/ai-agent-namespace: "true"
|
||||
agentic-os.io/agent: "bernard"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: interface
|
||||
labels:
|
||||
agentic-os.io/layer: "interface"
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: AppProject
|
||||
metadata:
|
||||
name: agentic-os
|
||||
namespace: argocd
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "0"
|
||||
spec:
|
||||
description: GitOps project for Agentic OS platform and workloads
|
||||
sourceRepos:
|
||||
- "*"
|
||||
destinations:
|
||||
- namespace: "*"
|
||||
server: https://kubernetes.default.svc
|
||||
clusterResourceWhitelist:
|
||||
- group: ""
|
||||
kind: Namespace
|
||||
- group: "apiextensions.k8s.io"
|
||||
kind: CustomResourceDefinition
|
||||
- group: "rbac.authorization.k8s.io"
|
||||
kind: ClusterRole
|
||||
- group: "rbac.authorization.k8s.io"
|
||||
kind: ClusterRoleBinding
|
||||
- group: "cilium.io"
|
||||
kind: CiliumClusterwideNetworkPolicy
|
||||
namespaceResourceWhitelist:
|
||||
- group: "*"
|
||||
kind: "*"
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# Root "app of apps". Point repoURL to your fork after clone.
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: agentic-os-root
|
||||
namespace: argocd
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-wave: "5"
|
||||
finalizers:
|
||||
- resources-finalizer.argocd.argoproj.io
|
||||
spec:
|
||||
project: agentic-os
|
||||
source:
|
||||
repoURL: http://192.168.8.248:3000/deepkoluguri/agentic-os.git
|
||||
targetRevision: main
|
||||
path: platform/bootstrap/apps
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: argocd
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: true
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Cluster
|
||||
metadata:
|
||||
name: agentic-os-pg
|
||||
namespace: platform-data
|
||||
spec:
|
||||
instances: 1
|
||||
primaryUpdateStrategy: unsupervised
|
||||
storage:
|
||||
size: 20Gi
|
||||
bootstrap:
|
||||
initdb:
|
||||
database: agentic_os
|
||||
owner: agentic_os
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Database
|
||||
metadata:
|
||||
name: temporal
|
||||
namespace: platform-data
|
||||
spec:
|
||||
name: temporal
|
||||
owner: agentic_os
|
||||
cluster:
|
||||
name: agentic-os-pg
|
||||
---
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Database
|
||||
metadata:
|
||||
name: langfuse
|
||||
namespace: platform-data
|
||||
spec:
|
||||
name: langfuse
|
||||
owner: agentic_os
|
||||
cluster:
|
||||
name: agentic-os-pg
|
||||
---
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Database
|
||||
metadata:
|
||||
name: temporal-visibility
|
||||
namespace: platform-data
|
||||
spec:
|
||||
name: temporal_visibility
|
||||
owner: agentic_os
|
||||
cluster:
|
||||
name: agentic-os-pg
|
||||
---
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Database
|
||||
metadata:
|
||||
name: gumbo
|
||||
namespace: platform-data
|
||||
spec:
|
||||
name: gumbo
|
||||
owner: agentic_os
|
||||
cluster:
|
||||
name: agentic-os-pg
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- cnpg/cluster.yaml
|
||||
- cnpg/databases.yaml
|
||||
- redis/redis.yaml
|
||||
- minio/minio.yaml
|
||||
- qdrant/qdrant.yaml
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: minio-root
|
||||
namespace: platform-data
|
||||
type: Opaque
|
||||
stringData:
|
||||
rootUser: agentic-os-minio
|
||||
rootPassword: change-me-minio-root
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: minio
|
||||
namespace: platform-data
|
||||
labels:
|
||||
app.kubernetes.io/name: minio
|
||||
spec:
|
||||
ports:
|
||||
- port: 9000
|
||||
targetPort: 9000
|
||||
name: api
|
||||
- port: 9001
|
||||
targetPort: 9001
|
||||
name: console
|
||||
selector:
|
||||
app.kubernetes.io/name: minio
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: minio
|
||||
namespace: platform-data
|
||||
spec:
|
||||
serviceName: minio
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: minio
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: minio
|
||||
spec:
|
||||
containers:
|
||||
- name: minio
|
||||
image: quay.io/minio/minio:RELEASE.2024-11-07T00-52-20Z
|
||||
args:
|
||||
- server
|
||||
- /data
|
||||
- --console-address
|
||||
- ":9001"
|
||||
env:
|
||||
- name: MINIO_ROOT_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: minio-root
|
||||
key: rootUser
|
||||
- name: MINIO_ROOT_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: minio-root
|
||||
key: rootPassword
|
||||
ports:
|
||||
- containerPort: 9000
|
||||
name: api
|
||||
- containerPort: 9001
|
||||
name: console
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: "2"
|
||||
memory: 2Gi
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 50Gi
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: qdrant
|
||||
namespace: platform-data
|
||||
labels:
|
||||
app.kubernetes.io/name: qdrant
|
||||
spec:
|
||||
ports:
|
||||
- port: 6333
|
||||
targetPort: 6333
|
||||
name: http
|
||||
- port: 6334
|
||||
targetPort: 6334
|
||||
name: grpc
|
||||
selector:
|
||||
app.kubernetes.io/name: qdrant
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: qdrant
|
||||
namespace: platform-data
|
||||
spec:
|
||||
serviceName: qdrant
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: qdrant
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: qdrant
|
||||
spec:
|
||||
containers:
|
||||
- name: qdrant
|
||||
image: qdrant/qdrant:v1.12.4
|
||||
ports:
|
||||
- containerPort: 6333
|
||||
name: http
|
||||
- containerPort: 6334
|
||||
name: grpc
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /qdrant/storage
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: "2"
|
||||
memory: 2Gi
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 20Gi
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: redis
|
||||
namespace: platform-data
|
||||
labels:
|
||||
app.kubernetes.io/name: redis
|
||||
spec:
|
||||
ports:
|
||||
- port: 6379
|
||||
targetPort: 6379
|
||||
name: redis
|
||||
selector:
|
||||
app.kubernetes.io/name: redis
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: redis
|
||||
namespace: platform-data
|
||||
spec:
|
||||
serviceName: redis
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: redis
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: redis
|
||||
spec:
|
||||
containers:
|
||||
- name: redis
|
||||
image: redis:7.4-alpine
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
name: redis
|
||||
args:
|
||||
- "--save"
|
||||
- ""
|
||||
- "--appendonly"
|
||||
- "no"
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits:
|
||||
cpu: "1"
|
||||
memory: 512Mi
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# Least-privilege egress for agent Job namespaces.
|
||||
# - DNS (kube-dns)
|
||||
# - LiteLLM (all LLM traffic is proxied here)
|
||||
# - MCP tool plane
|
||||
# - Postgres (LangGraph AsyncPostgresSaver checkpoints + structured agent state)
|
||||
#
|
||||
# Object storage (MinIO) remains reachable only from MCP servers, not from agent pods.
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumClusterwideNetworkPolicy
|
||||
metadata:
|
||||
name: agentic-os-ai-agents-egress
|
||||
spec:
|
||||
endpointSelector:
|
||||
matchExpressions:
|
||||
- key: io.kubernetes.metadata.namespace
|
||||
operator: In
|
||||
values:
|
||||
- ai-agents-gumbo
|
||||
- ai-agents-bernard
|
||||
egress:
|
||||
- toEndpoints:
|
||||
- matchLabels:
|
||||
k8s:io.kubernetes.pod.namespace: kube-system
|
||||
k8s-app: kube-dns
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "53"
|
||||
protocol: UDP
|
||||
- port: "53"
|
||||
protocol: TCP
|
||||
- toEndpoints:
|
||||
- matchLabels:
|
||||
k8s:io.kubernetes.pod.namespace: ai-core
|
||||
app.kubernetes.io/name: litellm
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "4000"
|
||||
protocol: TCP
|
||||
- toEndpoints:
|
||||
- matchLabels:
|
||||
k8s:io.kubernetes.pod.namespace: tools-mcp
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "8080"
|
||||
protocol: TCP
|
||||
- port: "3000"
|
||||
protocol: TCP
|
||||
- toEndpoints:
|
||||
- matchLabels:
|
||||
k8s:io.kubernetes.pod.namespace: platform-data
|
||||
cnpg.io/cluster: agentic-os-pg
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "5432"
|
||||
protocol: TCP
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
apiVersion: cilium.io/v2
|
||||
kind: CiliumNetworkPolicy
|
||||
metadata:
|
||||
name: litellm-ingress-from-agents
|
||||
namespace: ai-core
|
||||
spec:
|
||||
endpointSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: litellm
|
||||
ingress:
|
||||
- fromEndpoints:
|
||||
- matchExpressions:
|
||||
- key: io.kubernetes.metadata.namespace
|
||||
operator: In
|
||||
values:
|
||||
- ai-agents-gumbo
|
||||
- ai-agents-bernard
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "4000"
|
||||
protocol: TCP
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources: []
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- gateway-api
|
||||
- traefik
|
||||
- cilium/policies/clusterwide-agents-egress.yaml
|
||||
- cilium/policies/cnp-litellm-ingress-from-agents.yaml
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources: []
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# Replace with ACME/Let's Encrypt or private CA issuer matching your cluster.
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: ClusterIssuer
|
||||
metadata:
|
||||
name: selfsigned-bootstrap
|
||||
spec:
|
||||
selfSigned: {}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
# Wire Infisical as the backing store for ExternalSecrets. Replace host/tokenRef before sync.
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ClusterSecretStore
|
||||
metadata:
|
||||
name: infisical
|
||||
spec:
|
||||
provider:
|
||||
infisical:
|
||||
host: https://app.infisical.com
|
||||
auth:
|
||||
universalAuthCredentials:
|
||||
clientId:
|
||||
key: clientId
|
||||
namespace: platform-security
|
||||
name: infisical-universal-auth
|
||||
clientSecret:
|
||||
key: clientSecret
|
||||
namespace: platform-security
|
||||
name: infisical-universal-auth
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# Alternative: HashiCorp Vault KV v2. Enable one ClusterSecretStore in your environment.
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ClusterSecretStore
|
||||
metadata:
|
||||
name: vault-kv2
|
||||
spec:
|
||||
provider:
|
||||
vault:
|
||||
server: "https://vault.platform-security.svc:8200"
|
||||
path: "secret"
|
||||
version: "v2"
|
||||
auth:
|
||||
kubernetes:
|
||||
mountPath: "kubernetes"
|
||||
role: "external-secrets"
|
||||
serviceAccountRef:
|
||||
name: external-secrets
|
||||
namespace: platform-security
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- external-secrets/cluster-secret-store-infisical.yaml
|
||||
- external-secrets/cluster-secret-store-vault.yaml
|
||||
- cert-manager/cluster-issuer-stub.yaml
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
param(
|
||||
[switch] $IncludeRemoteArgoCD
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$RepoRoot = Resolve-Path (Join-Path $PSScriptRoot "..")
|
||||
|
||||
function Test-KustomizeDir {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string] $RelativePath,
|
||||
[Parameter(Mandatory = $true)][string] $Label
|
||||
)
|
||||
|
||||
$path = Join-Path $RepoRoot $RelativePath
|
||||
if (-not (Test-Path $path)) {
|
||||
throw "Missing kustomize path: $path"
|
||||
}
|
||||
|
||||
Write-Host "== $Label ($RelativePath) ==" -ForegroundColor Cyan
|
||||
$out = & kubectl kustomize $path 2>&1
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host $out
|
||||
throw "kubectl kustomize failed for $RelativePath (exit $LASTEXITCODE)"
|
||||
}
|
||||
|
||||
if (-not $out) {
|
||||
throw "kubectl kustomize produced empty output for $RelativePath"
|
||||
}
|
||||
|
||||
$docCount = ($out | Select-String -Pattern "^---$" -AllMatches).Matches.Count + 1
|
||||
Write-Host ("OK ({0} YAML documents emitted)" -f $docCount) -ForegroundColor Green
|
||||
}
|
||||
|
||||
$targets = @(
|
||||
@{ Rel = "platform\bootstrap"; Label = "platform-bootstrap" },
|
||||
@{ Rel = "platform\bootstrap\apps"; Label = "argocd-app-of-apps" },
|
||||
@{ Rel = "platform\networking"; Label = "platform-networking" },
|
||||
@{ Rel = "platform\security"; Label = "platform-security" },
|
||||
@{ Rel = "platform\data"; Label = "platform-data" },
|
||||
@{ Rel = "observability"; Label = "observability" },
|
||||
@{ Rel = "ai-core"; Label = "ai-core" },
|
||||
@{ Rel = "tools-mcp"; Label = "tools-mcp" },
|
||||
@{ Rel = "agents\k8s"; Label = "agents-k8s" },
|
||||
@{ Rel = "interface\k8s"; Label = "interface-k8s" }
|
||||
)
|
||||
|
||||
foreach ($t in $targets) {
|
||||
Test-KustomizeDir -RelativePath $t.Rel -Label $t.Label
|
||||
}
|
||||
|
||||
if ($IncludeRemoteArgoCD) {
|
||||
Test-KustomizeDir -RelativePath "platform\bootstrap\initial-argocd" -Label "initial-argocd (remote)"
|
||||
}
|
||||
|
||||
Write-Host "All requested kustomize builds succeeded." -ForegroundColor Green
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- mcp-filesystem/minio-secret.yaml
|
||||
- mcp-filesystem/deployment.yaml
|
||||
- mcp-filesystem/service.yaml
|
||||
- mcp-github/secret-stub.yaml
|
||||
- mcp-github/deployment.yaml
|
||||
- mcp-github/service.yaml
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
FROM python:3.11-slim
|
||||
WORKDIR /app
|
||||
RUN pip install --no-cache-dir "mcp[cli]>=1.2.0" boto3
|
||||
COPY app/server.py /app/server.py
|
||||
ENV HOST=0.0.0.0
|
||||
ENV PORT=8080
|
||||
CMD ["python", "/app/server.py"]
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
"""Read-only MinIO (S3) bridge exposed as MCP tools over SSE."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import boto3
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
BUCKET = os.environ.get("MCP_FS_BUCKET", "agent-workspaces")
|
||||
ENDPOINT = os.environ.get("AWS_ENDPOINT_URL", "http://minio.platform-data.svc.cluster.local:9000")
|
||||
REGION = os.environ.get("AWS_REGION", "us-east-1")
|
||||
HOST = os.environ.get("HOST", "0.0.0.0")
|
||||
PORT = int(os.environ.get("PORT", "8080"))
|
||||
|
||||
_session = boto3.session.Session()
|
||||
_client = _session.client(
|
||||
"s3",
|
||||
endpoint_url=ENDPOINT,
|
||||
region_name=REGION,
|
||||
aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"],
|
||||
aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"],
|
||||
)
|
||||
|
||||
mcp = FastMCP("agentic-os-mcp-filesystem", host=HOST, port=PORT)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def get_object_text(key: str) -> str:
|
||||
"""Return the UTF-8 text body for `key` in the configured read-only bucket."""
|
||||
obj = _client.get_object(Bucket=BUCKET, Key=key)
|
||||
body: bytes = obj["Body"].read()
|
||||
return body.decode("utf-8", errors="replace")
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
def list_objects(prefix: str = "") -> list[str]:
|
||||
"""List object keys under optional prefix (read-only)."""
|
||||
keys: list[str] = []
|
||||
paginator = _client.get_paginator("list_objects_v2")
|
||||
for page in paginator.paginate(Bucket=BUCKET, Prefix=prefix):
|
||||
for item in page.get("Contents", []) or []:
|
||||
keys.append(item["Key"])
|
||||
return keys
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run(transport="sse")
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mcp-filesystem
|
||||
namespace: tools-mcp
|
||||
labels:
|
||||
app.kubernetes.io/name: mcp-filesystem
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: mcp-filesystem
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: mcp-filesystem
|
||||
spec:
|
||||
containers:
|
||||
- name: mcp
|
||||
image: agentic-os/mcp-filesystem:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: MCP_FS_BUCKET
|
||||
value: agent-workspaces
|
||||
- name: AWS_ENDPOINT_URL
|
||||
value: http://minio.platform-data.svc.cluster.local:9000
|
||||
- name: AWS_REGION
|
||||
value: us-east-1
|
||||
- name: AWS_ACCESS_KEY_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mcp-filesystem-minio
|
||||
key: access_key
|
||||
- name: AWS_SECRET_ACCESS_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mcp-filesystem-minio
|
||||
key: secret_key
|
||||
- name: HOST
|
||||
value: "0.0.0.0"
|
||||
- name: PORT
|
||||
value: "8080"
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: mcp
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 8080
|
||||
initialDelaySeconds: 3
|
||||
periodSeconds: 10
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
cpu: "1"
|
||||
memory: 512Mi
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: mcp-filesystem-minio
|
||||
namespace: tools-mcp
|
||||
type: Opaque
|
||||
stringData:
|
||||
access_key: agentic-os-minio
|
||||
secret_key: change-me-minio-root
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mcp-filesystem
|
||||
namespace: tools-mcp
|
||||
labels:
|
||||
app.kubernetes.io/name: mcp-filesystem
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: mcp-filesystem
|
||||
ports:
|
||||
- name: mcp
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mcp-github
|
||||
namespace: tools-mcp
|
||||
labels:
|
||||
app.kubernetes.io/name: mcp-github
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: mcp-github
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: mcp-github
|
||||
spec:
|
||||
containers:
|
||||
- name: mcp
|
||||
image: python:3.11-slim
|
||||
imagePullPolicy: IfNotPresent
|
||||
command: ["sleep", "infinity"]
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
name: mcp
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: mcp-github-token
|
||||
namespace: tools-mcp
|
||||
type: Opaque
|
||||
stringData:
|
||||
token: ""
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mcp-github
|
||||
namespace: tools-mcp
|
||||
labels:
|
||||
app.kubernetes.io/name: mcp-github
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: mcp-github
|
||||
ports:
|
||||
- name: mcp
|
||||
port: 3000
|
||||
targetPort: 3000
|
||||
Loading…
Reference in New Issue