diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b8e3768d9a..c351a0f8c6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -15,7 +15,6 @@ env: # Cache key components for better organization CACHE_KEY_PREFIX: kagent-v2 BRANCH_CACHE_KEY: ${{ github.head_ref || github.ref_name }} - AGENT_SANDBOX_VERSION: v0.3.10 # Consistent builder configuration BUILDX_BUILDER_NAME: kagent-builder-v0.23.0 BUILDX_VERSION: v0.23.0 @@ -78,13 +77,6 @@ jobs: run: | make create-kind-cluster - - name: Install agent-sandbox - run: | - kubectl apply -f "https://github.com/kubernetes-sigs/agent-sandbox/releases/download/${AGENT_SANDBOX_VERSION}/manifest.yaml" - timeout 90s bash -c 'until [ "$(kubectl get crd sandboxes.agents.x-k8s.io -o jsonpath="{.status.conditions[?(@.type==\"Established\")].status}" 2>/dev/null)" = "True" ]; do sleep 1; done' - kubectl rollout status deployment/agent-sandbox-controller -n agent-sandbox-system --timeout=120s - kubectl wait --for=condition=Ready pod -l app=agent-sandbox-controller -n agent-sandbox-system --timeout=120s - - name: Install Kagent id: install-kagent env: @@ -150,10 +142,6 @@ jobs: echo "::error::Failed to run e2e tests" echo "::error::Kubectl get pods -n kagent" kubectl describe pods -n kagent - echo "::error::Kubectl get pods -n agent-sandbox-system" - kubectl get pods -n agent-sandbox-system -o wide || true - echo "::error::Kubectl logs -n agent-sandbox-system deployment/agent-sandbox-controller" - kubectl logs -n agent-sandbox-system deployment/agent-sandbox-controller || true echo "::error::Kubectl get events -n kagent" kubectl get events -n kagent echo "::error::Kubectl get agents -n kagent" diff --git a/Makefile b/Makefile index af3cfd6d98..17a4286432 100644 --- a/Makefile +++ b/Makefile @@ -423,14 +423,14 @@ helm-install-provider: helm-version check-api-key --timeout 5m \ --kube-context kind-$(KIND_CLUSTER_NAME) \ --wait \ - --set ui.service.type=LoadBalancer \ + --set ui.service.type=ClusterIP \ --set registry=$(DOCKER_REGISTRY) \ --set imagePullPolicy=Always \ --set tag=$(VERSION) \ --set controller.loglevel=debug \ --set controller.image.pullPolicy=Always \ --set ui.image.pullPolicy=Always \ - --set controller.service.type=LoadBalancer \ + --set controller.service.type=ClusterIP \ --set providers.openAI.apiKey=$(OPENAI_API_KEY) \ --set providers.azureOpenAI.apiKey=$(AZUREOPENAI_API_KEY) \ --set providers.anthropic.apiKey=$(ANTHROPIC_API_KEY) \ diff --git a/design/EP-XXXX-acp-integration.md b/design/EP-XXXX-acp-integration.md new file mode 100644 index 0000000000..a705c6eaf6 --- /dev/null +++ b/design/EP-XXXX-acp-integration.md @@ -0,0 +1,243 @@ +# EP-XXXX: Agent Client Protocol (ACP) integration for AgentHarness backends + +* Issue: [#XXXX](https://github.com/kagent-dev/kagent/issues/XXXX) + +## Background + +The [Agent Client Protocol (ACP)](https://agentclientprotocol.com/) standardizes communication between clients (editors, UIs) and coding agents, much like LSP standardized language server integration. It is a JSON-RPC 2.0 protocol, by default spoken over stdio with the agent running as a subprocess of the client. + +On transports, the [v1 Transports spec](https://agentclientprotocol.com/protocol/v1/transports) defines: + +1. **stdio** — the only fully-specified transport ("agents and clients SHOULD support stdio whenever possible"); +2. **Streamable HTTP** — listed in the spec but explicitly marked *"in discussion, draft proposal in progress"*; the draft is actively iterating upstream (e.g., [agent-client-protocol#1124](https://github.com/agentclientprotocol/agent-client-protocol/pull/1124), "Revisions on streamable-http/ws GET streams", covering both HTTP and WebSocket streams); +3. **Custom Transports** — the protocol is explicitly transport-agnostic: any bidirectional channel is permitted as long as the JSON-RPC message format and lifecycle are preserved. This clause is what legitimizes OpenClaw's stdio→WebSocket gateway bridge today. + +Key protocol elements (v1): + +* **Lifecycle**: `initialize` (version + capability negotiation) → optional `authenticate` → `session/new` / `session/load` / `session/resume` / `session/close` → `session/prompt` turns, ending with a stop reason. +* **Streaming**: the agent emits `session/update` notifications during a turn — message chunks (agent/user/thought), `tool_call` / `tool_call_update`, plans, and available slash commands. +* **Bidirectional**: the agent can call back into the client — `session/request_permission` (a built-in human-in-the-loop primitive), `fs/read_text_file`, `fs/write_text_file`, and `terminal/*` methods. +* **MCP passthrough**: `session/new` carries a working directory and a list of MCP servers the agent should connect to (stdio required; HTTP/SSE behind `mcpCapabilities`). +* **Extensibility**: `_meta` fields on any message, `_`-prefixed custom methods, and custom capabilities advertised at initialization. + +Both harness backends kagent supports today already implement ACP, in different ways: + +* **OpenClaw** ([docs](https://docs.openclaw.ai/cli/acp)): `openclaw acp` is a stdio→WebSocket *bridge* that forwards ACP traffic to an OpenClaw Gateway (`--url wss://host:18789 --token …`). The bridge process can run anywhere that can reach the gateway — it does **not** need to run inside the sandbox. ACP sessions map to Gateway session keys (`--session agent:main:main`, or per-session via `_meta.sessionKey`). Implemented: `initialize`, `session/new`, `session/prompt`, cancel, list, resume, close; partial: `session/load` (event-ledger replay), `session/set_mode`, exec-approval relay via `session/request_permission`, tool-call streaming. Not supported: per-session `mcpServers` (rejected), client `fs/*` and `terminal/*` callbacks, plan/thought streaming. +* **Hermes** ([docs](https://hermes-agent.nousresearch.com/docs/user-guide/features/acp)): `hermes acp` runs the full agent *in-process* as a stdio ACP server (requires the `hermes-agent[acp]` extra). It uses a curated `hermes-acp` toolset (file, terminal, web, memory, skills, execute_code, delegate_task, vision), reads the same `~/.hermes/{.env,config.yaml}` configuration kagent's Hermes bootstrap already writes, and supports `allow_once` / `allow_session` / `allow_always` / `deny` approval semantics through `session/request_permission`. ACP sessions are tracked in-memory per server process; `list/load/resume/fork` are scoped to that process lifetime. + +kagent's current integration with these harnesses is entirely proprietary: + +* The `AgentHarness` CRD (`go/api/v1alpha2/agentharness_types.go`) selects a `backend` (`openclaw`, `hermes`). [Substrate](https://github.com/agent-substrate/substrate) is the only supported sandbox runtime. +* Interaction happens through the backend's own gateway HTTP API, proxied by the kagent controller at `/api/agentharnesses/{namespace}/{name}/gateway/`. Substrate has no SSH and no exec/attach channel into actors. The ate API is lifecycle-only: `CreateActor`/`SuspendActor`/`ResumeActor`/`DeleteActor`; the only path into an actor is network ingress via the atenet router. We can't rely on substrate adding ssh support any time soon, possibly never. + +* On Substrate, the per-backend interaction story is: + * **OpenClaw**: the proxied OpenClaw web control UI — works, but is backend-specific. + * **Hermes**: no interaction channel at all — its TUI requires SSH. + * **Codex and other coding agents**: TUI-only; no web UI to proxy, no SSH to launch them — unintegrable on Substrate today. +* Gateway authentication uses a Bearer token (`spec.substrate.gatewayToken` / `spec.substrate.gatewayTokenSecretRef` on the Substrate runtime). +* Harnesses are *not* agents from kagent's perspective: they cannot be invoked through the A2A surface (`/api/a2a/{namespace}/{name}`), cannot be used as subagents/tools, and each backend requires bespoke kagent code for chat-style interaction. + +Meanwhile, kagent's native agent protocol is A2A: declarative and BYO agents serve A2A on port 8080, the controller proxies and streams it, and human-in-the-loop (HITL) flows ride on A2A task pause/resume (`docs/architecture/a2a-subagents.md`). + +This EP proposes adopting ACP as the standard protocol surface between kagent and harness backends, and evaluates the integration options. + +## Motivation + +The immediate problem is the **interaction channel on Substrate**. The TUI path depends on SSH, which Substrate does not support (and may never support). That leaves OpenClaw usable only through its own proxied web UI, Hermes with no interaction channel at all, and TUI-only agents like Codex impossible to integrate. Each backend also requires bespoke integration code: gateway API client, chat proxying, session handling, and approval plumbing. + +ACP dissolves both problems at once: OpenClaw, Hermes, Codex, and much of the broader ecosystem (Gemini CLI, Claude Agent, Goose, etc.) already implement ACP. If kagent implements the *client* side of ACP once, it can talk to all of them — no SSH, no TUI, no per-backend UI. By speaking ACP, kagent can: + +* provide a chat interaction channel for every harness backend on Substrate, including ones that only ship a TUI today (Codex, etc.), +* interact with any ACP-capable harness through one code path, +* promote harnesses from opaque sandboxes to first-class agents that the UI, other agents, and A2A clients can talk to, +* reuse ACP's built-in `session/request_permission` flow to surface harness tool approvals in kagent's existing HITL UX, +* reduce the per-backend surface area in `go/core/pkg/sandboxbackend/`. + +### Goals + +* Define a single, backend-agnostic protocol surface (ACP) for conversational interaction with `AgentHarness` resources. +* Expose ACP-connected harnesses as A2A agents so they are addressable at `/api/a2a/{namespace}/{name}`, usable from the kagent UI, and invocable as subagents/tools. +* Map ACP `session/request_permission` onto kagent's HITL approval flow (A2A input-required / task pause-resume). +* Support session lifecycle parity where the backend allows it: create, prompt, cancel, list, resume, close. +* Keep the existing gateway proxy path working unchanged; ACP is additive and opt-in. +* Target the Substrate runtime exclusively; no SSH/exec-based transports. +* Establish a pattern that extends to additional ACP-capable backends (Codex, Gemini CLI, …) without new interaction-channel work. + +Success criteria: a user can chat with an OpenClaw or Hermes harness from the kagent UI through the standard agent chat surface, see streamed tool activity, and answer approval prompts — without any backend-specific UI code. + +### Non-Goals + +* Replacing A2A as kagent's native agent protocol. ACP is the harness-facing protocol; A2A remains the kagent-facing one. +* Exposing kagent agents *as ACP servers* to IDEs (Zed, VS Code). This is valuable but is a separate enhancement (see Alternatives / Future Work). +* Per-session MCP server injection for OpenClaw — the upstream bridge explicitly rejects `mcpServers` in bridge mode; MCP must be configured on the gateway/agent side. +* Implementing ACP client `fs/*` or `terminal/*` callbacks. Neither backend calls them today (OpenClaw's bridge never does; Hermes uses its own sandbox-local tools), and the harness filesystem is remote from kagent's perspective. +* Replicating the TUI experience. ACP gives a structured chat/approval surface, not a terminal; there is no plan to emulate terminals over Substrate. +* Migrating kagent's own agents (declarative/BYO) to ACP — see "Scope: which agents need ACP (and which don't)" below. +* Rewriting the AgentHarness UI (control-UI proxy) — that remains as-is. + +### Scope: which agents need ACP (and which don't) + +The shim and the bridge are keyed to the agent's **native protocol surface**, not to where it runs: + +| Agent type | Native surface | In Substrate needs | +|---|---|---| +| Declarative / BYO kagent agent | A2A over HTTP :8080 | Nothing — atenet routes HTTP directly; the existing `/api/a2a/{ns}/{name}` proxy works unchanged | +| Harness backends (OpenClaw, Hermes, Codex, …) | ACP over stdio | `acp-shim` (stdio→WS) + A2A↔ACP bridge | + +Declarative agents running as Substrate actors do **not** need the shim: they already serve A2A over HTTP, and HTTP ingress is exactly what atenet provides. The shim exists solely for agents whose only interface is stdio. + +Conversely, moving kagent's own agents to ACP would be a strict downgrade and is explicitly out of scope: + +* **Wrong direction** — in ACP the platform is the *client* and the coding agent is the *server*; declarative agents would need to grow an ACP server implementation in the ADK, replacing working A2A code for no new capability. +* **A2A is a superset for kagent's needs** — agent cards/discovery, the task model, push notifications, and subagent composition have no ACP equivalent; ACP is deliberately scoped to "a client drives one coding agent over a session." +* **No interop gain** — ACP's value here is meeting third-party agents where they already are. kagent's own agents are already on the platform-native protocol. + +A2A remains the lingua franca inside kagent; ACP is an adapter at the edge for foreign agents. + +## Implementation Details + +Three options were considered. **Option A is the recommended direction**; B and C are documented for completeness. + +### Option A (recommended): A2A↔ACP bridge — harness as a first-class agent + +Add a Go ACP client package and a per-harness bridge component that makes an ACP-capable harness look like a regular kagent agent over A2A. + +``` +UI / A2A client + │ A2A (JSON-RPC over HTTP/SSE) + ▼ +kagent controller ── /api/a2a/{ns}/{name} ──► A2A↔ACP bridge (ACP client) + │ ACP over WS (atenet ingress) + ▼ + ┌─ Substrate sandbox ─────────┐ + │ acp-shim (WS ↔ stdio) │ + │ │ stdin/stdout │ + │ ▼ │ + │ openclaw acp / hermes acp │ + │ / codex acp / … │ + └─────────────────────────────┘ +``` + +**New components** + +* `go/core/pkg/acp` (or similar): minimal ACP v1 client — JSON-RPC framing, `initialize` capability negotiation, session lifecycle, `session/update` notification dispatch, and server→client request handling (`session/request_permission`). +* A bridge runner per harness, owned by the AgentHarness controller, that: + 1. connects to the harness's in-sandbox `acp-shim` endpoint over WS (see transport below), + 2. registers an A2A endpoint for the harness at `/api/a2a/{namespace}/{name}`, + 3. translates between the two protocols. +* `acp-shim`: a small agent-agnostic stdio↔WebSocket adapter that runs inside the sandbox (design below). + +**Protocol mapping** + +| A2A (kagent native) | ACP (harness) | +|---|---| +| `message/send` (new conversation) | `session/new` + `session/prompt` | +| `message/send` (existing conversation) | `session/prompt` (same `sessionId`) | +| Streamed task events (text chunks) | `session/update` `agent_message_chunk` / `agent_thought_chunk` | +| Streamed tool events (function_call / tool_result parts) | `session/update` `tool_call` / `tool_call_update` | +| Task `input-required` (HITL pause) | `session/request_permission` (bridge responds with the user's selected option) | +| Task cancel | `session/cancel` | +| Task completion + final status | `session/prompt` response stop reason | +| kagent session/conversation id | ACP `sessionId` (persisted in kagent DB alongside the conversation) | + +**Transport: a uniform in-sandbox shim (`acp-shim`)** + +All backends are reached the same way: a single agent-agnostic shim runs inside the Substrate sandbox, exposing the agent's stdio ACP server over a WebSocket endpoint reachable through the atenet ingress (which supports WS upgrade). This is permitted by ACP's Custom Transports clause — the JSON-RPC message format and lifecycle are preserved; only the pipe moves onto the network. + +The shim is a transport adapter and nothing more. On accepting a connection it: + +1. **Auth-gates the WS handshake** — validates a bearer token (mounted file or env, same pattern as `gatewayToken`). This is the one responsibility stdio doesn't have: a network listener needs explicit auth where "I spawned the process" sufficed before. +2. **Spawns (or attaches to) the agent subprocess** with the configured argv/env/cwd. +3. **Pumps frames**: one WS text frame ⇄ one newline-delimited JSON-RPC message on the child's stdin/stdout. Child stderr goes to container logs. The shim never parses message contents — it is ACP-version-agnostic. +4. **Couples lifecycles**: WS close → SIGTERM (grace) → SIGKILL; child exit → WS close with a status code the bridge can distinguish (crash vs. clean exit). A configurable grace window keeps the child alive across bridge reconnects so `session/load`/`session/resume` can recover in-memory sessions (important for Hermes). + +Deliberately *out* of the shim: JSON-RPC parsing, session management, protocol translation — all of that lives in the bridge. Expected size: a few hundred lines of Go, statically linked. + +The shim binary is identical for every backend; only its configuration differs: + +```yaml +command: ["hermes", "acp"] # the only truly per-backend part +workdir: /sandbox +env: { ... } # backend credentials/config (written by bootstrap) +listen: :9000 +tokenFile: /var/run/acp/token +childPolicy: long-lived # or per-connection +``` + +**Image packaging**: because Substrate has no exec channel, the agent binary and the shim must both be present in the workload image at build time — but image prep is already mandatory for every backend (`spec.image` / `spec.substrate.workloadImage`). Adding a backend costs a `COPY acp-shim` line plus an entrypoint setting. If Substrate workload templates support an init-container + shared volume, the shim could instead be injected at workload creation, letting stock upstream agent images work unmodified (needs verification). + +**Per-backend child commands** + +| Backend | Child command | Notes | +|---|---|---| +| OpenClaw | `openclaw acp --url ws://localhost: --token-file …` | `openclaw acp` is itself a stdio→WS bridge; pointed at the sandbox-local gateway it behaves like any other stdio ACP agent under the shim. Session pinning via `--session`/`_meta.sessionKey`. | +| Hermes | `hermes acp` | Requires the `hermes-agent[acp]` extra in the image; reads the `~/.hermes/{.env,config.yaml}` kagent's bootstrap already writes. In-memory sessions → `childPolicy: long-lived`. | +| Codex (future) | `codex acp` | Credentials (`~/.codex/auth.json` / `OPENAI_API_KEY`) written by bootstrap. | +| Gemini CLI, Goose, … (future) | `gemini --experimental-acp`, etc. | Same pattern; no new transport work. | + +Using the shim for OpenClaw too (rather than running `openclaw acp` on the kagent side against the gateway WSS) keeps one code path for all backends: every harness is "shim + stdio ACP command in the sandbox", and the bridge needs exactly one transport implementation. Backend differences that remain — advertised capabilities, approval semantics, session durability — surface through ACP `initialize` negotiation, not through transport variation. + +If/when the upstream Streamable HTTP transport stabilizes and agents adopt it, agents would serve ACP over HTTP natively in-sandbox and the shim disappears — with no changes to the bridge's A2A-facing API. + +**CRD sketch** + +ACP exposure is opt-in via a new optional field on `AgentHarnessSpec`: + +```yaml +apiVersion: kagent.dev/v1alpha2 +kind: AgentHarness +spec: + backend: openclaw + runtime: substrate + substrate: + gatewayTokenSecretRef: { name: my-harness-token } + acp: # new, optional + enabled: true + sessionKey: agent:main:main # optional; OpenClaw only — pins the Gateway session key +``` + +When `spec.acp.enabled` is true and the harness is Ready, the controller starts the bridge and publishes the A2A endpoint (e.g., in `status.connection` and the agents list). No change to existing fields. Future ACP-only backends (e.g., `codex`) slot into the same shape: backend-specific bootstrap plus an ACP transport — no new interaction-channel machinery. + +**Capability degradation** + +The bridge MUST honor `initialize` capabilities: e.g., only offer session resume/list where advertised, and degrade gracefully (OpenClaw has no plan/thought streaming; Hermes sessions don't survive a server-process restart). Unsupported ACP features simply don't surface in A2A events. + +**UI impact** + +Because the bridge exposes harnesses as standard A2A agents, the existing kagent chat surface (agent chat page, streaming, HITL approval prompts) works as-is — that is the main payoff of Option A. Expected UI changes are limited to: + +* listing ACP-enabled harnesses alongside regular agents (sourced from the same agents list the bridge registers into), +* an entry point on the AgentHarness detail page ("Chat") linking to the standard agent chat for that harness, +* optionally rendering ACP-specific niceties the A2A mapping already carries (thought chunks, tool-call progress) — these reuse the existing event-rendering components. + +No new chat code path, no backend-specific UI. The bespoke OpenClaw control-UI proxy remains available unchanged. + +### Option B: UI chat via ACP only + +Teach the kagent UI/HTTP server to drive harness sessions over ACP directly (replacing the bespoke gateway chat proxy) without exposing an A2A surface. Smaller scope than Option A, but harnesses remain non-composable — they can't be used as subagents/tools or reached by external A2A clients — and a second chat code path persists in the UI. Rejected as the primary direction; Option A subsumes it. + +### Option C: kagent agents as ACP servers (future work) + +The inverse direction: a `kagent acp` CLI that speaks ACP over stdio to an IDE (Zed, VS Code ACP client) and forwards to a cluster-hosted kagent agent over A2A — analogous to `openclaw acp`'s relationship to its gateway. This would let any ACP-capable editor drive kagent agents. Deliberately out of scope here; should be its own EP. + +### Test Plan + +* **Unit**: ACP client package tested against a fake in-process ACP server (table-driven, covering capability negotiation, session lifecycle, update dispatch, permission round-trips, and malformed-frame handling). Bridge translation logic tested with golden A2A event sequences for given ACP update streams. `acp-shim` tested with a fake stdio child (frame pumping, auth rejection, lifecycle coupling, reconnect grace window). +* **Protocol smoke tests**: following OpenClaw's documented proof procedure — drive `openclaw acp` over stdio with raw JSON-RPC frames covering `initialize`, `session/new`, `session/list` (absolute `cwd`), `session/resume`, `session/close`, duplicate close, missing resume; assert advertised `sessionCapabilities` and gateway `sessions.list` log entries. +* **E2E**: extend the existing harness e2e setup (Kind + AgentHarness, Substrate runtime) with an ACP-enabled OpenClaw harness; send an A2A `message/send` through `/api/a2a/{ns}/{name}`, assert streamed chunks and a terminal task state. Hermes e2e gated on the in-sandbox transport shim being available in CI. + +## Alternatives + +* **Keep proprietary gateway APIs per backend** — status quo. Works, but every new backend multiplies bespoke code, and harnesses stay outside the agent graph. +* **Wrap harnesses via MCP instead** (`openclaw mcp serve`, Hermes MCP) — exposes harness capabilities as *tools*, not conversational agents; loses streaming UX, session semantics, and the approval flow. Complementary, not a substitute. +* **Wait for ACP Streamable HTTP transport standardization** — the [v1 Transports spec](https://agentclientprotocol.com/protocol/v1/transports) lists Streamable HTTP as a draft proposal in progress (with WS stream revisions already merged into the draft text via [#1124](https://github.com/agentclientprotocol/agent-client-protocol/pull/1124)), so this is closer than "unspecified future work" — but it is not yet normative, and neither OpenClaw nor Hermes serves it. The OpenClaw WS bridge already gives a remote-friendly path today under the Custom Transports clause, and the bridge interface can swap to Streamable HTTP later without API changes. + +## Open Questions + +* **Substrate + stdio-only ACP agents (Hermes, Codex, …)**: all backends use the in-sandbox `acp-shim` (see Implementation Details). Remaining sub-questions: shim delivery (baked into workload images vs. injected via init-container + shared volume, if Substrate workload templates allow it), and whether to wait for upstream Streamable HTTP adoption instead for some backends. The shim, once built, covers OpenClaw, Hermes, Codex, and any other stdio ACP agent uniformly. +* **Shim child policy**: one child per WS connection (clean lifecycle) vs. one long-lived child with exactly-one-client semantics (preserves in-memory ACP sessions — required for Hermes). Proposed default: long-lived, configurable per backend. +* **Hermes session durability**: ACP sessions are in-memory per `hermes acp` process; a harness or bridge restart loses ACP session mappings even though Hermes' own task persistence survives. Should the bridge transparently start a new ACP session bound to the same kagent conversation, and how is that surfaced to the user? +* ~~**Gateway WS reachability**~~ — resolved: the atenet router's Envoy configuration explicitly enables WebSocket upgrades (`UpgradeConfigs: {UpgradeType: websocket}` in `cmd/atenet/internal/app/router/xds.go`), so both `openclaw acp --url wss://…` and an in-sandbox WS shim are reachable through the Substrate ingress path. +* **Auth model**: ACP defines an `authenticate` method, but OpenClaw's bridge authenticates to the gateway out-of-band via token. Should kagent standardize on out-of-band auth (Secret-resolved tokens, as today) and ignore ACP `authenticate`, or support both? +* **Bridge placement**: controller-managed subprocess vs. per-harness sidecar/Deployment. Sidecar isolates failure domains and scales naturally, but adds a pod per harness; subprocess is cheaper but couples bridge lifetime to the controller. +* **EP number**: placeholder `XXXX` pending issue creation; rename the file and update links before merging. diff --git a/docker/acp-sandbox/Dockerfile b/docker/acp-sandbox/Dockerfile new file mode 100644 index 0000000000..0304acfdf9 --- /dev/null +++ b/docker/acp-sandbox/Dockerfile @@ -0,0 +1,168 @@ +# kagent ACP sandbox image family. +# +# Layout: +# Stage "builder" — compiles the acp-shim static binary from this repo. +# Stage "base" — minimal runtime + acp-shim entrypoint. Published as +# ghcr.io/kagent-dev/kagent/acp-sandbox-base. Contains +# NO agent: it is the uniform transport layer. +# Agent targets — thin layers that install one stdio ACP agent and set +# its launch command. Build with --target : +# +# docker build -f docker/acp-sandbox/Dockerfile --target hermes go/ +# docker build -f docker/acp-sandbox/Dockerfile --target openclaw go/ +# +# The contract between base and agent layers is intentionally tiny: +# - ENTRYPOINT is acp-shim; it serves ws://0.0.0.0:9000/acp. +# - The agent layer provides the child command either as CMD args after +# "--" or via the ACP_SHIM_CHILD env var. +# - The bearer token is mounted at /var/run/acp/token by the Substrate +# workload spec (same pattern as the AgentHarness gatewayToken). +# +# OpenClaw note: NemoClaw's sandbox-base is built from the NemoClaw repo +# for a different runtime, so we don't extend it. The "openclaw" target below installs +# the OpenClaw CLI directly on the kagent base (same npm install NemoClaw's +# Dockerfile.base uses) and runs its own gateway alongside the shim. + +### Stage 0: build the acp-shim Go binary +ARG BASE_IMAGE_REGISTRY=cgr.dev +ARG BUILDPLATFORM +FROM --platform=$BUILDPLATFORM $BASE_IMAGE_REGISTRY/chainguard/go:latest AS builder +ARG TARGETARCH +ARG TARGETOS + +WORKDIR /workspace + +COPY go.mod go.sum ./ +RUN --mount=type=cache,target=/root/go/pkg/mod,rw \ + --mount=type=cache,target=/root/.cache/go-build,rw \ + go mod download + +COPY api/ api/ +COPY core/ core/ +COPY adk/ adk/ + +ARG LDFLAGS +RUN --mount=type=cache,target=/root/go/pkg/mod,rw \ + --mount=type=cache,target=/root/.cache/go-build,rw \ + CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} \ + go build -a -trimpath -ldflags "$LDFLAGS" -o /acp-shim ./core/cmd/acp-shim + +### Stage 1: kagent-owned sandbox base — shim only, no agent. +FROM debian:trixie-slim AS base +ENV DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + git \ + && rm -rf /var/lib/apt/lists/* + +# Single unprivileged user. Substrate provides the isolation boundary +# (microVM); we don't need a separate gateway/sandbox user split here. +RUN groupadd -r agent && useradd -r -m -g agent -d /home/agent -s /bin/bash agent \ + && mkdir -p /var/run/acp \ + && chown agent:agent /var/run/acp + +COPY --from=builder /acp-shim /usr/local/bin/acp-shim + +USER agent +WORKDIR /home/agent +EXPOSE 9000 + +ENV ACP_SHIM_TOKEN_FILE=/var/run/acp/token + +ENTRYPOINT ["/usr/local/bin/acp-shim"] +# Agent layers append the child command, e.g.: +# CMD ["--", "hermes", "acp"] + +### Shared: base + Node.js 22 for npm-distributed agents. Debian trixie's +### apt nodejs is v20, below OpenClaw's >=22.19 requirement (NemoClaw's +### sandbox-base uses node:22-trixie-slim for the same reason), so take +### the runtime from the official image. +FROM node:22-trixie-slim AS node +FROM base AS node-base +USER root +COPY --from=node /usr/local/bin/node /usr/local/bin/node +COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules +RUN ln -s ../lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \ + && ln -s ../lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx \ + && node --version && npm --version +USER agent + +### Agent target: Hermes (pip) +FROM base AS hermes +USER root +RUN apt-get update && apt-get install -y --no-install-recommends python3 python3-pip python3-venv \ + && rm -rf /var/lib/apt/lists/* +USER agent +RUN python3 -m venv /home/agent/.venv \ + && /home/agent/.venv/bin/pip install --no-cache-dir "hermes-agent[acp]" +ENV PATH="/home/agent/.venv/bin:${PATH}" +# Hermes ACP sessions are in-memory: the child MUST be long-lived (default) +# so session/load works across bridge reconnects. +CMD ["--", "hermes", "acp"] + +### Agent target: OpenClaw — built on the kagent base, NOT on NemoClaw's +### sandbox-base (that image is built from the NemoClaw repo and carries +### NemoClaw-specific contracts — gateway/sandbox users, gosu, .openclaw +### tree — that we don't want to depend on). We install the OpenClaw CLI +### the same way NemoClaw's Dockerfile.base does: npm install -g. +### +### `openclaw acp` is a stdio→WS bridge that needs a sandbox-local gateway, +### so this target uses a launcher that starts `openclaw gateway` in the +### background before the shim takes over. +FROM node-base AS openclaw +ARG OPENCLAW_VERSION=2026.5.27 +USER root +RUN npm install -g "openclaw@${OPENCLAW_VERSION}" +# Gateway helper: start the sandbox-local gateway if it isn't accepting +# connections, and wait for it. Used both at boot and from the ACP child +# wrapper — under Substrate the actor is checkpointed/restored and any +# process started before the snapshot (the gateway, or a supervising +# subshell) cannot be relied on afterwards, so the gateway is re-ensured +# at connection time by a freshly spawned process. +COPY --chmod=755 <<'EOF' /usr/local/bin/openclaw-gateway-ensure +#!/bin/sh +set -u +: "${OPENCLAW_GATEWAY_PORT:=18789}" +probe() { curl -s -o /dev/null --max-time 2 "http://127.0.0.1:${OPENCLAW_GATEWAY_PORT}/"; } +probe && exit 0 +nohup openclaw gateway run --port "${OPENCLAW_GATEWAY_PORT}" --allow-unconfigured \ + >> "${HOME}/.openclaw/gateway.log" 2>&1 & +i=0 +while [ "$i" -lt 60 ]; do + probe && exit 0 + i=$((i+1)) + sleep 1 +done +echo "openclaw-gateway-ensure: gateway did not come up on :${OPENCLAW_GATEWAY_PORT}" >&2 +exit 1 +EOF +# ACP child: spawned by the shim per bridge; guarantees a live gateway +# before exec'ing the stdio<->WS bridge. +COPY --chmod=755 <<'EOF' /usr/local/bin/openclaw-acp-child +#!/bin/sh +set -eu +: "${OPENCLAW_GATEWAY_PORT:=18789}" +/usr/local/bin/openclaw-gateway-ensure +exec openclaw acp --url "ws://127.0.0.1:${OPENCLAW_GATEWAY_PORT}" +EOF +COPY --chmod=755 <<'EOF' /usr/local/bin/openclaw-acp-entrypoint +#!/bin/sh +# Write the gateway config (loopback bind, auth mode "none" — gateway and +# ACP client share this container; the externally reachable surface is the +# shim, which enforces its own bearer token), pre-warm the gateway so the +# golden snapshot includes a hot npm/node page cache, then hand off to the +# shim with the gateway-ensuring child wrapper. +set -eu +: "${OPENCLAW_GATEWAY_PORT:=18789}" +mkdir -p "${HOME}/.openclaw" +if [ ! -f "${HOME}/.openclaw/openclaw.json" ]; then + printf '{"gateway":{"port":%s,"bind":"loopback","auth":{"mode":"none"}}}\n' \ + "${OPENCLAW_GATEWAY_PORT}" > "${HOME}/.openclaw/openclaw.json" +fi +/usr/local/bin/openclaw-gateway-ensure || true +exec /usr/local/bin/acp-shim "$@" -- /usr/local/bin/openclaw-acp-child +EOF +USER agent +ENTRYPOINT ["/usr/local/bin/openclaw-acp-entrypoint"] +CMD [] diff --git a/docker/acp-sandbox/README.md b/docker/acp-sandbox/README.md new file mode 100644 index 0000000000..b59c090364 --- /dev/null +++ b/docker/acp-sandbox/README.md @@ -0,0 +1,97 @@ +# ACP sandbox images + +Prototype image family for kagent's ACP integration (see +[design/EP-XXXX-acp-integration.md](../../design/EP-XXXX-acp-integration.md)). +Every image in this family runs the same entrypoint — `acp-shim` +([go/core/cmd/acp-shim](../../go/core/cmd/acp-shim)) — which exposes a stdio +ACP agent over `ws://0.0.0.0:9000/acp`, reachable through Substrate's atenet +ingress (WebSocket upgrades are enabled there). + +## Stages and targets + +The [Dockerfile](Dockerfile) defines: + +| Stage / target | Buildable | What it is | +|---|---|---| +| `builder` | internal | Compiles the `acp-shim` static binary from this repo. | +| `base` | `--target base` | kagent-owned runtime (`debian:trixie-slim`) with a single unprivileged `agent` user and the shim as `ENTRYPOINT`. Contains **no agent** — it is the uniform transport layer. | +| `node` / `node-base` | internal | `base` + Node.js 22 (trixie apt ships v20, below OpenClaw's >=22.19 requirement). | +| `hermes` | `--target hermes` | `base` + Hermes installed via pip (`hermes-agent[acp]`). Child command: `hermes acp`. | +| `openclaw` | `--target openclaw` | `node-base` + the OpenClaw CLI (`npm install -g openclaw`). Runs a sandbox-local `openclaw gateway` alongside the shim via a small launcher. | + +The base↔agent contract is intentionally tiny: + +- `ENTRYPOINT` is `acp-shim`; it serves `ws://0.0.0.0:9000/acp`. +- The agent layer provides the child command as `CMD` args after `--`, or via + the `ACP_SHIM_CHILD` env var. +- The bearer token is read from the file in `ACP_SHIM_TOKEN_FILE` + (default `/var/run/acp/token`). + +### Why a kagent-owned base instead of extending NemoClaw's sandbox-base + +`ghcr.io/kagent-dev/nemoclaw/sandbox-base` carries NemoClaw-specific +contracts: its gateway/sandbox user split, gosu privilege separation, and +`.openclaw` directory tree are a stable contract with NemoClaw, not with +Substrate. Substrate sandboxes get isolation from the microVM boundary, so that +machinery is dead weight here — and adopting the image would couple every agent +to NemoClaw's release cadence. So the `openclaw` target installs the OpenClaw +CLI directly on the kagent base (the same `npm install -g` NemoClaw's +`Dockerfile.base` uses) rather than extending NemoClaw's image. + +## Building + +From the repo root (build context is `go/`): + +```sh +docker build -f docker/acp-sandbox/Dockerfile --target base -t kagent/acp-sandbox-base go/ +docker build -f docker/acp-sandbox/Dockerfile --target hermes -t kagent/acp-sandbox-hermes go/ +docker build -f docker/acp-sandbox/Dockerfile --target openclaw -t kagent/acp-sandbox-openclaw go/ +``` + +## Smoke test (no cluster needed) + +`base` has no agent, so smoke-test an agent target (`hermes` is simplest — no +gateway): + +```sh +echo -n s3cret > /tmp/token +docker run --rm -p 9000:9000 -v /tmp/token:/var/run/acp/token kagent/acp-sandbox-hermes +# then from another shell, speak newline-delimited JSON-RPC over WS: +websocat -H "Authorization: Bearer s3cret" ws://localhost:9000/acp +{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":1,"clientCapabilities":{}}} +``` + +The shim accepts the token either as an `Authorization: Bearer` header or as an +`access_token` query parameter (`ws://localhost:9000/acp?access_token=s3cret`). + +## Standalone cluster tests + +Two manifests in this folder exercise the image **without** the AgentHarness +controller. Each file's header comment has the full step-by-step; in short: + +- [test-deployment.yaml](test-deployment.yaml) — runs the `openclaw` image as a + plain Kubernetes Deployment + Service. Easiest path; works with a kind-loaded + local image. + + ```sh + docker build -f docker/acp-sandbox/Dockerfile --target openclaw -t acp-sandbox-openclaw:dev go/ + kind load docker-image acp-sandbox-openclaw:dev --name kagent + kubectl apply -f docker/acp-sandbox/test-deployment.yaml + kubectl -n kagent port-forward svc/acp-shim-test 9000:9000 + websocat "ws://localhost:9000/acp?access_token=dev-token" + ``` + +- [test-substrate.yaml](test-substrate.yaml) — runs the same image as a real + Substrate actor (gVisor microVM) via a manual `ActorTemplate` + `ate-api` + `CreateActor`. Requires pushing a digest-pinned image to a registry (Substrate + workers pull it themselves). See the file header for the `grpcurl` / atenet + routing details. + +## Open items (tracked in the EP) + +- OpenClaw target: the `openclaw gateway` invocation in the launcher is a + best-guess prototype — needs the real port/auth wiring verified. +- Agent credential injection (`~/.hermes/.env`, OpenClaw provider keys, ...) + belongs to the harness bootstrap, not these images. +- Whether the shim is baked (this approach) or injected via init container + + shared volume. diff --git a/docker/acp-sandbox/test-deployment.yaml b/docker/acp-sandbox/test-deployment.yaml new file mode 100644 index 0000000000..38f6ed7b1a --- /dev/null +++ b/docker/acp-sandbox/test-deployment.yaml @@ -0,0 +1,94 @@ +# Standalone test deployment for the acp-shim openclaw image. +# +# This bypasses the AgentHarness controller entirely (the harness runtimes +# don't yet know about the shim — that's EP implementation work) and runs +# the image as a plain Deployment so the shim can be exercised end to end: +# +# docker build -f docker/acp-sandbox/Dockerfile --target openclaw \ +# -t acp-sandbox-openclaw:dev go/ +# kind load docker-image acp-sandbox-openclaw:dev --name +# kubectl apply -f docker/acp-sandbox/test-deployment.yaml +# kubectl -n kagent port-forward svc/acp-shim-test 9000:9000 +# +# Then connect with websocat: +# +# websocat "ws://localhost:9000/acp?access_token=dev-token" +# {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":1,"clientCapabilities":{}}} +apiVersion: v1 +kind: Secret +metadata: + name: acp-shim-test-token + namespace: kagent +stringData: + token: dev-token +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: acp-shim-test + namespace: kagent + labels: + app: acp-shim-test +spec: + replicas: 1 + selector: + matchLabels: + app: acp-shim-test + template: + metadata: + labels: + app: acp-shim-test + spec: + containers: + - name: openclaw + image: acp-sandbox-openclaw:dev + imagePullPolicy: IfNotPresent + ports: + - name: acp + containerPort: 9000 + env: + # OpenClaw needs at least one model provider to do useful work; + # for shim transport testing, initialize/session plumbing is + # enough. + - name: OPENCLAW_GATEWAY_PORT + value: "18789" + envFrom: + # Model provider API keys for real prompts. Same env-var contract + # the harness uses (openclaw.DefaultAPIKeyEnvVar: _API_KEY). + # Create with: + # kubectl -n kagent create secret generic openclaw-provider-keys \ + # --from-literal=OPENAI_API_KEY=sk-... + # then: kubectl -n kagent rollout restart deployment/acp-shim-test + - secretRef: + name: openclaw-provider-keys + optional: true + volumeMounts: + - name: acp-token + mountPath: /var/run/acp + readOnly: true + readinessProbe: + httpGet: + path: /healthz + port: acp + initialDelaySeconds: 2 + periodSeconds: 5 + volumes: + - name: acp-token + secret: + secretName: acp-shim-test-token + items: + - key: token + path: token +--- +apiVersion: v1 +kind: Service +metadata: + name: acp-shim-test + namespace: kagent +spec: + selector: + app: acp-shim-test + ports: + - name: acp + port: 9000 + targetPort: acp diff --git a/docker/acp-sandbox/test-substrate.yaml b/docker/acp-sandbox/test-substrate.yaml new file mode 100644 index 0000000000..fde6ce3b76 --- /dev/null +++ b/docker/acp-sandbox/test-substrate.yaml @@ -0,0 +1,110 @@ +# Standalone Substrate test for the acp-shim openclaw image. +# +# This runs the image as a real Substrate actor (gVisor microVM) instead of a +# plain Deployment, bypassing the AgentHarness controller (manual ActorTemplate +# + manual ate-api CreateActor). The shim listens on :80 because atenet-router +# routes Host {actorID}.actors.resources.substrate.ate.dev to actor port 80. +# +# Substrate workers pull the image themselves while building the golden +# snapshot, so a kind-local image tag is not visible to them — push the image +# to a registry first (ttl.sh works for throwaway testing). Substrate also +# requires digest-pinned image refs (changing the image invalidates +# snapshots), so grab the digest from the push output: +# +# docker build -f docker/acp-sandbox/Dockerfile --target openclaw \ +# -t ttl.sh/kagent-acp-openclaw:4h go/ +# docker push ttl.sh/kagent-acp-openclaw:4h # prints the sha256 digest +# # update spec.containers[0].image below with @sha256:... +# kubectl apply -f docker/acp-sandbox/test-substrate.yaml +# kubectl -n kagent get actortemplate acp-shim-test -w # wait Phase=Ready +# +# Create the actor via ate-api (gRPC reflection is enabled; auth is a +# projected SA token with the api.ate-system.svc audience): +# +# TOKEN=$(kubectl -n kagent create token kagent-controller --audience=api.ate-system.svc) +# kubectl -n ate-system port-forward svc/api 8443:443 & +# grpcurl -insecure -H "authorization: Bearer $TOKEN" \ +# -d '{"actor_id":"acp-shim-dev","actor_template_namespace":"kagent","actor_template_name":"acp-shim-test"}' \ +# localhost:8443 ateapi.Control/CreateActor +# +# Connect through atenet-router (Host-header routing): +# +# kubectl -n ate-system port-forward svc/atenet-router 8080:80 & +# curl -s -H "Host: acp-shim-dev.actors.resources.substrate.ate.dev" \ +# http://localhost:8080/healthz +# websocat -H "Host: acp-shim-dev.actors.resources.substrate.ate.dev" \ +# "ws://localhost:8080/acp?access_token=dev-token" +# +# (Browsers can't set a Host header; the kagent controller's +# /api/agentharnesses/{ns}/{name}/acp proxy handles that for the UI — this +# manual websocat path is only for testing without the controller.) +# +# Cleanup: +# grpcurl ... -d '{"actor_id":"acp-shim-dev"}' localhost:8443 ateapi.Control/DeleteActor +# kubectl delete -f docker/acp-sandbox/test-substrate.yaml +# +# Notes / open items: +# - The token reaches the shim via the ACP_SHIM_TOKEN env var (secretKeyRef): +# ActorTemplate containers support env but not volume mounts. +# - The image runs as the unprivileged "agent" user; binding :80 relies on +# gVisor's netstack not enforcing privileged ports. If the actor logs an +# EACCES bind error this needs a root user or a routable high port. +# - pauseImage/runsc values mirror the kagent controller defaults +# (go/core/pkg/app/app.go) — the same ones its generated templates use. +apiVersion: v1 +kind: Secret +metadata: + name: acp-shim-test-token + namespace: kagent +stringData: + token: dev-token +--- +apiVersion: ate.dev/v1alpha1 +kind: ActorTemplate +metadata: + name: acp-shim-test + namespace: kagent + labels: + app: acp-shim-test +spec: + pauseImage: gcr.io/gke-release/pause@sha256:bcbd57ba5653580ec647b16d8163cdd1112df3609129b01f912a8032e48265da + runsc: + amd64: + url: gs://gvisor/releases/nightly/2026-05-19/x86_64/runsc + sha256Hash: a397be1abc2420d26bce6c70e6e2ff96c73aaaab929756c56f5e2089ea842b63 + arm64: + url: gs://gvisor/releases/nightly/2026-05-19/aarch64/runsc + sha256Hash: 1ba2366ae2efceba166046f51a4104f9261c9cb72c6db8f5b3fe2dc57dea86b9 + workerPoolRef: + name: kagent-default + namespace: kagent + snapshotsConfig: + location: gs://ate-snapshots/kagent/acp-shim-test + containers: + - name: openclaw + # Must be digest-pinned (substrate rejects mutable tags) and pullable + # by the workers — push first, then paste the digest from the push. + image: ttl.sh/kagent-acp-openclaw@sha256:f8c7b73253dd00098d3f2cb2c3a3d7585fa549daadeefdacd563362e4d40c7e6 + # Same launcher as the plain-Deployment test, but the shim binds :80 + # (the atenet-router target port) instead of :9000. + command: + - /usr/local/bin/openclaw-acp-entrypoint + - --listen + - ":80" + env: + - name: ACP_SHIM_TOKEN + valueFrom: + secretKeyRef: + name: acp-shim-test-token + key: token + - name: OPENCLAW_GATEWAY_PORT + value: "18789" + # Model provider key for real prompts (optional for transport tests). + # kubectl -n kagent create secret generic openclaw-provider-keys \ + # --from-literal=OPENAI_API_KEY=sk-... + - name: OPENAI_API_KEY + valueFrom: + secretKeyRef: + name: kagent-openai + key: OPENAI_API_KEY + optional: true diff --git a/go/Makefile b/go/Makefile index b2dc7c35b9..f7f77294af 100644 --- a/go/Makefile +++ b/go/Makefile @@ -161,8 +161,6 @@ ENVTEST_K8S_VERSION ?= $(shell go list -m -f "{{ .Version }}" k8s.io/api | awk - GOLANGCI_LINT_VERSION ?= v2.12.2 KAL_VERSION ?= v0.0.0-20260423112246-3fa174937a6b SQLC_VERSION ?= v1.30.0 -BUF ?= $(LOCALBIN)/buf -BUF_VERSION ?= v1.52.1 .PHONY: controller-gen controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. @@ -196,11 +194,6 @@ sqlc: $(SQLC) ## Download sqlc locally if necessary. $(SQLC): $(LOCALBIN) $(call go-install-tool,$(SQLC),github.com/sqlc-dev/sqlc/cmd/sqlc,$(SQLC_VERSION)) -.PHONY: buf -buf: $(BUF) ## Download buf locally if necessary (OpenShell proto generation). -$(BUF): $(LOCALBIN) - $(call go-install-tool,$(BUF),github.com/bufbuild/buf/cmd/buf,$(BUF_VERSION)) - # go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist # $1 - target path with name of binary # $2 - package url which can be installed @@ -216,7 +209,3 @@ mv $(1) $(1)-$(3) ;\ } ;\ ln -sf $(1)-$(3) $(1) endef - -.PHONY: openshell-proto -openshell-proto: buf ## Regenerate OpenShell stubs from api/openshell/proto (buf; needs protoc-gen-go + protoc-gen-go-grpc on PATH). - @cd api/openshell/proto && $(BUF) generate diff --git a/go/api/config/crd/bases/kagent.dev_agentharnesses.yaml b/go/api/config/crd/bases/kagent.dev_agentharnesses.yaml index 9c4c1ee2a6..39371dfebc 100644 --- a/go/api/config/crd/bases/kagent.dev_agentharnesses.yaml +++ b/go/api/config/crd/bases/kagent.dev_agentharnesses.yaml @@ -38,8 +38,8 @@ spec: schema: openAPIV3Schema: description: |- - AgentHarness is a generic remote execution environment provisioned by a backend - (e.g. OpenShell) and addressable by exec/SSH. + AgentHarness is a generic remote execution environment provisioned by a + backend (OpenClaw or Hermes) running on Agent Substrate. properties: apiVersion: description: |- @@ -71,7 +71,6 @@ spec: description: Backend selects the control plane to use. Required. enum: - openclaw - - nemoclaw - hermes type: string channels: @@ -203,8 +202,8 @@ spec: exclusive rule: '!(size(self.allowedUserIDs) > 0 && has(self.allowedUserIDsFrom))' openclaw: - description: OpenClaw configures OpenClaw/NemoClaw-specific - Slack routing. + description: OpenClaw configures OpenClaw-specific Slack + routing. properties: allowlistChannels: description: AllowlistChannels is required when channelAccess @@ -492,8 +491,8 @@ spec: image: description: |- Image is the container image to run in the harness VM, if the backend - supports per-resource images. Backends openclaw and nemoclaw pin the image - to the NemoClaw sandbox base when this field is empty; backend hermes pins + supports per-resource images. Backend openclaw pins the image + to the OpenClaw sandbox base when this field is empty; backend hermes pins to the Hermes sandbox base image when empty. type: string modelConfigRef: @@ -515,11 +514,10 @@ spec: type: array type: object runtime: - default: openshell + default: substrate description: Runtime selects the harness provisioning stack. Defaults - to openshell when unset. + to substrate when unset. enum: - - openshell - substrate type: string substrate: @@ -528,6 +526,9 @@ spec: gatewayToken: description: |- GatewayToken is the OpenClaw gateway Bearer token for this harness. + Optional: when neither gatewayToken nor gatewayTokenSecretRef is set, the + controller generates a random token and stores it in a Secret named + "-gateway-token" (key "token"), which can be retrieved later. Prefer gatewayTokenSecretRef for production secrets. minLength: 1 type: string @@ -574,15 +575,13 @@ spec: - name type: object workloadImage: - description: WorkloadImage overrides the default nemoclaw/openclaw - sandbox image in the ActorTemplate. + description: WorkloadImage overrides the default openclaw sandbox + image in the ActorTemplate. type: string type: object x-kubernetes-validations: - - message: Exactly one of gatewayToken or gatewayTokenSecretRef must - be specified - rule: (has(self.gatewayToken) && !has(self.gatewayTokenSecretRef)) - || (!has(self.gatewayToken) && has(self.gatewayTokenSecretRef)) + - message: Specify at most one of gatewayToken or gatewayTokenSecretRef + rule: '!(has(self.gatewayToken) && has(self.gatewayTokenSecretRef))' required: - backend type: object @@ -590,8 +589,8 @@ spec: - message: slack backend-specific settings must match spec.backend rule: '!has(self.channels) || self.channels.all(c, c.type != ''slack'' || (has(c.slack) && ((self.backend == ''hermes'' && has(c.slack.hermes) - && !has(c.slack.openclaw)) || ((self.backend == ''openclaw'' || self.backend - == ''nemoclaw'') && has(c.slack.openclaw) && !has(c.slack.hermes)))))' + && !has(c.slack.openclaw)) || (self.backend == ''openclaw'' && has(c.slack.openclaw) + && !has(c.slack.hermes)))))' - message: spec.substrate may only be set when runtime is substrate rule: '!has(self.substrate) || self.runtime == ''substrate''' - message: spec.substrate is required when runtime is substrate @@ -610,7 +609,6 @@ spec: environment. Additional backends may be added in the future. enum: - openclaw - - nemoclaw - hermes type: string id: diff --git a/go/api/config/crd/bases/kagent.dev_sandboxagents.yaml b/go/api/config/crd/bases/kagent.dev_sandboxagents.yaml index 3f8f594b50..8fce16ac0b 100644 --- a/go/api/config/crd/bases/kagent.dev_sandboxagents.yaml +++ b/go/api/config/crd/bases/kagent.dev_sandboxagents.yaml @@ -27,7 +27,7 @@ spec: schema: openAPIV3Schema: description: SandboxAgent declares an agent that runs in an isolated sandbox - (agent-sandbox or Agent Substrate). + on Agent Substrate. properties: apiVersion: description: |- @@ -10951,14 +10951,6 @@ spec: rule: '!has(self.systemMessage) || !has(self.systemMessageFrom)' description: type: string - platform: - default: agent-sandbox - description: Platform selects the sandbox control plane. Defaults - to agent-sandbox. - enum: - - agent-sandbox - - substrate - type: string sandbox: description: |- Sandbox configures sandboxed execution behavior shared across runtimes. @@ -11296,8 +11288,7 @@ spec: type: array type: object substrate: - description: Substrate is optional substrate-specific settings when - platform is substrate. + description: Substrate is optional Agent Substrate-specific settings. properties: snapshotsConfig: description: |- @@ -11335,12 +11326,10 @@ spec: type: string type: object x-kubernetes-validations: - - message: spec.skills is not supported when spec.platform is substrate - rule: '!has(self.skills) || self.platform != ''substrate''' - - message: spec.substrate may only be set when spec.platform is substrate - rule: '!has(self.substrate) || self.platform == ''substrate''' - - message: BYO agents are not supported when spec.platform is substrate - rule: '!has(self.type) || self.type != ''BYO'' || self.platform != ''substrate''' + - message: spec.skills is not supported for sandbox agents + rule: '!has(self.skills)' + - message: BYO agents are not supported for sandbox agents + rule: '!has(self.type) || self.type != ''BYO''' - message: type must be specified rule: has(self.type) - message: type must be either Declarative or BYO diff --git a/go/api/httpapi/types.go b/go/api/httpapi/types.go index 0ef5eb3a61..3e1d1e62d7 100644 --- a/go/api/httpapi/types.go +++ b/go/api/httpapi/types.go @@ -136,25 +136,17 @@ func AgentResourceFrom(agent v1alpha2.AgentObject) *AgentResource { return res } -// OpenshellAgentHarnessListEntry is set when this row is a kagent.dev/v1alpha2 AgentHarness (openshell backend), -// merged into GET /api/agents for UI alongside Agent CRs. -type OpenshellAgentHarnessListEntry struct { - Backend v1alpha2.AgentHarnessBackendType `json:"backend"` - GatewaySandboxName string `json:"gatewaySandboxName"` - ModelConfigRef string `json:"modelConfigRef,omitempty"` - BackendRefID string `json:"backendRefId,omitempty"` - Endpoint string `json:"endpoint,omitempty"` -} - // SubstrateAgentHarnessListEntry is set when runtime is substrate. type SubstrateAgentHarnessListEntry struct { - Backend v1alpha2.AgentHarnessBackendType `json:"backend"` - Runtime v1alpha2.AgentHarnessRuntime `json:"runtime"` - ActorID string `json:"actorId,omitempty"` - GatewayUIPath string `json:"gatewayUIPath,omitempty"` - ModelConfigRef string `json:"modelConfigRef,omitempty"` - BackendRefID string `json:"backendRefId,omitempty"` - Endpoint string `json:"endpoint,omitempty"` + Backend v1alpha2.AgentHarnessBackendType `json:"backend"` + Runtime v1alpha2.AgentHarnessRuntime `json:"runtime"` + ActorID string `json:"actorId,omitempty"` + // AcpPath is the server-side ACP WebSocket proxy path for chatting with + // the harness from the kagent UI. + AcpPath string `json:"acpPath,omitempty"` + ModelConfigRef string `json:"modelConfigRef,omitempty"` + BackendRefID string `json:"backendRefId,omitempty"` + Endpoint string `json:"endpoint,omitempty"` } type AgentResponse struct { @@ -169,7 +161,6 @@ type AgentResponse struct { DeploymentReady bool `json:"deploymentReady"` Accepted bool `json:"accepted"` WorkloadMode v1alpha2.WorkloadMode `json:"workloadMode,omitempty"` - OpenshellAgentHarness *OpenshellAgentHarnessListEntry `json:"openshellAgentHarness,omitempty"` SubstrateAgentHarness *SubstrateAgentHarnessListEntry `json:"substrateAgentHarness,omitempty"` } diff --git a/go/api/openshell/gen/computev1/compute_driver.pb.go b/go/api/openshell/gen/computev1/compute_driver.pb.go deleted file mode 100644 index 1d635b64ad..0000000000 --- a/go/api/openshell/gen/computev1/compute_driver.pb.go +++ /dev/null @@ -1,1822 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.11 -// protoc (unknown) -// source: compute_driver.proto - -package computev1 - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - structpb "google.golang.org/protobuf/types/known/structpb" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type GetCapabilitiesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetCapabilitiesRequest) Reset() { - *x = GetCapabilitiesRequest{} - mi := &file_compute_driver_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetCapabilitiesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetCapabilitiesRequest) ProtoMessage() {} - -func (x *GetCapabilitiesRequest) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetCapabilitiesRequest.ProtoReflect.Descriptor instead. -func (*GetCapabilitiesRequest) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{0} -} - -type GetCapabilitiesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Human-readable driver name. - DriverName string `protobuf:"bytes,1,opt,name=driver_name,json=driverName,proto3" json:"driver_name,omitempty"` - // Driver implementation version string. - DriverVersion string `protobuf:"bytes,2,opt,name=driver_version,json=driverVersion,proto3" json:"driver_version,omitempty"` - // Default sandbox image recommended by the driver. - DefaultImage string `protobuf:"bytes,3,opt,name=default_image,json=defaultImage,proto3" json:"default_image,omitempty"` - // True when the driver can provision GPU-backed sandboxes. - SupportsGpu bool `protobuf:"varint,4,opt,name=supports_gpu,json=supportsGpu,proto3" json:"supports_gpu,omitempty"` - // Number of GPUs available for sandbox assignment. - GpuCount uint32 `protobuf:"varint,5,opt,name=gpu_count,json=gpuCount,proto3" json:"gpu_count,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetCapabilitiesResponse) Reset() { - *x = GetCapabilitiesResponse{} - mi := &file_compute_driver_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetCapabilitiesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetCapabilitiesResponse) ProtoMessage() {} - -func (x *GetCapabilitiesResponse) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetCapabilitiesResponse.ProtoReflect.Descriptor instead. -func (*GetCapabilitiesResponse) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{1} -} - -func (x *GetCapabilitiesResponse) GetDriverName() string { - if x != nil { - return x.DriverName - } - return "" -} - -func (x *GetCapabilitiesResponse) GetDriverVersion() string { - if x != nil { - return x.DriverVersion - } - return "" -} - -func (x *GetCapabilitiesResponse) GetDefaultImage() string { - if x != nil { - return x.DefaultImage - } - return "" -} - -func (x *GetCapabilitiesResponse) GetSupportsGpu() bool { - if x != nil { - return x.SupportsGpu - } - return false -} - -func (x *GetCapabilitiesResponse) GetGpuCount() uint32 { - if x != nil { - return x.GpuCount - } - return 0 -} - -// Driver-owned sandbox model used for create requests and platform observations. -// -// This intentionally omits gateway-owned lifecycle fields such as the public -// `openshell.v1.SandboxPhase` and persisted metadata. The gateway derives and -// stores those fields after translating driver observations. -type DriverSandbox struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Stable sandbox ID assigned by the gateway. - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - // Compute-runtime sandbox name. - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - // Compute-platform namespace or equivalent tenancy boundary. - Namespace string `protobuf:"bytes,3,opt,name=namespace,proto3" json:"namespace,omitempty"` - // Provisioning input supplied by the gateway. Drivers may omit this in - // observed snapshots returned by Get/List/Watch. - Spec *DriverSandboxSpec `protobuf:"bytes,4,opt,name=spec,proto3" json:"spec,omitempty"` - // Raw platform-observed status. - Status *DriverSandboxStatus `protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DriverSandbox) Reset() { - *x = DriverSandbox{} - mi := &file_compute_driver_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DriverSandbox) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DriverSandbox) ProtoMessage() {} - -func (x *DriverSandbox) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[2] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DriverSandbox.ProtoReflect.Descriptor instead. -func (*DriverSandbox) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{2} -} - -func (x *DriverSandbox) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *DriverSandbox) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *DriverSandbox) GetNamespace() string { - if x != nil { - return x.Namespace - } - return "" -} - -func (x *DriverSandbox) GetSpec() *DriverSandboxSpec { - if x != nil { - return x.Spec - } - return nil -} - -func (x *DriverSandbox) GetStatus() *DriverSandboxStatus { - if x != nil { - return x.Status - } - return nil -} - -// Driver-owned provisioning inputs required to create a sandbox. -type DriverSandboxSpec struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Log level exposed to processes running inside the sandbox. - LogLevel string `protobuf:"bytes,1,opt,name=log_level,json=logLevel,proto3" json:"log_level,omitempty"` - // Environment variables injected into the sandbox runtime. - Environment map[string]string `protobuf:"bytes,5,rep,name=environment,proto3" json:"environment,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Runtime template consumed by the driver during provisioning. - Template *DriverSandboxTemplate `protobuf:"bytes,6,opt,name=template,proto3" json:"template,omitempty"` - // Request NVIDIA GPU resources for this sandbox. - Gpu bool `protobuf:"varint,9,opt,name=gpu,proto3" json:"gpu,omitempty"` - // Optional PCI BDF address (e.g. "0000:2d:00.0") or device index - // (e.g. "0", "1"). When empty with gpu=true, the driver assigns the - // first available GPU. - GpuDevice string `protobuf:"bytes,10,opt,name=gpu_device,json=gpuDevice,proto3" json:"gpu_device,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DriverSandboxSpec) Reset() { - *x = DriverSandboxSpec{} - mi := &file_compute_driver_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DriverSandboxSpec) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DriverSandboxSpec) ProtoMessage() {} - -func (x *DriverSandboxSpec) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[3] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DriverSandboxSpec.ProtoReflect.Descriptor instead. -func (*DriverSandboxSpec) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{3} -} - -func (x *DriverSandboxSpec) GetLogLevel() string { - if x != nil { - return x.LogLevel - } - return "" -} - -func (x *DriverSandboxSpec) GetEnvironment() map[string]string { - if x != nil { - return x.Environment - } - return nil -} - -func (x *DriverSandboxSpec) GetTemplate() *DriverSandboxTemplate { - if x != nil { - return x.Template - } - return nil -} - -func (x *DriverSandboxSpec) GetGpu() bool { - if x != nil { - return x.Gpu - } - return false -} - -func (x *DriverSandboxSpec) GetGpuDevice() string { - if x != nil { - return x.GpuDevice - } - return "" -} - -// Driver-owned runtime template consumed by the compute platform. -// -// This message describes the sandbox workload in backend-neutral terms. -// Platform-specific knobs (Kubernetes runtimeClassName, annotations, -// volumeClaimTemplates, etc.) belong in `platform_config`. -type DriverSandboxTemplate struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Fully-qualified OCI image reference used to boot the sandbox. - Image string `protobuf:"bytes,1,opt,name=image,proto3" json:"image,omitempty"` - // Socket path inside the sandbox where the agent service listens. - AgentSocketPath string `protobuf:"bytes,3,opt,name=agent_socket_path,json=agentSocketPath,proto3" json:"agent_socket_path,omitempty"` - // Metadata labels applied to compute-platform resources. - // Drivers map these to the platform's native tagging mechanism - // (Kubernetes labels, cloud instance tags, etc.). - Labels map[string]string `protobuf:"bytes,4,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Additional environment variables injected into the sandbox runtime. - Environment map[string]string `protobuf:"bytes,6,rep,name=environment,proto3" json:"environment,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Typed compute-resource requirements for the sandbox workload. - Resources *DriverResourceRequirements `protobuf:"bytes,10,opt,name=resources,proto3" json:"resources,omitempty"` - // Opaque, platform-specific configuration passed through to the driver. - // The gateway does not inspect this; each driver defines its own schema. - // For the Kubernetes driver this carries fields such as runtimeClassName, - // annotations, and volumeClaimTemplates. - PlatformConfig *structpb.Struct `protobuf:"bytes,11,opt,name=platform_config,json=platformConfig,proto3" json:"platform_config,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DriverSandboxTemplate) Reset() { - *x = DriverSandboxTemplate{} - mi := &file_compute_driver_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DriverSandboxTemplate) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DriverSandboxTemplate) ProtoMessage() {} - -func (x *DriverSandboxTemplate) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[4] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DriverSandboxTemplate.ProtoReflect.Descriptor instead. -func (*DriverSandboxTemplate) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{4} -} - -func (x *DriverSandboxTemplate) GetImage() string { - if x != nil { - return x.Image - } - return "" -} - -func (x *DriverSandboxTemplate) GetAgentSocketPath() string { - if x != nil { - return x.AgentSocketPath - } - return "" -} - -func (x *DriverSandboxTemplate) GetLabels() map[string]string { - if x != nil { - return x.Labels - } - return nil -} - -func (x *DriverSandboxTemplate) GetEnvironment() map[string]string { - if x != nil { - return x.Environment - } - return nil -} - -func (x *DriverSandboxTemplate) GetResources() *DriverResourceRequirements { - if x != nil { - return x.Resources - } - return nil -} - -func (x *DriverSandboxTemplate) GetPlatformConfig() *structpb.Struct { - if x != nil { - return x.PlatformConfig - } - return nil -} - -// Typed compute-resource requirements. -// -// Values use Kubernetes-style quantity strings (e.g. "500m", "2", "4Gi") -// because they are a well-known, widely-adopted notation. Drivers for -// non-Kubernetes platforms must parse these strings into their native units. -type DriverResourceRequirements struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Minimum CPU cores requested (e.g. "500m", "2"). - CpuRequest string `protobuf:"bytes,1,opt,name=cpu_request,json=cpuRequest,proto3" json:"cpu_request,omitempty"` - // Maximum CPU cores allowed (e.g. "500m", "4"). - CpuLimit string `protobuf:"bytes,2,opt,name=cpu_limit,json=cpuLimit,proto3" json:"cpu_limit,omitempty"` - // Minimum memory requested (e.g. "256Mi", "4Gi"). - MemoryRequest string `protobuf:"bytes,3,opt,name=memory_request,json=memoryRequest,proto3" json:"memory_request,omitempty"` - // Maximum memory allowed (e.g. "512Mi", "8Gi"). - MemoryLimit string `protobuf:"bytes,4,opt,name=memory_limit,json=memoryLimit,proto3" json:"memory_limit,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DriverResourceRequirements) Reset() { - *x = DriverResourceRequirements{} - mi := &file_compute_driver_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DriverResourceRequirements) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DriverResourceRequirements) ProtoMessage() {} - -func (x *DriverResourceRequirements) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[5] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DriverResourceRequirements.ProtoReflect.Descriptor instead. -func (*DriverResourceRequirements) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{5} -} - -func (x *DriverResourceRequirements) GetCpuRequest() string { - if x != nil { - return x.CpuRequest - } - return "" -} - -func (x *DriverResourceRequirements) GetCpuLimit() string { - if x != nil { - return x.CpuLimit - } - return "" -} - -func (x *DriverResourceRequirements) GetMemoryRequest() string { - if x != nil { - return x.MemoryRequest - } - return "" -} - -func (x *DriverResourceRequirements) GetMemoryLimit() string { - if x != nil { - return x.MemoryLimit - } - return "" -} - -// Raw status observed directly from the compute platform. -// -// The gateway derives the public `openshell.v1.SandboxPhase` from these -// conditions plus `deleting`. -type DriverSandboxStatus struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Compute-platform sandbox object name. - SandboxName string `protobuf:"bytes,1,opt,name=sandbox_name,json=sandboxName,proto3" json:"sandbox_name,omitempty"` - // Platform-assigned instance identifier for the compute unit running the - // sandbox agent (e.g. Kubernetes pod name, VM instance ID, hostname). - // The gateway uses this to correlate incoming connections back to a sandbox. - InstanceId string `protobuf:"bytes,2,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"` - // File descriptor or address for reaching the agent service inside the - // sandbox, when available. - AgentFd string `protobuf:"bytes,3,opt,name=agent_fd,json=agentFd,proto3" json:"agent_fd,omitempty"` - // File descriptor or address for reaching the sandbox supervisor service, - // when available. - SandboxFd string `protobuf:"bytes,4,opt,name=sandbox_fd,json=sandboxFd,proto3" json:"sandbox_fd,omitempty"` - // Raw readiness and lifecycle conditions reported by the platform. - Conditions []*DriverCondition `protobuf:"bytes,5,rep,name=conditions,proto3" json:"conditions,omitempty"` - // True when the compute platform has begun deleting this sandbox. - Deleting bool `protobuf:"varint,6,opt,name=deleting,proto3" json:"deleting,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DriverSandboxStatus) Reset() { - *x = DriverSandboxStatus{} - mi := &file_compute_driver_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DriverSandboxStatus) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DriverSandboxStatus) ProtoMessage() {} - -func (x *DriverSandboxStatus) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[6] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DriverSandboxStatus.ProtoReflect.Descriptor instead. -func (*DriverSandboxStatus) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{6} -} - -func (x *DriverSandboxStatus) GetSandboxName() string { - if x != nil { - return x.SandboxName - } - return "" -} - -func (x *DriverSandboxStatus) GetInstanceId() string { - if x != nil { - return x.InstanceId - } - return "" -} - -func (x *DriverSandboxStatus) GetAgentFd() string { - if x != nil { - return x.AgentFd - } - return "" -} - -func (x *DriverSandboxStatus) GetSandboxFd() string { - if x != nil { - return x.SandboxFd - } - return "" -} - -func (x *DriverSandboxStatus) GetConditions() []*DriverCondition { - if x != nil { - return x.Conditions - } - return nil -} - -func (x *DriverSandboxStatus) GetDeleting() bool { - if x != nil { - return x.Deleting - } - return false -} - -// Raw compute-platform condition. -type DriverCondition struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Condition class reported by the compute platform. - Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` - // Condition status value such as `True`, `False`, or `Unknown`. - Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` - // Short machine-readable reason associated with the condition. - Reason string `protobuf:"bytes,3,opt,name=reason,proto3" json:"reason,omitempty"` - // Human-readable condition message. - Message string `protobuf:"bytes,4,opt,name=message,proto3" json:"message,omitempty"` - // Timestamp reported by the platform for the last transition. - LastTransitionTime string `protobuf:"bytes,5,opt,name=last_transition_time,json=lastTransitionTime,proto3" json:"last_transition_time,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DriverCondition) Reset() { - *x = DriverCondition{} - mi := &file_compute_driver_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DriverCondition) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DriverCondition) ProtoMessage() {} - -func (x *DriverCondition) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[7] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DriverCondition.ProtoReflect.Descriptor instead. -func (*DriverCondition) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{7} -} - -func (x *DriverCondition) GetType() string { - if x != nil { - return x.Type - } - return "" -} - -func (x *DriverCondition) GetStatus() string { - if x != nil { - return x.Status - } - return "" -} - -func (x *DriverCondition) GetReason() string { - if x != nil { - return x.Reason - } - return "" -} - -func (x *DriverCondition) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -func (x *DriverCondition) GetLastTransitionTime() string { - if x != nil { - return x.LastTransitionTime - } - return "" -} - -// Raw compute-platform event correlated to a sandbox. -type DriverPlatformEvent struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Event timestamp in milliseconds since epoch. - TimestampMs int64 `protobuf:"varint,1,opt,name=timestamp_ms,json=timestampMs,proto3" json:"timestamp_ms,omitempty"` - // Event source (for example `kubernetes`). - Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"` - // Event type or severity (for example `Normal` or `Warning`). - Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` - // Short machine-readable reason code. - Reason string `protobuf:"bytes,4,opt,name=reason,proto3" json:"reason,omitempty"` - // Human-readable event message. - Message string `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"` - // Optional platform-specific metadata attached to the event. - Metadata map[string]string `protobuf:"bytes,6,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DriverPlatformEvent) Reset() { - *x = DriverPlatformEvent{} - mi := &file_compute_driver_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DriverPlatformEvent) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DriverPlatformEvent) ProtoMessage() {} - -func (x *DriverPlatformEvent) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[8] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DriverPlatformEvent.ProtoReflect.Descriptor instead. -func (*DriverPlatformEvent) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{8} -} - -func (x *DriverPlatformEvent) GetTimestampMs() int64 { - if x != nil { - return x.TimestampMs - } - return 0 -} - -func (x *DriverPlatformEvent) GetSource() string { - if x != nil { - return x.Source - } - return "" -} - -func (x *DriverPlatformEvent) GetType() string { - if x != nil { - return x.Type - } - return "" -} - -func (x *DriverPlatformEvent) GetReason() string { - if x != nil { - return x.Reason - } - return "" -} - -func (x *DriverPlatformEvent) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -func (x *DriverPlatformEvent) GetMetadata() map[string]string { - if x != nil { - return x.Metadata - } - return nil -} - -type ValidateSandboxCreateRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Proposed sandbox configuration to validate before provisioning. - Sandbox *DriverSandbox `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ValidateSandboxCreateRequest) Reset() { - *x = ValidateSandboxCreateRequest{} - mi := &file_compute_driver_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ValidateSandboxCreateRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ValidateSandboxCreateRequest) ProtoMessage() {} - -func (x *ValidateSandboxCreateRequest) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[9] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ValidateSandboxCreateRequest.ProtoReflect.Descriptor instead. -func (*ValidateSandboxCreateRequest) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{9} -} - -func (x *ValidateSandboxCreateRequest) GetSandbox() *DriverSandbox { - if x != nil { - return x.Sandbox - } - return nil -} - -type ValidateSandboxCreateResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ValidateSandboxCreateResponse) Reset() { - *x = ValidateSandboxCreateResponse{} - mi := &file_compute_driver_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ValidateSandboxCreateResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ValidateSandboxCreateResponse) ProtoMessage() {} - -func (x *ValidateSandboxCreateResponse) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[10] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ValidateSandboxCreateResponse.ProtoReflect.Descriptor instead. -func (*ValidateSandboxCreateResponse) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{10} -} - -type GetSandboxRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Stable sandbox ID stored by the gateway. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Compute-runtime name used by the driver. - SandboxName string `protobuf:"bytes,2,opt,name=sandbox_name,json=sandboxName,proto3" json:"sandbox_name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxRequest) Reset() { - *x = GetSandboxRequest{} - mi := &file_compute_driver_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxRequest) ProtoMessage() {} - -func (x *GetSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[11] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxRequest.ProtoReflect.Descriptor instead. -func (*GetSandboxRequest) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{11} -} - -func (x *GetSandboxRequest) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *GetSandboxRequest) GetSandboxName() string { - if x != nil { - return x.SandboxName - } - return "" -} - -type GetSandboxResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Platform-observed sandbox snapshot returned by the driver. - Sandbox *DriverSandbox `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxResponse) Reset() { - *x = GetSandboxResponse{} - mi := &file_compute_driver_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxResponse) ProtoMessage() {} - -func (x *GetSandboxResponse) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[12] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxResponse.ProtoReflect.Descriptor instead. -func (*GetSandboxResponse) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{12} -} - -func (x *GetSandboxResponse) GetSandbox() *DriverSandbox { - if x != nil { - return x.Sandbox - } - return nil -} - -type ListSandboxesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListSandboxesRequest) Reset() { - *x = ListSandboxesRequest{} - mi := &file_compute_driver_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListSandboxesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListSandboxesRequest) ProtoMessage() {} - -func (x *ListSandboxesRequest) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[13] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListSandboxesRequest.ProtoReflect.Descriptor instead. -func (*ListSandboxesRequest) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{13} -} - -type ListSandboxesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Platform-observed sandbox snapshots returned by the driver. - Sandboxes []*DriverSandbox `protobuf:"bytes,1,rep,name=sandboxes,proto3" json:"sandboxes,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListSandboxesResponse) Reset() { - *x = ListSandboxesResponse{} - mi := &file_compute_driver_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListSandboxesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListSandboxesResponse) ProtoMessage() {} - -func (x *ListSandboxesResponse) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[14] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListSandboxesResponse.ProtoReflect.Descriptor instead. -func (*ListSandboxesResponse) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{14} -} - -func (x *ListSandboxesResponse) GetSandboxes() []*DriverSandbox { - if x != nil { - return x.Sandboxes - } - return nil -} - -type CreateSandboxRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox configuration to provision on the compute platform. - Sandbox *DriverSandbox `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CreateSandboxRequest) Reset() { - *x = CreateSandboxRequest{} - mi := &file_compute_driver_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CreateSandboxRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateSandboxRequest) ProtoMessage() {} - -func (x *CreateSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[15] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateSandboxRequest.ProtoReflect.Descriptor instead. -func (*CreateSandboxRequest) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{15} -} - -func (x *CreateSandboxRequest) GetSandbox() *DriverSandbox { - if x != nil { - return x.Sandbox - } - return nil -} - -type CreateSandboxResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CreateSandboxResponse) Reset() { - *x = CreateSandboxResponse{} - mi := &file_compute_driver_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CreateSandboxResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateSandboxResponse) ProtoMessage() {} - -func (x *CreateSandboxResponse) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[16] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateSandboxResponse.ProtoReflect.Descriptor instead. -func (*CreateSandboxResponse) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{16} -} - -type StopSandboxRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Stable sandbox ID stored by the gateway. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Compute-runtime name used by the driver. - SandboxName string `protobuf:"bytes,2,opt,name=sandbox_name,json=sandboxName,proto3" json:"sandbox_name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *StopSandboxRequest) Reset() { - *x = StopSandboxRequest{} - mi := &file_compute_driver_proto_msgTypes[17] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *StopSandboxRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StopSandboxRequest) ProtoMessage() {} - -func (x *StopSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[17] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use StopSandboxRequest.ProtoReflect.Descriptor instead. -func (*StopSandboxRequest) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{17} -} - -func (x *StopSandboxRequest) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *StopSandboxRequest) GetSandboxName() string { - if x != nil { - return x.SandboxName - } - return "" -} - -type StopSandboxResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *StopSandboxResponse) Reset() { - *x = StopSandboxResponse{} - mi := &file_compute_driver_proto_msgTypes[18] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *StopSandboxResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StopSandboxResponse) ProtoMessage() {} - -func (x *StopSandboxResponse) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[18] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use StopSandboxResponse.ProtoReflect.Descriptor instead. -func (*StopSandboxResponse) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{18} -} - -type DeleteSandboxRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Stable sandbox ID stored by the gateway. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Compute-runtime name used by the driver. - SandboxName string `protobuf:"bytes,2,opt,name=sandbox_name,json=sandboxName,proto3" json:"sandbox_name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteSandboxRequest) Reset() { - *x = DeleteSandboxRequest{} - mi := &file_compute_driver_proto_msgTypes[19] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteSandboxRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteSandboxRequest) ProtoMessage() {} - -func (x *DeleteSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[19] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteSandboxRequest.ProtoReflect.Descriptor instead. -func (*DeleteSandboxRequest) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{19} -} - -func (x *DeleteSandboxRequest) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *DeleteSandboxRequest) GetSandboxName() string { - if x != nil { - return x.SandboxName - } - return "" -} - -type DeleteSandboxResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // True when a platform resource was deleted by this request. - Deleted bool `protobuf:"varint,1,opt,name=deleted,proto3" json:"deleted,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteSandboxResponse) Reset() { - *x = DeleteSandboxResponse{} - mi := &file_compute_driver_proto_msgTypes[20] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteSandboxResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteSandboxResponse) ProtoMessage() {} - -func (x *DeleteSandboxResponse) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[20] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteSandboxResponse.ProtoReflect.Descriptor instead. -func (*DeleteSandboxResponse) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{20} -} - -func (x *DeleteSandboxResponse) GetDeleted() bool { - if x != nil { - return x.Deleted - } - return false -} - -type WatchSandboxesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *WatchSandboxesRequest) Reset() { - *x = WatchSandboxesRequest{} - mi := &file_compute_driver_proto_msgTypes[21] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *WatchSandboxesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WatchSandboxesRequest) ProtoMessage() {} - -func (x *WatchSandboxesRequest) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[21] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WatchSandboxesRequest.ProtoReflect.Descriptor instead. -func (*WatchSandboxesRequest) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{21} -} - -type WatchSandboxesSandboxEvent struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Updated driver-native snapshot for one sandbox. - Sandbox *DriverSandbox `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *WatchSandboxesSandboxEvent) Reset() { - *x = WatchSandboxesSandboxEvent{} - mi := &file_compute_driver_proto_msgTypes[22] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *WatchSandboxesSandboxEvent) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WatchSandboxesSandboxEvent) ProtoMessage() {} - -func (x *WatchSandboxesSandboxEvent) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[22] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WatchSandboxesSandboxEvent.ProtoReflect.Descriptor instead. -func (*WatchSandboxesSandboxEvent) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{22} -} - -func (x *WatchSandboxesSandboxEvent) GetSandbox() *DriverSandbox { - if x != nil { - return x.Sandbox - } - return nil -} - -type WatchSandboxesDeletedEvent struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox ID removed from the compute platform. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *WatchSandboxesDeletedEvent) Reset() { - *x = WatchSandboxesDeletedEvent{} - mi := &file_compute_driver_proto_msgTypes[23] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *WatchSandboxesDeletedEvent) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WatchSandboxesDeletedEvent) ProtoMessage() {} - -func (x *WatchSandboxesDeletedEvent) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[23] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WatchSandboxesDeletedEvent.ProtoReflect.Descriptor instead. -func (*WatchSandboxesDeletedEvent) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{23} -} - -func (x *WatchSandboxesDeletedEvent) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -type WatchSandboxesPlatformEvent struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox ID correlated to the platform event. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Raw platform event emitted for the sandbox. - Event *DriverPlatformEvent `protobuf:"bytes,2,opt,name=event,proto3" json:"event,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *WatchSandboxesPlatformEvent) Reset() { - *x = WatchSandboxesPlatformEvent{} - mi := &file_compute_driver_proto_msgTypes[24] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *WatchSandboxesPlatformEvent) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WatchSandboxesPlatformEvent) ProtoMessage() {} - -func (x *WatchSandboxesPlatformEvent) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[24] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WatchSandboxesPlatformEvent.ProtoReflect.Descriptor instead. -func (*WatchSandboxesPlatformEvent) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{24} -} - -func (x *WatchSandboxesPlatformEvent) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *WatchSandboxesPlatformEvent) GetEvent() *DriverPlatformEvent { - if x != nil { - return x.Event - } - return nil -} - -type WatchSandboxesEvent struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Payload: - // - // *WatchSandboxesEvent_Sandbox - // *WatchSandboxesEvent_Deleted - // *WatchSandboxesEvent_PlatformEvent - Payload isWatchSandboxesEvent_Payload `protobuf_oneof:"payload"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *WatchSandboxesEvent) Reset() { - *x = WatchSandboxesEvent{} - mi := &file_compute_driver_proto_msgTypes[25] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *WatchSandboxesEvent) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WatchSandboxesEvent) ProtoMessage() {} - -func (x *WatchSandboxesEvent) ProtoReflect() protoreflect.Message { - mi := &file_compute_driver_proto_msgTypes[25] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WatchSandboxesEvent.ProtoReflect.Descriptor instead. -func (*WatchSandboxesEvent) Descriptor() ([]byte, []int) { - return file_compute_driver_proto_rawDescGZIP(), []int{25} -} - -func (x *WatchSandboxesEvent) GetPayload() isWatchSandboxesEvent_Payload { - if x != nil { - return x.Payload - } - return nil -} - -func (x *WatchSandboxesEvent) GetSandbox() *WatchSandboxesSandboxEvent { - if x != nil { - if x, ok := x.Payload.(*WatchSandboxesEvent_Sandbox); ok { - return x.Sandbox - } - } - return nil -} - -func (x *WatchSandboxesEvent) GetDeleted() *WatchSandboxesDeletedEvent { - if x != nil { - if x, ok := x.Payload.(*WatchSandboxesEvent_Deleted); ok { - return x.Deleted - } - } - return nil -} - -func (x *WatchSandboxesEvent) GetPlatformEvent() *WatchSandboxesPlatformEvent { - if x != nil { - if x, ok := x.Payload.(*WatchSandboxesEvent_PlatformEvent); ok { - return x.PlatformEvent - } - } - return nil -} - -type isWatchSandboxesEvent_Payload interface { - isWatchSandboxesEvent_Payload() -} - -type WatchSandboxesEvent_Sandbox struct { - // Updated or newly observed sandbox snapshot. - Sandbox *WatchSandboxesSandboxEvent `protobuf:"bytes,1,opt,name=sandbox,proto3,oneof"` -} - -type WatchSandboxesEvent_Deleted struct { - // Sandbox deletion observation. - Deleted *WatchSandboxesDeletedEvent `protobuf:"bytes,2,opt,name=deleted,proto3,oneof"` -} - -type WatchSandboxesEvent_PlatformEvent struct { - // Raw platform event correlated to a sandbox. - PlatformEvent *WatchSandboxesPlatformEvent `protobuf:"bytes,3,opt,name=platform_event,json=platformEvent,proto3,oneof"` -} - -func (*WatchSandboxesEvent_Sandbox) isWatchSandboxesEvent_Payload() {} - -func (*WatchSandboxesEvent_Deleted) isWatchSandboxesEvent_Payload() {} - -func (*WatchSandboxesEvent_PlatformEvent) isWatchSandboxesEvent_Payload() {} - -var File_compute_driver_proto protoreflect.FileDescriptor - -const file_compute_driver_proto_rawDesc = "" + - "\n" + - "\x14compute_driver.proto\x12\x14openshell.compute.v1\x1a\x1cgoogle/protobuf/struct.proto\"\x18\n" + - "\x16GetCapabilitiesRequest\"\xc6\x01\n" + - "\x17GetCapabilitiesResponse\x12\x1f\n" + - "\vdriver_name\x18\x01 \x01(\tR\n" + - "driverName\x12%\n" + - "\x0edriver_version\x18\x02 \x01(\tR\rdriverVersion\x12#\n" + - "\rdefault_image\x18\x03 \x01(\tR\fdefaultImage\x12!\n" + - "\fsupports_gpu\x18\x04 \x01(\bR\vsupportsGpu\x12\x1b\n" + - "\tgpu_count\x18\x05 \x01(\rR\bgpuCount\"\xd1\x01\n" + - "\rDriverSandbox\x12\x0e\n" + - "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + - "\x04name\x18\x02 \x01(\tR\x04name\x12\x1c\n" + - "\tnamespace\x18\x03 \x01(\tR\tnamespace\x12;\n" + - "\x04spec\x18\x04 \x01(\v2'.openshell.compute.v1.DriverSandboxSpecR\x04spec\x12A\n" + - "\x06status\x18\x05 \x01(\v2).openshell.compute.v1.DriverSandboxStatusR\x06status\"\xc6\x02\n" + - "\x11DriverSandboxSpec\x12\x1b\n" + - "\tlog_level\x18\x01 \x01(\tR\blogLevel\x12Z\n" + - "\venvironment\x18\x05 \x03(\v28.openshell.compute.v1.DriverSandboxSpec.EnvironmentEntryR\venvironment\x12G\n" + - "\btemplate\x18\x06 \x01(\v2+.openshell.compute.v1.DriverSandboxTemplateR\btemplate\x12\x10\n" + - "\x03gpu\x18\t \x01(\bR\x03gpu\x12\x1d\n" + - "\n" + - "gpu_device\x18\n" + - " \x01(\tR\tgpuDevice\x1a>\n" + - "\x10EnvironmentEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x97\x04\n" + - "\x15DriverSandboxTemplate\x12\x14\n" + - "\x05image\x18\x01 \x01(\tR\x05image\x12*\n" + - "\x11agent_socket_path\x18\x03 \x01(\tR\x0fagentSocketPath\x12O\n" + - "\x06labels\x18\x04 \x03(\v27.openshell.compute.v1.DriverSandboxTemplate.LabelsEntryR\x06labels\x12^\n" + - "\venvironment\x18\x06 \x03(\v2<.openshell.compute.v1.DriverSandboxTemplate.EnvironmentEntryR\venvironment\x12N\n" + - "\tresources\x18\n" + - " \x01(\v20.openshell.compute.v1.DriverResourceRequirementsR\tresources\x12@\n" + - "\x0fplatform_config\x18\v \x01(\v2\x17.google.protobuf.StructR\x0eplatformConfig\x1a9\n" + - "\vLabelsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a>\n" + - "\x10EnvironmentEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xa4\x01\n" + - "\x1aDriverResourceRequirements\x12\x1f\n" + - "\vcpu_request\x18\x01 \x01(\tR\n" + - "cpuRequest\x12\x1b\n" + - "\tcpu_limit\x18\x02 \x01(\tR\bcpuLimit\x12%\n" + - "\x0ememory_request\x18\x03 \x01(\tR\rmemoryRequest\x12!\n" + - "\fmemory_limit\x18\x04 \x01(\tR\vmemoryLimit\"\xf6\x01\n" + - "\x13DriverSandboxStatus\x12!\n" + - "\fsandbox_name\x18\x01 \x01(\tR\vsandboxName\x12\x1f\n" + - "\vinstance_id\x18\x02 \x01(\tR\n" + - "instanceId\x12\x19\n" + - "\bagent_fd\x18\x03 \x01(\tR\aagentFd\x12\x1d\n" + - "\n" + - "sandbox_fd\x18\x04 \x01(\tR\tsandboxFd\x12E\n" + - "\n" + - "conditions\x18\x05 \x03(\v2%.openshell.compute.v1.DriverConditionR\n" + - "conditions\x12\x1a\n" + - "\bdeleting\x18\x06 \x01(\bR\bdeleting\"\xa1\x01\n" + - "\x0fDriverCondition\x12\x12\n" + - "\x04type\x18\x01 \x01(\tR\x04type\x12\x16\n" + - "\x06status\x18\x02 \x01(\tR\x06status\x12\x16\n" + - "\x06reason\x18\x03 \x01(\tR\x06reason\x12\x18\n" + - "\amessage\x18\x04 \x01(\tR\amessage\x120\n" + - "\x14last_transition_time\x18\x05 \x01(\tR\x12lastTransitionTime\"\xa8\x02\n" + - "\x13DriverPlatformEvent\x12!\n" + - "\ftimestamp_ms\x18\x01 \x01(\x03R\vtimestampMs\x12\x16\n" + - "\x06source\x18\x02 \x01(\tR\x06source\x12\x12\n" + - "\x04type\x18\x03 \x01(\tR\x04type\x12\x16\n" + - "\x06reason\x18\x04 \x01(\tR\x06reason\x12\x18\n" + - "\amessage\x18\x05 \x01(\tR\amessage\x12S\n" + - "\bmetadata\x18\x06 \x03(\v27.openshell.compute.v1.DriverPlatformEvent.MetadataEntryR\bmetadata\x1a;\n" + - "\rMetadataEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"]\n" + - "\x1cValidateSandboxCreateRequest\x12=\n" + - "\asandbox\x18\x01 \x01(\v2#.openshell.compute.v1.DriverSandboxR\asandbox\"\x1f\n" + - "\x1dValidateSandboxCreateResponse\"U\n" + - "\x11GetSandboxRequest\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12!\n" + - "\fsandbox_name\x18\x02 \x01(\tR\vsandboxName\"S\n" + - "\x12GetSandboxResponse\x12=\n" + - "\asandbox\x18\x01 \x01(\v2#.openshell.compute.v1.DriverSandboxR\asandbox\"\x16\n" + - "\x14ListSandboxesRequest\"Z\n" + - "\x15ListSandboxesResponse\x12A\n" + - "\tsandboxes\x18\x01 \x03(\v2#.openshell.compute.v1.DriverSandboxR\tsandboxes\"U\n" + - "\x14CreateSandboxRequest\x12=\n" + - "\asandbox\x18\x01 \x01(\v2#.openshell.compute.v1.DriverSandboxR\asandbox\"\x17\n" + - "\x15CreateSandboxResponse\"V\n" + - "\x12StopSandboxRequest\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12!\n" + - "\fsandbox_name\x18\x02 \x01(\tR\vsandboxName\"\x15\n" + - "\x13StopSandboxResponse\"X\n" + - "\x14DeleteSandboxRequest\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12!\n" + - "\fsandbox_name\x18\x02 \x01(\tR\vsandboxName\"1\n" + - "\x15DeleteSandboxResponse\x12\x18\n" + - "\adeleted\x18\x01 \x01(\bR\adeleted\"\x17\n" + - "\x15WatchSandboxesRequest\"[\n" + - "\x1aWatchSandboxesSandboxEvent\x12=\n" + - "\asandbox\x18\x01 \x01(\v2#.openshell.compute.v1.DriverSandboxR\asandbox\";\n" + - "\x1aWatchSandboxesDeletedEvent\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\"}\n" + - "\x1bWatchSandboxesPlatformEvent\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12?\n" + - "\x05event\x18\x02 \x01(\v2).openshell.compute.v1.DriverPlatformEventR\x05event\"\x98\x02\n" + - "\x13WatchSandboxesEvent\x12L\n" + - "\asandbox\x18\x01 \x01(\v20.openshell.compute.v1.WatchSandboxesSandboxEventH\x00R\asandbox\x12L\n" + - "\adeleted\x18\x02 \x01(\v20.openshell.compute.v1.WatchSandboxesDeletedEventH\x00R\adeleted\x12Z\n" + - "\x0eplatform_event\x18\x03 \x01(\v21.openshell.compute.v1.WatchSandboxesPlatformEventH\x00R\rplatformEventB\t\n" + - "\apayload2\xf1\x06\n" + - "\rComputeDriver\x12n\n" + - "\x0fGetCapabilities\x12,.openshell.compute.v1.GetCapabilitiesRequest\x1a-.openshell.compute.v1.GetCapabilitiesResponse\x12\x80\x01\n" + - "\x15ValidateSandboxCreate\x122.openshell.compute.v1.ValidateSandboxCreateRequest\x1a3.openshell.compute.v1.ValidateSandboxCreateResponse\x12_\n" + - "\n" + - "GetSandbox\x12'.openshell.compute.v1.GetSandboxRequest\x1a(.openshell.compute.v1.GetSandboxResponse\x12h\n" + - "\rListSandboxes\x12*.openshell.compute.v1.ListSandboxesRequest\x1a+.openshell.compute.v1.ListSandboxesResponse\x12h\n" + - "\rCreateSandbox\x12*.openshell.compute.v1.CreateSandboxRequest\x1a+.openshell.compute.v1.CreateSandboxResponse\x12b\n" + - "\vStopSandbox\x12(.openshell.compute.v1.StopSandboxRequest\x1a).openshell.compute.v1.StopSandboxResponse\x12h\n" + - "\rDeleteSandbox\x12*.openshell.compute.v1.DeleteSandboxRequest\x1a+.openshell.compute.v1.DeleteSandboxResponse\x12j\n" + - "\x0eWatchSandboxes\x12+.openshell.compute.v1.WatchSandboxesRequest\x1a).openshell.compute.v1.WatchSandboxesEvent0\x01B\xdd\x01\n" + - "\x18com.openshell.compute.v1B\x12ComputeDriverProtoP\x01Z;github.com/kagent-dev/kagent/go/api/openshell/gen/computev1\xa2\x02\x03OCX\xaa\x02\x14Openshell.Compute.V1\xca\x02\x14Openshell\\Compute\\V1\xe2\x02 Openshell\\Compute\\V1\\GPBMetadata\xea\x02\x16Openshell::Compute::V1b\x06proto3" - -var ( - file_compute_driver_proto_rawDescOnce sync.Once - file_compute_driver_proto_rawDescData []byte -) - -func file_compute_driver_proto_rawDescGZIP() []byte { - file_compute_driver_proto_rawDescOnce.Do(func() { - file_compute_driver_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_compute_driver_proto_rawDesc), len(file_compute_driver_proto_rawDesc))) - }) - return file_compute_driver_proto_rawDescData -} - -var file_compute_driver_proto_msgTypes = make([]protoimpl.MessageInfo, 30) -var file_compute_driver_proto_goTypes = []any{ - (*GetCapabilitiesRequest)(nil), // 0: openshell.compute.v1.GetCapabilitiesRequest - (*GetCapabilitiesResponse)(nil), // 1: openshell.compute.v1.GetCapabilitiesResponse - (*DriverSandbox)(nil), // 2: openshell.compute.v1.DriverSandbox - (*DriverSandboxSpec)(nil), // 3: openshell.compute.v1.DriverSandboxSpec - (*DriverSandboxTemplate)(nil), // 4: openshell.compute.v1.DriverSandboxTemplate - (*DriverResourceRequirements)(nil), // 5: openshell.compute.v1.DriverResourceRequirements - (*DriverSandboxStatus)(nil), // 6: openshell.compute.v1.DriverSandboxStatus - (*DriverCondition)(nil), // 7: openshell.compute.v1.DriverCondition - (*DriverPlatformEvent)(nil), // 8: openshell.compute.v1.DriverPlatformEvent - (*ValidateSandboxCreateRequest)(nil), // 9: openshell.compute.v1.ValidateSandboxCreateRequest - (*ValidateSandboxCreateResponse)(nil), // 10: openshell.compute.v1.ValidateSandboxCreateResponse - (*GetSandboxRequest)(nil), // 11: openshell.compute.v1.GetSandboxRequest - (*GetSandboxResponse)(nil), // 12: openshell.compute.v1.GetSandboxResponse - (*ListSandboxesRequest)(nil), // 13: openshell.compute.v1.ListSandboxesRequest - (*ListSandboxesResponse)(nil), // 14: openshell.compute.v1.ListSandboxesResponse - (*CreateSandboxRequest)(nil), // 15: openshell.compute.v1.CreateSandboxRequest - (*CreateSandboxResponse)(nil), // 16: openshell.compute.v1.CreateSandboxResponse - (*StopSandboxRequest)(nil), // 17: openshell.compute.v1.StopSandboxRequest - (*StopSandboxResponse)(nil), // 18: openshell.compute.v1.StopSandboxResponse - (*DeleteSandboxRequest)(nil), // 19: openshell.compute.v1.DeleteSandboxRequest - (*DeleteSandboxResponse)(nil), // 20: openshell.compute.v1.DeleteSandboxResponse - (*WatchSandboxesRequest)(nil), // 21: openshell.compute.v1.WatchSandboxesRequest - (*WatchSandboxesSandboxEvent)(nil), // 22: openshell.compute.v1.WatchSandboxesSandboxEvent - (*WatchSandboxesDeletedEvent)(nil), // 23: openshell.compute.v1.WatchSandboxesDeletedEvent - (*WatchSandboxesPlatformEvent)(nil), // 24: openshell.compute.v1.WatchSandboxesPlatformEvent - (*WatchSandboxesEvent)(nil), // 25: openshell.compute.v1.WatchSandboxesEvent - nil, // 26: openshell.compute.v1.DriverSandboxSpec.EnvironmentEntry - nil, // 27: openshell.compute.v1.DriverSandboxTemplate.LabelsEntry - nil, // 28: openshell.compute.v1.DriverSandboxTemplate.EnvironmentEntry - nil, // 29: openshell.compute.v1.DriverPlatformEvent.MetadataEntry - (*structpb.Struct)(nil), // 30: google.protobuf.Struct -} -var file_compute_driver_proto_depIdxs = []int32{ - 3, // 0: openshell.compute.v1.DriverSandbox.spec:type_name -> openshell.compute.v1.DriverSandboxSpec - 6, // 1: openshell.compute.v1.DriverSandbox.status:type_name -> openshell.compute.v1.DriverSandboxStatus - 26, // 2: openshell.compute.v1.DriverSandboxSpec.environment:type_name -> openshell.compute.v1.DriverSandboxSpec.EnvironmentEntry - 4, // 3: openshell.compute.v1.DriverSandboxSpec.template:type_name -> openshell.compute.v1.DriverSandboxTemplate - 27, // 4: openshell.compute.v1.DriverSandboxTemplate.labels:type_name -> openshell.compute.v1.DriverSandboxTemplate.LabelsEntry - 28, // 5: openshell.compute.v1.DriverSandboxTemplate.environment:type_name -> openshell.compute.v1.DriverSandboxTemplate.EnvironmentEntry - 5, // 6: openshell.compute.v1.DriverSandboxTemplate.resources:type_name -> openshell.compute.v1.DriverResourceRequirements - 30, // 7: openshell.compute.v1.DriverSandboxTemplate.platform_config:type_name -> google.protobuf.Struct - 7, // 8: openshell.compute.v1.DriverSandboxStatus.conditions:type_name -> openshell.compute.v1.DriverCondition - 29, // 9: openshell.compute.v1.DriverPlatformEvent.metadata:type_name -> openshell.compute.v1.DriverPlatformEvent.MetadataEntry - 2, // 10: openshell.compute.v1.ValidateSandboxCreateRequest.sandbox:type_name -> openshell.compute.v1.DriverSandbox - 2, // 11: openshell.compute.v1.GetSandboxResponse.sandbox:type_name -> openshell.compute.v1.DriverSandbox - 2, // 12: openshell.compute.v1.ListSandboxesResponse.sandboxes:type_name -> openshell.compute.v1.DriverSandbox - 2, // 13: openshell.compute.v1.CreateSandboxRequest.sandbox:type_name -> openshell.compute.v1.DriverSandbox - 2, // 14: openshell.compute.v1.WatchSandboxesSandboxEvent.sandbox:type_name -> openshell.compute.v1.DriverSandbox - 8, // 15: openshell.compute.v1.WatchSandboxesPlatformEvent.event:type_name -> openshell.compute.v1.DriverPlatformEvent - 22, // 16: openshell.compute.v1.WatchSandboxesEvent.sandbox:type_name -> openshell.compute.v1.WatchSandboxesSandboxEvent - 23, // 17: openshell.compute.v1.WatchSandboxesEvent.deleted:type_name -> openshell.compute.v1.WatchSandboxesDeletedEvent - 24, // 18: openshell.compute.v1.WatchSandboxesEvent.platform_event:type_name -> openshell.compute.v1.WatchSandboxesPlatformEvent - 0, // 19: openshell.compute.v1.ComputeDriver.GetCapabilities:input_type -> openshell.compute.v1.GetCapabilitiesRequest - 9, // 20: openshell.compute.v1.ComputeDriver.ValidateSandboxCreate:input_type -> openshell.compute.v1.ValidateSandboxCreateRequest - 11, // 21: openshell.compute.v1.ComputeDriver.GetSandbox:input_type -> openshell.compute.v1.GetSandboxRequest - 13, // 22: openshell.compute.v1.ComputeDriver.ListSandboxes:input_type -> openshell.compute.v1.ListSandboxesRequest - 15, // 23: openshell.compute.v1.ComputeDriver.CreateSandbox:input_type -> openshell.compute.v1.CreateSandboxRequest - 17, // 24: openshell.compute.v1.ComputeDriver.StopSandbox:input_type -> openshell.compute.v1.StopSandboxRequest - 19, // 25: openshell.compute.v1.ComputeDriver.DeleteSandbox:input_type -> openshell.compute.v1.DeleteSandboxRequest - 21, // 26: openshell.compute.v1.ComputeDriver.WatchSandboxes:input_type -> openshell.compute.v1.WatchSandboxesRequest - 1, // 27: openshell.compute.v1.ComputeDriver.GetCapabilities:output_type -> openshell.compute.v1.GetCapabilitiesResponse - 10, // 28: openshell.compute.v1.ComputeDriver.ValidateSandboxCreate:output_type -> openshell.compute.v1.ValidateSandboxCreateResponse - 12, // 29: openshell.compute.v1.ComputeDriver.GetSandbox:output_type -> openshell.compute.v1.GetSandboxResponse - 14, // 30: openshell.compute.v1.ComputeDriver.ListSandboxes:output_type -> openshell.compute.v1.ListSandboxesResponse - 16, // 31: openshell.compute.v1.ComputeDriver.CreateSandbox:output_type -> openshell.compute.v1.CreateSandboxResponse - 18, // 32: openshell.compute.v1.ComputeDriver.StopSandbox:output_type -> openshell.compute.v1.StopSandboxResponse - 20, // 33: openshell.compute.v1.ComputeDriver.DeleteSandbox:output_type -> openshell.compute.v1.DeleteSandboxResponse - 25, // 34: openshell.compute.v1.ComputeDriver.WatchSandboxes:output_type -> openshell.compute.v1.WatchSandboxesEvent - 27, // [27:35] is the sub-list for method output_type - 19, // [19:27] is the sub-list for method input_type - 19, // [19:19] is the sub-list for extension type_name - 19, // [19:19] is the sub-list for extension extendee - 0, // [0:19] is the sub-list for field type_name -} - -func init() { file_compute_driver_proto_init() } -func file_compute_driver_proto_init() { - if File_compute_driver_proto != nil { - return - } - file_compute_driver_proto_msgTypes[25].OneofWrappers = []any{ - (*WatchSandboxesEvent_Sandbox)(nil), - (*WatchSandboxesEvent_Deleted)(nil), - (*WatchSandboxesEvent_PlatformEvent)(nil), - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_compute_driver_proto_rawDesc), len(file_compute_driver_proto_rawDesc)), - NumEnums: 0, - NumMessages: 30, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_compute_driver_proto_goTypes, - DependencyIndexes: file_compute_driver_proto_depIdxs, - MessageInfos: file_compute_driver_proto_msgTypes, - }.Build() - File_compute_driver_proto = out.File - file_compute_driver_proto_goTypes = nil - file_compute_driver_proto_depIdxs = nil -} diff --git a/go/api/openshell/gen/computev1/compute_driver_grpc.pb.go b/go/api/openshell/gen/computev1/compute_driver_grpc.pb.go deleted file mode 100644 index 5bbb329873..0000000000 --- a/go/api/openshell/gen/computev1/compute_driver_grpc.pb.go +++ /dev/null @@ -1,428 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.5.1 -// - protoc (unknown) -// source: compute_driver.proto - -package computev1 - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 - -const ( - ComputeDriver_GetCapabilities_FullMethodName = "/openshell.compute.v1.ComputeDriver/GetCapabilities" - ComputeDriver_ValidateSandboxCreate_FullMethodName = "/openshell.compute.v1.ComputeDriver/ValidateSandboxCreate" - ComputeDriver_GetSandbox_FullMethodName = "/openshell.compute.v1.ComputeDriver/GetSandbox" - ComputeDriver_ListSandboxes_FullMethodName = "/openshell.compute.v1.ComputeDriver/ListSandboxes" - ComputeDriver_CreateSandbox_FullMethodName = "/openshell.compute.v1.ComputeDriver/CreateSandbox" - ComputeDriver_StopSandbox_FullMethodName = "/openshell.compute.v1.ComputeDriver/StopSandbox" - ComputeDriver_DeleteSandbox_FullMethodName = "/openshell.compute.v1.ComputeDriver/DeleteSandbox" - ComputeDriver_WatchSandboxes_FullMethodName = "/openshell.compute.v1.ComputeDriver/WatchSandboxes" -) - -// ComputeDriverClient is the client API for ComputeDriver service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -// -// Internal compute-driver contract used by the gateway. -// -// Conventions: -// - This file owns driver-native request, response, and observation types. -// - Compute drivers must not import or return the public `openshell.v1.Sandbox` -// resource model. -// - The gateway translates between these internal driver-native messages and -// the public OpenShell API resource model. -type ComputeDriverClient interface { - // Report driver capabilities and defaults. - GetCapabilities(ctx context.Context, in *GetCapabilitiesRequest, opts ...grpc.CallOption) (*GetCapabilitiesResponse, error) - // Validate a sandbox before create-time provisioning. - ValidateSandboxCreate(ctx context.Context, in *ValidateSandboxCreateRequest, opts ...grpc.CallOption) (*ValidateSandboxCreateResponse, error) - // Fetch the platform-observed sandbox state for one sandbox. - GetSandbox(ctx context.Context, in *GetSandboxRequest, opts ...grpc.CallOption) (*GetSandboxResponse, error) - // List platform-observed sandbox state for all sandboxes. - ListSandboxes(ctx context.Context, in *ListSandboxesRequest, opts ...grpc.CallOption) (*ListSandboxesResponse, error) - // Provision platform resources for a sandbox. - CreateSandbox(ctx context.Context, in *CreateSandboxRequest, opts ...grpc.CallOption) (*CreateSandboxResponse, error) - // Stop platform resources for a sandbox without deleting its record. - StopSandbox(ctx context.Context, in *StopSandboxRequest, opts ...grpc.CallOption) (*StopSandboxResponse, error) - // Tear down platform resources for a sandbox. - DeleteSandbox(ctx context.Context, in *DeleteSandboxRequest, opts ...grpc.CallOption) (*DeleteSandboxResponse, error) - // Stream sandbox observations from the platform. - WatchSandboxes(ctx context.Context, in *WatchSandboxesRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[WatchSandboxesEvent], error) -} - -type computeDriverClient struct { - cc grpc.ClientConnInterface -} - -func NewComputeDriverClient(cc grpc.ClientConnInterface) ComputeDriverClient { - return &computeDriverClient{cc} -} - -func (c *computeDriverClient) GetCapabilities(ctx context.Context, in *GetCapabilitiesRequest, opts ...grpc.CallOption) (*GetCapabilitiesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetCapabilitiesResponse) - err := c.cc.Invoke(ctx, ComputeDriver_GetCapabilities_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *computeDriverClient) ValidateSandboxCreate(ctx context.Context, in *ValidateSandboxCreateRequest, opts ...grpc.CallOption) (*ValidateSandboxCreateResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ValidateSandboxCreateResponse) - err := c.cc.Invoke(ctx, ComputeDriver_ValidateSandboxCreate_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *computeDriverClient) GetSandbox(ctx context.Context, in *GetSandboxRequest, opts ...grpc.CallOption) (*GetSandboxResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetSandboxResponse) - err := c.cc.Invoke(ctx, ComputeDriver_GetSandbox_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *computeDriverClient) ListSandboxes(ctx context.Context, in *ListSandboxesRequest, opts ...grpc.CallOption) (*ListSandboxesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ListSandboxesResponse) - err := c.cc.Invoke(ctx, ComputeDriver_ListSandboxes_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *computeDriverClient) CreateSandbox(ctx context.Context, in *CreateSandboxRequest, opts ...grpc.CallOption) (*CreateSandboxResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(CreateSandboxResponse) - err := c.cc.Invoke(ctx, ComputeDriver_CreateSandbox_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *computeDriverClient) StopSandbox(ctx context.Context, in *StopSandboxRequest, opts ...grpc.CallOption) (*StopSandboxResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(StopSandboxResponse) - err := c.cc.Invoke(ctx, ComputeDriver_StopSandbox_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *computeDriverClient) DeleteSandbox(ctx context.Context, in *DeleteSandboxRequest, opts ...grpc.CallOption) (*DeleteSandboxResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(DeleteSandboxResponse) - err := c.cc.Invoke(ctx, ComputeDriver_DeleteSandbox_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *computeDriverClient) WatchSandboxes(ctx context.Context, in *WatchSandboxesRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[WatchSandboxesEvent], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &ComputeDriver_ServiceDesc.Streams[0], ComputeDriver_WatchSandboxes_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[WatchSandboxesRequest, WatchSandboxesEvent]{ClientStream: stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type ComputeDriver_WatchSandboxesClient = grpc.ServerStreamingClient[WatchSandboxesEvent] - -// ComputeDriverServer is the server API for ComputeDriver service. -// All implementations must embed UnimplementedComputeDriverServer -// for forward compatibility. -// -// Internal compute-driver contract used by the gateway. -// -// Conventions: -// - This file owns driver-native request, response, and observation types. -// - Compute drivers must not import or return the public `openshell.v1.Sandbox` -// resource model. -// - The gateway translates between these internal driver-native messages and -// the public OpenShell API resource model. -type ComputeDriverServer interface { - // Report driver capabilities and defaults. - GetCapabilities(context.Context, *GetCapabilitiesRequest) (*GetCapabilitiesResponse, error) - // Validate a sandbox before create-time provisioning. - ValidateSandboxCreate(context.Context, *ValidateSandboxCreateRequest) (*ValidateSandboxCreateResponse, error) - // Fetch the platform-observed sandbox state for one sandbox. - GetSandbox(context.Context, *GetSandboxRequest) (*GetSandboxResponse, error) - // List platform-observed sandbox state for all sandboxes. - ListSandboxes(context.Context, *ListSandboxesRequest) (*ListSandboxesResponse, error) - // Provision platform resources for a sandbox. - CreateSandbox(context.Context, *CreateSandboxRequest) (*CreateSandboxResponse, error) - // Stop platform resources for a sandbox without deleting its record. - StopSandbox(context.Context, *StopSandboxRequest) (*StopSandboxResponse, error) - // Tear down platform resources for a sandbox. - DeleteSandbox(context.Context, *DeleteSandboxRequest) (*DeleteSandboxResponse, error) - // Stream sandbox observations from the platform. - WatchSandboxes(*WatchSandboxesRequest, grpc.ServerStreamingServer[WatchSandboxesEvent]) error - mustEmbedUnimplementedComputeDriverServer() -} - -// UnimplementedComputeDriverServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedComputeDriverServer struct{} - -func (UnimplementedComputeDriverServer) GetCapabilities(context.Context, *GetCapabilitiesRequest) (*GetCapabilitiesResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetCapabilities not implemented") -} -func (UnimplementedComputeDriverServer) ValidateSandboxCreate(context.Context, *ValidateSandboxCreateRequest) (*ValidateSandboxCreateResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ValidateSandboxCreate not implemented") -} -func (UnimplementedComputeDriverServer) GetSandbox(context.Context, *GetSandboxRequest) (*GetSandboxResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetSandbox not implemented") -} -func (UnimplementedComputeDriverServer) ListSandboxes(context.Context, *ListSandboxesRequest) (*ListSandboxesResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListSandboxes not implemented") -} -func (UnimplementedComputeDriverServer) CreateSandbox(context.Context, *CreateSandboxRequest) (*CreateSandboxResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreateSandbox not implemented") -} -func (UnimplementedComputeDriverServer) StopSandbox(context.Context, *StopSandboxRequest) (*StopSandboxResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method StopSandbox not implemented") -} -func (UnimplementedComputeDriverServer) DeleteSandbox(context.Context, *DeleteSandboxRequest) (*DeleteSandboxResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method DeleteSandbox not implemented") -} -func (UnimplementedComputeDriverServer) WatchSandboxes(*WatchSandboxesRequest, grpc.ServerStreamingServer[WatchSandboxesEvent]) error { - return status.Errorf(codes.Unimplemented, "method WatchSandboxes not implemented") -} -func (UnimplementedComputeDriverServer) mustEmbedUnimplementedComputeDriverServer() {} -func (UnimplementedComputeDriverServer) testEmbeddedByValue() {} - -// UnsafeComputeDriverServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to ComputeDriverServer will -// result in compilation errors. -type UnsafeComputeDriverServer interface { - mustEmbedUnimplementedComputeDriverServer() -} - -func RegisterComputeDriverServer(s grpc.ServiceRegistrar, srv ComputeDriverServer) { - // If the following call pancis, it indicates UnimplementedComputeDriverServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } - s.RegisterService(&ComputeDriver_ServiceDesc, srv) -} - -func _ComputeDriver_GetCapabilities_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetCapabilitiesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ComputeDriverServer).GetCapabilities(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: ComputeDriver_GetCapabilities_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ComputeDriverServer).GetCapabilities(ctx, req.(*GetCapabilitiesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _ComputeDriver_ValidateSandboxCreate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ValidateSandboxCreateRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ComputeDriverServer).ValidateSandboxCreate(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: ComputeDriver_ValidateSandboxCreate_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ComputeDriverServer).ValidateSandboxCreate(ctx, req.(*ValidateSandboxCreateRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _ComputeDriver_GetSandbox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetSandboxRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ComputeDriverServer).GetSandbox(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: ComputeDriver_GetSandbox_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ComputeDriverServer).GetSandbox(ctx, req.(*GetSandboxRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _ComputeDriver_ListSandboxes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListSandboxesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ComputeDriverServer).ListSandboxes(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: ComputeDriver_ListSandboxes_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ComputeDriverServer).ListSandboxes(ctx, req.(*ListSandboxesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _ComputeDriver_CreateSandbox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateSandboxRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ComputeDriverServer).CreateSandbox(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: ComputeDriver_CreateSandbox_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ComputeDriverServer).CreateSandbox(ctx, req.(*CreateSandboxRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _ComputeDriver_StopSandbox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(StopSandboxRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ComputeDriverServer).StopSandbox(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: ComputeDriver_StopSandbox_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ComputeDriverServer).StopSandbox(ctx, req.(*StopSandboxRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _ComputeDriver_DeleteSandbox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteSandboxRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ComputeDriverServer).DeleteSandbox(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: ComputeDriver_DeleteSandbox_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ComputeDriverServer).DeleteSandbox(ctx, req.(*DeleteSandboxRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _ComputeDriver_WatchSandboxes_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(WatchSandboxesRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(ComputeDriverServer).WatchSandboxes(m, &grpc.GenericServerStream[WatchSandboxesRequest, WatchSandboxesEvent]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type ComputeDriver_WatchSandboxesServer = grpc.ServerStreamingServer[WatchSandboxesEvent] - -// ComputeDriver_ServiceDesc is the grpc.ServiceDesc for ComputeDriver service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var ComputeDriver_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "openshell.compute.v1.ComputeDriver", - HandlerType: (*ComputeDriverServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "GetCapabilities", - Handler: _ComputeDriver_GetCapabilities_Handler, - }, - { - MethodName: "ValidateSandboxCreate", - Handler: _ComputeDriver_ValidateSandboxCreate_Handler, - }, - { - MethodName: "GetSandbox", - Handler: _ComputeDriver_GetSandbox_Handler, - }, - { - MethodName: "ListSandboxes", - Handler: _ComputeDriver_ListSandboxes_Handler, - }, - { - MethodName: "CreateSandbox", - Handler: _ComputeDriver_CreateSandbox_Handler, - }, - { - MethodName: "StopSandbox", - Handler: _ComputeDriver_StopSandbox_Handler, - }, - { - MethodName: "DeleteSandbox", - Handler: _ComputeDriver_DeleteSandbox_Handler, - }, - }, - Streams: []grpc.StreamDesc{ - { - StreamName: "WatchSandboxes", - Handler: _ComputeDriver_WatchSandboxes_Handler, - ServerStreams: true, - }, - }, - Metadata: "compute_driver.proto", -} diff --git a/go/api/openshell/gen/datamodelv1/datamodel.pb.go b/go/api/openshell/gen/datamodelv1/datamodel.pb.go deleted file mode 100644 index 1d55e8ea91..0000000000 --- a/go/api/openshell/gen/datamodelv1/datamodel.pb.go +++ /dev/null @@ -1,269 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.11 -// protoc (unknown) -// source: datamodel.proto - -package datamodelv1 - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// Kubernetes-style metadata shared by all top-level OpenShell domain objects. -// -// This structure provides consistent metadata (identity, labels, timestamps, -// resource versioning) across Sandbox, Provider, SshSession, and other resources. -type ObjectMeta struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Stable object ID generated by the gateway. - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - // Human-readable object name (unique per object type). - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - // Milliseconds since Unix epoch when the object was created. - CreatedAtMs int64 `protobuf:"varint,3,opt,name=created_at_ms,json=createdAtMs,proto3" json:"created_at_ms,omitempty"` - // Key-value labels for filtering and organization. - // Labels must follow Kubernetes conventions: alphanumeric + `-._/`, max 63 chars per segment. - Labels map[string]string `protobuf:"bytes,4,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Optimistic concurrency control version. - // Incremented by the gateway on each update. Clients can use this for compare-and-swap operations. - ResourceVersion uint64 `protobuf:"varint,5,opt,name=resource_version,json=resourceVersion,proto3" json:"resource_version,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ObjectMeta) Reset() { - *x = ObjectMeta{} - mi := &file_datamodel_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ObjectMeta) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ObjectMeta) ProtoMessage() {} - -func (x *ObjectMeta) ProtoReflect() protoreflect.Message { - mi := &file_datamodel_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ObjectMeta.ProtoReflect.Descriptor instead. -func (*ObjectMeta) Descriptor() ([]byte, []int) { - return file_datamodel_proto_rawDescGZIP(), []int{0} -} - -func (x *ObjectMeta) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *ObjectMeta) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ObjectMeta) GetCreatedAtMs() int64 { - if x != nil { - return x.CreatedAtMs - } - return 0 -} - -func (x *ObjectMeta) GetLabels() map[string]string { - if x != nil { - return x.Labels - } - return nil -} - -func (x *ObjectMeta) GetResourceVersion() uint64 { - if x != nil { - return x.ResourceVersion - } - return 0 -} - -// Provider model stored by OpenShell. -type Provider struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Kubernetes-style metadata (id, name, labels, timestamps, resource version). - Metadata *ObjectMeta `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` - // Canonical provider type slug (for example: "claude", "gitlab"). - Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` - // Secret values used for authentication. - Credentials map[string]string `protobuf:"bytes,3,rep,name=credentials,proto3" json:"credentials,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Non-secret provider configuration. - Config map[string]string `protobuf:"bytes,4,rep,name=config,proto3" json:"config,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *Provider) Reset() { - *x = Provider{} - mi := &file_datamodel_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Provider) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Provider) ProtoMessage() {} - -func (x *Provider) ProtoReflect() protoreflect.Message { - mi := &file_datamodel_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Provider.ProtoReflect.Descriptor instead. -func (*Provider) Descriptor() ([]byte, []int) { - return file_datamodel_proto_rawDescGZIP(), []int{1} -} - -func (x *Provider) GetMetadata() *ObjectMeta { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *Provider) GetType() string { - if x != nil { - return x.Type - } - return "" -} - -func (x *Provider) GetCredentials() map[string]string { - if x != nil { - return x.Credentials - } - return nil -} - -func (x *Provider) GetConfig() map[string]string { - if x != nil { - return x.Config - } - return nil -} - -var File_datamodel_proto protoreflect.FileDescriptor - -const file_datamodel_proto_rawDesc = "" + - "\n" + - "\x0fdatamodel.proto\x12\x16openshell.datamodel.v1\"\x82\x02\n" + - "\n" + - "ObjectMeta\x12\x0e\n" + - "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + - "\x04name\x18\x02 \x01(\tR\x04name\x12\"\n" + - "\rcreated_at_ms\x18\x03 \x01(\x03R\vcreatedAtMs\x12F\n" + - "\x06labels\x18\x04 \x03(\v2..openshell.datamodel.v1.ObjectMeta.LabelsEntryR\x06labels\x12)\n" + - "\x10resource_version\x18\x05 \x01(\x04R\x0fresourceVersion\x1a9\n" + - "\vLabelsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xf4\x02\n" + - "\bProvider\x12>\n" + - "\bmetadata\x18\x01 \x01(\v2\".openshell.datamodel.v1.ObjectMetaR\bmetadata\x12\x12\n" + - "\x04type\x18\x02 \x01(\tR\x04type\x12S\n" + - "\vcredentials\x18\x03 \x03(\v21.openshell.datamodel.v1.Provider.CredentialsEntryR\vcredentials\x12D\n" + - "\x06config\x18\x04 \x03(\v2,.openshell.datamodel.v1.Provider.ConfigEntryR\x06config\x1a>\n" + - "\x10CredentialsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a9\n" + - "\vConfigEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\xe5\x01\n" + - "\x1acom.openshell.datamodel.v1B\x0eDatamodelProtoP\x01Z=github.com/kagent-dev/kagent/go/api/openshell/gen/datamodelv1\xa2\x02\x03ODX\xaa\x02\x16Openshell.Datamodel.V1\xca\x02\x16Openshell\\Datamodel\\V1\xe2\x02\"Openshell\\Datamodel\\V1\\GPBMetadata\xea\x02\x18Openshell::Datamodel::V1b\x06proto3" - -var ( - file_datamodel_proto_rawDescOnce sync.Once - file_datamodel_proto_rawDescData []byte -) - -func file_datamodel_proto_rawDescGZIP() []byte { - file_datamodel_proto_rawDescOnce.Do(func() { - file_datamodel_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_datamodel_proto_rawDesc), len(file_datamodel_proto_rawDesc))) - }) - return file_datamodel_proto_rawDescData -} - -var file_datamodel_proto_msgTypes = make([]protoimpl.MessageInfo, 5) -var file_datamodel_proto_goTypes = []any{ - (*ObjectMeta)(nil), // 0: openshell.datamodel.v1.ObjectMeta - (*Provider)(nil), // 1: openshell.datamodel.v1.Provider - nil, // 2: openshell.datamodel.v1.ObjectMeta.LabelsEntry - nil, // 3: openshell.datamodel.v1.Provider.CredentialsEntry - nil, // 4: openshell.datamodel.v1.Provider.ConfigEntry -} -var file_datamodel_proto_depIdxs = []int32{ - 2, // 0: openshell.datamodel.v1.ObjectMeta.labels:type_name -> openshell.datamodel.v1.ObjectMeta.LabelsEntry - 0, // 1: openshell.datamodel.v1.Provider.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 3, // 2: openshell.datamodel.v1.Provider.credentials:type_name -> openshell.datamodel.v1.Provider.CredentialsEntry - 4, // 3: openshell.datamodel.v1.Provider.config:type_name -> openshell.datamodel.v1.Provider.ConfigEntry - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name -} - -func init() { file_datamodel_proto_init() } -func file_datamodel_proto_init() { - if File_datamodel_proto != nil { - return - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_datamodel_proto_rawDesc), len(file_datamodel_proto_rawDesc)), - NumEnums: 0, - NumMessages: 5, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_datamodel_proto_goTypes, - DependencyIndexes: file_datamodel_proto_depIdxs, - MessageInfos: file_datamodel_proto_msgTypes, - }.Build() - File_datamodel_proto = out.File - file_datamodel_proto_goTypes = nil - file_datamodel_proto_depIdxs = nil -} diff --git a/go/api/openshell/gen/inferencev1/inference.pb.go b/go/api/openshell/gen/inferencev1/inference.pb.go deleted file mode 100644 index c184850547..0000000000 --- a/go/api/openshell/gen/inferencev1/inference.pb.go +++ /dev/null @@ -1,840 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.11 -// protoc (unknown) -// source: inference.proto - -package inferencev1 - -import ( - datamodelv1 "github.com/kagent-dev/kagent/go/api/openshell/gen/datamodelv1" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// Persisted cluster inference configuration. -// -// Only `provider_name` and `model_id` are stored; endpoint, protocols, -// credentials, and auth style are resolved from the provider at bundle time. -type ClusterInferenceConfig struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Provider record name backing this route. - ProviderName string `protobuf:"bytes,1,opt,name=provider_name,json=providerName,proto3" json:"provider_name,omitempty"` - // Model identifier to force on generation calls. - ModelId string `protobuf:"bytes,2,opt,name=model_id,json=modelId,proto3" json:"model_id,omitempty"` - // Per-route request timeout in seconds. 0 means use default (60s). - TimeoutSecs uint64 `protobuf:"varint,3,opt,name=timeout_secs,json=timeoutSecs,proto3" json:"timeout_secs,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ClusterInferenceConfig) Reset() { - *x = ClusterInferenceConfig{} - mi := &file_inference_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ClusterInferenceConfig) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ClusterInferenceConfig) ProtoMessage() {} - -func (x *ClusterInferenceConfig) ProtoReflect() protoreflect.Message { - mi := &file_inference_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ClusterInferenceConfig.ProtoReflect.Descriptor instead. -func (*ClusterInferenceConfig) Descriptor() ([]byte, []int) { - return file_inference_proto_rawDescGZIP(), []int{0} -} - -func (x *ClusterInferenceConfig) GetProviderName() string { - if x != nil { - return x.ProviderName - } - return "" -} - -func (x *ClusterInferenceConfig) GetModelId() string { - if x != nil { - return x.ModelId - } - return "" -} - -func (x *ClusterInferenceConfig) GetTimeoutSecs() uint64 { - if x != nil { - return x.TimeoutSecs - } - return 0 -} - -// Storage envelope for the managed cluster inference route. -type InferenceRoute struct { - state protoimpl.MessageState `protogen:"open.v1"` - Metadata *datamodelv1.ObjectMeta `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` - Config *ClusterInferenceConfig `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` - // Monotonic version incremented on every update. - Version uint64 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *InferenceRoute) Reset() { - *x = InferenceRoute{} - mi := &file_inference_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *InferenceRoute) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*InferenceRoute) ProtoMessage() {} - -func (x *InferenceRoute) ProtoReflect() protoreflect.Message { - mi := &file_inference_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use InferenceRoute.ProtoReflect.Descriptor instead. -func (*InferenceRoute) Descriptor() ([]byte, []int) { - return file_inference_proto_rawDescGZIP(), []int{1} -} - -func (x *InferenceRoute) GetMetadata() *datamodelv1.ObjectMeta { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *InferenceRoute) GetConfig() *ClusterInferenceConfig { - if x != nil { - return x.Config - } - return nil -} - -func (x *InferenceRoute) GetVersion() uint64 { - if x != nil { - return x.Version - } - return 0 -} - -type SetClusterInferenceRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Provider record name to use for credentials + endpoint mapping. - ProviderName string `protobuf:"bytes,1,opt,name=provider_name,json=providerName,proto3" json:"provider_name,omitempty"` - // Model identifier to force on generation calls. - ModelId string `protobuf:"bytes,2,opt,name=model_id,json=modelId,proto3" json:"model_id,omitempty"` - // Route name to target. Empty string defaults to "inference.local" (user-facing). - // Use "sandbox-system" for the sandbox system-level inference route. - RouteName string `protobuf:"bytes,3,opt,name=route_name,json=routeName,proto3" json:"route_name,omitempty"` - // Verify the resolved upstream endpoint synchronously before persistence. - Verify bool `protobuf:"varint,4,opt,name=verify,proto3" json:"verify,omitempty"` - // Skip synchronous endpoint validation before persistence. - NoVerify bool `protobuf:"varint,5,opt,name=no_verify,json=noVerify,proto3" json:"no_verify,omitempty"` - // Per-route request timeout in seconds. 0 means use default (60s). - TimeoutSecs uint64 `protobuf:"varint,6,opt,name=timeout_secs,json=timeoutSecs,proto3" json:"timeout_secs,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SetClusterInferenceRequest) Reset() { - *x = SetClusterInferenceRequest{} - mi := &file_inference_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SetClusterInferenceRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SetClusterInferenceRequest) ProtoMessage() {} - -func (x *SetClusterInferenceRequest) ProtoReflect() protoreflect.Message { - mi := &file_inference_proto_msgTypes[2] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SetClusterInferenceRequest.ProtoReflect.Descriptor instead. -func (*SetClusterInferenceRequest) Descriptor() ([]byte, []int) { - return file_inference_proto_rawDescGZIP(), []int{2} -} - -func (x *SetClusterInferenceRequest) GetProviderName() string { - if x != nil { - return x.ProviderName - } - return "" -} - -func (x *SetClusterInferenceRequest) GetModelId() string { - if x != nil { - return x.ModelId - } - return "" -} - -func (x *SetClusterInferenceRequest) GetRouteName() string { - if x != nil { - return x.RouteName - } - return "" -} - -func (x *SetClusterInferenceRequest) GetVerify() bool { - if x != nil { - return x.Verify - } - return false -} - -func (x *SetClusterInferenceRequest) GetNoVerify() bool { - if x != nil { - return x.NoVerify - } - return false -} - -func (x *SetClusterInferenceRequest) GetTimeoutSecs() uint64 { - if x != nil { - return x.TimeoutSecs - } - return 0 -} - -type ValidatedEndpoint struct { - state protoimpl.MessageState `protogen:"open.v1"` - Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` - Protocol string `protobuf:"bytes,2,opt,name=protocol,proto3" json:"protocol,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ValidatedEndpoint) Reset() { - *x = ValidatedEndpoint{} - mi := &file_inference_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ValidatedEndpoint) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ValidatedEndpoint) ProtoMessage() {} - -func (x *ValidatedEndpoint) ProtoReflect() protoreflect.Message { - mi := &file_inference_proto_msgTypes[3] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ValidatedEndpoint.ProtoReflect.Descriptor instead. -func (*ValidatedEndpoint) Descriptor() ([]byte, []int) { - return file_inference_proto_rawDescGZIP(), []int{3} -} - -func (x *ValidatedEndpoint) GetUrl() string { - if x != nil { - return x.Url - } - return "" -} - -func (x *ValidatedEndpoint) GetProtocol() string { - if x != nil { - return x.Protocol - } - return "" -} - -type SetClusterInferenceResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - ProviderName string `protobuf:"bytes,1,opt,name=provider_name,json=providerName,proto3" json:"provider_name,omitempty"` - ModelId string `protobuf:"bytes,2,opt,name=model_id,json=modelId,proto3" json:"model_id,omitempty"` - Version uint64 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"` - // Route name that was configured. - RouteName string `protobuf:"bytes,4,opt,name=route_name,json=routeName,proto3" json:"route_name,omitempty"` - // Whether endpoint verification ran as part of this request. - ValidationPerformed bool `protobuf:"varint,5,opt,name=validation_performed,json=validationPerformed,proto3" json:"validation_performed,omitempty"` - // The concrete endpoints that were probed during validation, when available. - ValidatedEndpoints []*ValidatedEndpoint `protobuf:"bytes,6,rep,name=validated_endpoints,json=validatedEndpoints,proto3" json:"validated_endpoints,omitempty"` - // Per-route request timeout in seconds that was persisted. - TimeoutSecs uint64 `protobuf:"varint,7,opt,name=timeout_secs,json=timeoutSecs,proto3" json:"timeout_secs,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SetClusterInferenceResponse) Reset() { - *x = SetClusterInferenceResponse{} - mi := &file_inference_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SetClusterInferenceResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SetClusterInferenceResponse) ProtoMessage() {} - -func (x *SetClusterInferenceResponse) ProtoReflect() protoreflect.Message { - mi := &file_inference_proto_msgTypes[4] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SetClusterInferenceResponse.ProtoReflect.Descriptor instead. -func (*SetClusterInferenceResponse) Descriptor() ([]byte, []int) { - return file_inference_proto_rawDescGZIP(), []int{4} -} - -func (x *SetClusterInferenceResponse) GetProviderName() string { - if x != nil { - return x.ProviderName - } - return "" -} - -func (x *SetClusterInferenceResponse) GetModelId() string { - if x != nil { - return x.ModelId - } - return "" -} - -func (x *SetClusterInferenceResponse) GetVersion() uint64 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *SetClusterInferenceResponse) GetRouteName() string { - if x != nil { - return x.RouteName - } - return "" -} - -func (x *SetClusterInferenceResponse) GetValidationPerformed() bool { - if x != nil { - return x.ValidationPerformed - } - return false -} - -func (x *SetClusterInferenceResponse) GetValidatedEndpoints() []*ValidatedEndpoint { - if x != nil { - return x.ValidatedEndpoints - } - return nil -} - -func (x *SetClusterInferenceResponse) GetTimeoutSecs() uint64 { - if x != nil { - return x.TimeoutSecs - } - return 0 -} - -type GetClusterInferenceRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Route name to query. Empty string defaults to "inference.local" (user-facing). - // Use "sandbox-system" for the sandbox system-level inference route. - RouteName string `protobuf:"bytes,1,opt,name=route_name,json=routeName,proto3" json:"route_name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetClusterInferenceRequest) Reset() { - *x = GetClusterInferenceRequest{} - mi := &file_inference_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetClusterInferenceRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetClusterInferenceRequest) ProtoMessage() {} - -func (x *GetClusterInferenceRequest) ProtoReflect() protoreflect.Message { - mi := &file_inference_proto_msgTypes[5] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetClusterInferenceRequest.ProtoReflect.Descriptor instead. -func (*GetClusterInferenceRequest) Descriptor() ([]byte, []int) { - return file_inference_proto_rawDescGZIP(), []int{5} -} - -func (x *GetClusterInferenceRequest) GetRouteName() string { - if x != nil { - return x.RouteName - } - return "" -} - -type GetClusterInferenceResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - ProviderName string `protobuf:"bytes,1,opt,name=provider_name,json=providerName,proto3" json:"provider_name,omitempty"` - ModelId string `protobuf:"bytes,2,opt,name=model_id,json=modelId,proto3" json:"model_id,omitempty"` - Version uint64 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"` - // Route name that was queried. - RouteName string `protobuf:"bytes,4,opt,name=route_name,json=routeName,proto3" json:"route_name,omitempty"` - // Per-route request timeout in seconds. 0 means default (60s). - TimeoutSecs uint64 `protobuf:"varint,5,opt,name=timeout_secs,json=timeoutSecs,proto3" json:"timeout_secs,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetClusterInferenceResponse) Reset() { - *x = GetClusterInferenceResponse{} - mi := &file_inference_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetClusterInferenceResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetClusterInferenceResponse) ProtoMessage() {} - -func (x *GetClusterInferenceResponse) ProtoReflect() protoreflect.Message { - mi := &file_inference_proto_msgTypes[6] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetClusterInferenceResponse.ProtoReflect.Descriptor instead. -func (*GetClusterInferenceResponse) Descriptor() ([]byte, []int) { - return file_inference_proto_rawDescGZIP(), []int{6} -} - -func (x *GetClusterInferenceResponse) GetProviderName() string { - if x != nil { - return x.ProviderName - } - return "" -} - -func (x *GetClusterInferenceResponse) GetModelId() string { - if x != nil { - return x.ModelId - } - return "" -} - -func (x *GetClusterInferenceResponse) GetVersion() uint64 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *GetClusterInferenceResponse) GetRouteName() string { - if x != nil { - return x.RouteName - } - return "" -} - -func (x *GetClusterInferenceResponse) GetTimeoutSecs() uint64 { - if x != nil { - return x.TimeoutSecs - } - return 0 -} - -type GetInferenceBundleRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetInferenceBundleRequest) Reset() { - *x = GetInferenceBundleRequest{} - mi := &file_inference_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetInferenceBundleRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetInferenceBundleRequest) ProtoMessage() {} - -func (x *GetInferenceBundleRequest) ProtoReflect() protoreflect.Message { - mi := &file_inference_proto_msgTypes[7] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetInferenceBundleRequest.ProtoReflect.Descriptor instead. -func (*GetInferenceBundleRequest) Descriptor() ([]byte, []int) { - return file_inference_proto_rawDescGZIP(), []int{7} -} - -// A single resolved route ready for sandbox-local execution. -type ResolvedRoute struct { - state protoimpl.MessageState `protogen:"open.v1"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - BaseUrl string `protobuf:"bytes,2,opt,name=base_url,json=baseUrl,proto3" json:"base_url,omitempty"` - Protocols []string `protobuf:"bytes,3,rep,name=protocols,proto3" json:"protocols,omitempty"` - ApiKey string `protobuf:"bytes,4,opt,name=api_key,json=apiKey,proto3" json:"api_key,omitempty"` - ModelId string `protobuf:"bytes,5,opt,name=model_id,json=modelId,proto3" json:"model_id,omitempty"` - ProviderType string `protobuf:"bytes,6,opt,name=provider_type,json=providerType,proto3" json:"provider_type,omitempty"` - // Per-route request timeout in seconds. 0 means use default (60s). - TimeoutSecs uint64 `protobuf:"varint,7,opt,name=timeout_secs,json=timeoutSecs,proto3" json:"timeout_secs,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ResolvedRoute) Reset() { - *x = ResolvedRoute{} - mi := &file_inference_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ResolvedRoute) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ResolvedRoute) ProtoMessage() {} - -func (x *ResolvedRoute) ProtoReflect() protoreflect.Message { - mi := &file_inference_proto_msgTypes[8] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ResolvedRoute.ProtoReflect.Descriptor instead. -func (*ResolvedRoute) Descriptor() ([]byte, []int) { - return file_inference_proto_rawDescGZIP(), []int{8} -} - -func (x *ResolvedRoute) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ResolvedRoute) GetBaseUrl() string { - if x != nil { - return x.BaseUrl - } - return "" -} - -func (x *ResolvedRoute) GetProtocols() []string { - if x != nil { - return x.Protocols - } - return nil -} - -func (x *ResolvedRoute) GetApiKey() string { - if x != nil { - return x.ApiKey - } - return "" -} - -func (x *ResolvedRoute) GetModelId() string { - if x != nil { - return x.ModelId - } - return "" -} - -func (x *ResolvedRoute) GetProviderType() string { - if x != nil { - return x.ProviderType - } - return "" -} - -func (x *ResolvedRoute) GetTimeoutSecs() uint64 { - if x != nil { - return x.TimeoutSecs - } - return 0 -} - -type GetInferenceBundleResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Routes []*ResolvedRoute `protobuf:"bytes,1,rep,name=routes,proto3" json:"routes,omitempty"` - // Opaque revision tag for cache freshness checks. - Revision string `protobuf:"bytes,2,opt,name=revision,proto3" json:"revision,omitempty"` - // Timestamp (epoch ms) when this bundle was generated. - GeneratedAtMs int64 `protobuf:"varint,3,opt,name=generated_at_ms,json=generatedAtMs,proto3" json:"generated_at_ms,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetInferenceBundleResponse) Reset() { - *x = GetInferenceBundleResponse{} - mi := &file_inference_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetInferenceBundleResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetInferenceBundleResponse) ProtoMessage() {} - -func (x *GetInferenceBundleResponse) ProtoReflect() protoreflect.Message { - mi := &file_inference_proto_msgTypes[9] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetInferenceBundleResponse.ProtoReflect.Descriptor instead. -func (*GetInferenceBundleResponse) Descriptor() ([]byte, []int) { - return file_inference_proto_rawDescGZIP(), []int{9} -} - -func (x *GetInferenceBundleResponse) GetRoutes() []*ResolvedRoute { - if x != nil { - return x.Routes - } - return nil -} - -func (x *GetInferenceBundleResponse) GetRevision() string { - if x != nil { - return x.Revision - } - return "" -} - -func (x *GetInferenceBundleResponse) GetGeneratedAtMs() int64 { - if x != nil { - return x.GeneratedAtMs - } - return 0 -} - -var File_inference_proto protoreflect.FileDescriptor - -const file_inference_proto_rawDesc = "" + - "\n" + - "\x0finference.proto\x12\x16openshell.inference.v1\x1a\x0fdatamodel.proto\"{\n" + - "\x16ClusterInferenceConfig\x12#\n" + - "\rprovider_name\x18\x01 \x01(\tR\fproviderName\x12\x19\n" + - "\bmodel_id\x18\x02 \x01(\tR\amodelId\x12!\n" + - "\ftimeout_secs\x18\x03 \x01(\x04R\vtimeoutSecs\"\xb2\x01\n" + - "\x0eInferenceRoute\x12>\n" + - "\bmetadata\x18\x01 \x01(\v2\".openshell.datamodel.v1.ObjectMetaR\bmetadata\x12F\n" + - "\x06config\x18\x02 \x01(\v2..openshell.inference.v1.ClusterInferenceConfigR\x06config\x12\x18\n" + - "\aversion\x18\x03 \x01(\x04R\aversion\"\xd3\x01\n" + - "\x1aSetClusterInferenceRequest\x12#\n" + - "\rprovider_name\x18\x01 \x01(\tR\fproviderName\x12\x19\n" + - "\bmodel_id\x18\x02 \x01(\tR\amodelId\x12\x1d\n" + - "\n" + - "route_name\x18\x03 \x01(\tR\trouteName\x12\x16\n" + - "\x06verify\x18\x04 \x01(\bR\x06verify\x12\x1b\n" + - "\tno_verify\x18\x05 \x01(\bR\bnoVerify\x12!\n" + - "\ftimeout_secs\x18\x06 \x01(\x04R\vtimeoutSecs\"A\n" + - "\x11ValidatedEndpoint\x12\x10\n" + - "\x03url\x18\x01 \x01(\tR\x03url\x12\x1a\n" + - "\bprotocol\x18\x02 \x01(\tR\bprotocol\"\xc8\x02\n" + - "\x1bSetClusterInferenceResponse\x12#\n" + - "\rprovider_name\x18\x01 \x01(\tR\fproviderName\x12\x19\n" + - "\bmodel_id\x18\x02 \x01(\tR\amodelId\x12\x18\n" + - "\aversion\x18\x03 \x01(\x04R\aversion\x12\x1d\n" + - "\n" + - "route_name\x18\x04 \x01(\tR\trouteName\x121\n" + - "\x14validation_performed\x18\x05 \x01(\bR\x13validationPerformed\x12Z\n" + - "\x13validated_endpoints\x18\x06 \x03(\v2).openshell.inference.v1.ValidatedEndpointR\x12validatedEndpoints\x12!\n" + - "\ftimeout_secs\x18\a \x01(\x04R\vtimeoutSecs\";\n" + - "\x1aGetClusterInferenceRequest\x12\x1d\n" + - "\n" + - "route_name\x18\x01 \x01(\tR\trouteName\"\xb9\x01\n" + - "\x1bGetClusterInferenceResponse\x12#\n" + - "\rprovider_name\x18\x01 \x01(\tR\fproviderName\x12\x19\n" + - "\bmodel_id\x18\x02 \x01(\tR\amodelId\x12\x18\n" + - "\aversion\x18\x03 \x01(\x04R\aversion\x12\x1d\n" + - "\n" + - "route_name\x18\x04 \x01(\tR\trouteName\x12!\n" + - "\ftimeout_secs\x18\x05 \x01(\x04R\vtimeoutSecs\"\x1b\n" + - "\x19GetInferenceBundleRequest\"\xd8\x01\n" + - "\rResolvedRoute\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12\x19\n" + - "\bbase_url\x18\x02 \x01(\tR\abaseUrl\x12\x1c\n" + - "\tprotocols\x18\x03 \x03(\tR\tprotocols\x12\x17\n" + - "\aapi_key\x18\x04 \x01(\tR\x06apiKey\x12\x19\n" + - "\bmodel_id\x18\x05 \x01(\tR\amodelId\x12#\n" + - "\rprovider_type\x18\x06 \x01(\tR\fproviderType\x12!\n" + - "\ftimeout_secs\x18\a \x01(\x04R\vtimeoutSecs\"\x9f\x01\n" + - "\x1aGetInferenceBundleResponse\x12=\n" + - "\x06routes\x18\x01 \x03(\v2%.openshell.inference.v1.ResolvedRouteR\x06routes\x12\x1a\n" + - "\brevision\x18\x02 \x01(\tR\brevision\x12&\n" + - "\x0fgenerated_at_ms\x18\x03 \x01(\x03R\rgeneratedAtMs2\x88\x03\n" + - "\tInference\x12{\n" + - "\x12GetInferenceBundle\x121.openshell.inference.v1.GetInferenceBundleRequest\x1a2.openshell.inference.v1.GetInferenceBundleResponse\x12~\n" + - "\x13SetClusterInference\x122.openshell.inference.v1.SetClusterInferenceRequest\x1a3.openshell.inference.v1.SetClusterInferenceResponse\x12~\n" + - "\x13GetClusterInference\x122.openshell.inference.v1.GetClusterInferenceRequest\x1a3.openshell.inference.v1.GetClusterInferenceResponseB\xe5\x01\n" + - "\x1acom.openshell.inference.v1B\x0eInferenceProtoP\x01Z=github.com/kagent-dev/kagent/go/api/openshell/gen/inferencev1\xa2\x02\x03OIX\xaa\x02\x16Openshell.Inference.V1\xca\x02\x16Openshell\\Inference\\V1\xe2\x02\"Openshell\\Inference\\V1\\GPBMetadata\xea\x02\x18Openshell::Inference::V1b\x06proto3" - -var ( - file_inference_proto_rawDescOnce sync.Once - file_inference_proto_rawDescData []byte -) - -func file_inference_proto_rawDescGZIP() []byte { - file_inference_proto_rawDescOnce.Do(func() { - file_inference_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_inference_proto_rawDesc), len(file_inference_proto_rawDesc))) - }) - return file_inference_proto_rawDescData -} - -var file_inference_proto_msgTypes = make([]protoimpl.MessageInfo, 10) -var file_inference_proto_goTypes = []any{ - (*ClusterInferenceConfig)(nil), // 0: openshell.inference.v1.ClusterInferenceConfig - (*InferenceRoute)(nil), // 1: openshell.inference.v1.InferenceRoute - (*SetClusterInferenceRequest)(nil), // 2: openshell.inference.v1.SetClusterInferenceRequest - (*ValidatedEndpoint)(nil), // 3: openshell.inference.v1.ValidatedEndpoint - (*SetClusterInferenceResponse)(nil), // 4: openshell.inference.v1.SetClusterInferenceResponse - (*GetClusterInferenceRequest)(nil), // 5: openshell.inference.v1.GetClusterInferenceRequest - (*GetClusterInferenceResponse)(nil), // 6: openshell.inference.v1.GetClusterInferenceResponse - (*GetInferenceBundleRequest)(nil), // 7: openshell.inference.v1.GetInferenceBundleRequest - (*ResolvedRoute)(nil), // 8: openshell.inference.v1.ResolvedRoute - (*GetInferenceBundleResponse)(nil), // 9: openshell.inference.v1.GetInferenceBundleResponse - (*datamodelv1.ObjectMeta)(nil), // 10: openshell.datamodel.v1.ObjectMeta -} -var file_inference_proto_depIdxs = []int32{ - 10, // 0: openshell.inference.v1.InferenceRoute.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 0, // 1: openshell.inference.v1.InferenceRoute.config:type_name -> openshell.inference.v1.ClusterInferenceConfig - 3, // 2: openshell.inference.v1.SetClusterInferenceResponse.validated_endpoints:type_name -> openshell.inference.v1.ValidatedEndpoint - 8, // 3: openshell.inference.v1.GetInferenceBundleResponse.routes:type_name -> openshell.inference.v1.ResolvedRoute - 7, // 4: openshell.inference.v1.Inference.GetInferenceBundle:input_type -> openshell.inference.v1.GetInferenceBundleRequest - 2, // 5: openshell.inference.v1.Inference.SetClusterInference:input_type -> openshell.inference.v1.SetClusterInferenceRequest - 5, // 6: openshell.inference.v1.Inference.GetClusterInference:input_type -> openshell.inference.v1.GetClusterInferenceRequest - 9, // 7: openshell.inference.v1.Inference.GetInferenceBundle:output_type -> openshell.inference.v1.GetInferenceBundleResponse - 4, // 8: openshell.inference.v1.Inference.SetClusterInference:output_type -> openshell.inference.v1.SetClusterInferenceResponse - 6, // 9: openshell.inference.v1.Inference.GetClusterInference:output_type -> openshell.inference.v1.GetClusterInferenceResponse - 7, // [7:10] is the sub-list for method output_type - 4, // [4:7] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name -} - -func init() { file_inference_proto_init() } -func file_inference_proto_init() { - if File_inference_proto != nil { - return - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_inference_proto_rawDesc), len(file_inference_proto_rawDesc)), - NumEnums: 0, - NumMessages: 10, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_inference_proto_goTypes, - DependencyIndexes: file_inference_proto_depIdxs, - MessageInfos: file_inference_proto_msgTypes, - }.Build() - File_inference_proto = out.File - file_inference_proto_goTypes = nil - file_inference_proto_depIdxs = nil -} diff --git a/go/api/openshell/gen/inferencev1/inference_grpc.pb.go b/go/api/openshell/gen/inferencev1/inference_grpc.pb.go deleted file mode 100644 index ff9eb20f47..0000000000 --- a/go/api/openshell/gen/inferencev1/inference_grpc.pb.go +++ /dev/null @@ -1,214 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.5.1 -// - protoc (unknown) -// source: inference.proto - -package inferencev1 - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 - -const ( - Inference_GetInferenceBundle_FullMethodName = "/openshell.inference.v1.Inference/GetInferenceBundle" - Inference_SetClusterInference_FullMethodName = "/openshell.inference.v1.Inference/SetClusterInference" - Inference_GetClusterInference_FullMethodName = "/openshell.inference.v1.Inference/GetClusterInference" -) - -// InferenceClient is the client API for Inference service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -// -// Inference service provides cluster inference configuration and bundle delivery. -type InferenceClient interface { - // Return the resolved inference route bundle for sandbox-local execution. - GetInferenceBundle(ctx context.Context, in *GetInferenceBundleRequest, opts ...grpc.CallOption) (*GetInferenceBundleResponse, error) - // Set cluster-level inference configuration. - // - // This controls how requests sent to `inference.local` are routed. - SetClusterInference(ctx context.Context, in *SetClusterInferenceRequest, opts ...grpc.CallOption) (*SetClusterInferenceResponse, error) - // Get cluster-level inference configuration. - GetClusterInference(ctx context.Context, in *GetClusterInferenceRequest, opts ...grpc.CallOption) (*GetClusterInferenceResponse, error) -} - -type inferenceClient struct { - cc grpc.ClientConnInterface -} - -func NewInferenceClient(cc grpc.ClientConnInterface) InferenceClient { - return &inferenceClient{cc} -} - -func (c *inferenceClient) GetInferenceBundle(ctx context.Context, in *GetInferenceBundleRequest, opts ...grpc.CallOption) (*GetInferenceBundleResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetInferenceBundleResponse) - err := c.cc.Invoke(ctx, Inference_GetInferenceBundle_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *inferenceClient) SetClusterInference(ctx context.Context, in *SetClusterInferenceRequest, opts ...grpc.CallOption) (*SetClusterInferenceResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(SetClusterInferenceResponse) - err := c.cc.Invoke(ctx, Inference_SetClusterInference_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *inferenceClient) GetClusterInference(ctx context.Context, in *GetClusterInferenceRequest, opts ...grpc.CallOption) (*GetClusterInferenceResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetClusterInferenceResponse) - err := c.cc.Invoke(ctx, Inference_GetClusterInference_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -// InferenceServer is the server API for Inference service. -// All implementations must embed UnimplementedInferenceServer -// for forward compatibility. -// -// Inference service provides cluster inference configuration and bundle delivery. -type InferenceServer interface { - // Return the resolved inference route bundle for sandbox-local execution. - GetInferenceBundle(context.Context, *GetInferenceBundleRequest) (*GetInferenceBundleResponse, error) - // Set cluster-level inference configuration. - // - // This controls how requests sent to `inference.local` are routed. - SetClusterInference(context.Context, *SetClusterInferenceRequest) (*SetClusterInferenceResponse, error) - // Get cluster-level inference configuration. - GetClusterInference(context.Context, *GetClusterInferenceRequest) (*GetClusterInferenceResponse, error) - mustEmbedUnimplementedInferenceServer() -} - -// UnimplementedInferenceServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedInferenceServer struct{} - -func (UnimplementedInferenceServer) GetInferenceBundle(context.Context, *GetInferenceBundleRequest) (*GetInferenceBundleResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetInferenceBundle not implemented") -} -func (UnimplementedInferenceServer) SetClusterInference(context.Context, *SetClusterInferenceRequest) (*SetClusterInferenceResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SetClusterInference not implemented") -} -func (UnimplementedInferenceServer) GetClusterInference(context.Context, *GetClusterInferenceRequest) (*GetClusterInferenceResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetClusterInference not implemented") -} -func (UnimplementedInferenceServer) mustEmbedUnimplementedInferenceServer() {} -func (UnimplementedInferenceServer) testEmbeddedByValue() {} - -// UnsafeInferenceServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to InferenceServer will -// result in compilation errors. -type UnsafeInferenceServer interface { - mustEmbedUnimplementedInferenceServer() -} - -func RegisterInferenceServer(s grpc.ServiceRegistrar, srv InferenceServer) { - // If the following call pancis, it indicates UnimplementedInferenceServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } - s.RegisterService(&Inference_ServiceDesc, srv) -} - -func _Inference_GetInferenceBundle_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetInferenceBundleRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(InferenceServer).GetInferenceBundle(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: Inference_GetInferenceBundle_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(InferenceServer).GetInferenceBundle(ctx, req.(*GetInferenceBundleRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Inference_SetClusterInference_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SetClusterInferenceRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(InferenceServer).SetClusterInference(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: Inference_SetClusterInference_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(InferenceServer).SetClusterInference(ctx, req.(*SetClusterInferenceRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Inference_GetClusterInference_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetClusterInferenceRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(InferenceServer).GetClusterInference(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: Inference_GetClusterInference_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(InferenceServer).GetClusterInference(ctx, req.(*GetClusterInferenceRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// Inference_ServiceDesc is the grpc.ServiceDesc for Inference service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var Inference_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "openshell.inference.v1.Inference", - HandlerType: (*InferenceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "GetInferenceBundle", - Handler: _Inference_GetInferenceBundle_Handler, - }, - { - MethodName: "SetClusterInference", - Handler: _Inference_SetClusterInference_Handler, - }, - { - MethodName: "GetClusterInference", - Handler: _Inference_GetClusterInference_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "inference.proto", -} diff --git a/go/api/openshell/gen/openshelltestv1/test.pb.go b/go/api/openshell/gen/openshelltestv1/test.pb.go deleted file mode 100644 index 685d3e88ab..0000000000 --- a/go/api/openshell/gen/openshelltestv1/test.pb.go +++ /dev/null @@ -1,146 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.11 -// protoc (unknown) -// source: test.proto - -package openshelltestv1 - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// Simple object for persistence tests. -type ObjectForTest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Count uint32 `protobuf:"varint,3,opt,name=count,proto3" json:"count,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ObjectForTest) Reset() { - *x = ObjectForTest{} - mi := &file_test_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ObjectForTest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ObjectForTest) ProtoMessage() {} - -func (x *ObjectForTest) ProtoReflect() protoreflect.Message { - mi := &file_test_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ObjectForTest.ProtoReflect.Descriptor instead. -func (*ObjectForTest) Descriptor() ([]byte, []int) { - return file_test_proto_rawDescGZIP(), []int{0} -} - -func (x *ObjectForTest) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *ObjectForTest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ObjectForTest) GetCount() uint32 { - if x != nil { - return x.Count - } - return 0 -} - -var File_test_proto protoreflect.FileDescriptor - -const file_test_proto_rawDesc = "" + - "\n" + - "\n" + - "test.proto\x12\x11openshell.test.v1\"I\n" + - "\rObjectForTest\x12\x0e\n" + - "\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n" + - "\x04name\x18\x02 \x01(\tR\x04name\x12\x14\n" + - "\x05count\x18\x03 \x01(\rR\x05countB\xcb\x01\n" + - "\x15com.openshell.test.v1B\tTestProtoP\x01ZAgithub.com/kagent-dev/kagent/go/api/openshell/gen/openshelltestv1\xa2\x02\x03OTX\xaa\x02\x11Openshell.Test.V1\xca\x02\x11Openshell\\Test\\V1\xe2\x02\x1dOpenshell\\Test\\V1\\GPBMetadata\xea\x02\x13Openshell::Test::V1b\x06proto3" - -var ( - file_test_proto_rawDescOnce sync.Once - file_test_proto_rawDescData []byte -) - -func file_test_proto_rawDescGZIP() []byte { - file_test_proto_rawDescOnce.Do(func() { - file_test_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_test_proto_rawDesc), len(file_test_proto_rawDesc))) - }) - return file_test_proto_rawDescData -} - -var file_test_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_test_proto_goTypes = []any{ - (*ObjectForTest)(nil), // 0: openshell.test.v1.ObjectForTest -} -var file_test_proto_depIdxs = []int32{ - 0, // [0:0] is the sub-list for method output_type - 0, // [0:0] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name -} - -func init() { file_test_proto_init() } -func file_test_proto_init() { - if File_test_proto != nil { - return - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_test_proto_rawDesc), len(file_test_proto_rawDesc)), - NumEnums: 0, - NumMessages: 1, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_test_proto_goTypes, - DependencyIndexes: file_test_proto_depIdxs, - MessageInfos: file_test_proto_msgTypes, - }.Build() - File_test_proto = out.File - file_test_proto_goTypes = nil - file_test_proto_depIdxs = nil -} diff --git a/go/api/openshell/gen/openshellv1/openshell.pb.go b/go/api/openshell/gen/openshellv1/openshell.pb.go deleted file mode 100644 index 67abdf8e3f..0000000000 --- a/go/api/openshell/gen/openshellv1/openshell.pb.go +++ /dev/null @@ -1,10496 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.11 -// protoc (unknown) -// source: openshell.proto - -package openshellv1 - -import ( - datamodelv1 "github.com/kagent-dev/kagent/go/api/openshell/gen/datamodelv1" - sandboxv1 "github.com/kagent-dev/kagent/go/api/openshell/gen/sandboxv1" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - structpb "google.golang.org/protobuf/types/known/structpb" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// High-level sandbox lifecycle phase derived by the gateway. -// -// Clients should rely on this normalized lifecycle summary for readiness and -// deletion decisions instead of interpreting raw conditions. -type SandboxPhase int32 - -const ( - SandboxPhase_SANDBOX_PHASE_UNSPECIFIED SandboxPhase = 0 - SandboxPhase_SANDBOX_PHASE_PROVISIONING SandboxPhase = 1 - SandboxPhase_SANDBOX_PHASE_READY SandboxPhase = 2 - SandboxPhase_SANDBOX_PHASE_ERROR SandboxPhase = 3 - SandboxPhase_SANDBOX_PHASE_DELETING SandboxPhase = 4 - SandboxPhase_SANDBOX_PHASE_UNKNOWN SandboxPhase = 5 -) - -// Enum value maps for SandboxPhase. -var ( - SandboxPhase_name = map[int32]string{ - 0: "SANDBOX_PHASE_UNSPECIFIED", - 1: "SANDBOX_PHASE_PROVISIONING", - 2: "SANDBOX_PHASE_READY", - 3: "SANDBOX_PHASE_ERROR", - 4: "SANDBOX_PHASE_DELETING", - 5: "SANDBOX_PHASE_UNKNOWN", - } - SandboxPhase_value = map[string]int32{ - "SANDBOX_PHASE_UNSPECIFIED": 0, - "SANDBOX_PHASE_PROVISIONING": 1, - "SANDBOX_PHASE_READY": 2, - "SANDBOX_PHASE_ERROR": 3, - "SANDBOX_PHASE_DELETING": 4, - "SANDBOX_PHASE_UNKNOWN": 5, - } -) - -func (x SandboxPhase) Enum() *SandboxPhase { - p := new(SandboxPhase) - *p = x - return p -} - -func (x SandboxPhase) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (SandboxPhase) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[0].Descriptor() -} - -func (SandboxPhase) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[0] -} - -func (x SandboxPhase) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use SandboxPhase.Descriptor instead. -func (SandboxPhase) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{0} -} - -// Stable provider profile categories used by clients for grouping and filtering. -type ProviderProfileCategory int32 - -const ( - ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_UNSPECIFIED ProviderProfileCategory = 0 - ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_OTHER ProviderProfileCategory = 1 - ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_INFERENCE ProviderProfileCategory = 2 - ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_AGENT ProviderProfileCategory = 3 - ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_SOURCE_CONTROL ProviderProfileCategory = 4 - ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_MESSAGING ProviderProfileCategory = 5 - ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_DATA ProviderProfileCategory = 6 - ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_KNOWLEDGE ProviderProfileCategory = 7 -) - -// Enum value maps for ProviderProfileCategory. -var ( - ProviderProfileCategory_name = map[int32]string{ - 0: "PROVIDER_PROFILE_CATEGORY_UNSPECIFIED", - 1: "PROVIDER_PROFILE_CATEGORY_OTHER", - 2: "PROVIDER_PROFILE_CATEGORY_INFERENCE", - 3: "PROVIDER_PROFILE_CATEGORY_AGENT", - 4: "PROVIDER_PROFILE_CATEGORY_SOURCE_CONTROL", - 5: "PROVIDER_PROFILE_CATEGORY_MESSAGING", - 6: "PROVIDER_PROFILE_CATEGORY_DATA", - 7: "PROVIDER_PROFILE_CATEGORY_KNOWLEDGE", - } - ProviderProfileCategory_value = map[string]int32{ - "PROVIDER_PROFILE_CATEGORY_UNSPECIFIED": 0, - "PROVIDER_PROFILE_CATEGORY_OTHER": 1, - "PROVIDER_PROFILE_CATEGORY_INFERENCE": 2, - "PROVIDER_PROFILE_CATEGORY_AGENT": 3, - "PROVIDER_PROFILE_CATEGORY_SOURCE_CONTROL": 4, - "PROVIDER_PROFILE_CATEGORY_MESSAGING": 5, - "PROVIDER_PROFILE_CATEGORY_DATA": 6, - "PROVIDER_PROFILE_CATEGORY_KNOWLEDGE": 7, - } -) - -func (x ProviderProfileCategory) Enum() *ProviderProfileCategory { - p := new(ProviderProfileCategory) - *p = x - return p -} - -func (x ProviderProfileCategory) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ProviderProfileCategory) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[1].Descriptor() -} - -func (ProviderProfileCategory) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[1] -} - -func (x ProviderProfileCategory) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ProviderProfileCategory.Descriptor instead. -func (ProviderProfileCategory) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{1} -} - -// Policy load status. -type PolicyStatus int32 - -const ( - PolicyStatus_POLICY_STATUS_UNSPECIFIED PolicyStatus = 0 - // Server received the update; sandbox has not yet loaded it. - PolicyStatus_POLICY_STATUS_PENDING PolicyStatus = 1 - // Sandbox successfully applied this policy version. - PolicyStatus_POLICY_STATUS_LOADED PolicyStatus = 2 - // Sandbox attempted to apply but failed; LKG policy remains active. - PolicyStatus_POLICY_STATUS_FAILED PolicyStatus = 3 - // A newer version was persisted before the sandbox loaded this one. - PolicyStatus_POLICY_STATUS_SUPERSEDED PolicyStatus = 4 -) - -// Enum value maps for PolicyStatus. -var ( - PolicyStatus_name = map[int32]string{ - 0: "POLICY_STATUS_UNSPECIFIED", - 1: "POLICY_STATUS_PENDING", - 2: "POLICY_STATUS_LOADED", - 3: "POLICY_STATUS_FAILED", - 4: "POLICY_STATUS_SUPERSEDED", - } - PolicyStatus_value = map[string]int32{ - "POLICY_STATUS_UNSPECIFIED": 0, - "POLICY_STATUS_PENDING": 1, - "POLICY_STATUS_LOADED": 2, - "POLICY_STATUS_FAILED": 3, - "POLICY_STATUS_SUPERSEDED": 4, - } -) - -func (x PolicyStatus) Enum() *PolicyStatus { - p := new(PolicyStatus) - *p = x - return p -} - -func (x PolicyStatus) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (PolicyStatus) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[2].Descriptor() -} - -func (PolicyStatus) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[2] -} - -func (x PolicyStatus) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use PolicyStatus.Descriptor instead. -func (PolicyStatus) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{2} -} - -// Service status enum. -type ServiceStatus int32 - -const ( - ServiceStatus_SERVICE_STATUS_UNSPECIFIED ServiceStatus = 0 - ServiceStatus_SERVICE_STATUS_HEALTHY ServiceStatus = 1 - ServiceStatus_SERVICE_STATUS_DEGRADED ServiceStatus = 2 - ServiceStatus_SERVICE_STATUS_UNHEALTHY ServiceStatus = 3 -) - -// Enum value maps for ServiceStatus. -var ( - ServiceStatus_name = map[int32]string{ - 0: "SERVICE_STATUS_UNSPECIFIED", - 1: "SERVICE_STATUS_HEALTHY", - 2: "SERVICE_STATUS_DEGRADED", - 3: "SERVICE_STATUS_UNHEALTHY", - } - ServiceStatus_value = map[string]int32{ - "SERVICE_STATUS_UNSPECIFIED": 0, - "SERVICE_STATUS_HEALTHY": 1, - "SERVICE_STATUS_DEGRADED": 2, - "SERVICE_STATUS_UNHEALTHY": 3, - } -) - -func (x ServiceStatus) Enum() *ServiceStatus { - p := new(ServiceStatus) - *p = x - return p -} - -func (x ServiceStatus) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (ServiceStatus) Descriptor() protoreflect.EnumDescriptor { - return file_openshell_proto_enumTypes[3].Descriptor() -} - -func (ServiceStatus) Type() protoreflect.EnumType { - return &file_openshell_proto_enumTypes[3] -} - -func (x ServiceStatus) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use ServiceStatus.Descriptor instead. -func (ServiceStatus) EnumDescriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{3} -} - -// Health check request. -type HealthRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *HealthRequest) Reset() { - *x = HealthRequest{} - mi := &file_openshell_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *HealthRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HealthRequest) ProtoMessage() {} - -func (x *HealthRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use HealthRequest.ProtoReflect.Descriptor instead. -func (*HealthRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{0} -} - -// Health check response. -type HealthResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Service status. - Status ServiceStatus `protobuf:"varint,1,opt,name=status,proto3,enum=openshell.v1.ServiceStatus" json:"status,omitempty"` - // Service version. - Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *HealthResponse) Reset() { - *x = HealthResponse{} - mi := &file_openshell_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *HealthResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*HealthResponse) ProtoMessage() {} - -func (x *HealthResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use HealthResponse.ProtoReflect.Descriptor instead. -func (*HealthResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{1} -} - -func (x *HealthResponse) GetStatus() ServiceStatus { - if x != nil { - return x.Status - } - return ServiceStatus_SERVICE_STATUS_UNSPECIFIED -} - -func (x *HealthResponse) GetVersion() string { - if x != nil { - return x.Version - } - return "" -} - -// Public sandbox resource exposed by the OpenShell API. -// -// This is the canonical gateway-owned view of a sandbox. It merges user intent -// (`spec`) with gateway-managed metadata and status derived from internal -// compute-driver observations. -// -// Note: The `namespace` field has been removed from the public API. It remains -// in the internal `DriverSandbox` message as a compute-driver implementation detail. -type Sandbox struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Kubernetes-style metadata (id, name, labels, timestamps, resource version). - Metadata *datamodelv1.ObjectMeta `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` - // Desired sandbox configuration submitted through the API. - Spec *SandboxSpec `protobuf:"bytes,2,opt,name=spec,proto3" json:"spec,omitempty"` - // Latest user-facing observed status derived by the gateway. - Status *SandboxStatus `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` - // Gateway-derived lifecycle summary. - Phase SandboxPhase `protobuf:"varint,4,opt,name=phase,proto3,enum=openshell.v1.SandboxPhase" json:"phase,omitempty"` - // Currently active policy version (updated when sandbox reports loaded). - CurrentPolicyVersion uint32 `protobuf:"varint,5,opt,name=current_policy_version,json=currentPolicyVersion,proto3" json:"current_policy_version,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *Sandbox) Reset() { - *x = Sandbox{} - mi := &file_openshell_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *Sandbox) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Sandbox) ProtoMessage() {} - -func (x *Sandbox) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[2] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Sandbox.ProtoReflect.Descriptor instead. -func (*Sandbox) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{2} -} - -func (x *Sandbox) GetMetadata() *datamodelv1.ObjectMeta { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *Sandbox) GetSpec() *SandboxSpec { - if x != nil { - return x.Spec - } - return nil -} - -func (x *Sandbox) GetStatus() *SandboxStatus { - if x != nil { - return x.Status - } - return nil -} - -func (x *Sandbox) GetPhase() SandboxPhase { - if x != nil { - return x.Phase - } - return SandboxPhase_SANDBOX_PHASE_UNSPECIFIED -} - -func (x *Sandbox) GetCurrentPolicyVersion() uint32 { - if x != nil { - return x.CurrentPolicyVersion - } - return 0 -} - -// Desired sandbox configuration provided through the public API. -type SandboxSpec struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Log level exposed to processes running inside the sandbox. - LogLevel string `protobuf:"bytes,1,opt,name=log_level,json=logLevel,proto3" json:"log_level,omitempty"` - // Environment variables injected into the sandbox runtime. - Environment map[string]string `protobuf:"bytes,5,rep,name=environment,proto3" json:"environment,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Container or VM template used to provision the sandbox. - Template *SandboxTemplate `protobuf:"bytes,6,opt,name=template,proto3" json:"template,omitempty"` - // Required sandbox policy configuration. - Policy *sandboxv1.SandboxPolicy `protobuf:"bytes,7,opt,name=policy,proto3" json:"policy,omitempty"` - // Provider names to attach to this sandbox. - Providers []string `protobuf:"bytes,8,rep,name=providers,proto3" json:"providers,omitempty"` - // Request NVIDIA GPU resources for this sandbox. - Gpu bool `protobuf:"varint,9,opt,name=gpu,proto3" json:"gpu,omitempty"` - // Optional PCI BDF address (e.g. "0000:2d:00.0") or device index - // (e.g. "0", "1"). When empty with gpu=true, the driver assigns the - // first available GPU. - GpuDevice string `protobuf:"bytes,10,opt,name=gpu_device,json=gpuDevice,proto3" json:"gpu_device,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxSpec) Reset() { - *x = SandboxSpec{} - mi := &file_openshell_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxSpec) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxSpec) ProtoMessage() {} - -func (x *SandboxSpec) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[3] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxSpec.ProtoReflect.Descriptor instead. -func (*SandboxSpec) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{3} -} - -func (x *SandboxSpec) GetLogLevel() string { - if x != nil { - return x.LogLevel - } - return "" -} - -func (x *SandboxSpec) GetEnvironment() map[string]string { - if x != nil { - return x.Environment - } - return nil -} - -func (x *SandboxSpec) GetTemplate() *SandboxTemplate { - if x != nil { - return x.Template - } - return nil -} - -func (x *SandboxSpec) GetPolicy() *sandboxv1.SandboxPolicy { - if x != nil { - return x.Policy - } - return nil -} - -func (x *SandboxSpec) GetProviders() []string { - if x != nil { - return x.Providers - } - return nil -} - -func (x *SandboxSpec) GetGpu() bool { - if x != nil { - return x.Gpu - } - return false -} - -func (x *SandboxSpec) GetGpuDevice() string { - if x != nil { - return x.GpuDevice - } - return "" -} - -// Public sandbox template mapped onto compute-driver template inputs. -type SandboxTemplate struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Fully-qualified OCI image reference used to boot the sandbox. - Image string `protobuf:"bytes,1,opt,name=image,proto3" json:"image,omitempty"` - // Optional runtime class name requested from the compute platform. - RuntimeClassName string `protobuf:"bytes,2,opt,name=runtime_class_name,json=runtimeClassName,proto3" json:"runtime_class_name,omitempty"` - // Optional agent socket path exposed to the workload. - AgentSocket string `protobuf:"bytes,3,opt,name=agent_socket,json=agentSocket,proto3" json:"agent_socket,omitempty"` - // Labels applied to compute-platform resources for this sandbox. - Labels map[string]string `protobuf:"bytes,4,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Annotations applied to compute-platform resources for this sandbox. - Annotations map[string]string `protobuf:"bytes,5,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Additional environment variables injected by the template. - Environment map[string]string `protobuf:"bytes,6,rep,name=environment,proto3" json:"environment,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Platform-specific compute resource requirements and limits. - Resources *structpb.Struct `protobuf:"bytes,7,opt,name=resources,proto3" json:"resources,omitempty"` - // Optional platform-specific volume claim templates. - VolumeClaimTemplates *structpb.Struct `protobuf:"bytes,9,opt,name=volume_claim_templates,json=volumeClaimTemplates,proto3" json:"volume_claim_templates,omitempty"` - // Enable Kubernetes user namespace isolation (hostUsers: false). - // When true, container UID 0 maps to a non-root host UID and capabilities - // become namespaced. Requires Kubernetes 1.33+ with user namespace support - // available (beta through 1.35, GA in 1.36+) and a supporting runtime. - // When unset, the cluster-wide default is used. - UserNamespaces *bool `protobuf:"varint,10,opt,name=user_namespaces,json=userNamespaces,proto3,oneof" json:"user_namespaces,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxTemplate) Reset() { - *x = SandboxTemplate{} - mi := &file_openshell_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxTemplate) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxTemplate) ProtoMessage() {} - -func (x *SandboxTemplate) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[4] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxTemplate.ProtoReflect.Descriptor instead. -func (*SandboxTemplate) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{4} -} - -func (x *SandboxTemplate) GetImage() string { - if x != nil { - return x.Image - } - return "" -} - -func (x *SandboxTemplate) GetRuntimeClassName() string { - if x != nil { - return x.RuntimeClassName - } - return "" -} - -func (x *SandboxTemplate) GetAgentSocket() string { - if x != nil { - return x.AgentSocket - } - return "" -} - -func (x *SandboxTemplate) GetLabels() map[string]string { - if x != nil { - return x.Labels - } - return nil -} - -func (x *SandboxTemplate) GetAnnotations() map[string]string { - if x != nil { - return x.Annotations - } - return nil -} - -func (x *SandboxTemplate) GetEnvironment() map[string]string { - if x != nil { - return x.Environment - } - return nil -} - -func (x *SandboxTemplate) GetResources() *structpb.Struct { - if x != nil { - return x.Resources - } - return nil -} - -func (x *SandboxTemplate) GetVolumeClaimTemplates() *structpb.Struct { - if x != nil { - return x.VolumeClaimTemplates - } - return nil -} - -func (x *SandboxTemplate) GetUserNamespaces() bool { - if x != nil && x.UserNamespaces != nil { - return *x.UserNamespaces - } - return false -} - -// User-facing sandbox status derived by the gateway from compute-driver observations. -// -// Lifecycle summary is exposed separately as `Sandbox.phase`. Public status does -// not embed driver-only flags such as `deleting`. -type SandboxStatus struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Compute-platform sandbox object name. - SandboxName string `protobuf:"bytes,1,opt,name=sandbox_name,json=sandboxName,proto3" json:"sandbox_name,omitempty"` - // Name of the agent pod or equivalent runtime instance. - AgentPod string `protobuf:"bytes,2,opt,name=agent_pod,json=agentPod,proto3" json:"agent_pod,omitempty"` - // File descriptor or endpoint for reaching the agent service, when available. - AgentFd string `protobuf:"bytes,3,opt,name=agent_fd,json=agentFd,proto3" json:"agent_fd,omitempty"` - // File descriptor or endpoint for reaching the sandbox service, when available. - SandboxFd string `protobuf:"bytes,4,opt,name=sandbox_fd,json=sandboxFd,proto3" json:"sandbox_fd,omitempty"` - // Latest user-facing readiness and lifecycle conditions. - Conditions []*SandboxCondition `protobuf:"bytes,5,rep,name=conditions,proto3" json:"conditions,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxStatus) Reset() { - *x = SandboxStatus{} - mi := &file_openshell_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxStatus) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxStatus) ProtoMessage() {} - -func (x *SandboxStatus) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[5] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxStatus.ProtoReflect.Descriptor instead. -func (*SandboxStatus) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{5} -} - -func (x *SandboxStatus) GetSandboxName() string { - if x != nil { - return x.SandboxName - } - return "" -} - -func (x *SandboxStatus) GetAgentPod() string { - if x != nil { - return x.AgentPod - } - return "" -} - -func (x *SandboxStatus) GetAgentFd() string { - if x != nil { - return x.AgentFd - } - return "" -} - -func (x *SandboxStatus) GetSandboxFd() string { - if x != nil { - return x.SandboxFd - } - return "" -} - -func (x *SandboxStatus) GetConditions() []*SandboxCondition { - if x != nil { - return x.Conditions - } - return nil -} - -// User-facing sandbox condition derived from driver-native conditions. -type SandboxCondition struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Condition class, typically mirroring the underlying platform condition type. - Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` - // Condition status value such as `True`, `False`, or `Unknown`. - Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` - // Short machine-readable reason associated with the condition. - Reason string `protobuf:"bytes,3,opt,name=reason,proto3" json:"reason,omitempty"` - // Human-readable condition message. - Message string `protobuf:"bytes,4,opt,name=message,proto3" json:"message,omitempty"` - // Timestamp reported by the underlying platform for the last transition. - LastTransitionTime string `protobuf:"bytes,5,opt,name=last_transition_time,json=lastTransitionTime,proto3" json:"last_transition_time,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxCondition) Reset() { - *x = SandboxCondition{} - mi := &file_openshell_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxCondition) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxCondition) ProtoMessage() {} - -func (x *SandboxCondition) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[6] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxCondition.ProtoReflect.Descriptor instead. -func (*SandboxCondition) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{6} -} - -func (x *SandboxCondition) GetType() string { - if x != nil { - return x.Type - } - return "" -} - -func (x *SandboxCondition) GetStatus() string { - if x != nil { - return x.Status - } - return "" -} - -func (x *SandboxCondition) GetReason() string { - if x != nil { - return x.Reason - } - return "" -} - -func (x *SandboxCondition) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -func (x *SandboxCondition) GetLastTransitionTime() string { - if x != nil { - return x.LastTransitionTime - } - return "" -} - -// Public platform event exposed on the sandbox watch stream. -type PlatformEvent struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Event timestamp in milliseconds since epoch. - TimestampMs int64 `protobuf:"varint,1,opt,name=timestamp_ms,json=timestampMs,proto3" json:"timestamp_ms,omitempty"` - // Event source (e.g. "kubernetes", "docker", "process"). - Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"` - // Event type/severity (e.g. "Normal", "Warning"). - Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` - // Short reason code (e.g. "Started", "Pulled", "Failed"). - Reason string `protobuf:"bytes,4,opt,name=reason,proto3" json:"reason,omitempty"` - // Human-readable event message. - Message string `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"` - // Optional metadata as key-value pairs. - Metadata map[string]string `protobuf:"bytes,6,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PlatformEvent) Reset() { - *x = PlatformEvent{} - mi := &file_openshell_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PlatformEvent) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PlatformEvent) ProtoMessage() {} - -func (x *PlatformEvent) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[7] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PlatformEvent.ProtoReflect.Descriptor instead. -func (*PlatformEvent) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{7} -} - -func (x *PlatformEvent) GetTimestampMs() int64 { - if x != nil { - return x.TimestampMs - } - return 0 -} - -func (x *PlatformEvent) GetSource() string { - if x != nil { - return x.Source - } - return "" -} - -func (x *PlatformEvent) GetType() string { - if x != nil { - return x.Type - } - return "" -} - -func (x *PlatformEvent) GetReason() string { - if x != nil { - return x.Reason - } - return "" -} - -func (x *PlatformEvent) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -func (x *PlatformEvent) GetMetadata() map[string]string { - if x != nil { - return x.Metadata - } - return nil -} - -// Create sandbox request. -type CreateSandboxRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Spec *SandboxSpec `protobuf:"bytes,1,opt,name=spec,proto3" json:"spec,omitempty"` - // Optional user-supplied sandbox name. When empty the server generates one. - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - // Optional labels for the sandbox (key-value metadata). - Labels map[string]string `protobuf:"bytes,3,rep,name=labels,proto3" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CreateSandboxRequest) Reset() { - *x = CreateSandboxRequest{} - mi := &file_openshell_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CreateSandboxRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateSandboxRequest) ProtoMessage() {} - -func (x *CreateSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[8] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateSandboxRequest.ProtoReflect.Descriptor instead. -func (*CreateSandboxRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{8} -} - -func (x *CreateSandboxRequest) GetSpec() *SandboxSpec { - if x != nil { - return x.Spec - } - return nil -} - -func (x *CreateSandboxRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *CreateSandboxRequest) GetLabels() map[string]string { - if x != nil { - return x.Labels - } - return nil -} - -// Get sandbox request. -type GetSandboxRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox name (canonical lookup key). - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxRequest) Reset() { - *x = GetSandboxRequest{} - mi := &file_openshell_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxRequest) ProtoMessage() {} - -func (x *GetSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[9] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxRequest.ProtoReflect.Descriptor instead. -func (*GetSandboxRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{9} -} - -func (x *GetSandboxRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -// List sandboxes request. -type ListSandboxesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Limit uint32 `protobuf:"varint,1,opt,name=limit,proto3" json:"limit,omitempty"` - Offset uint32 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` - // Optional label selector for filtering (format: "key1=value1,key2=value2"). - LabelSelector string `protobuf:"bytes,3,opt,name=label_selector,json=labelSelector,proto3" json:"label_selector,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListSandboxesRequest) Reset() { - *x = ListSandboxesRequest{} - mi := &file_openshell_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListSandboxesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListSandboxesRequest) ProtoMessage() {} - -func (x *ListSandboxesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[10] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListSandboxesRequest.ProtoReflect.Descriptor instead. -func (*ListSandboxesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{10} -} - -func (x *ListSandboxesRequest) GetLimit() uint32 { - if x != nil { - return x.Limit - } - return 0 -} - -func (x *ListSandboxesRequest) GetOffset() uint32 { - if x != nil { - return x.Offset - } - return 0 -} - -func (x *ListSandboxesRequest) GetLabelSelector() string { - if x != nil { - return x.LabelSelector - } - return "" -} - -// List providers attached to a sandbox request. -type ListSandboxProvidersRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox name (canonical lookup key). - SandboxName string `protobuf:"bytes,1,opt,name=sandbox_name,json=sandboxName,proto3" json:"sandbox_name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListSandboxProvidersRequest) Reset() { - *x = ListSandboxProvidersRequest{} - mi := &file_openshell_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListSandboxProvidersRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListSandboxProvidersRequest) ProtoMessage() {} - -func (x *ListSandboxProvidersRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[11] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListSandboxProvidersRequest.ProtoReflect.Descriptor instead. -func (*ListSandboxProvidersRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{11} -} - -func (x *ListSandboxProvidersRequest) GetSandboxName() string { - if x != nil { - return x.SandboxName - } - return "" -} - -// Attach provider to sandbox request. -type AttachSandboxProviderRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox name (canonical lookup key). - SandboxName string `protobuf:"bytes,1,opt,name=sandbox_name,json=sandboxName,proto3" json:"sandbox_name,omitempty"` - // Provider name to attach. - ProviderName string `protobuf:"bytes,2,opt,name=provider_name,json=providerName,proto3" json:"provider_name,omitempty"` - // Expected resource version for optimistic concurrency control. - // If 0, the server uses the current version (backward compatibility). - // If non-zero, the server validates that the sandbox's current resource_version - // matches this value before applying the mutation, returning ABORTED on mismatch. - ExpectedResourceVersion uint64 `protobuf:"varint,3,opt,name=expected_resource_version,json=expectedResourceVersion,proto3" json:"expected_resource_version,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *AttachSandboxProviderRequest) Reset() { - *x = AttachSandboxProviderRequest{} - mi := &file_openshell_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AttachSandboxProviderRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AttachSandboxProviderRequest) ProtoMessage() {} - -func (x *AttachSandboxProviderRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[12] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AttachSandboxProviderRequest.ProtoReflect.Descriptor instead. -func (*AttachSandboxProviderRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{12} -} - -func (x *AttachSandboxProviderRequest) GetSandboxName() string { - if x != nil { - return x.SandboxName - } - return "" -} - -func (x *AttachSandboxProviderRequest) GetProviderName() string { - if x != nil { - return x.ProviderName - } - return "" -} - -func (x *AttachSandboxProviderRequest) GetExpectedResourceVersion() uint64 { - if x != nil { - return x.ExpectedResourceVersion - } - return 0 -} - -// Detach provider from sandbox request. -type DetachSandboxProviderRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox name (canonical lookup key). - SandboxName string `protobuf:"bytes,1,opt,name=sandbox_name,json=sandboxName,proto3" json:"sandbox_name,omitempty"` - // Provider name to detach. - ProviderName string `protobuf:"bytes,2,opt,name=provider_name,json=providerName,proto3" json:"provider_name,omitempty"` - // Expected resource version for optimistic concurrency control. - // If 0, the server uses the current version (backward compatibility). - // If non-zero, the server validates that the sandbox's current resource_version - // matches this value before applying the mutation, returning ABORTED on mismatch. - ExpectedResourceVersion uint64 `protobuf:"varint,3,opt,name=expected_resource_version,json=expectedResourceVersion,proto3" json:"expected_resource_version,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DetachSandboxProviderRequest) Reset() { - *x = DetachSandboxProviderRequest{} - mi := &file_openshell_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DetachSandboxProviderRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DetachSandboxProviderRequest) ProtoMessage() {} - -func (x *DetachSandboxProviderRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[13] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DetachSandboxProviderRequest.ProtoReflect.Descriptor instead. -func (*DetachSandboxProviderRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{13} -} - -func (x *DetachSandboxProviderRequest) GetSandboxName() string { - if x != nil { - return x.SandboxName - } - return "" -} - -func (x *DetachSandboxProviderRequest) GetProviderName() string { - if x != nil { - return x.ProviderName - } - return "" -} - -func (x *DetachSandboxProviderRequest) GetExpectedResourceVersion() uint64 { - if x != nil { - return x.ExpectedResourceVersion - } - return 0 -} - -// Delete sandbox request. -type DeleteSandboxRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox name (canonical lookup key). - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteSandboxRequest) Reset() { - *x = DeleteSandboxRequest{} - mi := &file_openshell_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteSandboxRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteSandboxRequest) ProtoMessage() {} - -func (x *DeleteSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[14] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteSandboxRequest.ProtoReflect.Descriptor instead. -func (*DeleteSandboxRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{14} -} - -func (x *DeleteSandboxRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -// Sandbox response. -type SandboxResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Sandbox *Sandbox `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxResponse) Reset() { - *x = SandboxResponse{} - mi := &file_openshell_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxResponse) ProtoMessage() {} - -func (x *SandboxResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[15] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxResponse.ProtoReflect.Descriptor instead. -func (*SandboxResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{15} -} - -func (x *SandboxResponse) GetSandbox() *Sandbox { - if x != nil { - return x.Sandbox - } - return nil -} - -// List sandboxes response. -type ListSandboxesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Sandboxes []*Sandbox `protobuf:"bytes,1,rep,name=sandboxes,proto3" json:"sandboxes,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListSandboxesResponse) Reset() { - *x = ListSandboxesResponse{} - mi := &file_openshell_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListSandboxesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListSandboxesResponse) ProtoMessage() {} - -func (x *ListSandboxesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[16] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListSandboxesResponse.ProtoReflect.Descriptor instead. -func (*ListSandboxesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{16} -} - -func (x *ListSandboxesResponse) GetSandboxes() []*Sandbox { - if x != nil { - return x.Sandboxes - } - return nil -} - -// List providers attached to a sandbox response. -type ListSandboxProvidersResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Providers []*datamodelv1.Provider `protobuf:"bytes,1,rep,name=providers,proto3" json:"providers,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListSandboxProvidersResponse) Reset() { - *x = ListSandboxProvidersResponse{} - mi := &file_openshell_proto_msgTypes[17] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListSandboxProvidersResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListSandboxProvidersResponse) ProtoMessage() {} - -func (x *ListSandboxProvidersResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[17] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListSandboxProvidersResponse.ProtoReflect.Descriptor instead. -func (*ListSandboxProvidersResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{17} -} - -func (x *ListSandboxProvidersResponse) GetProviders() []*datamodelv1.Provider { - if x != nil { - return x.Providers - } - return nil -} - -// Attach provider to sandbox response. -type AttachSandboxProviderResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Sandbox *Sandbox `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // True when the provider was newly attached. False means it was already attached. - Attached bool `protobuf:"varint,2,opt,name=attached,proto3" json:"attached,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *AttachSandboxProviderResponse) Reset() { - *x = AttachSandboxProviderResponse{} - mi := &file_openshell_proto_msgTypes[18] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AttachSandboxProviderResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AttachSandboxProviderResponse) ProtoMessage() {} - -func (x *AttachSandboxProviderResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[18] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AttachSandboxProviderResponse.ProtoReflect.Descriptor instead. -func (*AttachSandboxProviderResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{18} -} - -func (x *AttachSandboxProviderResponse) GetSandbox() *Sandbox { - if x != nil { - return x.Sandbox - } - return nil -} - -func (x *AttachSandboxProviderResponse) GetAttached() bool { - if x != nil { - return x.Attached - } - return false -} - -// Detach provider from sandbox response. -type DetachSandboxProviderResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Sandbox *Sandbox `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // True when the provider was removed. False means it was not attached. - Detached bool `protobuf:"varint,2,opt,name=detached,proto3" json:"detached,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DetachSandboxProviderResponse) Reset() { - *x = DetachSandboxProviderResponse{} - mi := &file_openshell_proto_msgTypes[19] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DetachSandboxProviderResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DetachSandboxProviderResponse) ProtoMessage() {} - -func (x *DetachSandboxProviderResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[19] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DetachSandboxProviderResponse.ProtoReflect.Descriptor instead. -func (*DetachSandboxProviderResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{19} -} - -func (x *DetachSandboxProviderResponse) GetSandbox() *Sandbox { - if x != nil { - return x.Sandbox - } - return nil -} - -func (x *DetachSandboxProviderResponse) GetDetached() bool { - if x != nil { - return x.Detached - } - return false -} - -// Delete sandbox response. -type DeleteSandboxResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Deleted bool `protobuf:"varint,1,opt,name=deleted,proto3" json:"deleted,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteSandboxResponse) Reset() { - *x = DeleteSandboxResponse{} - mi := &file_openshell_proto_msgTypes[20] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteSandboxResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteSandboxResponse) ProtoMessage() {} - -func (x *DeleteSandboxResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[20] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteSandboxResponse.ProtoReflect.Descriptor instead. -func (*DeleteSandboxResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{20} -} - -func (x *DeleteSandboxResponse) GetDeleted() bool { - if x != nil { - return x.Deleted - } - return false -} - -// Create SSH session request. -type CreateSshSessionRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox id. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CreateSshSessionRequest) Reset() { - *x = CreateSshSessionRequest{} - mi := &file_openshell_proto_msgTypes[21] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CreateSshSessionRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateSshSessionRequest) ProtoMessage() {} - -func (x *CreateSshSessionRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[21] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateSshSessionRequest.ProtoReflect.Descriptor instead. -func (*CreateSshSessionRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{21} -} - -func (x *CreateSshSessionRequest) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -// Create SSH session response. -// -// Fields are interpolated into an SSH `ProxyCommand` string that OpenSSH -// executes through `/bin/sh -c` on the caller's workstation. Servers MUST -// uphold the charset contract below; clients MUST reject responses that -// violate it. The client's own escaping provides defense-in-depth, but -// narrow charsets close injection vectors at the trust boundary. -type CreateSshSessionResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox id. [A-Za-z0-9._-]{1,128}. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Session token for the gateway tunnel. URL-safe ASCII - // ([A-Za-z0-9._~+/=-]) up to 4096 bytes. No shell metacharacters or - // whitespace. - Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"` - // Gateway host for SSH proxy connection. IPv4 address, bracketed IPv6 - // address, or DNS hostname (Punycode-encoded for IDN). Alphanumeric plus - // `.-:[]` only, up to 253 bytes. - GatewayHost string `protobuf:"bytes,3,opt,name=gateway_host,json=gatewayHost,proto3" json:"gateway_host,omitempty"` - // Gateway port for SSH proxy connection. Must be in range 1..=65535. - GatewayPort uint32 `protobuf:"varint,4,opt,name=gateway_port,json=gatewayPort,proto3" json:"gateway_port,omitempty"` - // Gateway scheme. Must be exactly "http" or "https". - GatewayScheme string `protobuf:"bytes,5,opt,name=gateway_scheme,json=gatewayScheme,proto3" json:"gateway_scheme,omitempty"` - // Optional host key fingerprint. If non-empty, [A-Za-z0-9:+/=-] only. - HostKeyFingerprint string `protobuf:"bytes,7,opt,name=host_key_fingerprint,json=hostKeyFingerprint,proto3" json:"host_key_fingerprint,omitempty"` - // Expiry timestamp in milliseconds since epoch. 0 means no expiry. - ExpiresAtMs int64 `protobuf:"varint,8,opt,name=expires_at_ms,json=expiresAtMs,proto3" json:"expires_at_ms,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CreateSshSessionResponse) Reset() { - *x = CreateSshSessionResponse{} - mi := &file_openshell_proto_msgTypes[22] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CreateSshSessionResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateSshSessionResponse) ProtoMessage() {} - -func (x *CreateSshSessionResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[22] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateSshSessionResponse.ProtoReflect.Descriptor instead. -func (*CreateSshSessionResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{22} -} - -func (x *CreateSshSessionResponse) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *CreateSshSessionResponse) GetToken() string { - if x != nil { - return x.Token - } - return "" -} - -func (x *CreateSshSessionResponse) GetGatewayHost() string { - if x != nil { - return x.GatewayHost - } - return "" -} - -func (x *CreateSshSessionResponse) GetGatewayPort() uint32 { - if x != nil { - return x.GatewayPort - } - return 0 -} - -func (x *CreateSshSessionResponse) GetGatewayScheme() string { - if x != nil { - return x.GatewayScheme - } - return "" -} - -func (x *CreateSshSessionResponse) GetHostKeyFingerprint() string { - if x != nil { - return x.HostKeyFingerprint - } - return "" -} - -func (x *CreateSshSessionResponse) GetExpiresAtMs() int64 { - if x != nil { - return x.ExpiresAtMs - } - return 0 -} - -// Request to expose an HTTP service running inside a sandbox. -type ExposeServiceRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox name. - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // Service name within the sandbox. - Service string `protobuf:"bytes,2,opt,name=service,proto3" json:"service,omitempty"` - // Loopback TCP port inside the sandbox. - TargetPort uint32 `protobuf:"varint,3,opt,name=target_port,json=targetPort,proto3" json:"target_port,omitempty"` - // Whether to print/use the browser-facing service URL. - Domain bool `protobuf:"varint,4,opt,name=domain,proto3" json:"domain,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ExposeServiceRequest) Reset() { - *x = ExposeServiceRequest{} - mi := &file_openshell_proto_msgTypes[23] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ExposeServiceRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExposeServiceRequest) ProtoMessage() {} - -func (x *ExposeServiceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[23] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExposeServiceRequest.ProtoReflect.Descriptor instead. -func (*ExposeServiceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{23} -} - -func (x *ExposeServiceRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *ExposeServiceRequest) GetService() string { - if x != nil { - return x.Service - } - return "" -} - -func (x *ExposeServiceRequest) GetTargetPort() uint32 { - if x != nil { - return x.TargetPort - } - return 0 -} - -func (x *ExposeServiceRequest) GetDomain() bool { - if x != nil { - return x.Domain - } - return false -} - -// Request to fetch an exposed sandbox service endpoint. -type GetServiceRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox name. - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // Service name within the sandbox. Empty selects the unnamed endpoint. - Service string `protobuf:"bytes,2,opt,name=service,proto3" json:"service,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetServiceRequest) Reset() { - *x = GetServiceRequest{} - mi := &file_openshell_proto_msgTypes[24] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetServiceRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetServiceRequest) ProtoMessage() {} - -func (x *GetServiceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[24] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetServiceRequest.ProtoReflect.Descriptor instead. -func (*GetServiceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{24} -} - -func (x *GetServiceRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *GetServiceRequest) GetService() string { - if x != nil { - return x.Service - } - return "" -} - -// Request to list exposed sandbox service endpoints. -type ListServicesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Optional sandbox name. Empty lists endpoints for all sandboxes. - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // Page size. Zero uses the server default. - Limit uint32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` - // Page offset. - Offset uint32 `protobuf:"varint,3,opt,name=offset,proto3" json:"offset,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListServicesRequest) Reset() { - *x = ListServicesRequest{} - mi := &file_openshell_proto_msgTypes[25] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListServicesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListServicesRequest) ProtoMessage() {} - -func (x *ListServicesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[25] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListServicesRequest.ProtoReflect.Descriptor instead. -func (*ListServicesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{25} -} - -func (x *ListServicesRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *ListServicesRequest) GetLimit() uint32 { - if x != nil { - return x.Limit - } - return 0 -} - -func (x *ListServicesRequest) GetOffset() uint32 { - if x != nil { - return x.Offset - } - return 0 -} - -// Response containing exposed sandbox service endpoints. -type ListServicesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Services []*ServiceEndpointResponse `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListServicesResponse) Reset() { - *x = ListServicesResponse{} - mi := &file_openshell_proto_msgTypes[26] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListServicesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListServicesResponse) ProtoMessage() {} - -func (x *ListServicesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[26] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListServicesResponse.ProtoReflect.Descriptor instead. -func (*ListServicesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{26} -} - -func (x *ListServicesResponse) GetServices() []*ServiceEndpointResponse { - if x != nil { - return x.Services - } - return nil -} - -// Request to delete an exposed sandbox service endpoint. -type DeleteServiceRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox name. - Sandbox string `protobuf:"bytes,1,opt,name=sandbox,proto3" json:"sandbox,omitempty"` - // Service name within the sandbox. Empty selects the unnamed endpoint. - Service string `protobuf:"bytes,2,opt,name=service,proto3" json:"service,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteServiceRequest) Reset() { - *x = DeleteServiceRequest{} - mi := &file_openshell_proto_msgTypes[27] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteServiceRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteServiceRequest) ProtoMessage() {} - -func (x *DeleteServiceRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[27] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteServiceRequest.ProtoReflect.Descriptor instead. -func (*DeleteServiceRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{27} -} - -func (x *DeleteServiceRequest) GetSandbox() string { - if x != nil { - return x.Sandbox - } - return "" -} - -func (x *DeleteServiceRequest) GetService() string { - if x != nil { - return x.Service - } - return "" -} - -// Response for deleting an exposed sandbox service endpoint. -type DeleteServiceResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // True when an endpoint existed and was deleted. - Deleted bool `protobuf:"varint,1,opt,name=deleted,proto3" json:"deleted,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteServiceResponse) Reset() { - *x = DeleteServiceResponse{} - mi := &file_openshell_proto_msgTypes[28] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteServiceResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteServiceResponse) ProtoMessage() {} - -func (x *DeleteServiceResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[28] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteServiceResponse.ProtoReflect.Descriptor instead. -func (*DeleteServiceResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{28} -} - -func (x *DeleteServiceResponse) GetDeleted() bool { - if x != nil { - return x.Deleted - } - return false -} - -// Persisted sandbox service endpoint. -type ServiceEndpoint struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Kubernetes-style metadata. - Metadata *datamodelv1.ObjectMeta `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` - // Sandbox object ID. - SandboxId string `protobuf:"bytes,2,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Sandbox name. - SandboxName string `protobuf:"bytes,3,opt,name=sandbox_name,json=sandboxName,proto3" json:"sandbox_name,omitempty"` - // Service name within the sandbox. - ServiceName string `protobuf:"bytes,4,opt,name=service_name,json=serviceName,proto3" json:"service_name,omitempty"` - // Loopback TCP port inside the sandbox. - TargetPort uint32 `protobuf:"varint,5,opt,name=target_port,json=targetPort,proto3" json:"target_port,omitempty"` - // Whether browser-facing service routing is enabled for this endpoint. - Domain bool `protobuf:"varint,6,opt,name=domain,proto3" json:"domain,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ServiceEndpoint) Reset() { - *x = ServiceEndpoint{} - mi := &file_openshell_proto_msgTypes[29] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ServiceEndpoint) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ServiceEndpoint) ProtoMessage() {} - -func (x *ServiceEndpoint) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[29] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ServiceEndpoint.ProtoReflect.Descriptor instead. -func (*ServiceEndpoint) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{29} -} - -func (x *ServiceEndpoint) GetMetadata() *datamodelv1.ObjectMeta { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *ServiceEndpoint) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *ServiceEndpoint) GetSandboxName() string { - if x != nil { - return x.SandboxName - } - return "" -} - -func (x *ServiceEndpoint) GetServiceName() string { - if x != nil { - return x.ServiceName - } - return "" -} - -func (x *ServiceEndpoint) GetTargetPort() uint32 { - if x != nil { - return x.TargetPort - } - return 0 -} - -func (x *ServiceEndpoint) GetDomain() bool { - if x != nil { - return x.Domain - } - return false -} - -// Response containing a service endpoint and, when available, its local URL. -type ServiceEndpointResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Endpoint *ServiceEndpoint `protobuf:"bytes,1,opt,name=endpoint,proto3" json:"endpoint,omitempty"` - Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ServiceEndpointResponse) Reset() { - *x = ServiceEndpointResponse{} - mi := &file_openshell_proto_msgTypes[30] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ServiceEndpointResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ServiceEndpointResponse) ProtoMessage() {} - -func (x *ServiceEndpointResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[30] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ServiceEndpointResponse.ProtoReflect.Descriptor instead. -func (*ServiceEndpointResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{30} -} - -func (x *ServiceEndpointResponse) GetEndpoint() *ServiceEndpoint { - if x != nil { - return x.Endpoint - } - return nil -} - -func (x *ServiceEndpointResponse) GetUrl() string { - if x != nil { - return x.Url - } - return "" -} - -// Revoke SSH session request. -type RevokeSshSessionRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Session token to revoke. - Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RevokeSshSessionRequest) Reset() { - *x = RevokeSshSessionRequest{} - mi := &file_openshell_proto_msgTypes[31] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RevokeSshSessionRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RevokeSshSessionRequest) ProtoMessage() {} - -func (x *RevokeSshSessionRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[31] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RevokeSshSessionRequest.ProtoReflect.Descriptor instead. -func (*RevokeSshSessionRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{31} -} - -func (x *RevokeSshSessionRequest) GetToken() string { - if x != nil { - return x.Token - } - return "" -} - -// Revoke SSH session response. -type RevokeSshSessionResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // True when a session was revoked. - Revoked bool `protobuf:"varint,1,opt,name=revoked,proto3" json:"revoked,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RevokeSshSessionResponse) Reset() { - *x = RevokeSshSessionResponse{} - mi := &file_openshell_proto_msgTypes[32] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RevokeSshSessionResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RevokeSshSessionResponse) ProtoMessage() {} - -func (x *RevokeSshSessionResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[32] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RevokeSshSessionResponse.ProtoReflect.Descriptor instead. -func (*RevokeSshSessionResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{32} -} - -func (x *RevokeSshSessionResponse) GetRevoked() bool { - if x != nil { - return x.Revoked - } - return false -} - -// Execute command request. -type ExecSandboxRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox id. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Command and arguments. - Command []string `protobuf:"bytes,2,rep,name=command,proto3" json:"command,omitempty"` - // Optional working directory. - Workdir string `protobuf:"bytes,3,opt,name=workdir,proto3" json:"workdir,omitempty"` - // Optional environment overrides. - Environment map[string]string `protobuf:"bytes,4,rep,name=environment,proto3" json:"environment,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Optional timeout in seconds. 0 means no timeout. - TimeoutSeconds uint32 `protobuf:"varint,5,opt,name=timeout_seconds,json=timeoutSeconds,proto3" json:"timeout_seconds,omitempty"` - // Optional stdin payload passed to the command. - Stdin []byte `protobuf:"bytes,6,opt,name=stdin,proto3" json:"stdin,omitempty"` - // Request a pseudo-terminal for the remote command. - Tty bool `protobuf:"varint,7,opt,name=tty,proto3" json:"tty,omitempty"` - // Initial terminal columns (used when tty=true, 0 = use default). - Cols uint32 `protobuf:"varint,8,opt,name=cols,proto3" json:"cols,omitempty"` - // Initial terminal rows (used when tty=true, 0 = use default). - Rows uint32 `protobuf:"varint,9,opt,name=rows,proto3" json:"rows,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ExecSandboxRequest) Reset() { - *x = ExecSandboxRequest{} - mi := &file_openshell_proto_msgTypes[33] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ExecSandboxRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExecSandboxRequest) ProtoMessage() {} - -func (x *ExecSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[33] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExecSandboxRequest.ProtoReflect.Descriptor instead. -func (*ExecSandboxRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{33} -} - -func (x *ExecSandboxRequest) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *ExecSandboxRequest) GetCommand() []string { - if x != nil { - return x.Command - } - return nil -} - -func (x *ExecSandboxRequest) GetWorkdir() string { - if x != nil { - return x.Workdir - } - return "" -} - -func (x *ExecSandboxRequest) GetEnvironment() map[string]string { - if x != nil { - return x.Environment - } - return nil -} - -func (x *ExecSandboxRequest) GetTimeoutSeconds() uint32 { - if x != nil { - return x.TimeoutSeconds - } - return 0 -} - -func (x *ExecSandboxRequest) GetStdin() []byte { - if x != nil { - return x.Stdin - } - return nil -} - -func (x *ExecSandboxRequest) GetTty() bool { - if x != nil { - return x.Tty - } - return false -} - -func (x *ExecSandboxRequest) GetCols() uint32 { - if x != nil { - return x.Cols - } - return 0 -} - -func (x *ExecSandboxRequest) GetRows() uint32 { - if x != nil { - return x.Rows - } - return 0 -} - -// One stdout chunk from a sandbox exec. -type ExecSandboxStdout struct { - state protoimpl.MessageState `protogen:"open.v1"` - Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ExecSandboxStdout) Reset() { - *x = ExecSandboxStdout{} - mi := &file_openshell_proto_msgTypes[34] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ExecSandboxStdout) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExecSandboxStdout) ProtoMessage() {} - -func (x *ExecSandboxStdout) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[34] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExecSandboxStdout.ProtoReflect.Descriptor instead. -func (*ExecSandboxStdout) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{34} -} - -func (x *ExecSandboxStdout) GetData() []byte { - if x != nil { - return x.Data - } - return nil -} - -// One stderr chunk from a sandbox exec. -type ExecSandboxStderr struct { - state protoimpl.MessageState `protogen:"open.v1"` - Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ExecSandboxStderr) Reset() { - *x = ExecSandboxStderr{} - mi := &file_openshell_proto_msgTypes[35] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ExecSandboxStderr) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExecSandboxStderr) ProtoMessage() {} - -func (x *ExecSandboxStderr) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[35] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExecSandboxStderr.ProtoReflect.Descriptor instead. -func (*ExecSandboxStderr) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{35} -} - -func (x *ExecSandboxStderr) GetData() []byte { - if x != nil { - return x.Data - } - return nil -} - -// Final exit status for a sandbox exec. -type ExecSandboxExit struct { - state protoimpl.MessageState `protogen:"open.v1"` - ExitCode int32 `protobuf:"varint,1,opt,name=exit_code,json=exitCode,proto3" json:"exit_code,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ExecSandboxExit) Reset() { - *x = ExecSandboxExit{} - mi := &file_openshell_proto_msgTypes[36] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ExecSandboxExit) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExecSandboxExit) ProtoMessage() {} - -func (x *ExecSandboxExit) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[36] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExecSandboxExit.ProtoReflect.Descriptor instead. -func (*ExecSandboxExit) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{36} -} - -func (x *ExecSandboxExit) GetExitCode() int32 { - if x != nil { - return x.ExitCode - } - return 0 -} - -// One event in a sandbox exec stream. -type ExecSandboxEvent struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Payload: - // - // *ExecSandboxEvent_Stdout - // *ExecSandboxEvent_Stderr - // *ExecSandboxEvent_Exit - Payload isExecSandboxEvent_Payload `protobuf_oneof:"payload"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ExecSandboxEvent) Reset() { - *x = ExecSandboxEvent{} - mi := &file_openshell_proto_msgTypes[37] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ExecSandboxEvent) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExecSandboxEvent) ProtoMessage() {} - -func (x *ExecSandboxEvent) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[37] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExecSandboxEvent.ProtoReflect.Descriptor instead. -func (*ExecSandboxEvent) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{37} -} - -func (x *ExecSandboxEvent) GetPayload() isExecSandboxEvent_Payload { - if x != nil { - return x.Payload - } - return nil -} - -func (x *ExecSandboxEvent) GetStdout() *ExecSandboxStdout { - if x != nil { - if x, ok := x.Payload.(*ExecSandboxEvent_Stdout); ok { - return x.Stdout - } - } - return nil -} - -func (x *ExecSandboxEvent) GetStderr() *ExecSandboxStderr { - if x != nil { - if x, ok := x.Payload.(*ExecSandboxEvent_Stderr); ok { - return x.Stderr - } - } - return nil -} - -func (x *ExecSandboxEvent) GetExit() *ExecSandboxExit { - if x != nil { - if x, ok := x.Payload.(*ExecSandboxEvent_Exit); ok { - return x.Exit - } - } - return nil -} - -type isExecSandboxEvent_Payload interface { - isExecSandboxEvent_Payload() -} - -type ExecSandboxEvent_Stdout struct { - Stdout *ExecSandboxStdout `protobuf:"bytes,1,opt,name=stdout,proto3,oneof"` -} - -type ExecSandboxEvent_Stderr struct { - Stderr *ExecSandboxStderr `protobuf:"bytes,2,opt,name=stderr,proto3,oneof"` -} - -type ExecSandboxEvent_Exit struct { - Exit *ExecSandboxExit `protobuf:"bytes,3,opt,name=exit,proto3,oneof"` -} - -func (*ExecSandboxEvent_Stdout) isExecSandboxEvent_Payload() {} - -func (*ExecSandboxEvent_Stderr) isExecSandboxEvent_Payload() {} - -func (*ExecSandboxEvent_Exit) isExecSandboxEvent_Payload() {} - -// Initial frame for one TCP forward stream. -type TcpForwardInit struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox id. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Optional service identifier for audit/correlation. - ServiceId string `protobuf:"bytes,4,opt,name=service_id,json=serviceId,proto3" json:"service_id,omitempty"` - // Target the gateway should request from the supervisor. - // - // Types that are valid to be assigned to Target: - // - // *TcpForwardInit_Ssh - // *TcpForwardInit_Tcp - Target isTcpForwardInit_Target `protobuf_oneof:"target"` - // Optional target-specific authorization token. SSH targets use this as the - // short-lived SSH session token issued by CreateSshSession. - AuthorizationToken string `protobuf:"bytes,7,opt,name=authorization_token,json=authorizationToken,proto3" json:"authorization_token,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *TcpForwardInit) Reset() { - *x = TcpForwardInit{} - mi := &file_openshell_proto_msgTypes[38] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *TcpForwardInit) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TcpForwardInit) ProtoMessage() {} - -func (x *TcpForwardInit) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[38] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TcpForwardInit.ProtoReflect.Descriptor instead. -func (*TcpForwardInit) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{38} -} - -func (x *TcpForwardInit) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *TcpForwardInit) GetServiceId() string { - if x != nil { - return x.ServiceId - } - return "" -} - -func (x *TcpForwardInit) GetTarget() isTcpForwardInit_Target { - if x != nil { - return x.Target - } - return nil -} - -func (x *TcpForwardInit) GetSsh() *SshRelayTarget { - if x != nil { - if x, ok := x.Target.(*TcpForwardInit_Ssh); ok { - return x.Ssh - } - } - return nil -} - -func (x *TcpForwardInit) GetTcp() *TcpRelayTarget { - if x != nil { - if x, ok := x.Target.(*TcpForwardInit_Tcp); ok { - return x.Tcp - } - } - return nil -} - -func (x *TcpForwardInit) GetAuthorizationToken() string { - if x != nil { - return x.AuthorizationToken - } - return "" -} - -type isTcpForwardInit_Target interface { - isTcpForwardInit_Target() -} - -type TcpForwardInit_Ssh struct { - Ssh *SshRelayTarget `protobuf:"bytes,5,opt,name=ssh,proto3,oneof"` -} - -type TcpForwardInit_Tcp struct { - Tcp *TcpRelayTarget `protobuf:"bytes,6,opt,name=tcp,proto3,oneof"` -} - -func (*TcpForwardInit_Ssh) isTcpForwardInit_Target() {} - -func (*TcpForwardInit_Tcp) isTcpForwardInit_Target() {} - -// A single frame on the CLI-to-gateway TCP forward stream. -type TcpForwardFrame struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Payload: - // - // *TcpForwardFrame_Init - // *TcpForwardFrame_Data - Payload isTcpForwardFrame_Payload `protobuf_oneof:"payload"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *TcpForwardFrame) Reset() { - *x = TcpForwardFrame{} - mi := &file_openshell_proto_msgTypes[39] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *TcpForwardFrame) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TcpForwardFrame) ProtoMessage() {} - -func (x *TcpForwardFrame) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[39] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TcpForwardFrame.ProtoReflect.Descriptor instead. -func (*TcpForwardFrame) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{39} -} - -func (x *TcpForwardFrame) GetPayload() isTcpForwardFrame_Payload { - if x != nil { - return x.Payload - } - return nil -} - -func (x *TcpForwardFrame) GetInit() *TcpForwardInit { - if x != nil { - if x, ok := x.Payload.(*TcpForwardFrame_Init); ok { - return x.Init - } - } - return nil -} - -func (x *TcpForwardFrame) GetData() []byte { - if x != nil { - if x, ok := x.Payload.(*TcpForwardFrame_Data); ok { - return x.Data - } - } - return nil -} - -type isTcpForwardFrame_Payload interface { - isTcpForwardFrame_Payload() -} - -type TcpForwardFrame_Init struct { - Init *TcpForwardInit `protobuf:"bytes,1,opt,name=init,proto3,oneof"` -} - -type TcpForwardFrame_Data struct { - Data []byte `protobuf:"bytes,2,opt,name=data,proto3,oneof"` -} - -func (*TcpForwardFrame_Init) isTcpForwardFrame_Payload() {} - -func (*TcpForwardFrame_Data) isTcpForwardFrame_Payload() {} - -// Client-to-server message for interactive exec. -type ExecSandboxInput struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Payload: - // - // *ExecSandboxInput_Start - // *ExecSandboxInput_Stdin - // *ExecSandboxInput_Resize - Payload isExecSandboxInput_Payload `protobuf_oneof:"payload"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ExecSandboxInput) Reset() { - *x = ExecSandboxInput{} - mi := &file_openshell_proto_msgTypes[40] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ExecSandboxInput) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExecSandboxInput) ProtoMessage() {} - -func (x *ExecSandboxInput) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[40] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExecSandboxInput.ProtoReflect.Descriptor instead. -func (*ExecSandboxInput) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{40} -} - -func (x *ExecSandboxInput) GetPayload() isExecSandboxInput_Payload { - if x != nil { - return x.Payload - } - return nil -} - -func (x *ExecSandboxInput) GetStart() *ExecSandboxRequest { - if x != nil { - if x, ok := x.Payload.(*ExecSandboxInput_Start); ok { - return x.Start - } - } - return nil -} - -func (x *ExecSandboxInput) GetStdin() []byte { - if x != nil { - if x, ok := x.Payload.(*ExecSandboxInput_Stdin); ok { - return x.Stdin - } - } - return nil -} - -func (x *ExecSandboxInput) GetResize() *ExecSandboxWindowResize { - if x != nil { - if x, ok := x.Payload.(*ExecSandboxInput_Resize); ok { - return x.Resize - } - } - return nil -} - -type isExecSandboxInput_Payload interface { - isExecSandboxInput_Payload() -} - -type ExecSandboxInput_Start struct { - // First message: exec request metadata. - Start *ExecSandboxRequest `protobuf:"bytes,1,opt,name=start,proto3,oneof"` -} - -type ExecSandboxInput_Stdin struct { - // Subsequent messages: raw stdin bytes. - Stdin []byte `protobuf:"bytes,2,opt,name=stdin,proto3,oneof"` -} - -type ExecSandboxInput_Resize struct { - // Terminal window size change. - Resize *ExecSandboxWindowResize `protobuf:"bytes,3,opt,name=resize,proto3,oneof"` -} - -func (*ExecSandboxInput_Start) isExecSandboxInput_Payload() {} - -func (*ExecSandboxInput_Stdin) isExecSandboxInput_Payload() {} - -func (*ExecSandboxInput_Resize) isExecSandboxInput_Payload() {} - -// Terminal window resize event for interactive exec. -type ExecSandboxWindowResize struct { - state protoimpl.MessageState `protogen:"open.v1"` - Cols uint32 `protobuf:"varint,1,opt,name=cols,proto3" json:"cols,omitempty"` - Rows uint32 `protobuf:"varint,2,opt,name=rows,proto3" json:"rows,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ExecSandboxWindowResize) Reset() { - *x = ExecSandboxWindowResize{} - mi := &file_openshell_proto_msgTypes[41] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ExecSandboxWindowResize) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ExecSandboxWindowResize) ProtoMessage() {} - -func (x *ExecSandboxWindowResize) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[41] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ExecSandboxWindowResize.ProtoReflect.Descriptor instead. -func (*ExecSandboxWindowResize) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{41} -} - -func (x *ExecSandboxWindowResize) GetCols() uint32 { - if x != nil { - return x.Cols - } - return 0 -} - -func (x *ExecSandboxWindowResize) GetRows() uint32 { - if x != nil { - return x.Rows - } - return 0 -} - -// SSH session record stored in persistence. -type SshSession struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Kubernetes-style metadata (id, name, labels, timestamps, resource version). - Metadata *datamodelv1.ObjectMeta `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` - // Sandbox id. - SandboxId string `protobuf:"bytes,2,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Session token. - Token string `protobuf:"bytes,3,opt,name=token,proto3" json:"token,omitempty"` - // Expiry timestamp in milliseconds since epoch. 0 means no expiry - // (backward-compatible default for sessions created before this field existed). - ExpiresAtMs int64 `protobuf:"varint,4,opt,name=expires_at_ms,json=expiresAtMs,proto3" json:"expires_at_ms,omitempty"` - // Revoked flag. - Revoked bool `protobuf:"varint,5,opt,name=revoked,proto3" json:"revoked,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SshSession) Reset() { - *x = SshSession{} - mi := &file_openshell_proto_msgTypes[42] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SshSession) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SshSession) ProtoMessage() {} - -func (x *SshSession) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[42] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SshSession.ProtoReflect.Descriptor instead. -func (*SshSession) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{42} -} - -func (x *SshSession) GetMetadata() *datamodelv1.ObjectMeta { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *SshSession) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *SshSession) GetToken() string { - if x != nil { - return x.Token - } - return "" -} - -func (x *SshSession) GetExpiresAtMs() int64 { - if x != nil { - return x.ExpiresAtMs - } - return 0 -} - -func (x *SshSession) GetRevoked() bool { - if x != nil { - return x.Revoked - } - return false -} - -// Watch sandbox request. -type WatchSandboxRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox id. - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - // Stream sandbox status snapshots. - FollowStatus bool `protobuf:"varint,2,opt,name=follow_status,json=followStatus,proto3" json:"follow_status,omitempty"` - // Stream openshell-server process logs correlated to this sandbox. - FollowLogs bool `protobuf:"varint,3,opt,name=follow_logs,json=followLogs,proto3" json:"follow_logs,omitempty"` - // Stream platform events correlated to this sandbox. - FollowEvents bool `protobuf:"varint,4,opt,name=follow_events,json=followEvents,proto3" json:"follow_events,omitempty"` - // Replay the last N log lines (best-effort) before following. - LogTailLines uint32 `protobuf:"varint,5,opt,name=log_tail_lines,json=logTailLines,proto3" json:"log_tail_lines,omitempty"` - // Replay the last N platform events (best-effort) before following. - EventTail uint32 `protobuf:"varint,6,opt,name=event_tail,json=eventTail,proto3" json:"event_tail,omitempty"` - // Stop streaming once the sandbox reaches a terminal phase (READY or ERROR). - StopOnTerminal bool `protobuf:"varint,7,opt,name=stop_on_terminal,json=stopOnTerminal,proto3" json:"stop_on_terminal,omitempty"` - // Only include log lines with timestamp >= this value (milliseconds since epoch). - // 0 means no time filter. Applies to both tail replay and live streaming. - LogSinceMs int64 `protobuf:"varint,8,opt,name=log_since_ms,json=logSinceMs,proto3" json:"log_since_ms,omitempty"` - // Filter by log source (e.g. "gateway", "sandbox"). Empty means all sources. - LogSources []string `protobuf:"bytes,9,rep,name=log_sources,json=logSources,proto3" json:"log_sources,omitempty"` - // Minimum log level to include (e.g. "INFO", "WARN", "ERROR"). Empty means all levels. - LogMinLevel string `protobuf:"bytes,10,opt,name=log_min_level,json=logMinLevel,proto3" json:"log_min_level,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *WatchSandboxRequest) Reset() { - *x = WatchSandboxRequest{} - mi := &file_openshell_proto_msgTypes[43] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *WatchSandboxRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*WatchSandboxRequest) ProtoMessage() {} - -func (x *WatchSandboxRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[43] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use WatchSandboxRequest.ProtoReflect.Descriptor instead. -func (*WatchSandboxRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{43} -} - -func (x *WatchSandboxRequest) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *WatchSandboxRequest) GetFollowStatus() bool { - if x != nil { - return x.FollowStatus - } - return false -} - -func (x *WatchSandboxRequest) GetFollowLogs() bool { - if x != nil { - return x.FollowLogs - } - return false -} - -func (x *WatchSandboxRequest) GetFollowEvents() bool { - if x != nil { - return x.FollowEvents - } - return false -} - -func (x *WatchSandboxRequest) GetLogTailLines() uint32 { - if x != nil { - return x.LogTailLines - } - return 0 -} - -func (x *WatchSandboxRequest) GetEventTail() uint32 { - if x != nil { - return x.EventTail - } - return 0 -} - -func (x *WatchSandboxRequest) GetStopOnTerminal() bool { - if x != nil { - return x.StopOnTerminal - } - return false -} - -func (x *WatchSandboxRequest) GetLogSinceMs() int64 { - if x != nil { - return x.LogSinceMs - } - return 0 -} - -func (x *WatchSandboxRequest) GetLogSources() []string { - if x != nil { - return x.LogSources - } - return nil -} - -func (x *WatchSandboxRequest) GetLogMinLevel() string { - if x != nil { - return x.LogMinLevel - } - return "" -} - -// One event in a sandbox watch stream. -type SandboxStreamEvent struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Payload: - // - // *SandboxStreamEvent_Sandbox - // *SandboxStreamEvent_Log - // *SandboxStreamEvent_Event - // *SandboxStreamEvent_Warning - // *SandboxStreamEvent_DraftPolicyUpdate - Payload isSandboxStreamEvent_Payload `protobuf_oneof:"payload"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxStreamEvent) Reset() { - *x = SandboxStreamEvent{} - mi := &file_openshell_proto_msgTypes[44] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxStreamEvent) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxStreamEvent) ProtoMessage() {} - -func (x *SandboxStreamEvent) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[44] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxStreamEvent.ProtoReflect.Descriptor instead. -func (*SandboxStreamEvent) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{44} -} - -func (x *SandboxStreamEvent) GetPayload() isSandboxStreamEvent_Payload { - if x != nil { - return x.Payload - } - return nil -} - -func (x *SandboxStreamEvent) GetSandbox() *Sandbox { - if x != nil { - if x, ok := x.Payload.(*SandboxStreamEvent_Sandbox); ok { - return x.Sandbox - } - } - return nil -} - -func (x *SandboxStreamEvent) GetLog() *SandboxLogLine { - if x != nil { - if x, ok := x.Payload.(*SandboxStreamEvent_Log); ok { - return x.Log - } - } - return nil -} - -func (x *SandboxStreamEvent) GetEvent() *PlatformEvent { - if x != nil { - if x, ok := x.Payload.(*SandboxStreamEvent_Event); ok { - return x.Event - } - } - return nil -} - -func (x *SandboxStreamEvent) GetWarning() *SandboxStreamWarning { - if x != nil { - if x, ok := x.Payload.(*SandboxStreamEvent_Warning); ok { - return x.Warning - } - } - return nil -} - -func (x *SandboxStreamEvent) GetDraftPolicyUpdate() *DraftPolicyUpdate { - if x != nil { - if x, ok := x.Payload.(*SandboxStreamEvent_DraftPolicyUpdate); ok { - return x.DraftPolicyUpdate - } - } - return nil -} - -type isSandboxStreamEvent_Payload interface { - isSandboxStreamEvent_Payload() -} - -type SandboxStreamEvent_Sandbox struct { - // Latest sandbox snapshot. - Sandbox *Sandbox `protobuf:"bytes,1,opt,name=sandbox,proto3,oneof"` -} - -type SandboxStreamEvent_Log struct { - // One server log line/event. - Log *SandboxLogLine `protobuf:"bytes,2,opt,name=log,proto3,oneof"` -} - -type SandboxStreamEvent_Event struct { - // One platform event. - Event *PlatformEvent `protobuf:"bytes,3,opt,name=event,proto3,oneof"` -} - -type SandboxStreamEvent_Warning struct { - // Warning from the server (e.g. missed messages due to lag). - Warning *SandboxStreamWarning `protobuf:"bytes,4,opt,name=warning,proto3,oneof"` -} - -type SandboxStreamEvent_DraftPolicyUpdate struct { - // Draft policy update notification. - DraftPolicyUpdate *DraftPolicyUpdate `protobuf:"bytes,5,opt,name=draft_policy_update,json=draftPolicyUpdate,proto3,oneof"` -} - -func (*SandboxStreamEvent_Sandbox) isSandboxStreamEvent_Payload() {} - -func (*SandboxStreamEvent_Log) isSandboxStreamEvent_Payload() {} - -func (*SandboxStreamEvent_Event) isSandboxStreamEvent_Payload() {} - -func (*SandboxStreamEvent_Warning) isSandboxStreamEvent_Payload() {} - -func (*SandboxStreamEvent_DraftPolicyUpdate) isSandboxStreamEvent_Payload() {} - -// Log line correlated to a sandbox. -type SandboxLogLine struct { - state protoimpl.MessageState `protogen:"open.v1"` - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - TimestampMs int64 `protobuf:"varint,2,opt,name=timestamp_ms,json=timestampMs,proto3" json:"timestamp_ms,omitempty"` - Level string `protobuf:"bytes,3,opt,name=level,proto3" json:"level,omitempty"` - Target string `protobuf:"bytes,4,opt,name=target,proto3" json:"target,omitempty"` - Message string `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"` - // Log source: "gateway" (server-side) or "sandbox" (supervisor). - // Empty is treated as "gateway" for backward compatibility. - Source string `protobuf:"bytes,6,opt,name=source,proto3" json:"source,omitempty"` - // Structured key-value fields from the tracing event (e.g. dst_host, action). - Fields map[string]string `protobuf:"bytes,7,rep,name=fields,proto3" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxLogLine) Reset() { - *x = SandboxLogLine{} - mi := &file_openshell_proto_msgTypes[45] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxLogLine) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxLogLine) ProtoMessage() {} - -func (x *SandboxLogLine) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[45] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxLogLine.ProtoReflect.Descriptor instead. -func (*SandboxLogLine) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{45} -} - -func (x *SandboxLogLine) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *SandboxLogLine) GetTimestampMs() int64 { - if x != nil { - return x.TimestampMs - } - return 0 -} - -func (x *SandboxLogLine) GetLevel() string { - if x != nil { - return x.Level - } - return "" -} - -func (x *SandboxLogLine) GetTarget() string { - if x != nil { - return x.Target - } - return "" -} - -func (x *SandboxLogLine) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -func (x *SandboxLogLine) GetSource() string { - if x != nil { - return x.Source - } - return "" -} - -func (x *SandboxLogLine) GetFields() map[string]string { - if x != nil { - return x.Fields - } - return nil -} - -type SandboxStreamWarning struct { - state protoimpl.MessageState `protogen:"open.v1"` - Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxStreamWarning) Reset() { - *x = SandboxStreamWarning{} - mi := &file_openshell_proto_msgTypes[46] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxStreamWarning) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxStreamWarning) ProtoMessage() {} - -func (x *SandboxStreamWarning) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[46] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxStreamWarning.ProtoReflect.Descriptor instead. -func (*SandboxStreamWarning) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{46} -} - -func (x *SandboxStreamWarning) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -// Create provider request. -type CreateProviderRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Provider *datamodelv1.Provider `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *CreateProviderRequest) Reset() { - *x = CreateProviderRequest{} - mi := &file_openshell_proto_msgTypes[47] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *CreateProviderRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CreateProviderRequest) ProtoMessage() {} - -func (x *CreateProviderRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[47] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CreateProviderRequest.ProtoReflect.Descriptor instead. -func (*CreateProviderRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{47} -} - -func (x *CreateProviderRequest) GetProvider() *datamodelv1.Provider { - if x != nil { - return x.Provider - } - return nil -} - -// Get provider request. -type GetProviderRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetProviderRequest) Reset() { - *x = GetProviderRequest{} - mi := &file_openshell_proto_msgTypes[48] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetProviderRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetProviderRequest) ProtoMessage() {} - -func (x *GetProviderRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[48] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetProviderRequest.ProtoReflect.Descriptor instead. -func (*GetProviderRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{48} -} - -func (x *GetProviderRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -// List providers request. -type ListProvidersRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Limit uint32 `protobuf:"varint,1,opt,name=limit,proto3" json:"limit,omitempty"` - Offset uint32 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListProvidersRequest) Reset() { - *x = ListProvidersRequest{} - mi := &file_openshell_proto_msgTypes[49] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListProvidersRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListProvidersRequest) ProtoMessage() {} - -func (x *ListProvidersRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[49] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListProvidersRequest.ProtoReflect.Descriptor instead. -func (*ListProvidersRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{49} -} - -func (x *ListProvidersRequest) GetLimit() uint32 { - if x != nil { - return x.Limit - } - return 0 -} - -func (x *ListProvidersRequest) GetOffset() uint32 { - if x != nil { - return x.Offset - } - return 0 -} - -// Update provider request. -type UpdateProviderRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Provider *datamodelv1.Provider `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *UpdateProviderRequest) Reset() { - *x = UpdateProviderRequest{} - mi := &file_openshell_proto_msgTypes[50] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *UpdateProviderRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UpdateProviderRequest) ProtoMessage() {} - -func (x *UpdateProviderRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[50] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UpdateProviderRequest.ProtoReflect.Descriptor instead. -func (*UpdateProviderRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{50} -} - -func (x *UpdateProviderRequest) GetProvider() *datamodelv1.Provider { - if x != nil { - return x.Provider - } - return nil -} - -// Delete provider request. -type DeleteProviderRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteProviderRequest) Reset() { - *x = DeleteProviderRequest{} - mi := &file_openshell_proto_msgTypes[51] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteProviderRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteProviderRequest) ProtoMessage() {} - -func (x *DeleteProviderRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[51] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteProviderRequest.ProtoReflect.Descriptor instead. -func (*DeleteProviderRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{51} -} - -func (x *DeleteProviderRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -// Provider response. -type ProviderResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Provider *datamodelv1.Provider `protobuf:"bytes,1,opt,name=provider,proto3" json:"provider,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderResponse) Reset() { - *x = ProviderResponse{} - mi := &file_openshell_proto_msgTypes[52] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderResponse) ProtoMessage() {} - -func (x *ProviderResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[52] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderResponse.ProtoReflect.Descriptor instead. -func (*ProviderResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{52} -} - -func (x *ProviderResponse) GetProvider() *datamodelv1.Provider { - if x != nil { - return x.Provider - } - return nil -} - -// List providers response. -type ListProvidersResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Providers []*datamodelv1.Provider `protobuf:"bytes,1,rep,name=providers,proto3" json:"providers,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListProvidersResponse) Reset() { - *x = ListProvidersResponse{} - mi := &file_openshell_proto_msgTypes[53] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListProvidersResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListProvidersResponse) ProtoMessage() {} - -func (x *ListProvidersResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[53] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListProvidersResponse.ProtoReflect.Descriptor instead. -func (*ListProvidersResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{53} -} - -func (x *ListProvidersResponse) GetProviders() []*datamodelv1.Provider { - if x != nil { - return x.Providers - } - return nil -} - -// List provider type profiles request. -type ListProviderProfilesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Limit uint32 `protobuf:"varint,1,opt,name=limit,proto3" json:"limit,omitempty"` - Offset uint32 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListProviderProfilesRequest) Reset() { - *x = ListProviderProfilesRequest{} - mi := &file_openshell_proto_msgTypes[54] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListProviderProfilesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListProviderProfilesRequest) ProtoMessage() {} - -func (x *ListProviderProfilesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[54] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListProviderProfilesRequest.ProtoReflect.Descriptor instead. -func (*ListProviderProfilesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{54} -} - -func (x *ListProviderProfilesRequest) GetLimit() uint32 { - if x != nil { - return x.Limit - } - return 0 -} - -func (x *ListProviderProfilesRequest) GetOffset() uint32 { - if x != nil { - return x.Offset - } - return 0 -} - -// Fetch provider type profile request. -type GetProviderProfileRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetProviderProfileRequest) Reset() { - *x = GetProviderProfileRequest{} - mi := &file_openshell_proto_msgTypes[55] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetProviderProfileRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetProviderProfileRequest) ProtoMessage() {} - -func (x *GetProviderProfileRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[55] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetProviderProfileRequest.ProtoReflect.Descriptor instead. -func (*GetProviderProfileRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{55} -} - -func (x *GetProviderProfileRequest) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -// Provider profile payload with optional source metadata for diagnostics. -type ProviderProfileImportItem struct { - state protoimpl.MessageState `protogen:"open.v1"` - Profile *ProviderProfile `protobuf:"bytes,1,opt,name=profile,proto3" json:"profile,omitempty"` - Source string `protobuf:"bytes,2,opt,name=source,proto3" json:"source,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderProfileImportItem) Reset() { - *x = ProviderProfileImportItem{} - mi := &file_openshell_proto_msgTypes[56] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderProfileImportItem) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderProfileImportItem) ProtoMessage() {} - -func (x *ProviderProfileImportItem) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[56] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderProfileImportItem.ProtoReflect.Descriptor instead. -func (*ProviderProfileImportItem) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{56} -} - -func (x *ProviderProfileImportItem) GetProfile() *ProviderProfile { - if x != nil { - return x.Profile - } - return nil -} - -func (x *ProviderProfileImportItem) GetSource() string { - if x != nil { - return x.Source - } - return "" -} - -// Provider profile validation diagnostic. -type ProviderProfileDiagnostic struct { - state protoimpl.MessageState `protogen:"open.v1"` - Source string `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"` - ProfileId string `protobuf:"bytes,2,opt,name=profile_id,json=profileId,proto3" json:"profile_id,omitempty"` - Field string `protobuf:"bytes,3,opt,name=field,proto3" json:"field,omitempty"` - Message string `protobuf:"bytes,4,opt,name=message,proto3" json:"message,omitempty"` - Severity string `protobuf:"bytes,5,opt,name=severity,proto3" json:"severity,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderProfileDiagnostic) Reset() { - *x = ProviderProfileDiagnostic{} - mi := &file_openshell_proto_msgTypes[57] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderProfileDiagnostic) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderProfileDiagnostic) ProtoMessage() {} - -func (x *ProviderProfileDiagnostic) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[57] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderProfileDiagnostic.ProtoReflect.Descriptor instead. -func (*ProviderProfileDiagnostic) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{57} -} - -func (x *ProviderProfileDiagnostic) GetSource() string { - if x != nil { - return x.Source - } - return "" -} - -func (x *ProviderProfileDiagnostic) GetProfileId() string { - if x != nil { - return x.ProfileId - } - return "" -} - -func (x *ProviderProfileDiagnostic) GetField() string { - if x != nil { - return x.Field - } - return "" -} - -func (x *ProviderProfileDiagnostic) GetMessage() string { - if x != nil { - return x.Message - } - return "" -} - -func (x *ProviderProfileDiagnostic) GetSeverity() string { - if x != nil { - return x.Severity - } - return "" -} - -// Provider credential declaration. -type ProviderProfileCredential struct { - state protoimpl.MessageState `protogen:"open.v1"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` - EnvVars []string `protobuf:"bytes,3,rep,name=env_vars,json=envVars,proto3" json:"env_vars,omitempty"` - Required bool `protobuf:"varint,4,opt,name=required,proto3" json:"required,omitempty"` - AuthStyle string `protobuf:"bytes,5,opt,name=auth_style,json=authStyle,proto3" json:"auth_style,omitempty"` - HeaderName string `protobuf:"bytes,6,opt,name=header_name,json=headerName,proto3" json:"header_name,omitempty"` - QueryParam string `protobuf:"bytes,7,opt,name=query_param,json=queryParam,proto3" json:"query_param,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderProfileCredential) Reset() { - *x = ProviderProfileCredential{} - mi := &file_openshell_proto_msgTypes[58] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderProfileCredential) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderProfileCredential) ProtoMessage() {} - -func (x *ProviderProfileCredential) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[58] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderProfileCredential.ProtoReflect.Descriptor instead. -func (*ProviderProfileCredential) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{58} -} - -func (x *ProviderProfileCredential) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ProviderProfileCredential) GetDescription() string { - if x != nil { - return x.Description - } - return "" -} - -func (x *ProviderProfileCredential) GetEnvVars() []string { - if x != nil { - return x.EnvVars - } - return nil -} - -func (x *ProviderProfileCredential) GetRequired() bool { - if x != nil { - return x.Required - } - return false -} - -func (x *ProviderProfileCredential) GetAuthStyle() string { - if x != nil { - return x.AuthStyle - } - return "" -} - -func (x *ProviderProfileCredential) GetHeaderName() string { - if x != nil { - return x.HeaderName - } - return "" -} - -func (x *ProviderProfileCredential) GetQueryParam() string { - if x != nil { - return x.QueryParam - } - return "" -} - -// Provider type profile metadata exposed to clients. -type ProviderProfile struct { - state protoimpl.MessageState `protogen:"open.v1"` - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - DisplayName string `protobuf:"bytes,2,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` - Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` - Category ProviderProfileCategory `protobuf:"varint,4,opt,name=category,proto3,enum=openshell.v1.ProviderProfileCategory" json:"category,omitempty"` - Credentials []*ProviderProfileCredential `protobuf:"bytes,5,rep,name=credentials,proto3" json:"credentials,omitempty"` - Endpoints []*sandboxv1.NetworkEndpoint `protobuf:"bytes,6,rep,name=endpoints,proto3" json:"endpoints,omitempty"` - Binaries []*sandboxv1.NetworkBinary `protobuf:"bytes,7,rep,name=binaries,proto3" json:"binaries,omitempty"` - InferenceCapable bool `protobuf:"varint,8,opt,name=inference_capable,json=inferenceCapable,proto3" json:"inference_capable,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderProfile) Reset() { - *x = ProviderProfile{} - mi := &file_openshell_proto_msgTypes[59] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderProfile) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderProfile) ProtoMessage() {} - -func (x *ProviderProfile) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[59] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderProfile.ProtoReflect.Descriptor instead. -func (*ProviderProfile) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{59} -} - -func (x *ProviderProfile) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *ProviderProfile) GetDisplayName() string { - if x != nil { - return x.DisplayName - } - return "" -} - -func (x *ProviderProfile) GetDescription() string { - if x != nil { - return x.Description - } - return "" -} - -func (x *ProviderProfile) GetCategory() ProviderProfileCategory { - if x != nil { - return x.Category - } - return ProviderProfileCategory_PROVIDER_PROFILE_CATEGORY_UNSPECIFIED -} - -func (x *ProviderProfile) GetCredentials() []*ProviderProfileCredential { - if x != nil { - return x.Credentials - } - return nil -} - -func (x *ProviderProfile) GetEndpoints() []*sandboxv1.NetworkEndpoint { - if x != nil { - return x.Endpoints - } - return nil -} - -func (x *ProviderProfile) GetBinaries() []*sandboxv1.NetworkBinary { - if x != nil { - return x.Binaries - } - return nil -} - -func (x *ProviderProfile) GetInferenceCapable() bool { - if x != nil { - return x.InferenceCapable - } - return false -} - -// Stored custom provider profile object. -type StoredProviderProfile struct { - state protoimpl.MessageState `protogen:"open.v1"` - Metadata *datamodelv1.ObjectMeta `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` - Profile *ProviderProfile `protobuf:"bytes,2,opt,name=profile,proto3" json:"profile,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *StoredProviderProfile) Reset() { - *x = StoredProviderProfile{} - mi := &file_openshell_proto_msgTypes[60] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *StoredProviderProfile) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StoredProviderProfile) ProtoMessage() {} - -func (x *StoredProviderProfile) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[60] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use StoredProviderProfile.ProtoReflect.Descriptor instead. -func (*StoredProviderProfile) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{60} -} - -func (x *StoredProviderProfile) GetMetadata() *datamodelv1.ObjectMeta { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *StoredProviderProfile) GetProfile() *ProviderProfile { - if x != nil { - return x.Profile - } - return nil -} - -// Provider profile response. -type ProviderProfileResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Profile *ProviderProfile `protobuf:"bytes,1,opt,name=profile,proto3" json:"profile,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProviderProfileResponse) Reset() { - *x = ProviderProfileResponse{} - mi := &file_openshell_proto_msgTypes[61] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProviderProfileResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProviderProfileResponse) ProtoMessage() {} - -func (x *ProviderProfileResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[61] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProviderProfileResponse.ProtoReflect.Descriptor instead. -func (*ProviderProfileResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{61} -} - -func (x *ProviderProfileResponse) GetProfile() *ProviderProfile { - if x != nil { - return x.Profile - } - return nil -} - -// List provider profiles response. -type ListProviderProfilesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Profiles []*ProviderProfile `protobuf:"bytes,1,rep,name=profiles,proto3" json:"profiles,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListProviderProfilesResponse) Reset() { - *x = ListProviderProfilesResponse{} - mi := &file_openshell_proto_msgTypes[62] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListProviderProfilesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListProviderProfilesResponse) ProtoMessage() {} - -func (x *ListProviderProfilesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[62] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListProviderProfilesResponse.ProtoReflect.Descriptor instead. -func (*ListProviderProfilesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{62} -} - -func (x *ListProviderProfilesResponse) GetProfiles() []*ProviderProfile { - if x != nil { - return x.Profiles - } - return nil -} - -// Import custom provider profiles request. -type ImportProviderProfilesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Profiles []*ProviderProfileImportItem `protobuf:"bytes,1,rep,name=profiles,proto3" json:"profiles,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ImportProviderProfilesRequest) Reset() { - *x = ImportProviderProfilesRequest{} - mi := &file_openshell_proto_msgTypes[63] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ImportProviderProfilesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ImportProviderProfilesRequest) ProtoMessage() {} - -func (x *ImportProviderProfilesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[63] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ImportProviderProfilesRequest.ProtoReflect.Descriptor instead. -func (*ImportProviderProfilesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{63} -} - -func (x *ImportProviderProfilesRequest) GetProfiles() []*ProviderProfileImportItem { - if x != nil { - return x.Profiles - } - return nil -} - -// Import custom provider profiles response. -type ImportProviderProfilesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Diagnostics []*ProviderProfileDiagnostic `protobuf:"bytes,1,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - Profiles []*ProviderProfile `protobuf:"bytes,2,rep,name=profiles,proto3" json:"profiles,omitempty"` - Imported bool `protobuf:"varint,3,opt,name=imported,proto3" json:"imported,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ImportProviderProfilesResponse) Reset() { - *x = ImportProviderProfilesResponse{} - mi := &file_openshell_proto_msgTypes[64] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ImportProviderProfilesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ImportProviderProfilesResponse) ProtoMessage() {} - -func (x *ImportProviderProfilesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[64] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ImportProviderProfilesResponse.ProtoReflect.Descriptor instead. -func (*ImportProviderProfilesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{64} -} - -func (x *ImportProviderProfilesResponse) GetDiagnostics() []*ProviderProfileDiagnostic { - if x != nil { - return x.Diagnostics - } - return nil -} - -func (x *ImportProviderProfilesResponse) GetProfiles() []*ProviderProfile { - if x != nil { - return x.Profiles - } - return nil -} - -func (x *ImportProviderProfilesResponse) GetImported() bool { - if x != nil { - return x.Imported - } - return false -} - -// Lint provider profiles request. -type LintProviderProfilesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Profiles []*ProviderProfileImportItem `protobuf:"bytes,1,rep,name=profiles,proto3" json:"profiles,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *LintProviderProfilesRequest) Reset() { - *x = LintProviderProfilesRequest{} - mi := &file_openshell_proto_msgTypes[65] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *LintProviderProfilesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LintProviderProfilesRequest) ProtoMessage() {} - -func (x *LintProviderProfilesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[65] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LintProviderProfilesRequest.ProtoReflect.Descriptor instead. -func (*LintProviderProfilesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{65} -} - -func (x *LintProviderProfilesRequest) GetProfiles() []*ProviderProfileImportItem { - if x != nil { - return x.Profiles - } - return nil -} - -// Lint provider profiles response. -type LintProviderProfilesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Diagnostics []*ProviderProfileDiagnostic `protobuf:"bytes,1,rep,name=diagnostics,proto3" json:"diagnostics,omitempty"` - Valid bool `protobuf:"varint,2,opt,name=valid,proto3" json:"valid,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *LintProviderProfilesResponse) Reset() { - *x = LintProviderProfilesResponse{} - mi := &file_openshell_proto_msgTypes[66] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *LintProviderProfilesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LintProviderProfilesResponse) ProtoMessage() {} - -func (x *LintProviderProfilesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[66] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LintProviderProfilesResponse.ProtoReflect.Descriptor instead. -func (*LintProviderProfilesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{66} -} - -func (x *LintProviderProfilesResponse) GetDiagnostics() []*ProviderProfileDiagnostic { - if x != nil { - return x.Diagnostics - } - return nil -} - -func (x *LintProviderProfilesResponse) GetValid() bool { - if x != nil { - return x.Valid - } - return false -} - -// Delete provider response. -type DeleteProviderResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Deleted bool `protobuf:"varint,1,opt,name=deleted,proto3" json:"deleted,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteProviderResponse) Reset() { - *x = DeleteProviderResponse{} - mi := &file_openshell_proto_msgTypes[67] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteProviderResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteProviderResponse) ProtoMessage() {} - -func (x *DeleteProviderResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[67] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteProviderResponse.ProtoReflect.Descriptor instead. -func (*DeleteProviderResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{67} -} - -func (x *DeleteProviderResponse) GetDeleted() bool { - if x != nil { - return x.Deleted - } - return false -} - -// Delete custom provider profile request. -type DeleteProviderProfileRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteProviderProfileRequest) Reset() { - *x = DeleteProviderProfileRequest{} - mi := &file_openshell_proto_msgTypes[68] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteProviderProfileRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteProviderProfileRequest) ProtoMessage() {} - -func (x *DeleteProviderProfileRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[68] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteProviderProfileRequest.ProtoReflect.Descriptor instead. -func (*DeleteProviderProfileRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{68} -} - -func (x *DeleteProviderProfileRequest) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -// Delete custom provider profile response. -type DeleteProviderProfileResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Deleted bool `protobuf:"varint,1,opt,name=deleted,proto3" json:"deleted,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DeleteProviderProfileResponse) Reset() { - *x = DeleteProviderProfileResponse{} - mi := &file_openshell_proto_msgTypes[69] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DeleteProviderProfileResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DeleteProviderProfileResponse) ProtoMessage() {} - -func (x *DeleteProviderProfileResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[69] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DeleteProviderProfileResponse.ProtoReflect.Descriptor instead. -func (*DeleteProviderProfileResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{69} -} - -func (x *DeleteProviderProfileResponse) GetDeleted() bool { - if x != nil { - return x.Deleted - } - return false -} - -// Get sandbox provider environment request. -type GetSandboxProviderEnvironmentRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The sandbox ID. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxProviderEnvironmentRequest) Reset() { - *x = GetSandboxProviderEnvironmentRequest{} - mi := &file_openshell_proto_msgTypes[70] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxProviderEnvironmentRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxProviderEnvironmentRequest) ProtoMessage() {} - -func (x *GetSandboxProviderEnvironmentRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[70] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxProviderEnvironmentRequest.ProtoReflect.Descriptor instead. -func (*GetSandboxProviderEnvironmentRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{70} -} - -func (x *GetSandboxProviderEnvironmentRequest) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -// Get sandbox provider environment response. -type GetSandboxProviderEnvironmentResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Provider credential environment variables. - Environment map[string]string `protobuf:"bytes,1,rep,name=environment,proto3" json:"environment,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Fingerprint for the provider credential inputs that produced environment. - ProviderEnvRevision uint64 `protobuf:"varint,2,opt,name=provider_env_revision,json=providerEnvRevision,proto3" json:"provider_env_revision,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxProviderEnvironmentResponse) Reset() { - *x = GetSandboxProviderEnvironmentResponse{} - mi := &file_openshell_proto_msgTypes[71] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxProviderEnvironmentResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxProviderEnvironmentResponse) ProtoMessage() {} - -func (x *GetSandboxProviderEnvironmentResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[71] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxProviderEnvironmentResponse.ProtoReflect.Descriptor instead. -func (*GetSandboxProviderEnvironmentResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{71} -} - -func (x *GetSandboxProviderEnvironmentResponse) GetEnvironment() map[string]string { - if x != nil { - return x.Environment - } - return nil -} - -func (x *GetSandboxProviderEnvironmentResponse) GetProviderEnvRevision() uint64 { - if x != nil { - return x.ProviderEnvRevision - } - return 0 -} - -// Update sandbox policy request. -type UpdateConfigRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox name (canonical lookup key). Required for sandbox-scoped updates. - // Not required when `global=true`. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // The new policy to apply. - // - // Sandbox scope (`global=false`): - // - only network_policies and inference fields may differ from create-time - // policy; static fields must match version 1. - // - // Global scope (`global=true`): - // - applies to all sandboxes in full (no merge). - Policy *sandboxv1.SandboxPolicy `protobuf:"bytes,2,opt,name=policy,proto3" json:"policy,omitempty"` - // Optional single setting key to mutate. - SettingKey string `protobuf:"bytes,3,opt,name=setting_key,json=settingKey,proto3" json:"setting_key,omitempty"` - // Setting value for upsert operations. - SettingValue *sandboxv1.SettingValue `protobuf:"bytes,4,opt,name=setting_value,json=settingValue,proto3" json:"setting_value,omitempty"` - // Delete the setting key from scope. - // Sandbox-scoped deletes are rejected; only global delete is supported. - DeleteSetting bool `protobuf:"varint,5,opt,name=delete_setting,json=deleteSetting,proto3" json:"delete_setting,omitempty"` - // Apply mutation at gateway-global scope. - Global bool `protobuf:"varint,6,opt,name=global,proto3" json:"global,omitempty"` - // Batched incremental policy merge operations. Sandbox-scoped only. - MergeOperations []*PolicyMergeOperation `protobuf:"bytes,7,rep,name=merge_operations,json=mergeOperations,proto3" json:"merge_operations,omitempty"` - // Expected resource version for optimistic concurrency control (sandbox-scoped only). - // If 0, the server uses the current version (backward compatibility). - // If non-zero, the server validates that the sandbox's current resource_version - // matches this value before applying the mutation, returning ABORTED on mismatch. - // Ignored for global-scoped updates. - ExpectedResourceVersion uint64 `protobuf:"varint,8,opt,name=expected_resource_version,json=expectedResourceVersion,proto3" json:"expected_resource_version,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *UpdateConfigRequest) Reset() { - *x = UpdateConfigRequest{} - mi := &file_openshell_proto_msgTypes[72] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *UpdateConfigRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UpdateConfigRequest) ProtoMessage() {} - -func (x *UpdateConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[72] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UpdateConfigRequest.ProtoReflect.Descriptor instead. -func (*UpdateConfigRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{72} -} - -func (x *UpdateConfigRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *UpdateConfigRequest) GetPolicy() *sandboxv1.SandboxPolicy { - if x != nil { - return x.Policy - } - return nil -} - -func (x *UpdateConfigRequest) GetSettingKey() string { - if x != nil { - return x.SettingKey - } - return "" -} - -func (x *UpdateConfigRequest) GetSettingValue() *sandboxv1.SettingValue { - if x != nil { - return x.SettingValue - } - return nil -} - -func (x *UpdateConfigRequest) GetDeleteSetting() bool { - if x != nil { - return x.DeleteSetting - } - return false -} - -func (x *UpdateConfigRequest) GetGlobal() bool { - if x != nil { - return x.Global - } - return false -} - -func (x *UpdateConfigRequest) GetMergeOperations() []*PolicyMergeOperation { - if x != nil { - return x.MergeOperations - } - return nil -} - -func (x *UpdateConfigRequest) GetExpectedResourceVersion() uint64 { - if x != nil { - return x.ExpectedResourceVersion - } - return 0 -} - -type PolicyMergeOperation struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Operation: - // - // *PolicyMergeOperation_AddRule - // *PolicyMergeOperation_RemoveEndpoint - // *PolicyMergeOperation_RemoveRule - // *PolicyMergeOperation_AddDenyRules - // *PolicyMergeOperation_AddAllowRules - // *PolicyMergeOperation_RemoveBinary - Operation isPolicyMergeOperation_Operation `protobuf_oneof:"operation"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PolicyMergeOperation) Reset() { - *x = PolicyMergeOperation{} - mi := &file_openshell_proto_msgTypes[73] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PolicyMergeOperation) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PolicyMergeOperation) ProtoMessage() {} - -func (x *PolicyMergeOperation) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[73] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PolicyMergeOperation.ProtoReflect.Descriptor instead. -func (*PolicyMergeOperation) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{73} -} - -func (x *PolicyMergeOperation) GetOperation() isPolicyMergeOperation_Operation { - if x != nil { - return x.Operation - } - return nil -} - -func (x *PolicyMergeOperation) GetAddRule() *AddNetworkRule { - if x != nil { - if x, ok := x.Operation.(*PolicyMergeOperation_AddRule); ok { - return x.AddRule - } - } - return nil -} - -func (x *PolicyMergeOperation) GetRemoveEndpoint() *RemoveNetworkEndpoint { - if x != nil { - if x, ok := x.Operation.(*PolicyMergeOperation_RemoveEndpoint); ok { - return x.RemoveEndpoint - } - } - return nil -} - -func (x *PolicyMergeOperation) GetRemoveRule() *RemoveNetworkRule { - if x != nil { - if x, ok := x.Operation.(*PolicyMergeOperation_RemoveRule); ok { - return x.RemoveRule - } - } - return nil -} - -func (x *PolicyMergeOperation) GetAddDenyRules() *AddDenyRules { - if x != nil { - if x, ok := x.Operation.(*PolicyMergeOperation_AddDenyRules); ok { - return x.AddDenyRules - } - } - return nil -} - -func (x *PolicyMergeOperation) GetAddAllowRules() *AddAllowRules { - if x != nil { - if x, ok := x.Operation.(*PolicyMergeOperation_AddAllowRules); ok { - return x.AddAllowRules - } - } - return nil -} - -func (x *PolicyMergeOperation) GetRemoveBinary() *RemoveNetworkBinary { - if x != nil { - if x, ok := x.Operation.(*PolicyMergeOperation_RemoveBinary); ok { - return x.RemoveBinary - } - } - return nil -} - -type isPolicyMergeOperation_Operation interface { - isPolicyMergeOperation_Operation() -} - -type PolicyMergeOperation_AddRule struct { - AddRule *AddNetworkRule `protobuf:"bytes,1,opt,name=add_rule,json=addRule,proto3,oneof"` -} - -type PolicyMergeOperation_RemoveEndpoint struct { - RemoveEndpoint *RemoveNetworkEndpoint `protobuf:"bytes,2,opt,name=remove_endpoint,json=removeEndpoint,proto3,oneof"` -} - -type PolicyMergeOperation_RemoveRule struct { - RemoveRule *RemoveNetworkRule `protobuf:"bytes,3,opt,name=remove_rule,json=removeRule,proto3,oneof"` -} - -type PolicyMergeOperation_AddDenyRules struct { - AddDenyRules *AddDenyRules `protobuf:"bytes,4,opt,name=add_deny_rules,json=addDenyRules,proto3,oneof"` -} - -type PolicyMergeOperation_AddAllowRules struct { - AddAllowRules *AddAllowRules `protobuf:"bytes,5,opt,name=add_allow_rules,json=addAllowRules,proto3,oneof"` -} - -type PolicyMergeOperation_RemoveBinary struct { - RemoveBinary *RemoveNetworkBinary `protobuf:"bytes,6,opt,name=remove_binary,json=removeBinary,proto3,oneof"` -} - -func (*PolicyMergeOperation_AddRule) isPolicyMergeOperation_Operation() {} - -func (*PolicyMergeOperation_RemoveEndpoint) isPolicyMergeOperation_Operation() {} - -func (*PolicyMergeOperation_RemoveRule) isPolicyMergeOperation_Operation() {} - -func (*PolicyMergeOperation_AddDenyRules) isPolicyMergeOperation_Operation() {} - -func (*PolicyMergeOperation_AddAllowRules) isPolicyMergeOperation_Operation() {} - -func (*PolicyMergeOperation_RemoveBinary) isPolicyMergeOperation_Operation() {} - -type AddNetworkRule struct { - state protoimpl.MessageState `protogen:"open.v1"` - RuleName string `protobuf:"bytes,1,opt,name=rule_name,json=ruleName,proto3" json:"rule_name,omitempty"` - Rule *sandboxv1.NetworkPolicyRule `protobuf:"bytes,2,opt,name=rule,proto3" json:"rule,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *AddNetworkRule) Reset() { - *x = AddNetworkRule{} - mi := &file_openshell_proto_msgTypes[74] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AddNetworkRule) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AddNetworkRule) ProtoMessage() {} - -func (x *AddNetworkRule) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[74] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AddNetworkRule.ProtoReflect.Descriptor instead. -func (*AddNetworkRule) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{74} -} - -func (x *AddNetworkRule) GetRuleName() string { - if x != nil { - return x.RuleName - } - return "" -} - -func (x *AddNetworkRule) GetRule() *sandboxv1.NetworkPolicyRule { - if x != nil { - return x.Rule - } - return nil -} - -type RemoveNetworkEndpoint struct { - state protoimpl.MessageState `protogen:"open.v1"` - RuleName string `protobuf:"bytes,1,opt,name=rule_name,json=ruleName,proto3" json:"rule_name,omitempty"` - Host string `protobuf:"bytes,2,opt,name=host,proto3" json:"host,omitempty"` - Port uint32 `protobuf:"varint,3,opt,name=port,proto3" json:"port,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RemoveNetworkEndpoint) Reset() { - *x = RemoveNetworkEndpoint{} - mi := &file_openshell_proto_msgTypes[75] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RemoveNetworkEndpoint) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RemoveNetworkEndpoint) ProtoMessage() {} - -func (x *RemoveNetworkEndpoint) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[75] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RemoveNetworkEndpoint.ProtoReflect.Descriptor instead. -func (*RemoveNetworkEndpoint) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{75} -} - -func (x *RemoveNetworkEndpoint) GetRuleName() string { - if x != nil { - return x.RuleName - } - return "" -} - -func (x *RemoveNetworkEndpoint) GetHost() string { - if x != nil { - return x.Host - } - return "" -} - -func (x *RemoveNetworkEndpoint) GetPort() uint32 { - if x != nil { - return x.Port - } - return 0 -} - -type RemoveNetworkRule struct { - state protoimpl.MessageState `protogen:"open.v1"` - RuleName string `protobuf:"bytes,1,opt,name=rule_name,json=ruleName,proto3" json:"rule_name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RemoveNetworkRule) Reset() { - *x = RemoveNetworkRule{} - mi := &file_openshell_proto_msgTypes[76] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RemoveNetworkRule) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RemoveNetworkRule) ProtoMessage() {} - -func (x *RemoveNetworkRule) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[76] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RemoveNetworkRule.ProtoReflect.Descriptor instead. -func (*RemoveNetworkRule) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{76} -} - -func (x *RemoveNetworkRule) GetRuleName() string { - if x != nil { - return x.RuleName - } - return "" -} - -type AddDenyRules struct { - state protoimpl.MessageState `protogen:"open.v1"` - Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` - Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` - DenyRules []*sandboxv1.L7DenyRule `protobuf:"bytes,3,rep,name=deny_rules,json=denyRules,proto3" json:"deny_rules,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *AddDenyRules) Reset() { - *x = AddDenyRules{} - mi := &file_openshell_proto_msgTypes[77] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AddDenyRules) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AddDenyRules) ProtoMessage() {} - -func (x *AddDenyRules) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[77] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AddDenyRules.ProtoReflect.Descriptor instead. -func (*AddDenyRules) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{77} -} - -func (x *AddDenyRules) GetHost() string { - if x != nil { - return x.Host - } - return "" -} - -func (x *AddDenyRules) GetPort() uint32 { - if x != nil { - return x.Port - } - return 0 -} - -func (x *AddDenyRules) GetDenyRules() []*sandboxv1.L7DenyRule { - if x != nil { - return x.DenyRules - } - return nil -} - -type AddAllowRules struct { - state protoimpl.MessageState `protogen:"open.v1"` - Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` - Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` - Rules []*sandboxv1.L7Rule `protobuf:"bytes,3,rep,name=rules,proto3" json:"rules,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *AddAllowRules) Reset() { - *x = AddAllowRules{} - mi := &file_openshell_proto_msgTypes[78] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *AddAllowRules) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AddAllowRules) ProtoMessage() {} - -func (x *AddAllowRules) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[78] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use AddAllowRules.ProtoReflect.Descriptor instead. -func (*AddAllowRules) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{78} -} - -func (x *AddAllowRules) GetHost() string { - if x != nil { - return x.Host - } - return "" -} - -func (x *AddAllowRules) GetPort() uint32 { - if x != nil { - return x.Port - } - return 0 -} - -func (x *AddAllowRules) GetRules() []*sandboxv1.L7Rule { - if x != nil { - return x.Rules - } - return nil -} - -type RemoveNetworkBinary struct { - state protoimpl.MessageState `protogen:"open.v1"` - RuleName string `protobuf:"bytes,1,opt,name=rule_name,json=ruleName,proto3" json:"rule_name,omitempty"` - BinaryPath string `protobuf:"bytes,2,opt,name=binary_path,json=binaryPath,proto3" json:"binary_path,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RemoveNetworkBinary) Reset() { - *x = RemoveNetworkBinary{} - mi := &file_openshell_proto_msgTypes[79] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RemoveNetworkBinary) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RemoveNetworkBinary) ProtoMessage() {} - -func (x *RemoveNetworkBinary) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[79] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RemoveNetworkBinary.ProtoReflect.Descriptor instead. -func (*RemoveNetworkBinary) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{79} -} - -func (x *RemoveNetworkBinary) GetRuleName() string { - if x != nil { - return x.RuleName - } - return "" -} - -func (x *RemoveNetworkBinary) GetBinaryPath() string { - if x != nil { - return x.BinaryPath - } - return "" -} - -// Update sandbox policy response. -type UpdateConfigResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Assigned policy version (monotonically increasing per sandbox). - Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - // SHA-256 hash of the serialized policy payload. - PolicyHash string `protobuf:"bytes,2,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - // Settings revision for the scope that was modified. - SettingsRevision uint64 `protobuf:"varint,3,opt,name=settings_revision,json=settingsRevision,proto3" json:"settings_revision,omitempty"` - // True when a setting delete operation removed an existing key. - Deleted bool `protobuf:"varint,4,opt,name=deleted,proto3" json:"deleted,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *UpdateConfigResponse) Reset() { - *x = UpdateConfigResponse{} - mi := &file_openshell_proto_msgTypes[80] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *UpdateConfigResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UpdateConfigResponse) ProtoMessage() {} - -func (x *UpdateConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[80] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UpdateConfigResponse.ProtoReflect.Descriptor instead. -func (*UpdateConfigResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{80} -} - -func (x *UpdateConfigResponse) GetVersion() uint32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *UpdateConfigResponse) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -func (x *UpdateConfigResponse) GetSettingsRevision() uint64 { - if x != nil { - return x.SettingsRevision - } - return 0 -} - -func (x *UpdateConfigResponse) GetDeleted() bool { - if x != nil { - return x.Deleted - } - return false -} - -// Get sandbox policy status request. -type GetSandboxPolicyStatusRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox name (canonical lookup key). Ignored when global is true. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // The specific policy version to query. 0 means latest. - Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` - // Query global policy revisions instead of a sandbox-scoped one. - Global bool `protobuf:"varint,3,opt,name=global,proto3" json:"global,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxPolicyStatusRequest) Reset() { - *x = GetSandboxPolicyStatusRequest{} - mi := &file_openshell_proto_msgTypes[81] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxPolicyStatusRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxPolicyStatusRequest) ProtoMessage() {} - -func (x *GetSandboxPolicyStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[81] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxPolicyStatusRequest.ProtoReflect.Descriptor instead. -func (*GetSandboxPolicyStatusRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{81} -} - -func (x *GetSandboxPolicyStatusRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *GetSandboxPolicyStatusRequest) GetVersion() uint32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *GetSandboxPolicyStatusRequest) GetGlobal() bool { - if x != nil { - return x.Global - } - return false -} - -// Get sandbox policy status response. -type GetSandboxPolicyStatusResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The queried policy revision. - Revision *SandboxPolicyRevision `protobuf:"bytes,1,opt,name=revision,proto3" json:"revision,omitempty"` - // The currently active (loaded) policy version for this sandbox. - ActiveVersion uint32 `protobuf:"varint,2,opt,name=active_version,json=activeVersion,proto3" json:"active_version,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxPolicyStatusResponse) Reset() { - *x = GetSandboxPolicyStatusResponse{} - mi := &file_openshell_proto_msgTypes[82] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxPolicyStatusResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxPolicyStatusResponse) ProtoMessage() {} - -func (x *GetSandboxPolicyStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[82] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxPolicyStatusResponse.ProtoReflect.Descriptor instead. -func (*GetSandboxPolicyStatusResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{82} -} - -func (x *GetSandboxPolicyStatusResponse) GetRevision() *SandboxPolicyRevision { - if x != nil { - return x.Revision - } - return nil -} - -func (x *GetSandboxPolicyStatusResponse) GetActiveVersion() uint32 { - if x != nil { - return x.ActiveVersion - } - return 0 -} - -// List sandbox policies request. -type ListSandboxPoliciesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox name (canonical lookup key). Ignored when global is true. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Limit uint32 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"` - Offset uint32 `protobuf:"varint,3,opt,name=offset,proto3" json:"offset,omitempty"` - // List global policy revisions instead of sandbox-scoped ones. - Global bool `protobuf:"varint,4,opt,name=global,proto3" json:"global,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListSandboxPoliciesRequest) Reset() { - *x = ListSandboxPoliciesRequest{} - mi := &file_openshell_proto_msgTypes[83] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListSandboxPoliciesRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListSandboxPoliciesRequest) ProtoMessage() {} - -func (x *ListSandboxPoliciesRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[83] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListSandboxPoliciesRequest.ProtoReflect.Descriptor instead. -func (*ListSandboxPoliciesRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{83} -} - -func (x *ListSandboxPoliciesRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ListSandboxPoliciesRequest) GetLimit() uint32 { - if x != nil { - return x.Limit - } - return 0 -} - -func (x *ListSandboxPoliciesRequest) GetOffset() uint32 { - if x != nil { - return x.Offset - } - return 0 -} - -func (x *ListSandboxPoliciesRequest) GetGlobal() bool { - if x != nil { - return x.Global - } - return false -} - -// List sandbox policies response. -type ListSandboxPoliciesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Revisions []*SandboxPolicyRevision `protobuf:"bytes,1,rep,name=revisions,proto3" json:"revisions,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ListSandboxPoliciesResponse) Reset() { - *x = ListSandboxPoliciesResponse{} - mi := &file_openshell_proto_msgTypes[84] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ListSandboxPoliciesResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ListSandboxPoliciesResponse) ProtoMessage() {} - -func (x *ListSandboxPoliciesResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[84] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ListSandboxPoliciesResponse.ProtoReflect.Descriptor instead. -func (*ListSandboxPoliciesResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{84} -} - -func (x *ListSandboxPoliciesResponse) GetRevisions() []*SandboxPolicyRevision { - if x != nil { - return x.Revisions - } - return nil -} - -// Report policy load status (called by sandbox runtime after reload attempt). -type ReportPolicyStatusRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox id. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // The policy version that was attempted. - Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` - // Load result status. - Status PolicyStatus `protobuf:"varint,3,opt,name=status,proto3,enum=openshell.v1.PolicyStatus" json:"status,omitempty"` - // Error message if status is FAILED. - LoadError string `protobuf:"bytes,4,opt,name=load_error,json=loadError,proto3" json:"load_error,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ReportPolicyStatusRequest) Reset() { - *x = ReportPolicyStatusRequest{} - mi := &file_openshell_proto_msgTypes[85] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ReportPolicyStatusRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReportPolicyStatusRequest) ProtoMessage() {} - -func (x *ReportPolicyStatusRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[85] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReportPolicyStatusRequest.ProtoReflect.Descriptor instead. -func (*ReportPolicyStatusRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{85} -} - -func (x *ReportPolicyStatusRequest) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *ReportPolicyStatusRequest) GetVersion() uint32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *ReportPolicyStatusRequest) GetStatus() PolicyStatus { - if x != nil { - return x.Status - } - return PolicyStatus_POLICY_STATUS_UNSPECIFIED -} - -func (x *ReportPolicyStatusRequest) GetLoadError() string { - if x != nil { - return x.LoadError - } - return "" -} - -// Report policy status response. -type ReportPolicyStatusResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ReportPolicyStatusResponse) Reset() { - *x = ReportPolicyStatusResponse{} - mi := &file_openshell_proto_msgTypes[86] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ReportPolicyStatusResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ReportPolicyStatusResponse) ProtoMessage() {} - -func (x *ReportPolicyStatusResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[86] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ReportPolicyStatusResponse.ProtoReflect.Descriptor instead. -func (*ReportPolicyStatusResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{86} -} - -// A versioned policy revision with metadata. -type SandboxPolicyRevision struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Policy version (monotonically increasing per sandbox). - Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - // SHA-256 hash of the serialized policy payload. - PolicyHash string `protobuf:"bytes,2,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - // Load status of this revision. - Status PolicyStatus `protobuf:"varint,3,opt,name=status,proto3,enum=openshell.v1.PolicyStatus" json:"status,omitempty"` - // Error message if status is FAILED. - LoadError string `protobuf:"bytes,4,opt,name=load_error,json=loadError,proto3" json:"load_error,omitempty"` - // Milliseconds since epoch when this revision was created. - CreatedAtMs int64 `protobuf:"varint,5,opt,name=created_at_ms,json=createdAtMs,proto3" json:"created_at_ms,omitempty"` - // Milliseconds since epoch when this revision was loaded by the sandbox. - LoadedAtMs int64 `protobuf:"varint,6,opt,name=loaded_at_ms,json=loadedAtMs,proto3" json:"loaded_at_ms,omitempty"` - // The full policy (only populated when explicitly requested). - Policy *sandboxv1.SandboxPolicy `protobuf:"bytes,7,opt,name=policy,proto3" json:"policy,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxPolicyRevision) Reset() { - *x = SandboxPolicyRevision{} - mi := &file_openshell_proto_msgTypes[87] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxPolicyRevision) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxPolicyRevision) ProtoMessage() {} - -func (x *SandboxPolicyRevision) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[87] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxPolicyRevision.ProtoReflect.Descriptor instead. -func (*SandboxPolicyRevision) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{87} -} - -func (x *SandboxPolicyRevision) GetVersion() uint32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *SandboxPolicyRevision) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -func (x *SandboxPolicyRevision) GetStatus() PolicyStatus { - if x != nil { - return x.Status - } - return PolicyStatus_POLICY_STATUS_UNSPECIFIED -} - -func (x *SandboxPolicyRevision) GetLoadError() string { - if x != nil { - return x.LoadError - } - return "" -} - -func (x *SandboxPolicyRevision) GetCreatedAtMs() int64 { - if x != nil { - return x.CreatedAtMs - } - return 0 -} - -func (x *SandboxPolicyRevision) GetLoadedAtMs() int64 { - if x != nil { - return x.LoadedAtMs - } - return 0 -} - -func (x *SandboxPolicyRevision) GetPolicy() *sandboxv1.SandboxPolicy { - if x != nil { - return x.Policy - } - return nil -} - -// Get sandbox logs request (one-shot fetch). -type GetSandboxLogsRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox id. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Maximum number of log lines to return. 0 means use default (2000). - Lines uint32 `protobuf:"varint,2,opt,name=lines,proto3" json:"lines,omitempty"` - // Only include logs with timestamp >= this value (ms since epoch). 0 means no filter. - SinceMs int64 `protobuf:"varint,3,opt,name=since_ms,json=sinceMs,proto3" json:"since_ms,omitempty"` - // Filter by log source (e.g. "gateway", "sandbox"). Empty means all sources. - Sources []string `protobuf:"bytes,4,rep,name=sources,proto3" json:"sources,omitempty"` - // Minimum log level to include (e.g. "INFO", "WARN", "ERROR"). Empty means all levels. - MinLevel string `protobuf:"bytes,5,opt,name=min_level,json=minLevel,proto3" json:"min_level,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxLogsRequest) Reset() { - *x = GetSandboxLogsRequest{} - mi := &file_openshell_proto_msgTypes[88] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxLogsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxLogsRequest) ProtoMessage() {} - -func (x *GetSandboxLogsRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[88] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxLogsRequest.ProtoReflect.Descriptor instead. -func (*GetSandboxLogsRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{88} -} - -func (x *GetSandboxLogsRequest) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *GetSandboxLogsRequest) GetLines() uint32 { - if x != nil { - return x.Lines - } - return 0 -} - -func (x *GetSandboxLogsRequest) GetSinceMs() int64 { - if x != nil { - return x.SinceMs - } - return 0 -} - -func (x *GetSandboxLogsRequest) GetSources() []string { - if x != nil { - return x.Sources - } - return nil -} - -func (x *GetSandboxLogsRequest) GetMinLevel() string { - if x != nil { - return x.MinLevel - } - return "" -} - -// Batch of log lines pushed from sandbox to server. -type PushSandboxLogsRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The sandbox ID. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Log lines to ingest. - Logs []*SandboxLogLine `protobuf:"bytes,2,rep,name=logs,proto3" json:"logs,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PushSandboxLogsRequest) Reset() { - *x = PushSandboxLogsRequest{} - mi := &file_openshell_proto_msgTypes[89] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PushSandboxLogsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PushSandboxLogsRequest) ProtoMessage() {} - -func (x *PushSandboxLogsRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[89] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PushSandboxLogsRequest.ProtoReflect.Descriptor instead. -func (*PushSandboxLogsRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{89} -} - -func (x *PushSandboxLogsRequest) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *PushSandboxLogsRequest) GetLogs() []*SandboxLogLine { - if x != nil { - return x.Logs - } - return nil -} - -// Push sandbox logs response. -type PushSandboxLogsResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PushSandboxLogsResponse) Reset() { - *x = PushSandboxLogsResponse{} - mi := &file_openshell_proto_msgTypes[90] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PushSandboxLogsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PushSandboxLogsResponse) ProtoMessage() {} - -func (x *PushSandboxLogsResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[90] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PushSandboxLogsResponse.ProtoReflect.Descriptor instead. -func (*PushSandboxLogsResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{90} -} - -// Get sandbox logs response. -type GetSandboxLogsResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Log lines in chronological order. - Logs []*SandboxLogLine `protobuf:"bytes,1,rep,name=logs,proto3" json:"logs,omitempty"` - // Total number of lines in the server's buffer for this sandbox. - BufferTotal uint32 `protobuf:"varint,2,opt,name=buffer_total,json=bufferTotal,proto3" json:"buffer_total,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxLogsResponse) Reset() { - *x = GetSandboxLogsResponse{} - mi := &file_openshell_proto_msgTypes[91] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxLogsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxLogsResponse) ProtoMessage() {} - -func (x *GetSandboxLogsResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[91] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxLogsResponse.ProtoReflect.Descriptor instead. -func (*GetSandboxLogsResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{91} -} - -func (x *GetSandboxLogsResponse) GetLogs() []*SandboxLogLine { - if x != nil { - return x.Logs - } - return nil -} - -func (x *GetSandboxLogsResponse) GetBufferTotal() uint32 { - if x != nil { - return x.BufferTotal - } - return 0 -} - -// Envelope for supervisor-to-gateway messages on the ConnectSupervisor stream. -type SupervisorMessage struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Payload: - // - // *SupervisorMessage_Hello - // *SupervisorMessage_Heartbeat - // *SupervisorMessage_RelayOpenResult - // *SupervisorMessage_RelayClose - Payload isSupervisorMessage_Payload `protobuf_oneof:"payload"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SupervisorMessage) Reset() { - *x = SupervisorMessage{} - mi := &file_openshell_proto_msgTypes[92] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SupervisorMessage) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SupervisorMessage) ProtoMessage() {} - -func (x *SupervisorMessage) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[92] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SupervisorMessage.ProtoReflect.Descriptor instead. -func (*SupervisorMessage) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{92} -} - -func (x *SupervisorMessage) GetPayload() isSupervisorMessage_Payload { - if x != nil { - return x.Payload - } - return nil -} - -func (x *SupervisorMessage) GetHello() *SupervisorHello { - if x != nil { - if x, ok := x.Payload.(*SupervisorMessage_Hello); ok { - return x.Hello - } - } - return nil -} - -func (x *SupervisorMessage) GetHeartbeat() *SupervisorHeartbeat { - if x != nil { - if x, ok := x.Payload.(*SupervisorMessage_Heartbeat); ok { - return x.Heartbeat - } - } - return nil -} - -func (x *SupervisorMessage) GetRelayOpenResult() *RelayOpenResult { - if x != nil { - if x, ok := x.Payload.(*SupervisorMessage_RelayOpenResult); ok { - return x.RelayOpenResult - } - } - return nil -} - -func (x *SupervisorMessage) GetRelayClose() *RelayClose { - if x != nil { - if x, ok := x.Payload.(*SupervisorMessage_RelayClose); ok { - return x.RelayClose - } - } - return nil -} - -type isSupervisorMessage_Payload interface { - isSupervisorMessage_Payload() -} - -type SupervisorMessage_Hello struct { - Hello *SupervisorHello `protobuf:"bytes,1,opt,name=hello,proto3,oneof"` -} - -type SupervisorMessage_Heartbeat struct { - Heartbeat *SupervisorHeartbeat `protobuf:"bytes,2,opt,name=heartbeat,proto3,oneof"` -} - -type SupervisorMessage_RelayOpenResult struct { - RelayOpenResult *RelayOpenResult `protobuf:"bytes,3,opt,name=relay_open_result,json=relayOpenResult,proto3,oneof"` -} - -type SupervisorMessage_RelayClose struct { - RelayClose *RelayClose `protobuf:"bytes,4,opt,name=relay_close,json=relayClose,proto3,oneof"` -} - -func (*SupervisorMessage_Hello) isSupervisorMessage_Payload() {} - -func (*SupervisorMessage_Heartbeat) isSupervisorMessage_Payload() {} - -func (*SupervisorMessage_RelayOpenResult) isSupervisorMessage_Payload() {} - -func (*SupervisorMessage_RelayClose) isSupervisorMessage_Payload() {} - -// Envelope for gateway-to-supervisor messages on the ConnectSupervisor stream. -type GatewayMessage struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Payload: - // - // *GatewayMessage_SessionAccepted - // *GatewayMessage_SessionRejected - // *GatewayMessage_Heartbeat - // *GatewayMessage_RelayOpen - // *GatewayMessage_RelayClose - Payload isGatewayMessage_Payload `protobuf_oneof:"payload"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GatewayMessage) Reset() { - *x = GatewayMessage{} - mi := &file_openshell_proto_msgTypes[93] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GatewayMessage) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GatewayMessage) ProtoMessage() {} - -func (x *GatewayMessage) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[93] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GatewayMessage.ProtoReflect.Descriptor instead. -func (*GatewayMessage) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{93} -} - -func (x *GatewayMessage) GetPayload() isGatewayMessage_Payload { - if x != nil { - return x.Payload - } - return nil -} - -func (x *GatewayMessage) GetSessionAccepted() *SessionAccepted { - if x != nil { - if x, ok := x.Payload.(*GatewayMessage_SessionAccepted); ok { - return x.SessionAccepted - } - } - return nil -} - -func (x *GatewayMessage) GetSessionRejected() *SessionRejected { - if x != nil { - if x, ok := x.Payload.(*GatewayMessage_SessionRejected); ok { - return x.SessionRejected - } - } - return nil -} - -func (x *GatewayMessage) GetHeartbeat() *GatewayHeartbeat { - if x != nil { - if x, ok := x.Payload.(*GatewayMessage_Heartbeat); ok { - return x.Heartbeat - } - } - return nil -} - -func (x *GatewayMessage) GetRelayOpen() *RelayOpen { - if x != nil { - if x, ok := x.Payload.(*GatewayMessage_RelayOpen); ok { - return x.RelayOpen - } - } - return nil -} - -func (x *GatewayMessage) GetRelayClose() *RelayClose { - if x != nil { - if x, ok := x.Payload.(*GatewayMessage_RelayClose); ok { - return x.RelayClose - } - } - return nil -} - -type isGatewayMessage_Payload interface { - isGatewayMessage_Payload() -} - -type GatewayMessage_SessionAccepted struct { - SessionAccepted *SessionAccepted `protobuf:"bytes,1,opt,name=session_accepted,json=sessionAccepted,proto3,oneof"` -} - -type GatewayMessage_SessionRejected struct { - SessionRejected *SessionRejected `protobuf:"bytes,2,opt,name=session_rejected,json=sessionRejected,proto3,oneof"` -} - -type GatewayMessage_Heartbeat struct { - Heartbeat *GatewayHeartbeat `protobuf:"bytes,3,opt,name=heartbeat,proto3,oneof"` -} - -type GatewayMessage_RelayOpen struct { - RelayOpen *RelayOpen `protobuf:"bytes,4,opt,name=relay_open,json=relayOpen,proto3,oneof"` -} - -type GatewayMessage_RelayClose struct { - RelayClose *RelayClose `protobuf:"bytes,5,opt,name=relay_close,json=relayClose,proto3,oneof"` -} - -func (*GatewayMessage_SessionAccepted) isGatewayMessage_Payload() {} - -func (*GatewayMessage_SessionRejected) isGatewayMessage_Payload() {} - -func (*GatewayMessage_Heartbeat) isGatewayMessage_Payload() {} - -func (*GatewayMessage_RelayOpen) isGatewayMessage_Payload() {} - -func (*GatewayMessage_RelayClose) isGatewayMessage_Payload() {} - -// Supervisor identifies itself and the sandbox it manages. -type SupervisorHello struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox ID this supervisor manages. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Supervisor instance ID (e.g. boot id or process epoch). - InstanceId string `protobuf:"bytes,2,opt,name=instance_id,json=instanceId,proto3" json:"instance_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SupervisorHello) Reset() { - *x = SupervisorHello{} - mi := &file_openshell_proto_msgTypes[94] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SupervisorHello) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SupervisorHello) ProtoMessage() {} - -func (x *SupervisorHello) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[94] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SupervisorHello.ProtoReflect.Descriptor instead. -func (*SupervisorHello) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{94} -} - -func (x *SupervisorHello) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *SupervisorHello) GetInstanceId() string { - if x != nil { - return x.InstanceId - } - return "" -} - -// Gateway accepts the supervisor session. -type SessionAccepted struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Gateway-assigned session ID for this connection. - SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` - // Recommended heartbeat interval in seconds. - HeartbeatIntervalSecs uint32 `protobuf:"varint,2,opt,name=heartbeat_interval_secs,json=heartbeatIntervalSecs,proto3" json:"heartbeat_interval_secs,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SessionAccepted) Reset() { - *x = SessionAccepted{} - mi := &file_openshell_proto_msgTypes[95] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SessionAccepted) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SessionAccepted) ProtoMessage() {} - -func (x *SessionAccepted) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[95] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SessionAccepted.ProtoReflect.Descriptor instead. -func (*SessionAccepted) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{95} -} - -func (x *SessionAccepted) GetSessionId() string { - if x != nil { - return x.SessionId - } - return "" -} - -func (x *SessionAccepted) GetHeartbeatIntervalSecs() uint32 { - if x != nil { - return x.HeartbeatIntervalSecs - } - return 0 -} - -// Gateway rejects the supervisor session. -type SessionRejected struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Human-readable rejection reason. - Reason string `protobuf:"bytes,1,opt,name=reason,proto3" json:"reason,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SessionRejected) Reset() { - *x = SessionRejected{} - mi := &file_openshell_proto_msgTypes[96] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SessionRejected) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SessionRejected) ProtoMessage() {} - -func (x *SessionRejected) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[96] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SessionRejected.ProtoReflect.Descriptor instead. -func (*SessionRejected) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{96} -} - -func (x *SessionRejected) GetReason() string { - if x != nil { - return x.Reason - } - return "" -} - -// Supervisor heartbeat. -type SupervisorHeartbeat struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SupervisorHeartbeat) Reset() { - *x = SupervisorHeartbeat{} - mi := &file_openshell_proto_msgTypes[97] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SupervisorHeartbeat) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SupervisorHeartbeat) ProtoMessage() {} - -func (x *SupervisorHeartbeat) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[97] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SupervisorHeartbeat.ProtoReflect.Descriptor instead. -func (*SupervisorHeartbeat) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{97} -} - -// Gateway heartbeat. -type GatewayHeartbeat struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GatewayHeartbeat) Reset() { - *x = GatewayHeartbeat{} - mi := &file_openshell_proto_msgTypes[98] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GatewayHeartbeat) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GatewayHeartbeat) ProtoMessage() {} - -func (x *GatewayHeartbeat) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[98] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GatewayHeartbeat.ProtoReflect.Descriptor instead. -func (*GatewayHeartbeat) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{98} -} - -// Gateway requests the supervisor to open a relay channel. -// -// On receiving this, the supervisor should initiate a RelayStream RPC to -// the gateway, sending a RelayInit in the first RelayFrame to associate -// the new HTTP/2 stream with the pending relay slot. The supervisor -// bridges that stream to the requested local target. -type RelayOpen struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Gateway-allocated channel identifier (UUID). - ChannelId string `protobuf:"bytes,1,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` - // Target the supervisor should dial inside the sandbox. - // If absent, supervisors treat the relay as SSH for compatibility. - // - // Types that are valid to be assigned to Target: - // - // *RelayOpen_Ssh - // *RelayOpen_Tcp - Target isRelayOpen_Target `protobuf_oneof:"target"` - // Optional service identifier for audit/correlation. - ServiceId string `protobuf:"bytes,5,opt,name=service_id,json=serviceId,proto3" json:"service_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RelayOpen) Reset() { - *x = RelayOpen{} - mi := &file_openshell_proto_msgTypes[99] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RelayOpen) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RelayOpen) ProtoMessage() {} - -func (x *RelayOpen) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[99] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RelayOpen.ProtoReflect.Descriptor instead. -func (*RelayOpen) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{99} -} - -func (x *RelayOpen) GetChannelId() string { - if x != nil { - return x.ChannelId - } - return "" -} - -func (x *RelayOpen) GetTarget() isRelayOpen_Target { - if x != nil { - return x.Target - } - return nil -} - -func (x *RelayOpen) GetSsh() *SshRelayTarget { - if x != nil { - if x, ok := x.Target.(*RelayOpen_Ssh); ok { - return x.Ssh - } - } - return nil -} - -func (x *RelayOpen) GetTcp() *TcpRelayTarget { - if x != nil { - if x, ok := x.Target.(*RelayOpen_Tcp); ok { - return x.Tcp - } - } - return nil -} - -func (x *RelayOpen) GetServiceId() string { - if x != nil { - return x.ServiceId - } - return "" -} - -type isRelayOpen_Target interface { - isRelayOpen_Target() -} - -type RelayOpen_Ssh struct { - Ssh *SshRelayTarget `protobuf:"bytes,2,opt,name=ssh,proto3,oneof"` -} - -type RelayOpen_Tcp struct { - Tcp *TcpRelayTarget `protobuf:"bytes,3,opt,name=tcp,proto3,oneof"` -} - -func (*RelayOpen_Ssh) isRelayOpen_Target() {} - -func (*RelayOpen_Tcp) isRelayOpen_Target() {} - -// Built-in SSH relay target. -type SshRelayTarget struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SshRelayTarget) Reset() { - *x = SshRelayTarget{} - mi := &file_openshell_proto_msgTypes[100] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SshRelayTarget) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SshRelayTarget) ProtoMessage() {} - -func (x *SshRelayTarget) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[100] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SshRelayTarget.ProtoReflect.Descriptor instead. -func (*SshRelayTarget) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{100} -} - -// TCP target dialed by the supervisor from inside the sandbox. -type TcpRelayTarget struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Phase 1 accepts loopback only: 127.0.0.1, ::1, or localhost. - Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` - // Target port. Must fit in u16 and be non-zero. - Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *TcpRelayTarget) Reset() { - *x = TcpRelayTarget{} - mi := &file_openshell_proto_msgTypes[101] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *TcpRelayTarget) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*TcpRelayTarget) ProtoMessage() {} - -func (x *TcpRelayTarget) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[101] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use TcpRelayTarget.ProtoReflect.Descriptor instead. -func (*TcpRelayTarget) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{101} -} - -func (x *TcpRelayTarget) GetHost() string { - if x != nil { - return x.Host - } - return "" -} - -func (x *TcpRelayTarget) GetPort() uint32 { - if x != nil { - return x.Port - } - return 0 -} - -// Initial RelayStream frame sent by the supervisor to claim a pending relay. -type RelayInit struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Gateway-allocated channel identifier (UUID). - ChannelId string `protobuf:"bytes,1,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RelayInit) Reset() { - *x = RelayInit{} - mi := &file_openshell_proto_msgTypes[102] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RelayInit) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RelayInit) ProtoMessage() {} - -func (x *RelayInit) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[102] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RelayInit.ProtoReflect.Descriptor instead. -func (*RelayInit) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{102} -} - -func (x *RelayInit) GetChannelId() string { - if x != nil { - return x.ChannelId - } - return "" -} - -// A single frame on the RelayStream RPC. -// -// The supervisor MUST send `init` as the first frame. All subsequent frames -// in either direction carry raw bytes in `data`. -type RelayFrame struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Payload: - // - // *RelayFrame_Init - // *RelayFrame_Data - Payload isRelayFrame_Payload `protobuf_oneof:"payload"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RelayFrame) Reset() { - *x = RelayFrame{} - mi := &file_openshell_proto_msgTypes[103] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RelayFrame) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RelayFrame) ProtoMessage() {} - -func (x *RelayFrame) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[103] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RelayFrame.ProtoReflect.Descriptor instead. -func (*RelayFrame) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{103} -} - -func (x *RelayFrame) GetPayload() isRelayFrame_Payload { - if x != nil { - return x.Payload - } - return nil -} - -func (x *RelayFrame) GetInit() *RelayInit { - if x != nil { - if x, ok := x.Payload.(*RelayFrame_Init); ok { - return x.Init - } - } - return nil -} - -func (x *RelayFrame) GetData() []byte { - if x != nil { - if x, ok := x.Payload.(*RelayFrame_Data); ok { - return x.Data - } - } - return nil -} - -type isRelayFrame_Payload interface { - isRelayFrame_Payload() -} - -type RelayFrame_Init struct { - Init *RelayInit `protobuf:"bytes,1,opt,name=init,proto3,oneof"` -} - -type RelayFrame_Data struct { - Data []byte `protobuf:"bytes,2,opt,name=data,proto3,oneof"` -} - -func (*RelayFrame_Init) isRelayFrame_Payload() {} - -func (*RelayFrame_Data) isRelayFrame_Payload() {} - -// Supervisor reports the result of a relay open request. -type RelayOpenResult struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Channel identifier from the RelayOpen request. - ChannelId string `protobuf:"bytes,1,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` - // True if the relay was successfully established. - Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` - // Error message if success is false. - Error string `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RelayOpenResult) Reset() { - *x = RelayOpenResult{} - mi := &file_openshell_proto_msgTypes[104] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RelayOpenResult) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RelayOpenResult) ProtoMessage() {} - -func (x *RelayOpenResult) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[104] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RelayOpenResult.ProtoReflect.Descriptor instead. -func (*RelayOpenResult) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{104} -} - -func (x *RelayOpenResult) GetChannelId() string { - if x != nil { - return x.ChannelId - } - return "" -} - -func (x *RelayOpenResult) GetSuccess() bool { - if x != nil { - return x.Success - } - return false -} - -func (x *RelayOpenResult) GetError() string { - if x != nil { - return x.Error - } - return "" -} - -// Either side requests closure of a relay channel. -type RelayClose struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Channel identifier to close. - ChannelId string `protobuf:"bytes,1,opt,name=channel_id,json=channelId,proto3" json:"channel_id,omitempty"` - // Optional reason for closure. - Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RelayClose) Reset() { - *x = RelayClose{} - mi := &file_openshell_proto_msgTypes[105] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RelayClose) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RelayClose) ProtoMessage() {} - -func (x *RelayClose) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[105] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RelayClose.ProtoReflect.Descriptor instead. -func (*RelayClose) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{105} -} - -func (x *RelayClose) GetChannelId() string { - if x != nil { - return x.ChannelId - } - return "" -} - -func (x *RelayClose) GetReason() string { - if x != nil { - return x.Reason - } - return "" -} - -// Observed HTTP method+path pattern from L7 inspection. -type L7RequestSample struct { - state protoimpl.MessageState `protogen:"open.v1"` - // HTTP method: GET, POST, PUT, DELETE, etc. - Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` - // HTTP path: /v1/models, /repos/myorg/issues - Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` - // L7 decision: "audit" or "deny" (allowed requests not collected). - Decision string `protobuf:"bytes,3,opt,name=decision,proto3" json:"decision,omitempty"` - // Number of times this (method, path) was observed. - Count uint32 `protobuf:"varint,4,opt,name=count,proto3" json:"count,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *L7RequestSample) Reset() { - *x = L7RequestSample{} - mi := &file_openshell_proto_msgTypes[106] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *L7RequestSample) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*L7RequestSample) ProtoMessage() {} - -func (x *L7RequestSample) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[106] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use L7RequestSample.ProtoReflect.Descriptor instead. -func (*L7RequestSample) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{106} -} - -func (x *L7RequestSample) GetMethod() string { - if x != nil { - return x.Method - } - return "" -} - -func (x *L7RequestSample) GetPath() string { - if x != nil { - return x.Path - } - return "" -} - -func (x *L7RequestSample) GetDecision() string { - if x != nil { - return x.Decision - } - return "" -} - -func (x *L7RequestSample) GetCount() uint32 { - if x != nil { - return x.Count - } - return 0 -} - -// Structured denial summary from sandbox aggregator. -type DenialSummary struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox ID that produced this summary. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - // Denied destination host. - Host string `protobuf:"bytes,2,opt,name=host,proto3" json:"host,omitempty"` - // Denied destination port. - Port uint32 `protobuf:"varint,3,opt,name=port,proto3" json:"port,omitempty"` - // Binary that attempted the connection. - Binary string `protobuf:"bytes,4,opt,name=binary,proto3" json:"binary,omitempty"` - // Process ancestor chain. - Ancestors []string `protobuf:"bytes,5,rep,name=ancestors,proto3" json:"ancestors,omitempty"` - // Denial reason from OPA evaluation. - DenyReason string `protobuf:"bytes,6,opt,name=deny_reason,json=denyReason,proto3" json:"deny_reason,omitempty"` - // First denial timestamp (ms since epoch). - FirstSeenMs int64 `protobuf:"varint,7,opt,name=first_seen_ms,json=firstSeenMs,proto3" json:"first_seen_ms,omitempty"` - // Most recent denial timestamp (ms since epoch). - LastSeenMs int64 `protobuf:"varint,8,opt,name=last_seen_ms,json=lastSeenMs,proto3" json:"last_seen_ms,omitempty"` - // Number of denials in the current window. - Count uint32 `protobuf:"varint,9,opt,name=count,proto3" json:"count,omitempty"` - // Events dropped during aggregator cooldown. - SuppressedCount uint32 `protobuf:"varint,10,opt,name=suppressed_count,json=suppressedCount,proto3" json:"suppressed_count,omitempty"` - // Cumulative lifetime count (never resets). - TotalCount uint32 `protobuf:"varint,11,opt,name=total_count,json=totalCount,proto3" json:"total_count,omitempty"` - // Distinct cmdline strings observed (sanitized of credentials). - SampleCmdlines []string `protobuf:"bytes,12,rep,name=sample_cmdlines,json=sampleCmdlines,proto3" json:"sample_cmdlines,omitempty"` - // SHA-256 of the binary for audit trail. - BinarySha256 string `protobuf:"bytes,13,opt,name=binary_sha256,json=binarySha256,proto3" json:"binary_sha256,omitempty"` - // True if emitted by stale-flush rather than threshold. - Persistent bool `protobuf:"varint,14,opt,name=persistent,proto3" json:"persistent,omitempty"` - // Denial category: "l4_deny", "l7_deny", "l7_audit", "ssrf". - DenialStage string `protobuf:"bytes,15,opt,name=denial_stage,json=denialStage,proto3" json:"denial_stage,omitempty"` - // Observed HTTP request patterns (from L7 inspection). - L7RequestSamples []*L7RequestSample `protobuf:"bytes,16,rep,name=l7_request_samples,json=l7RequestSamples,proto3" json:"l7_request_samples,omitempty"` - // True if L7 inspection was active during observation window. - L7InspectionActive bool `protobuf:"varint,17,opt,name=l7_inspection_active,json=l7InspectionActive,proto3" json:"l7_inspection_active,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DenialSummary) Reset() { - *x = DenialSummary{} - mi := &file_openshell_proto_msgTypes[107] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DenialSummary) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DenialSummary) ProtoMessage() {} - -func (x *DenialSummary) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[107] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DenialSummary.ProtoReflect.Descriptor instead. -func (*DenialSummary) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{107} -} - -func (x *DenialSummary) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *DenialSummary) GetHost() string { - if x != nil { - return x.Host - } - return "" -} - -func (x *DenialSummary) GetPort() uint32 { - if x != nil { - return x.Port - } - return 0 -} - -func (x *DenialSummary) GetBinary() string { - if x != nil { - return x.Binary - } - return "" -} - -func (x *DenialSummary) GetAncestors() []string { - if x != nil { - return x.Ancestors - } - return nil -} - -func (x *DenialSummary) GetDenyReason() string { - if x != nil { - return x.DenyReason - } - return "" -} - -func (x *DenialSummary) GetFirstSeenMs() int64 { - if x != nil { - return x.FirstSeenMs - } - return 0 -} - -func (x *DenialSummary) GetLastSeenMs() int64 { - if x != nil { - return x.LastSeenMs - } - return 0 -} - -func (x *DenialSummary) GetCount() uint32 { - if x != nil { - return x.Count - } - return 0 -} - -func (x *DenialSummary) GetSuppressedCount() uint32 { - if x != nil { - return x.SuppressedCount - } - return 0 -} - -func (x *DenialSummary) GetTotalCount() uint32 { - if x != nil { - return x.TotalCount - } - return 0 -} - -func (x *DenialSummary) GetSampleCmdlines() []string { - if x != nil { - return x.SampleCmdlines - } - return nil -} - -func (x *DenialSummary) GetBinarySha256() string { - if x != nil { - return x.BinarySha256 - } - return "" -} - -func (x *DenialSummary) GetPersistent() bool { - if x != nil { - return x.Persistent - } - return false -} - -func (x *DenialSummary) GetDenialStage() string { - if x != nil { - return x.DenialStage - } - return "" -} - -func (x *DenialSummary) GetL7RequestSamples() []*L7RequestSample { - if x != nil { - return x.L7RequestSamples - } - return nil -} - -func (x *DenialSummary) GetL7InspectionActive() bool { - if x != nil { - return x.L7InspectionActive - } - return false -} - -// A proposed policy rule with rationale and approval status. -type PolicyChunk struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Unique chunk identifier. - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - // Approval status: "pending", "approved", "rejected". - Status string `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` - // Proposed network_policies map key. - RuleName string `protobuf:"bytes,3,opt,name=rule_name,json=ruleName,proto3" json:"rule_name,omitempty"` - // The proposed network policy rule. - ProposedRule *sandboxv1.NetworkPolicyRule `protobuf:"bytes,4,opt,name=proposed_rule,json=proposedRule,proto3" json:"proposed_rule,omitempty"` - // Human-readable explanation of why this rule is proposed. - Rationale string `protobuf:"bytes,5,opt,name=rationale,proto3" json:"rationale,omitempty"` - // Security concerns flagged by analysis (empty if none). - SecurityNotes string `protobuf:"bytes,6,opt,name=security_notes,json=securityNotes,proto3" json:"security_notes,omitempty"` - // Analysis confidence (0.0-1.0). 0 for mechanistic mode. - Confidence float32 `protobuf:"fixed32,7,opt,name=confidence,proto3" json:"confidence,omitempty"` - // IDs of denial summaries that led to this chunk. - DenialSummaryIds []string `protobuf:"bytes,8,rep,name=denial_summary_ids,json=denialSummaryIds,proto3" json:"denial_summary_ids,omitempty"` - // Creation timestamp (ms since epoch). - CreatedAtMs int64 `protobuf:"varint,9,opt,name=created_at_ms,json=createdAtMs,proto3" json:"created_at_ms,omitempty"` - // When the user approved/rejected (ms since epoch). 0 if undecided. - DecidedAtMs int64 `protobuf:"varint,10,opt,name=decided_at_ms,json=decidedAtMs,proto3" json:"decided_at_ms,omitempty"` - // Recommendation stage: "initial" or "refined" (progressive L7 visibility). - Stage string `protobuf:"bytes,11,opt,name=stage,proto3" json:"stage,omitempty"` - // For stage="refined": the initial chunk this replaces. - SupersedesChunkId string `protobuf:"bytes,12,opt,name=supersedes_chunk_id,json=supersedesChunkId,proto3" json:"supersedes_chunk_id,omitempty"` - // How many times this endpoint has been seen across denial flush cycles. - HitCount int32 `protobuf:"varint,13,opt,name=hit_count,json=hitCount,proto3" json:"hit_count,omitempty"` - // First time this endpoint was proposed (ms since epoch). - FirstSeenMs int64 `protobuf:"varint,14,opt,name=first_seen_ms,json=firstSeenMs,proto3" json:"first_seen_ms,omitempty"` - // Most recent time this endpoint was re-proposed (ms since epoch). - LastSeenMs int64 `protobuf:"varint,15,opt,name=last_seen_ms,json=lastSeenMs,proto3" json:"last_seen_ms,omitempty"` - // Binary path that triggered the denial (denormalized for display convenience). - Binary string `protobuf:"bytes,16,opt,name=binary,proto3" json:"binary,omitempty"` - // Validation verdict from gateway-side static checks (prover output). - // Free-form summary string for human consumption in the inbox card. - // Empty until the prover has run for this chunk. - ValidationResult string `protobuf:"bytes,17,opt,name=validation_result,json=validationResult,proto3" json:"validation_result,omitempty"` - // Operator-supplied free-form text accompanying a rejection. Populated - // when the reviewer rejects via `RejectDraftChunkRequest.reason`; surfaced - // back to the in-sandbox agent so it can revise the proposal. - // Empty for non-rejected chunks. - RejectionReason string `protobuf:"bytes,18,opt,name=rejection_reason,json=rejectionReason,proto3" json:"rejection_reason,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PolicyChunk) Reset() { - *x = PolicyChunk{} - mi := &file_openshell_proto_msgTypes[108] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PolicyChunk) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PolicyChunk) ProtoMessage() {} - -func (x *PolicyChunk) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[108] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PolicyChunk.ProtoReflect.Descriptor instead. -func (*PolicyChunk) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{108} -} - -func (x *PolicyChunk) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *PolicyChunk) GetStatus() string { - if x != nil { - return x.Status - } - return "" -} - -func (x *PolicyChunk) GetRuleName() string { - if x != nil { - return x.RuleName - } - return "" -} - -func (x *PolicyChunk) GetProposedRule() *sandboxv1.NetworkPolicyRule { - if x != nil { - return x.ProposedRule - } - return nil -} - -func (x *PolicyChunk) GetRationale() string { - if x != nil { - return x.Rationale - } - return "" -} - -func (x *PolicyChunk) GetSecurityNotes() string { - if x != nil { - return x.SecurityNotes - } - return "" -} - -func (x *PolicyChunk) GetConfidence() float32 { - if x != nil { - return x.Confidence - } - return 0 -} - -func (x *PolicyChunk) GetDenialSummaryIds() []string { - if x != nil { - return x.DenialSummaryIds - } - return nil -} - -func (x *PolicyChunk) GetCreatedAtMs() int64 { - if x != nil { - return x.CreatedAtMs - } - return 0 -} - -func (x *PolicyChunk) GetDecidedAtMs() int64 { - if x != nil { - return x.DecidedAtMs - } - return 0 -} - -func (x *PolicyChunk) GetStage() string { - if x != nil { - return x.Stage - } - return "" -} - -func (x *PolicyChunk) GetSupersedesChunkId() string { - if x != nil { - return x.SupersedesChunkId - } - return "" -} - -func (x *PolicyChunk) GetHitCount() int32 { - if x != nil { - return x.HitCount - } - return 0 -} - -func (x *PolicyChunk) GetFirstSeenMs() int64 { - if x != nil { - return x.FirstSeenMs - } - return 0 -} - -func (x *PolicyChunk) GetLastSeenMs() int64 { - if x != nil { - return x.LastSeenMs - } - return 0 -} - -func (x *PolicyChunk) GetBinary() string { - if x != nil { - return x.Binary - } - return "" -} - -func (x *PolicyChunk) GetValidationResult() string { - if x != nil { - return x.ValidationResult - } - return "" -} - -func (x *PolicyChunk) GetRejectionReason() string { - if x != nil { - return x.RejectionReason - } - return "" -} - -// Notification that the draft policy was updated. -type DraftPolicyUpdate struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Current draft version. - DraftVersion uint64 `protobuf:"varint,1,opt,name=draft_version,json=draftVersion,proto3" json:"draft_version,omitempty"` - // Number of new chunks added in this update. - NewChunks uint32 `protobuf:"varint,2,opt,name=new_chunks,json=newChunks,proto3" json:"new_chunks,omitempty"` - // Total pending chunks awaiting approval. - TotalPending uint32 `protobuf:"varint,3,opt,name=total_pending,json=totalPending,proto3" json:"total_pending,omitempty"` - // Brief description of what changed. - Summary string `protobuf:"bytes,4,opt,name=summary,proto3" json:"summary,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DraftPolicyUpdate) Reset() { - *x = DraftPolicyUpdate{} - mi := &file_openshell_proto_msgTypes[109] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DraftPolicyUpdate) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DraftPolicyUpdate) ProtoMessage() {} - -func (x *DraftPolicyUpdate) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[109] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DraftPolicyUpdate.ProtoReflect.Descriptor instead. -func (*DraftPolicyUpdate) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{109} -} - -func (x *DraftPolicyUpdate) GetDraftVersion() uint64 { - if x != nil { - return x.DraftVersion - } - return 0 -} - -func (x *DraftPolicyUpdate) GetNewChunks() uint32 { - if x != nil { - return x.NewChunks - } - return 0 -} - -func (x *DraftPolicyUpdate) GetTotalPending() uint32 { - if x != nil { - return x.TotalPending - } - return 0 -} - -func (x *DraftPolicyUpdate) GetSummary() string { - if x != nil { - return x.Summary - } - return "" -} - -// Submit analysis results from sandbox to gateway. -type SubmitPolicyAnalysisRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Aggregated denial summaries. - Summaries []*DenialSummary `protobuf:"bytes,1,rep,name=summaries,proto3" json:"summaries,omitempty"` - // Proposed policy chunks (validated by sandbox OPA engine). - ProposedChunks []*PolicyChunk `protobuf:"bytes,2,rep,name=proposed_chunks,json=proposedChunks,proto3" json:"proposed_chunks,omitempty"` - // Analysis mode. `mechanistic` is the observation-driven path from the - // denial aggregator — chunks targeting the same host|port|binary fold - // into one row with hit_count incremented. `agent_authored` is an - // intentional proposal from an in-sandbox agent — each submission lands - // as its own chunk so the redraft-after-rejection loop has a stable id - // to watch. Other values are treated as agent-style (no dedup) so a new - // mode does not silently collapse proposals. - AnalysisMode string `protobuf:"bytes,3,opt,name=analysis_mode,json=analysisMode,proto3" json:"analysis_mode,omitempty"` - // Sandbox name. - Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SubmitPolicyAnalysisRequest) Reset() { - *x = SubmitPolicyAnalysisRequest{} - mi := &file_openshell_proto_msgTypes[110] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SubmitPolicyAnalysisRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SubmitPolicyAnalysisRequest) ProtoMessage() {} - -func (x *SubmitPolicyAnalysisRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[110] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SubmitPolicyAnalysisRequest.ProtoReflect.Descriptor instead. -func (*SubmitPolicyAnalysisRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{110} -} - -func (x *SubmitPolicyAnalysisRequest) GetSummaries() []*DenialSummary { - if x != nil { - return x.Summaries - } - return nil -} - -func (x *SubmitPolicyAnalysisRequest) GetProposedChunks() []*PolicyChunk { - if x != nil { - return x.ProposedChunks - } - return nil -} - -func (x *SubmitPolicyAnalysisRequest) GetAnalysisMode() string { - if x != nil { - return x.AnalysisMode - } - return "" -} - -func (x *SubmitPolicyAnalysisRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -type SubmitPolicyAnalysisResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Number of chunks accepted by the gateway. - AcceptedChunks uint32 `protobuf:"varint,1,opt,name=accepted_chunks,json=acceptedChunks,proto3" json:"accepted_chunks,omitempty"` - // Number of chunks rejected by gateway validation. - RejectedChunks uint32 `protobuf:"varint,2,opt,name=rejected_chunks,json=rejectedChunks,proto3" json:"rejected_chunks,omitempty"` - // Reasons for each rejected chunk. - RejectionReasons []string `protobuf:"bytes,3,rep,name=rejection_reasons,json=rejectionReasons,proto3" json:"rejection_reasons,omitempty"` - // Server-assigned chunk IDs for the accepted chunks, in submission order. - // Agents use these to watch proposal state via policy.local's - // GET /v1/proposals/{id} and /wait endpoints. - AcceptedChunkIds []string `protobuf:"bytes,4,rep,name=accepted_chunk_ids,json=acceptedChunkIds,proto3" json:"accepted_chunk_ids,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SubmitPolicyAnalysisResponse) Reset() { - *x = SubmitPolicyAnalysisResponse{} - mi := &file_openshell_proto_msgTypes[111] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SubmitPolicyAnalysisResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SubmitPolicyAnalysisResponse) ProtoMessage() {} - -func (x *SubmitPolicyAnalysisResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[111] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SubmitPolicyAnalysisResponse.ProtoReflect.Descriptor instead. -func (*SubmitPolicyAnalysisResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{111} -} - -func (x *SubmitPolicyAnalysisResponse) GetAcceptedChunks() uint32 { - if x != nil { - return x.AcceptedChunks - } - return 0 -} - -func (x *SubmitPolicyAnalysisResponse) GetRejectedChunks() uint32 { - if x != nil { - return x.RejectedChunks - } - return 0 -} - -func (x *SubmitPolicyAnalysisResponse) GetRejectionReasons() []string { - if x != nil { - return x.RejectionReasons - } - return nil -} - -func (x *SubmitPolicyAnalysisResponse) GetAcceptedChunkIds() []string { - if x != nil { - return x.AcceptedChunkIds - } - return nil -} - -// Get draft policy for a sandbox. -type GetDraftPolicyRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox name. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Optional status filter: "pending", "approved", "rejected", or "" for all. - StatusFilter string `protobuf:"bytes,2,opt,name=status_filter,json=statusFilter,proto3" json:"status_filter,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetDraftPolicyRequest) Reset() { - *x = GetDraftPolicyRequest{} - mi := &file_openshell_proto_msgTypes[112] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetDraftPolicyRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetDraftPolicyRequest) ProtoMessage() {} - -func (x *GetDraftPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[112] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetDraftPolicyRequest.ProtoReflect.Descriptor instead. -func (*GetDraftPolicyRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{112} -} - -func (x *GetDraftPolicyRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *GetDraftPolicyRequest) GetStatusFilter() string { - if x != nil { - return x.StatusFilter - } - return "" -} - -type GetDraftPolicyResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Draft policy chunks. - Chunks []*PolicyChunk `protobuf:"bytes,1,rep,name=chunks,proto3" json:"chunks,omitempty"` - // LLM-generated summary of all analysis (empty in mechanistic mode). - RollingSummary string `protobuf:"bytes,2,opt,name=rolling_summary,json=rollingSummary,proto3" json:"rolling_summary,omitempty"` - // Current draft version. - DraftVersion uint64 `protobuf:"varint,3,opt,name=draft_version,json=draftVersion,proto3" json:"draft_version,omitempty"` - // When the last analysis completed (ms since epoch). - LastAnalyzedAtMs int64 `protobuf:"varint,4,opt,name=last_analyzed_at_ms,json=lastAnalyzedAtMs,proto3" json:"last_analyzed_at_ms,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetDraftPolicyResponse) Reset() { - *x = GetDraftPolicyResponse{} - mi := &file_openshell_proto_msgTypes[113] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetDraftPolicyResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetDraftPolicyResponse) ProtoMessage() {} - -func (x *GetDraftPolicyResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[113] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetDraftPolicyResponse.ProtoReflect.Descriptor instead. -func (*GetDraftPolicyResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{113} -} - -func (x *GetDraftPolicyResponse) GetChunks() []*PolicyChunk { - if x != nil { - return x.Chunks - } - return nil -} - -func (x *GetDraftPolicyResponse) GetRollingSummary() string { - if x != nil { - return x.RollingSummary - } - return "" -} - -func (x *GetDraftPolicyResponse) GetDraftVersion() uint64 { - if x != nil { - return x.DraftVersion - } - return 0 -} - -func (x *GetDraftPolicyResponse) GetLastAnalyzedAtMs() int64 { - if x != nil { - return x.LastAnalyzedAtMs - } - return 0 -} - -// Approve a single draft chunk. -type ApproveDraftChunkRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox name. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Chunk ID to approve. - ChunkId string `protobuf:"bytes,2,opt,name=chunk_id,json=chunkId,proto3" json:"chunk_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ApproveDraftChunkRequest) Reset() { - *x = ApproveDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[114] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ApproveDraftChunkRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ApproveDraftChunkRequest) ProtoMessage() {} - -func (x *ApproveDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[114] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ApproveDraftChunkRequest.ProtoReflect.Descriptor instead. -func (*ApproveDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{114} -} - -func (x *ApproveDraftChunkRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ApproveDraftChunkRequest) GetChunkId() string { - if x != nil { - return x.ChunkId - } - return "" -} - -type ApproveDraftChunkResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // New policy version after merge. - PolicyVersion uint32 `protobuf:"varint,1,opt,name=policy_version,json=policyVersion,proto3" json:"policy_version,omitempty"` - // SHA-256 hash of the new policy. - PolicyHash string `protobuf:"bytes,2,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ApproveDraftChunkResponse) Reset() { - *x = ApproveDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[115] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ApproveDraftChunkResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ApproveDraftChunkResponse) ProtoMessage() {} - -func (x *ApproveDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[115] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ApproveDraftChunkResponse.ProtoReflect.Descriptor instead. -func (*ApproveDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{115} -} - -func (x *ApproveDraftChunkResponse) GetPolicyVersion() uint32 { - if x != nil { - return x.PolicyVersion - } - return 0 -} - -func (x *ApproveDraftChunkResponse) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -// Reject a single draft chunk. -type RejectDraftChunkRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox name. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Chunk ID to reject. - ChunkId string `protobuf:"bytes,2,opt,name=chunk_id,json=chunkId,proto3" json:"chunk_id,omitempty"` - // Optional reason for rejection (fed to LLM context in future analysis). - Reason string `protobuf:"bytes,3,opt,name=reason,proto3" json:"reason,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RejectDraftChunkRequest) Reset() { - *x = RejectDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[116] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RejectDraftChunkRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RejectDraftChunkRequest) ProtoMessage() {} - -func (x *RejectDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[116] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RejectDraftChunkRequest.ProtoReflect.Descriptor instead. -func (*RejectDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{116} -} - -func (x *RejectDraftChunkRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *RejectDraftChunkRequest) GetChunkId() string { - if x != nil { - return x.ChunkId - } - return "" -} - -func (x *RejectDraftChunkRequest) GetReason() string { - if x != nil { - return x.Reason - } - return "" -} - -type RejectDraftChunkResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *RejectDraftChunkResponse) Reset() { - *x = RejectDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[117] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *RejectDraftChunkResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RejectDraftChunkResponse) ProtoMessage() {} - -func (x *RejectDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[117] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use RejectDraftChunkResponse.ProtoReflect.Descriptor instead. -func (*RejectDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{117} -} - -// Approve all pending chunks. -type ApproveAllDraftChunksRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox name. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Include chunks with security_notes (default false: skips them). - IncludeSecurityFlagged bool `protobuf:"varint,2,opt,name=include_security_flagged,json=includeSecurityFlagged,proto3" json:"include_security_flagged,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ApproveAllDraftChunksRequest) Reset() { - *x = ApproveAllDraftChunksRequest{} - mi := &file_openshell_proto_msgTypes[118] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ApproveAllDraftChunksRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ApproveAllDraftChunksRequest) ProtoMessage() {} - -func (x *ApproveAllDraftChunksRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[118] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ApproveAllDraftChunksRequest.ProtoReflect.Descriptor instead. -func (*ApproveAllDraftChunksRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{118} -} - -func (x *ApproveAllDraftChunksRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ApproveAllDraftChunksRequest) GetIncludeSecurityFlagged() bool { - if x != nil { - return x.IncludeSecurityFlagged - } - return false -} - -type ApproveAllDraftChunksResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // New policy version after merge. - PolicyVersion uint32 `protobuf:"varint,1,opt,name=policy_version,json=policyVersion,proto3" json:"policy_version,omitempty"` - // SHA-256 hash of the new policy. - PolicyHash string `protobuf:"bytes,2,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - // Number of chunks approved. - ChunksApproved uint32 `protobuf:"varint,3,opt,name=chunks_approved,json=chunksApproved,proto3" json:"chunks_approved,omitempty"` - // Number of chunks skipped (security-flagged). - ChunksSkipped uint32 `protobuf:"varint,4,opt,name=chunks_skipped,json=chunksSkipped,proto3" json:"chunks_skipped,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ApproveAllDraftChunksResponse) Reset() { - *x = ApproveAllDraftChunksResponse{} - mi := &file_openshell_proto_msgTypes[119] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ApproveAllDraftChunksResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ApproveAllDraftChunksResponse) ProtoMessage() {} - -func (x *ApproveAllDraftChunksResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[119] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ApproveAllDraftChunksResponse.ProtoReflect.Descriptor instead. -func (*ApproveAllDraftChunksResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{119} -} - -func (x *ApproveAllDraftChunksResponse) GetPolicyVersion() uint32 { - if x != nil { - return x.PolicyVersion - } - return 0 -} - -func (x *ApproveAllDraftChunksResponse) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -func (x *ApproveAllDraftChunksResponse) GetChunksApproved() uint32 { - if x != nil { - return x.ChunksApproved - } - return 0 -} - -func (x *ApproveAllDraftChunksResponse) GetChunksSkipped() uint32 { - if x != nil { - return x.ChunksSkipped - } - return 0 -} - -// Edit a pending chunk in-place. -type EditDraftChunkRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox name. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Chunk ID to edit. - ChunkId string `protobuf:"bytes,2,opt,name=chunk_id,json=chunkId,proto3" json:"chunk_id,omitempty"` - // The modified rule (replaces existing proposed_rule). - ProposedRule *sandboxv1.NetworkPolicyRule `protobuf:"bytes,3,opt,name=proposed_rule,json=proposedRule,proto3" json:"proposed_rule,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *EditDraftChunkRequest) Reset() { - *x = EditDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[120] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *EditDraftChunkRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*EditDraftChunkRequest) ProtoMessage() {} - -func (x *EditDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[120] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use EditDraftChunkRequest.ProtoReflect.Descriptor instead. -func (*EditDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{120} -} - -func (x *EditDraftChunkRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *EditDraftChunkRequest) GetChunkId() string { - if x != nil { - return x.ChunkId - } - return "" -} - -func (x *EditDraftChunkRequest) GetProposedRule() *sandboxv1.NetworkPolicyRule { - if x != nil { - return x.ProposedRule - } - return nil -} - -type EditDraftChunkResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *EditDraftChunkResponse) Reset() { - *x = EditDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[121] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *EditDraftChunkResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*EditDraftChunkResponse) ProtoMessage() {} - -func (x *EditDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[121] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use EditDraftChunkResponse.ProtoReflect.Descriptor instead. -func (*EditDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{121} -} - -// Reverse an approval (remove merged rule from active policy). -type UndoDraftChunkRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox name. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Chunk ID to undo. - ChunkId string `protobuf:"bytes,2,opt,name=chunk_id,json=chunkId,proto3" json:"chunk_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *UndoDraftChunkRequest) Reset() { - *x = UndoDraftChunkRequest{} - mi := &file_openshell_proto_msgTypes[122] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *UndoDraftChunkRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UndoDraftChunkRequest) ProtoMessage() {} - -func (x *UndoDraftChunkRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[122] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UndoDraftChunkRequest.ProtoReflect.Descriptor instead. -func (*UndoDraftChunkRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{122} -} - -func (x *UndoDraftChunkRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *UndoDraftChunkRequest) GetChunkId() string { - if x != nil { - return x.ChunkId - } - return "" -} - -type UndoDraftChunkResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // New policy version after removal. - PolicyVersion uint32 `protobuf:"varint,1,opt,name=policy_version,json=policyVersion,proto3" json:"policy_version,omitempty"` - // SHA-256 hash of the updated policy. - PolicyHash string `protobuf:"bytes,2,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *UndoDraftChunkResponse) Reset() { - *x = UndoDraftChunkResponse{} - mi := &file_openshell_proto_msgTypes[123] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *UndoDraftChunkResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*UndoDraftChunkResponse) ProtoMessage() {} - -func (x *UndoDraftChunkResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[123] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use UndoDraftChunkResponse.ProtoReflect.Descriptor instead. -func (*UndoDraftChunkResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{123} -} - -func (x *UndoDraftChunkResponse) GetPolicyVersion() uint32 { - if x != nil { - return x.PolicyVersion - } - return 0 -} - -func (x *UndoDraftChunkResponse) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -// Clear all pending draft chunks for a sandbox. -type ClearDraftChunksRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox name. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ClearDraftChunksRequest) Reset() { - *x = ClearDraftChunksRequest{} - mi := &file_openshell_proto_msgTypes[124] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ClearDraftChunksRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ClearDraftChunksRequest) ProtoMessage() {} - -func (x *ClearDraftChunksRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[124] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ClearDraftChunksRequest.ProtoReflect.Descriptor instead. -func (*ClearDraftChunksRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{124} -} - -func (x *ClearDraftChunksRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -type ClearDraftChunksResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Number of chunks cleared. - ChunksCleared uint32 `protobuf:"varint,1,opt,name=chunks_cleared,json=chunksCleared,proto3" json:"chunks_cleared,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ClearDraftChunksResponse) Reset() { - *x = ClearDraftChunksResponse{} - mi := &file_openshell_proto_msgTypes[125] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ClearDraftChunksResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ClearDraftChunksResponse) ProtoMessage() {} - -func (x *ClearDraftChunksResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[125] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ClearDraftChunksResponse.ProtoReflect.Descriptor instead. -func (*ClearDraftChunksResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{125} -} - -func (x *ClearDraftChunksResponse) GetChunksCleared() uint32 { - if x != nil { - return x.ChunksCleared - } - return 0 -} - -// Get decision history for a sandbox's draft policy. -type GetDraftHistoryRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Sandbox name. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetDraftHistoryRequest) Reset() { - *x = GetDraftHistoryRequest{} - mi := &file_openshell_proto_msgTypes[126] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetDraftHistoryRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetDraftHistoryRequest) ProtoMessage() {} - -func (x *GetDraftHistoryRequest) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[126] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetDraftHistoryRequest.ProtoReflect.Descriptor instead. -func (*GetDraftHistoryRequest) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{126} -} - -func (x *GetDraftHistoryRequest) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -type DraftHistoryEntry struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Event timestamp (ms since epoch). - TimestampMs int64 `protobuf:"varint,1,opt,name=timestamp_ms,json=timestampMs,proto3" json:"timestamp_ms,omitempty"` - // Event type: "denial_detected", "analysis_cycle", "approved", - // "rejected", "edited", "undone", "cleared". - EventType string `protobuf:"bytes,2,opt,name=event_type,json=eventType,proto3" json:"event_type,omitempty"` - // Human-readable description. - Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"` - // Associated chunk ID (if applicable). - ChunkId string `protobuf:"bytes,4,opt,name=chunk_id,json=chunkId,proto3" json:"chunk_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DraftHistoryEntry) Reset() { - *x = DraftHistoryEntry{} - mi := &file_openshell_proto_msgTypes[127] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DraftHistoryEntry) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DraftHistoryEntry) ProtoMessage() {} - -func (x *DraftHistoryEntry) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[127] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DraftHistoryEntry.ProtoReflect.Descriptor instead. -func (*DraftHistoryEntry) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{127} -} - -func (x *DraftHistoryEntry) GetTimestampMs() int64 { - if x != nil { - return x.TimestampMs - } - return 0 -} - -func (x *DraftHistoryEntry) GetEventType() string { - if x != nil { - return x.EventType - } - return "" -} - -func (x *DraftHistoryEntry) GetDescription() string { - if x != nil { - return x.Description - } - return "" -} - -func (x *DraftHistoryEntry) GetChunkId() string { - if x != nil { - return x.ChunkId - } - return "" -} - -type GetDraftHistoryResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Chronological decision history. - Entries []*DraftHistoryEntry `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetDraftHistoryResponse) Reset() { - *x = GetDraftHistoryResponse{} - mi := &file_openshell_proto_msgTypes[128] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetDraftHistoryResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetDraftHistoryResponse) ProtoMessage() {} - -func (x *GetDraftHistoryResponse) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[128] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetDraftHistoryResponse.ProtoReflect.Descriptor instead. -func (*GetDraftHistoryResponse) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{128} -} - -func (x *GetDraftHistoryResponse) GetEntries() []*DraftHistoryEntry { - if x != nil { - return x.Entries - } - return nil -} - -// Stored payload for a policy revision row in the generic objects table. -type PolicyRevisionPayload struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Serialized policy contents. - Policy *sandboxv1.SandboxPolicy `protobuf:"bytes,1,opt,name=policy,proto3" json:"policy,omitempty"` - // Deterministic hash of the policy payload. - Hash string `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"` - // Load error reported by the sandbox, if any. - LoadError string `protobuf:"bytes,3,opt,name=load_error,json=loadError,proto3" json:"load_error,omitempty"` - // When the policy version was reported as loaded (ms since epoch). 0 if unset. - LoadedAtMs int64 `protobuf:"varint,4,opt,name=loaded_at_ms,json=loadedAtMs,proto3" json:"loaded_at_ms,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *PolicyRevisionPayload) Reset() { - *x = PolicyRevisionPayload{} - mi := &file_openshell_proto_msgTypes[129] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *PolicyRevisionPayload) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*PolicyRevisionPayload) ProtoMessage() {} - -func (x *PolicyRevisionPayload) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[129] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use PolicyRevisionPayload.ProtoReflect.Descriptor instead. -func (*PolicyRevisionPayload) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{129} -} - -func (x *PolicyRevisionPayload) GetPolicy() *sandboxv1.SandboxPolicy { - if x != nil { - return x.Policy - } - return nil -} - -func (x *PolicyRevisionPayload) GetHash() string { - if x != nil { - return x.Hash - } - return "" -} - -func (x *PolicyRevisionPayload) GetLoadError() string { - if x != nil { - return x.LoadError - } - return "" -} - -func (x *PolicyRevisionPayload) GetLoadedAtMs() int64 { - if x != nil { - return x.LoadedAtMs - } - return 0 -} - -// Stored payload for a draft policy chunk row in the generic objects table. -type DraftChunkPayload struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Proposed network_policies map key. - RuleName string `protobuf:"bytes,1,opt,name=rule_name,json=ruleName,proto3" json:"rule_name,omitempty"` - // Proposed network policy rule. - ProposedRule *sandboxv1.NetworkPolicyRule `protobuf:"bytes,2,opt,name=proposed_rule,json=proposedRule,proto3" json:"proposed_rule,omitempty"` - // Human-readable explanation of why this rule is proposed. - Rationale string `protobuf:"bytes,3,opt,name=rationale,proto3" json:"rationale,omitempty"` - // Security concerns flagged by analysis (empty if none). - SecurityNotes string `protobuf:"bytes,4,opt,name=security_notes,json=securityNotes,proto3" json:"security_notes,omitempty"` - // Analysis confidence (0.0-1.0). 0 for mechanistic mode. - Confidence float32 `protobuf:"fixed32,5,opt,name=confidence,proto3" json:"confidence,omitempty"` - // When the user approved/rejected (ms since epoch). 0 if undecided. - DecidedAtMs int64 `protobuf:"varint,6,opt,name=decided_at_ms,json=decidedAtMs,proto3" json:"decided_at_ms,omitempty"` - // Denormalized endpoint host for dedup and display. - Host string `protobuf:"bytes,7,opt,name=host,proto3" json:"host,omitempty"` - // Denormalized endpoint port for dedup and display. - Port int32 `protobuf:"varint,8,opt,name=port,proto3" json:"port,omitempty"` - // Binary path that triggered the denial. - Binary string `protobuf:"bytes,9,opt,name=binary,proto3" json:"binary,omitempty"` - // Current draft version for the owning sandbox. - DraftVersion int64 `protobuf:"varint,10,opt,name=draft_version,json=draftVersion,proto3" json:"draft_version,omitempty"` - // Gateway prover verdict for this chunk; empty until prover runs. - // Mirrors PolicyChunk.validation_result. - ValidationResult string `protobuf:"bytes,11,opt,name=validation_result,json=validationResult,proto3" json:"validation_result,omitempty"` - // Operator-supplied free-form rejection text; empty for non-rejected - // chunks. Mirrors PolicyChunk.rejection_reason. - RejectionReason string `protobuf:"bytes,12,opt,name=rejection_reason,json=rejectionReason,proto3" json:"rejection_reason,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *DraftChunkPayload) Reset() { - *x = DraftChunkPayload{} - mi := &file_openshell_proto_msgTypes[130] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *DraftChunkPayload) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DraftChunkPayload) ProtoMessage() {} - -func (x *DraftChunkPayload) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[130] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DraftChunkPayload.ProtoReflect.Descriptor instead. -func (*DraftChunkPayload) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{130} -} - -func (x *DraftChunkPayload) GetRuleName() string { - if x != nil { - return x.RuleName - } - return "" -} - -func (x *DraftChunkPayload) GetProposedRule() *sandboxv1.NetworkPolicyRule { - if x != nil { - return x.ProposedRule - } - return nil -} - -func (x *DraftChunkPayload) GetRationale() string { - if x != nil { - return x.Rationale - } - return "" -} - -func (x *DraftChunkPayload) GetSecurityNotes() string { - if x != nil { - return x.SecurityNotes - } - return "" -} - -func (x *DraftChunkPayload) GetConfidence() float32 { - if x != nil { - return x.Confidence - } - return 0 -} - -func (x *DraftChunkPayload) GetDecidedAtMs() int64 { - if x != nil { - return x.DecidedAtMs - } - return 0 -} - -func (x *DraftChunkPayload) GetHost() string { - if x != nil { - return x.Host - } - return "" -} - -func (x *DraftChunkPayload) GetPort() int32 { - if x != nil { - return x.Port - } - return 0 -} - -func (x *DraftChunkPayload) GetBinary() string { - if x != nil { - return x.Binary - } - return "" -} - -func (x *DraftChunkPayload) GetDraftVersion() int64 { - if x != nil { - return x.DraftVersion - } - return 0 -} - -func (x *DraftChunkPayload) GetValidationResult() string { - if x != nil { - return x.ValidationResult - } - return "" -} - -func (x *DraftChunkPayload) GetRejectionReason() string { - if x != nil { - return x.RejectionReason - } - return "" -} - -// Internal stored policy revision row materialized from the generic objects table. -type StoredPolicyRevision struct { - state protoimpl.MessageState `protogen:"open.v1"` - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - SandboxId string `protobuf:"bytes,2,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - Version int64 `protobuf:"varint,3,opt,name=version,proto3" json:"version,omitempty"` - PolicyPayload []byte `protobuf:"bytes,4,opt,name=policy_payload,json=policyPayload,proto3" json:"policy_payload,omitempty"` - PolicyHash string `protobuf:"bytes,5,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - Status string `protobuf:"bytes,6,opt,name=status,proto3" json:"status,omitempty"` - LoadError *string `protobuf:"bytes,7,opt,name=load_error,json=loadError,proto3,oneof" json:"load_error,omitempty"` - CreatedAtMs int64 `protobuf:"varint,8,opt,name=created_at_ms,json=createdAtMs,proto3" json:"created_at_ms,omitempty"` - LoadedAtMs *int64 `protobuf:"varint,9,opt,name=loaded_at_ms,json=loadedAtMs,proto3,oneof" json:"loaded_at_ms,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *StoredPolicyRevision) Reset() { - *x = StoredPolicyRevision{} - mi := &file_openshell_proto_msgTypes[131] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *StoredPolicyRevision) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StoredPolicyRevision) ProtoMessage() {} - -func (x *StoredPolicyRevision) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[131] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use StoredPolicyRevision.ProtoReflect.Descriptor instead. -func (*StoredPolicyRevision) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{131} -} - -func (x *StoredPolicyRevision) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *StoredPolicyRevision) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *StoredPolicyRevision) GetVersion() int64 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *StoredPolicyRevision) GetPolicyPayload() []byte { - if x != nil { - return x.PolicyPayload - } - return nil -} - -func (x *StoredPolicyRevision) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -func (x *StoredPolicyRevision) GetStatus() string { - if x != nil { - return x.Status - } - return "" -} - -func (x *StoredPolicyRevision) GetLoadError() string { - if x != nil && x.LoadError != nil { - return *x.LoadError - } - return "" -} - -func (x *StoredPolicyRevision) GetCreatedAtMs() int64 { - if x != nil { - return x.CreatedAtMs - } - return 0 -} - -func (x *StoredPolicyRevision) GetLoadedAtMs() int64 { - if x != nil && x.LoadedAtMs != nil { - return *x.LoadedAtMs - } - return 0 -} - -// Internal stored draft chunk row materialized from the generic objects table. -type StoredDraftChunk struct { - state protoimpl.MessageState `protogen:"open.v1"` - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - SandboxId string `protobuf:"bytes,2,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - DraftVersion int64 `protobuf:"varint,3,opt,name=draft_version,json=draftVersion,proto3" json:"draft_version,omitempty"` - Status string `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"` - RuleName string `protobuf:"bytes,5,opt,name=rule_name,json=ruleName,proto3" json:"rule_name,omitempty"` - ProposedRule []byte `protobuf:"bytes,6,opt,name=proposed_rule,json=proposedRule,proto3" json:"proposed_rule,omitempty"` - Rationale string `protobuf:"bytes,7,opt,name=rationale,proto3" json:"rationale,omitempty"` - SecurityNotes string `protobuf:"bytes,8,opt,name=security_notes,json=securityNotes,proto3" json:"security_notes,omitempty"` - Confidence float64 `protobuf:"fixed64,9,opt,name=confidence,proto3" json:"confidence,omitempty"` - CreatedAtMs int64 `protobuf:"varint,10,opt,name=created_at_ms,json=createdAtMs,proto3" json:"created_at_ms,omitempty"` - DecidedAtMs *int64 `protobuf:"varint,11,opt,name=decided_at_ms,json=decidedAtMs,proto3,oneof" json:"decided_at_ms,omitempty"` - Host string `protobuf:"bytes,12,opt,name=host,proto3" json:"host,omitempty"` - Port int32 `protobuf:"varint,13,opt,name=port,proto3" json:"port,omitempty"` - Binary string `protobuf:"bytes,14,opt,name=binary,proto3" json:"binary,omitempty"` - HitCount int32 `protobuf:"varint,15,opt,name=hit_count,json=hitCount,proto3" json:"hit_count,omitempty"` - FirstSeenMs int64 `protobuf:"varint,16,opt,name=first_seen_ms,json=firstSeenMs,proto3" json:"first_seen_ms,omitempty"` - LastSeenMs int64 `protobuf:"varint,17,opt,name=last_seen_ms,json=lastSeenMs,proto3" json:"last_seen_ms,omitempty"` - // Gateway prover verdict; empty until the prover runs. See PolicyChunk. - ValidationResult string `protobuf:"bytes,18,opt,name=validation_result,json=validationResult,proto3" json:"validation_result,omitempty"` - // Operator-supplied free-form rejection text. See PolicyChunk. - RejectionReason string `protobuf:"bytes,19,opt,name=rejection_reason,json=rejectionReason,proto3" json:"rejection_reason,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *StoredDraftChunk) Reset() { - *x = StoredDraftChunk{} - mi := &file_openshell_proto_msgTypes[132] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *StoredDraftChunk) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*StoredDraftChunk) ProtoMessage() {} - -func (x *StoredDraftChunk) ProtoReflect() protoreflect.Message { - mi := &file_openshell_proto_msgTypes[132] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use StoredDraftChunk.ProtoReflect.Descriptor instead. -func (*StoredDraftChunk) Descriptor() ([]byte, []int) { - return file_openshell_proto_rawDescGZIP(), []int{132} -} - -func (x *StoredDraftChunk) GetId() string { - if x != nil { - return x.Id - } - return "" -} - -func (x *StoredDraftChunk) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -func (x *StoredDraftChunk) GetDraftVersion() int64 { - if x != nil { - return x.DraftVersion - } - return 0 -} - -func (x *StoredDraftChunk) GetStatus() string { - if x != nil { - return x.Status - } - return "" -} - -func (x *StoredDraftChunk) GetRuleName() string { - if x != nil { - return x.RuleName - } - return "" -} - -func (x *StoredDraftChunk) GetProposedRule() []byte { - if x != nil { - return x.ProposedRule - } - return nil -} - -func (x *StoredDraftChunk) GetRationale() string { - if x != nil { - return x.Rationale - } - return "" -} - -func (x *StoredDraftChunk) GetSecurityNotes() string { - if x != nil { - return x.SecurityNotes - } - return "" -} - -func (x *StoredDraftChunk) GetConfidence() float64 { - if x != nil { - return x.Confidence - } - return 0 -} - -func (x *StoredDraftChunk) GetCreatedAtMs() int64 { - if x != nil { - return x.CreatedAtMs - } - return 0 -} - -func (x *StoredDraftChunk) GetDecidedAtMs() int64 { - if x != nil && x.DecidedAtMs != nil { - return *x.DecidedAtMs - } - return 0 -} - -func (x *StoredDraftChunk) GetHost() string { - if x != nil { - return x.Host - } - return "" -} - -func (x *StoredDraftChunk) GetPort() int32 { - if x != nil { - return x.Port - } - return 0 -} - -func (x *StoredDraftChunk) GetBinary() string { - if x != nil { - return x.Binary - } - return "" -} - -func (x *StoredDraftChunk) GetHitCount() int32 { - if x != nil { - return x.HitCount - } - return 0 -} - -func (x *StoredDraftChunk) GetFirstSeenMs() int64 { - if x != nil { - return x.FirstSeenMs - } - return 0 -} - -func (x *StoredDraftChunk) GetLastSeenMs() int64 { - if x != nil { - return x.LastSeenMs - } - return 0 -} - -func (x *StoredDraftChunk) GetValidationResult() string { - if x != nil { - return x.ValidationResult - } - return "" -} - -func (x *StoredDraftChunk) GetRejectionReason() string { - if x != nil { - return x.RejectionReason - } - return "" -} - -var File_openshell_proto protoreflect.FileDescriptor - -const file_openshell_proto_rawDesc = "" + - "\n" + - "\x0fopenshell.proto\x12\fopenshell.v1\x1a\x0fdatamodel.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\rsandbox.proto\"\x0f\n" + - "\rHealthRequest\"_\n" + - "\x0eHealthResponse\x123\n" + - "\x06status\x18\x01 \x01(\x0e2\x1b.openshell.v1.ServiceStatusR\x06status\x12\x18\n" + - "\aversion\x18\x02 \x01(\tR\aversion\"\x95\x02\n" + - "\aSandbox\x12>\n" + - "\bmetadata\x18\x01 \x01(\v2\".openshell.datamodel.v1.ObjectMetaR\bmetadata\x12-\n" + - "\x04spec\x18\x02 \x01(\v2\x19.openshell.v1.SandboxSpecR\x04spec\x123\n" + - "\x06status\x18\x03 \x01(\v2\x1b.openshell.v1.SandboxStatusR\x06status\x120\n" + - "\x05phase\x18\x04 \x01(\x0e2\x1a.openshell.v1.SandboxPhaseR\x05phase\x124\n" + - "\x16current_policy_version\x18\x05 \x01(\rR\x14currentPolicyVersion\"\xff\x02\n" + - "\vSandboxSpec\x12\x1b\n" + - "\tlog_level\x18\x01 \x01(\tR\blogLevel\x12L\n" + - "\venvironment\x18\x05 \x03(\v2*.openshell.v1.SandboxSpec.EnvironmentEntryR\venvironment\x129\n" + - "\btemplate\x18\x06 \x01(\v2\x1d.openshell.v1.SandboxTemplateR\btemplate\x12;\n" + - "\x06policy\x18\a \x01(\v2#.openshell.sandbox.v1.SandboxPolicyR\x06policy\x12\x1c\n" + - "\tproviders\x18\b \x03(\tR\tproviders\x12\x10\n" + - "\x03gpu\x18\t \x01(\bR\x03gpu\x12\x1d\n" + - "\n" + - "gpu_device\x18\n" + - " \x01(\tR\tgpuDevice\x1a>\n" + - "\x10EnvironmentEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xe2\x05\n" + - "\x0fSandboxTemplate\x12\x14\n" + - "\x05image\x18\x01 \x01(\tR\x05image\x12,\n" + - "\x12runtime_class_name\x18\x02 \x01(\tR\x10runtimeClassName\x12!\n" + - "\fagent_socket\x18\x03 \x01(\tR\vagentSocket\x12A\n" + - "\x06labels\x18\x04 \x03(\v2).openshell.v1.SandboxTemplate.LabelsEntryR\x06labels\x12P\n" + - "\vannotations\x18\x05 \x03(\v2..openshell.v1.SandboxTemplate.AnnotationsEntryR\vannotations\x12P\n" + - "\venvironment\x18\x06 \x03(\v2..openshell.v1.SandboxTemplate.EnvironmentEntryR\venvironment\x125\n" + - "\tresources\x18\a \x01(\v2\x17.google.protobuf.StructR\tresources\x12M\n" + - "\x16volume_claim_templates\x18\t \x01(\v2\x17.google.protobuf.StructR\x14volumeClaimTemplates\x12,\n" + - "\x0fuser_namespaces\x18\n" + - " \x01(\bH\x00R\x0euserNamespaces\x88\x01\x01\x1a9\n" + - "\vLabelsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a>\n" + - "\x10AnnotationsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a>\n" + - "\x10EnvironmentEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x12\n" + - "\x10_user_namespaces\"\xc9\x01\n" + - "\rSandboxStatus\x12!\n" + - "\fsandbox_name\x18\x01 \x01(\tR\vsandboxName\x12\x1b\n" + - "\tagent_pod\x18\x02 \x01(\tR\bagentPod\x12\x19\n" + - "\bagent_fd\x18\x03 \x01(\tR\aagentFd\x12\x1d\n" + - "\n" + - "sandbox_fd\x18\x04 \x01(\tR\tsandboxFd\x12>\n" + - "\n" + - "conditions\x18\x05 \x03(\v2\x1e.openshell.v1.SandboxConditionR\n" + - "conditions\"\xa2\x01\n" + - "\x10SandboxCondition\x12\x12\n" + - "\x04type\x18\x01 \x01(\tR\x04type\x12\x16\n" + - "\x06status\x18\x02 \x01(\tR\x06status\x12\x16\n" + - "\x06reason\x18\x03 \x01(\tR\x06reason\x12\x18\n" + - "\amessage\x18\x04 \x01(\tR\amessage\x120\n" + - "\x14last_transition_time\x18\x05 \x01(\tR\x12lastTransitionTime\"\x94\x02\n" + - "\rPlatformEvent\x12!\n" + - "\ftimestamp_ms\x18\x01 \x01(\x03R\vtimestampMs\x12\x16\n" + - "\x06source\x18\x02 \x01(\tR\x06source\x12\x12\n" + - "\x04type\x18\x03 \x01(\tR\x04type\x12\x16\n" + - "\x06reason\x18\x04 \x01(\tR\x06reason\x12\x18\n" + - "\amessage\x18\x05 \x01(\tR\amessage\x12E\n" + - "\bmetadata\x18\x06 \x03(\v2).openshell.v1.PlatformEvent.MetadataEntryR\bmetadata\x1a;\n" + - "\rMetadataEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xdc\x01\n" + - "\x14CreateSandboxRequest\x12-\n" + - "\x04spec\x18\x01 \x01(\v2\x19.openshell.v1.SandboxSpecR\x04spec\x12\x12\n" + - "\x04name\x18\x02 \x01(\tR\x04name\x12F\n" + - "\x06labels\x18\x03 \x03(\v2..openshell.v1.CreateSandboxRequest.LabelsEntryR\x06labels\x1a9\n" + - "\vLabelsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"'\n" + - "\x11GetSandboxRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\"k\n" + - "\x14ListSandboxesRequest\x12\x14\n" + - "\x05limit\x18\x01 \x01(\rR\x05limit\x12\x16\n" + - "\x06offset\x18\x02 \x01(\rR\x06offset\x12%\n" + - "\x0elabel_selector\x18\x03 \x01(\tR\rlabelSelector\"@\n" + - "\x1bListSandboxProvidersRequest\x12!\n" + - "\fsandbox_name\x18\x01 \x01(\tR\vsandboxName\"\xa2\x01\n" + - "\x1cAttachSandboxProviderRequest\x12!\n" + - "\fsandbox_name\x18\x01 \x01(\tR\vsandboxName\x12#\n" + - "\rprovider_name\x18\x02 \x01(\tR\fproviderName\x12:\n" + - "\x19expected_resource_version\x18\x03 \x01(\x04R\x17expectedResourceVersion\"\xa2\x01\n" + - "\x1cDetachSandboxProviderRequest\x12!\n" + - "\fsandbox_name\x18\x01 \x01(\tR\vsandboxName\x12#\n" + - "\rprovider_name\x18\x02 \x01(\tR\fproviderName\x12:\n" + - "\x19expected_resource_version\x18\x03 \x01(\x04R\x17expectedResourceVersion\"*\n" + - "\x14DeleteSandboxRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\"B\n" + - "\x0fSandboxResponse\x12/\n" + - "\asandbox\x18\x01 \x01(\v2\x15.openshell.v1.SandboxR\asandbox\"L\n" + - "\x15ListSandboxesResponse\x123\n" + - "\tsandboxes\x18\x01 \x03(\v2\x15.openshell.v1.SandboxR\tsandboxes\"^\n" + - "\x1cListSandboxProvidersResponse\x12>\n" + - "\tproviders\x18\x01 \x03(\v2 .openshell.datamodel.v1.ProviderR\tproviders\"l\n" + - "\x1dAttachSandboxProviderResponse\x12/\n" + - "\asandbox\x18\x01 \x01(\v2\x15.openshell.v1.SandboxR\asandbox\x12\x1a\n" + - "\battached\x18\x02 \x01(\bR\battached\"l\n" + - "\x1dDetachSandboxProviderResponse\x12/\n" + - "\asandbox\x18\x01 \x01(\v2\x15.openshell.v1.SandboxR\asandbox\x12\x1a\n" + - "\bdetached\x18\x02 \x01(\bR\bdetached\"1\n" + - "\x15DeleteSandboxResponse\x12\x18\n" + - "\adeleted\x18\x01 \x01(\bR\adeleted\"8\n" + - "\x17CreateSshSessionRequest\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\"\x92\x02\n" + - "\x18CreateSshSessionResponse\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x14\n" + - "\x05token\x18\x02 \x01(\tR\x05token\x12!\n" + - "\fgateway_host\x18\x03 \x01(\tR\vgatewayHost\x12!\n" + - "\fgateway_port\x18\x04 \x01(\rR\vgatewayPort\x12%\n" + - "\x0egateway_scheme\x18\x05 \x01(\tR\rgatewayScheme\x120\n" + - "\x14host_key_fingerprint\x18\a \x01(\tR\x12hostKeyFingerprint\x12\"\n" + - "\rexpires_at_ms\x18\b \x01(\x03R\vexpiresAtMs\"\x83\x01\n" + - "\x14ExposeServiceRequest\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12\x18\n" + - "\aservice\x18\x02 \x01(\tR\aservice\x12\x1f\n" + - "\vtarget_port\x18\x03 \x01(\rR\n" + - "targetPort\x12\x16\n" + - "\x06domain\x18\x04 \x01(\bR\x06domain\"G\n" + - "\x11GetServiceRequest\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12\x18\n" + - "\aservice\x18\x02 \x01(\tR\aservice\"]\n" + - "\x13ListServicesRequest\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12\x14\n" + - "\x05limit\x18\x02 \x01(\rR\x05limit\x12\x16\n" + - "\x06offset\x18\x03 \x01(\rR\x06offset\"Y\n" + - "\x14ListServicesResponse\x12A\n" + - "\bservices\x18\x01 \x03(\v2%.openshell.v1.ServiceEndpointResponseR\bservices\"J\n" + - "\x14DeleteServiceRequest\x12\x18\n" + - "\asandbox\x18\x01 \x01(\tR\asandbox\x12\x18\n" + - "\aservice\x18\x02 \x01(\tR\aservice\"1\n" + - "\x15DeleteServiceResponse\x12\x18\n" + - "\adeleted\x18\x01 \x01(\bR\adeleted\"\xef\x01\n" + - "\x0fServiceEndpoint\x12>\n" + - "\bmetadata\x18\x01 \x01(\v2\".openshell.datamodel.v1.ObjectMetaR\bmetadata\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x02 \x01(\tR\tsandboxId\x12!\n" + - "\fsandbox_name\x18\x03 \x01(\tR\vsandboxName\x12!\n" + - "\fservice_name\x18\x04 \x01(\tR\vserviceName\x12\x1f\n" + - "\vtarget_port\x18\x05 \x01(\rR\n" + - "targetPort\x12\x16\n" + - "\x06domain\x18\x06 \x01(\bR\x06domain\"f\n" + - "\x17ServiceEndpointResponse\x129\n" + - "\bendpoint\x18\x01 \x01(\v2\x1d.openshell.v1.ServiceEndpointR\bendpoint\x12\x10\n" + - "\x03url\x18\x02 \x01(\tR\x03url\"/\n" + - "\x17RevokeSshSessionRequest\x12\x14\n" + - "\x05token\x18\x01 \x01(\tR\x05token\"4\n" + - "\x18RevokeSshSessionResponse\x12\x18\n" + - "\arevoked\x18\x01 \x01(\bR\arevoked\"\xf5\x02\n" + - "\x12ExecSandboxRequest\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x18\n" + - "\acommand\x18\x02 \x03(\tR\acommand\x12\x18\n" + - "\aworkdir\x18\x03 \x01(\tR\aworkdir\x12S\n" + - "\venvironment\x18\x04 \x03(\v21.openshell.v1.ExecSandboxRequest.EnvironmentEntryR\venvironment\x12'\n" + - "\x0ftimeout_seconds\x18\x05 \x01(\rR\x0etimeoutSeconds\x12\x14\n" + - "\x05stdin\x18\x06 \x01(\fR\x05stdin\x12\x10\n" + - "\x03tty\x18\a \x01(\bR\x03tty\x12\x12\n" + - "\x04cols\x18\b \x01(\rR\x04cols\x12\x12\n" + - "\x04rows\x18\t \x01(\rR\x04rows\x1a>\n" + - "\x10EnvironmentEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"'\n" + - "\x11ExecSandboxStdout\x12\x12\n" + - "\x04data\x18\x01 \x01(\fR\x04data\"'\n" + - "\x11ExecSandboxStderr\x12\x12\n" + - "\x04data\x18\x01 \x01(\fR\x04data\".\n" + - "\x0fExecSandboxExit\x12\x1b\n" + - "\texit_code\x18\x01 \x01(\x05R\bexitCode\"\xc8\x01\n" + - "\x10ExecSandboxEvent\x129\n" + - "\x06stdout\x18\x01 \x01(\v2\x1f.openshell.v1.ExecSandboxStdoutH\x00R\x06stdout\x129\n" + - "\x06stderr\x18\x02 \x01(\v2\x1f.openshell.v1.ExecSandboxStderrH\x00R\x06stderr\x123\n" + - "\x04exit\x18\x03 \x01(\v2\x1d.openshell.v1.ExecSandboxExitH\x00R\x04exitB\t\n" + - "\apayload\"\xed\x01\n" + - "\x0eTcpForwardInit\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x1d\n" + - "\n" + - "service_id\x18\x04 \x01(\tR\tserviceId\x120\n" + - "\x03ssh\x18\x05 \x01(\v2\x1c.openshell.v1.SshRelayTargetH\x00R\x03ssh\x120\n" + - "\x03tcp\x18\x06 \x01(\v2\x1c.openshell.v1.TcpRelayTargetH\x00R\x03tcp\x12/\n" + - "\x13authorization_token\x18\a \x01(\tR\x12authorizationTokenB\b\n" + - "\x06target\"f\n" + - "\x0fTcpForwardFrame\x122\n" + - "\x04init\x18\x01 \x01(\v2\x1c.openshell.v1.TcpForwardInitH\x00R\x04init\x12\x14\n" + - "\x04data\x18\x02 \x01(\fH\x00R\x04dataB\t\n" + - "\apayload\"\xb0\x01\n" + - "\x10ExecSandboxInput\x128\n" + - "\x05start\x18\x01 \x01(\v2 .openshell.v1.ExecSandboxRequestH\x00R\x05start\x12\x16\n" + - "\x05stdin\x18\x02 \x01(\fH\x00R\x05stdin\x12?\n" + - "\x06resize\x18\x03 \x01(\v2%.openshell.v1.ExecSandboxWindowResizeH\x00R\x06resizeB\t\n" + - "\apayload\"A\n" + - "\x17ExecSandboxWindowResize\x12\x12\n" + - "\x04cols\x18\x01 \x01(\rR\x04cols\x12\x12\n" + - "\x04rows\x18\x02 \x01(\rR\x04rows\"\xbf\x01\n" + - "\n" + - "SshSession\x12>\n" + - "\bmetadata\x18\x01 \x01(\v2\".openshell.datamodel.v1.ObjectMetaR\bmetadata\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x02 \x01(\tR\tsandboxId\x12\x14\n" + - "\x05token\x18\x03 \x01(\tR\x05token\x12\"\n" + - "\rexpires_at_ms\x18\x04 \x01(\x03R\vexpiresAtMs\x12\x18\n" + - "\arevoked\x18\x05 \x01(\bR\arevoked\"\xe6\x02\n" + - "\x13WatchSandboxRequest\x12\x0e\n" + - "\x02id\x18\x01 \x01(\tR\x02id\x12#\n" + - "\rfollow_status\x18\x02 \x01(\bR\ffollowStatus\x12\x1f\n" + - "\vfollow_logs\x18\x03 \x01(\bR\n" + - "followLogs\x12#\n" + - "\rfollow_events\x18\x04 \x01(\bR\ffollowEvents\x12$\n" + - "\x0elog_tail_lines\x18\x05 \x01(\rR\flogTailLines\x12\x1d\n" + - "\n" + - "event_tail\x18\x06 \x01(\rR\teventTail\x12(\n" + - "\x10stop_on_terminal\x18\a \x01(\bR\x0estopOnTerminal\x12 \n" + - "\flog_since_ms\x18\b \x01(\x03R\n" + - "logSinceMs\x12\x1f\n" + - "\vlog_sources\x18\t \x03(\tR\n" + - "logSources\x12\"\n" + - "\rlog_min_level\x18\n" + - " \x01(\tR\vlogMinLevel\"\xcc\x02\n" + - "\x12SandboxStreamEvent\x121\n" + - "\asandbox\x18\x01 \x01(\v2\x15.openshell.v1.SandboxH\x00R\asandbox\x120\n" + - "\x03log\x18\x02 \x01(\v2\x1c.openshell.v1.SandboxLogLineH\x00R\x03log\x123\n" + - "\x05event\x18\x03 \x01(\v2\x1b.openshell.v1.PlatformEventH\x00R\x05event\x12>\n" + - "\awarning\x18\x04 \x01(\v2\".openshell.v1.SandboxStreamWarningH\x00R\awarning\x12Q\n" + - "\x13draft_policy_update\x18\x05 \x01(\v2\x1f.openshell.v1.DraftPolicyUpdateH\x00R\x11draftPolicyUpdateB\t\n" + - "\apayload\"\xaf\x02\n" + - "\x0eSandboxLogLine\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12!\n" + - "\ftimestamp_ms\x18\x02 \x01(\x03R\vtimestampMs\x12\x14\n" + - "\x05level\x18\x03 \x01(\tR\x05level\x12\x16\n" + - "\x06target\x18\x04 \x01(\tR\x06target\x12\x18\n" + - "\amessage\x18\x05 \x01(\tR\amessage\x12\x16\n" + - "\x06source\x18\x06 \x01(\tR\x06source\x12@\n" + - "\x06fields\x18\a \x03(\v2(.openshell.v1.SandboxLogLine.FieldsEntryR\x06fields\x1a9\n" + - "\vFieldsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"0\n" + - "\x14SandboxStreamWarning\x12\x18\n" + - "\amessage\x18\x01 \x01(\tR\amessage\"U\n" + - "\x15CreateProviderRequest\x12<\n" + - "\bprovider\x18\x01 \x01(\v2 .openshell.datamodel.v1.ProviderR\bprovider\"(\n" + - "\x12GetProviderRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\"D\n" + - "\x14ListProvidersRequest\x12\x14\n" + - "\x05limit\x18\x01 \x01(\rR\x05limit\x12\x16\n" + - "\x06offset\x18\x02 \x01(\rR\x06offset\"U\n" + - "\x15UpdateProviderRequest\x12<\n" + - "\bprovider\x18\x01 \x01(\v2 .openshell.datamodel.v1.ProviderR\bprovider\"+\n" + - "\x15DeleteProviderRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\"P\n" + - "\x10ProviderResponse\x12<\n" + - "\bprovider\x18\x01 \x01(\v2 .openshell.datamodel.v1.ProviderR\bprovider\"W\n" + - "\x15ListProvidersResponse\x12>\n" + - "\tproviders\x18\x01 \x03(\v2 .openshell.datamodel.v1.ProviderR\tproviders\"K\n" + - "\x1bListProviderProfilesRequest\x12\x14\n" + - "\x05limit\x18\x01 \x01(\rR\x05limit\x12\x16\n" + - "\x06offset\x18\x02 \x01(\rR\x06offset\"+\n" + - "\x19GetProviderProfileRequest\x12\x0e\n" + - "\x02id\x18\x01 \x01(\tR\x02id\"l\n" + - "\x19ProviderProfileImportItem\x127\n" + - "\aprofile\x18\x01 \x01(\v2\x1d.openshell.v1.ProviderProfileR\aprofile\x12\x16\n" + - "\x06source\x18\x02 \x01(\tR\x06source\"\x9e\x01\n" + - "\x19ProviderProfileDiagnostic\x12\x16\n" + - "\x06source\x18\x01 \x01(\tR\x06source\x12\x1d\n" + - "\n" + - "profile_id\x18\x02 \x01(\tR\tprofileId\x12\x14\n" + - "\x05field\x18\x03 \x01(\tR\x05field\x12\x18\n" + - "\amessage\x18\x04 \x01(\tR\amessage\x12\x1a\n" + - "\bseverity\x18\x05 \x01(\tR\bseverity\"\xe9\x01\n" + - "\x19ProviderProfileCredential\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12 \n" + - "\vdescription\x18\x02 \x01(\tR\vdescription\x12\x19\n" + - "\benv_vars\x18\x03 \x03(\tR\aenvVars\x12\x1a\n" + - "\brequired\x18\x04 \x01(\bR\brequired\x12\x1d\n" + - "\n" + - "auth_style\x18\x05 \x01(\tR\tauthStyle\x12\x1f\n" + - "\vheader_name\x18\x06 \x01(\tR\n" + - "headerName\x12\x1f\n" + - "\vquery_param\x18\a \x01(\tR\n" + - "queryParam\"\xa7\x03\n" + - "\x0fProviderProfile\x12\x0e\n" + - "\x02id\x18\x01 \x01(\tR\x02id\x12!\n" + - "\fdisplay_name\x18\x02 \x01(\tR\vdisplayName\x12 \n" + - "\vdescription\x18\x03 \x01(\tR\vdescription\x12A\n" + - "\bcategory\x18\x04 \x01(\x0e2%.openshell.v1.ProviderProfileCategoryR\bcategory\x12I\n" + - "\vcredentials\x18\x05 \x03(\v2'.openshell.v1.ProviderProfileCredentialR\vcredentials\x12C\n" + - "\tendpoints\x18\x06 \x03(\v2%.openshell.sandbox.v1.NetworkEndpointR\tendpoints\x12?\n" + - "\bbinaries\x18\a \x03(\v2#.openshell.sandbox.v1.NetworkBinaryR\bbinaries\x12+\n" + - "\x11inference_capable\x18\b \x01(\bR\x10inferenceCapable\"\x90\x01\n" + - "\x15StoredProviderProfile\x12>\n" + - "\bmetadata\x18\x01 \x01(\v2\".openshell.datamodel.v1.ObjectMetaR\bmetadata\x127\n" + - "\aprofile\x18\x02 \x01(\v2\x1d.openshell.v1.ProviderProfileR\aprofile\"R\n" + - "\x17ProviderProfileResponse\x127\n" + - "\aprofile\x18\x01 \x01(\v2\x1d.openshell.v1.ProviderProfileR\aprofile\"Y\n" + - "\x1cListProviderProfilesResponse\x129\n" + - "\bprofiles\x18\x01 \x03(\v2\x1d.openshell.v1.ProviderProfileR\bprofiles\"d\n" + - "\x1dImportProviderProfilesRequest\x12C\n" + - "\bprofiles\x18\x01 \x03(\v2'.openshell.v1.ProviderProfileImportItemR\bprofiles\"\xc2\x01\n" + - "\x1eImportProviderProfilesResponse\x12I\n" + - "\vdiagnostics\x18\x01 \x03(\v2'.openshell.v1.ProviderProfileDiagnosticR\vdiagnostics\x129\n" + - "\bprofiles\x18\x02 \x03(\v2\x1d.openshell.v1.ProviderProfileR\bprofiles\x12\x1a\n" + - "\bimported\x18\x03 \x01(\bR\bimported\"b\n" + - "\x1bLintProviderProfilesRequest\x12C\n" + - "\bprofiles\x18\x01 \x03(\v2'.openshell.v1.ProviderProfileImportItemR\bprofiles\"\x7f\n" + - "\x1cLintProviderProfilesResponse\x12I\n" + - "\vdiagnostics\x18\x01 \x03(\v2'.openshell.v1.ProviderProfileDiagnosticR\vdiagnostics\x12\x14\n" + - "\x05valid\x18\x02 \x01(\bR\x05valid\"2\n" + - "\x16DeleteProviderResponse\x12\x18\n" + - "\adeleted\x18\x01 \x01(\bR\adeleted\".\n" + - "\x1cDeleteProviderProfileRequest\x12\x0e\n" + - "\x02id\x18\x01 \x01(\tR\x02id\"9\n" + - "\x1dDeleteProviderProfileResponse\x12\x18\n" + - "\adeleted\x18\x01 \x01(\bR\adeleted\"E\n" + - "$GetSandboxProviderEnvironmentRequest\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\"\x83\x02\n" + - "%GetSandboxProviderEnvironmentResponse\x12f\n" + - "\venvironment\x18\x01 \x03(\v2D.openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntryR\venvironment\x122\n" + - "\x15provider_env_revision\x18\x02 \x01(\x04R\x13providerEnvRevision\x1a>\n" + - "\x10EnvironmentEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x9a\x03\n" + - "\x13UpdateConfigRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12;\n" + - "\x06policy\x18\x02 \x01(\v2#.openshell.sandbox.v1.SandboxPolicyR\x06policy\x12\x1f\n" + - "\vsetting_key\x18\x03 \x01(\tR\n" + - "settingKey\x12G\n" + - "\rsetting_value\x18\x04 \x01(\v2\".openshell.sandbox.v1.SettingValueR\fsettingValue\x12%\n" + - "\x0edelete_setting\x18\x05 \x01(\bR\rdeleteSetting\x12\x16\n" + - "\x06global\x18\x06 \x01(\bR\x06global\x12M\n" + - "\x10merge_operations\x18\a \x03(\v2\".openshell.v1.PolicyMergeOperationR\x0fmergeOperations\x12:\n" + - "\x19expected_resource_version\x18\b \x01(\x04R\x17expectedResourceVersion\"\xc7\x03\n" + - "\x14PolicyMergeOperation\x129\n" + - "\badd_rule\x18\x01 \x01(\v2\x1c.openshell.v1.AddNetworkRuleH\x00R\aaddRule\x12N\n" + - "\x0fremove_endpoint\x18\x02 \x01(\v2#.openshell.v1.RemoveNetworkEndpointH\x00R\x0eremoveEndpoint\x12B\n" + - "\vremove_rule\x18\x03 \x01(\v2\x1f.openshell.v1.RemoveNetworkRuleH\x00R\n" + - "removeRule\x12B\n" + - "\x0eadd_deny_rules\x18\x04 \x01(\v2\x1a.openshell.v1.AddDenyRulesH\x00R\faddDenyRules\x12E\n" + - "\x0fadd_allow_rules\x18\x05 \x01(\v2\x1b.openshell.v1.AddAllowRulesH\x00R\raddAllowRules\x12H\n" + - "\rremove_binary\x18\x06 \x01(\v2!.openshell.v1.RemoveNetworkBinaryH\x00R\fremoveBinaryB\v\n" + - "\toperation\"j\n" + - "\x0eAddNetworkRule\x12\x1b\n" + - "\trule_name\x18\x01 \x01(\tR\bruleName\x12;\n" + - "\x04rule\x18\x02 \x01(\v2'.openshell.sandbox.v1.NetworkPolicyRuleR\x04rule\"\\\n" + - "\x15RemoveNetworkEndpoint\x12\x1b\n" + - "\trule_name\x18\x01 \x01(\tR\bruleName\x12\x12\n" + - "\x04host\x18\x02 \x01(\tR\x04host\x12\x12\n" + - "\x04port\x18\x03 \x01(\rR\x04port\"0\n" + - "\x11RemoveNetworkRule\x12\x1b\n" + - "\trule_name\x18\x01 \x01(\tR\bruleName\"w\n" + - "\fAddDenyRules\x12\x12\n" + - "\x04host\x18\x01 \x01(\tR\x04host\x12\x12\n" + - "\x04port\x18\x02 \x01(\rR\x04port\x12?\n" + - "\n" + - "deny_rules\x18\x03 \x03(\v2 .openshell.sandbox.v1.L7DenyRuleR\tdenyRules\"k\n" + - "\rAddAllowRules\x12\x12\n" + - "\x04host\x18\x01 \x01(\tR\x04host\x12\x12\n" + - "\x04port\x18\x02 \x01(\rR\x04port\x122\n" + - "\x05rules\x18\x03 \x03(\v2\x1c.openshell.sandbox.v1.L7RuleR\x05rules\"S\n" + - "\x13RemoveNetworkBinary\x12\x1b\n" + - "\trule_name\x18\x01 \x01(\tR\bruleName\x12\x1f\n" + - "\vbinary_path\x18\x02 \x01(\tR\n" + - "binaryPath\"\x98\x01\n" + - "\x14UpdateConfigResponse\x12\x18\n" + - "\aversion\x18\x01 \x01(\rR\aversion\x12\x1f\n" + - "\vpolicy_hash\x18\x02 \x01(\tR\n" + - "policyHash\x12+\n" + - "\x11settings_revision\x18\x03 \x01(\x04R\x10settingsRevision\x12\x18\n" + - "\adeleted\x18\x04 \x01(\bR\adeleted\"e\n" + - "\x1dGetSandboxPolicyStatusRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n" + - "\aversion\x18\x02 \x01(\rR\aversion\x12\x16\n" + - "\x06global\x18\x03 \x01(\bR\x06global\"\x88\x01\n" + - "\x1eGetSandboxPolicyStatusResponse\x12?\n" + - "\brevision\x18\x01 \x01(\v2#.openshell.v1.SandboxPolicyRevisionR\brevision\x12%\n" + - "\x0eactive_version\x18\x02 \x01(\rR\ractiveVersion\"v\n" + - "\x1aListSandboxPoliciesRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12\x14\n" + - "\x05limit\x18\x02 \x01(\rR\x05limit\x12\x16\n" + - "\x06offset\x18\x03 \x01(\rR\x06offset\x12\x16\n" + - "\x06global\x18\x04 \x01(\bR\x06global\"`\n" + - "\x1bListSandboxPoliciesResponse\x12A\n" + - "\trevisions\x18\x01 \x03(\v2#.openshell.v1.SandboxPolicyRevisionR\trevisions\"\xa7\x01\n" + - "\x19ReportPolicyStatusRequest\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x18\n" + - "\aversion\x18\x02 \x01(\rR\aversion\x122\n" + - "\x06status\x18\x03 \x01(\x0e2\x1a.openshell.v1.PolicyStatusR\x06status\x12\x1d\n" + - "\n" + - "load_error\x18\x04 \x01(\tR\tloadError\"\x1c\n" + - "\x1aReportPolicyStatusResponse\"\xa8\x02\n" + - "\x15SandboxPolicyRevision\x12\x18\n" + - "\aversion\x18\x01 \x01(\rR\aversion\x12\x1f\n" + - "\vpolicy_hash\x18\x02 \x01(\tR\n" + - "policyHash\x122\n" + - "\x06status\x18\x03 \x01(\x0e2\x1a.openshell.v1.PolicyStatusR\x06status\x12\x1d\n" + - "\n" + - "load_error\x18\x04 \x01(\tR\tloadError\x12\"\n" + - "\rcreated_at_ms\x18\x05 \x01(\x03R\vcreatedAtMs\x12 \n" + - "\floaded_at_ms\x18\x06 \x01(\x03R\n" + - "loadedAtMs\x12;\n" + - "\x06policy\x18\a \x01(\v2#.openshell.sandbox.v1.SandboxPolicyR\x06policy\"\x9e\x01\n" + - "\x15GetSandboxLogsRequest\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x14\n" + - "\x05lines\x18\x02 \x01(\rR\x05lines\x12\x19\n" + - "\bsince_ms\x18\x03 \x01(\x03R\asinceMs\x12\x18\n" + - "\asources\x18\x04 \x03(\tR\asources\x12\x1b\n" + - "\tmin_level\x18\x05 \x01(\tR\bminLevel\"i\n" + - "\x16PushSandboxLogsRequest\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x120\n" + - "\x04logs\x18\x02 \x03(\v2\x1c.openshell.v1.SandboxLogLineR\x04logs\"\x19\n" + - "\x17PushSandboxLogsResponse\"m\n" + - "\x16GetSandboxLogsResponse\x120\n" + - "\x04logs\x18\x01 \x03(\v2\x1c.openshell.v1.SandboxLogLineR\x04logs\x12!\n" + - "\fbuffer_total\x18\x02 \x01(\rR\vbufferTotal\"\xa2\x02\n" + - "\x11SupervisorMessage\x125\n" + - "\x05hello\x18\x01 \x01(\v2\x1d.openshell.v1.SupervisorHelloH\x00R\x05hello\x12A\n" + - "\theartbeat\x18\x02 \x01(\v2!.openshell.v1.SupervisorHeartbeatH\x00R\theartbeat\x12K\n" + - "\x11relay_open_result\x18\x03 \x01(\v2\x1d.openshell.v1.RelayOpenResultH\x00R\x0frelayOpenResult\x12;\n" + - "\vrelay_close\x18\x04 \x01(\v2\x18.openshell.v1.RelayCloseH\x00R\n" + - "relayCloseB\t\n" + - "\apayload\"\xea\x02\n" + - "\x0eGatewayMessage\x12J\n" + - "\x10session_accepted\x18\x01 \x01(\v2\x1d.openshell.v1.SessionAcceptedH\x00R\x0fsessionAccepted\x12J\n" + - "\x10session_rejected\x18\x02 \x01(\v2\x1d.openshell.v1.SessionRejectedH\x00R\x0fsessionRejected\x12>\n" + - "\theartbeat\x18\x03 \x01(\v2\x1e.openshell.v1.GatewayHeartbeatH\x00R\theartbeat\x128\n" + - "\n" + - "relay_open\x18\x04 \x01(\v2\x17.openshell.v1.RelayOpenH\x00R\trelayOpen\x12;\n" + - "\vrelay_close\x18\x05 \x01(\v2\x18.openshell.v1.RelayCloseH\x00R\n" + - "relayCloseB\t\n" + - "\apayload\"Q\n" + - "\x0fSupervisorHello\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x1f\n" + - "\vinstance_id\x18\x02 \x01(\tR\n" + - "instanceId\"h\n" + - "\x0fSessionAccepted\x12\x1d\n" + - "\n" + - "session_id\x18\x01 \x01(\tR\tsessionId\x126\n" + - "\x17heartbeat_interval_secs\x18\x02 \x01(\rR\x15heartbeatIntervalSecs\")\n" + - "\x0fSessionRejected\x12\x16\n" + - "\x06reason\x18\x01 \x01(\tR\x06reason\"\x15\n" + - "\x13SupervisorHeartbeat\"\x12\n" + - "\x10GatewayHeartbeat\"\xb7\x01\n" + - "\tRelayOpen\x12\x1d\n" + - "\n" + - "channel_id\x18\x01 \x01(\tR\tchannelId\x120\n" + - "\x03ssh\x18\x02 \x01(\v2\x1c.openshell.v1.SshRelayTargetH\x00R\x03ssh\x120\n" + - "\x03tcp\x18\x03 \x01(\v2\x1c.openshell.v1.TcpRelayTargetH\x00R\x03tcp\x12\x1d\n" + - "\n" + - "service_id\x18\x05 \x01(\tR\tserviceIdB\b\n" + - "\x06target\"\x10\n" + - "\x0eSshRelayTarget\"8\n" + - "\x0eTcpRelayTarget\x12\x12\n" + - "\x04host\x18\x01 \x01(\tR\x04host\x12\x12\n" + - "\x04port\x18\x02 \x01(\rR\x04port\"*\n" + - "\tRelayInit\x12\x1d\n" + - "\n" + - "channel_id\x18\x01 \x01(\tR\tchannelId\"\\\n" + - "\n" + - "RelayFrame\x12-\n" + - "\x04init\x18\x01 \x01(\v2\x17.openshell.v1.RelayInitH\x00R\x04init\x12\x14\n" + - "\x04data\x18\x02 \x01(\fH\x00R\x04dataB\t\n" + - "\apayload\"`\n" + - "\x0fRelayOpenResult\x12\x1d\n" + - "\n" + - "channel_id\x18\x01 \x01(\tR\tchannelId\x12\x18\n" + - "\asuccess\x18\x02 \x01(\bR\asuccess\x12\x14\n" + - "\x05error\x18\x03 \x01(\tR\x05error\"C\n" + - "\n" + - "RelayClose\x12\x1d\n" + - "\n" + - "channel_id\x18\x01 \x01(\tR\tchannelId\x12\x16\n" + - "\x06reason\x18\x02 \x01(\tR\x06reason\"o\n" + - "\x0fL7RequestSample\x12\x16\n" + - "\x06method\x18\x01 \x01(\tR\x06method\x12\x12\n" + - "\x04path\x18\x02 \x01(\tR\x04path\x12\x1a\n" + - "\bdecision\x18\x03 \x01(\tR\bdecision\x12\x14\n" + - "\x05count\x18\x04 \x01(\rR\x05count\"\xe5\x04\n" + - "\rDenialSummary\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\x12\x12\n" + - "\x04host\x18\x02 \x01(\tR\x04host\x12\x12\n" + - "\x04port\x18\x03 \x01(\rR\x04port\x12\x16\n" + - "\x06binary\x18\x04 \x01(\tR\x06binary\x12\x1c\n" + - "\tancestors\x18\x05 \x03(\tR\tancestors\x12\x1f\n" + - "\vdeny_reason\x18\x06 \x01(\tR\n" + - "denyReason\x12\"\n" + - "\rfirst_seen_ms\x18\a \x01(\x03R\vfirstSeenMs\x12 \n" + - "\flast_seen_ms\x18\b \x01(\x03R\n" + - "lastSeenMs\x12\x14\n" + - "\x05count\x18\t \x01(\rR\x05count\x12)\n" + - "\x10suppressed_count\x18\n" + - " \x01(\rR\x0fsuppressedCount\x12\x1f\n" + - "\vtotal_count\x18\v \x01(\rR\n" + - "totalCount\x12'\n" + - "\x0fsample_cmdlines\x18\f \x03(\tR\x0esampleCmdlines\x12#\n" + - "\rbinary_sha256\x18\r \x01(\tR\fbinarySha256\x12\x1e\n" + - "\n" + - "persistent\x18\x0e \x01(\bR\n" + - "persistent\x12!\n" + - "\fdenial_stage\x18\x0f \x01(\tR\vdenialStage\x12K\n" + - "\x12l7_request_samples\x18\x10 \x03(\v2\x1d.openshell.v1.L7RequestSampleR\x10l7RequestSamples\x120\n" + - "\x14l7_inspection_active\x18\x11 \x01(\bR\x12l7InspectionActive\"\x94\x05\n" + - "\vPolicyChunk\x12\x0e\n" + - "\x02id\x18\x01 \x01(\tR\x02id\x12\x16\n" + - "\x06status\x18\x02 \x01(\tR\x06status\x12\x1b\n" + - "\trule_name\x18\x03 \x01(\tR\bruleName\x12L\n" + - "\rproposed_rule\x18\x04 \x01(\v2'.openshell.sandbox.v1.NetworkPolicyRuleR\fproposedRule\x12\x1c\n" + - "\trationale\x18\x05 \x01(\tR\trationale\x12%\n" + - "\x0esecurity_notes\x18\x06 \x01(\tR\rsecurityNotes\x12\x1e\n" + - "\n" + - "confidence\x18\a \x01(\x02R\n" + - "confidence\x12,\n" + - "\x12denial_summary_ids\x18\b \x03(\tR\x10denialSummaryIds\x12\"\n" + - "\rcreated_at_ms\x18\t \x01(\x03R\vcreatedAtMs\x12\"\n" + - "\rdecided_at_ms\x18\n" + - " \x01(\x03R\vdecidedAtMs\x12\x14\n" + - "\x05stage\x18\v \x01(\tR\x05stage\x12.\n" + - "\x13supersedes_chunk_id\x18\f \x01(\tR\x11supersedesChunkId\x12\x1b\n" + - "\thit_count\x18\r \x01(\x05R\bhitCount\x12\"\n" + - "\rfirst_seen_ms\x18\x0e \x01(\x03R\vfirstSeenMs\x12 \n" + - "\flast_seen_ms\x18\x0f \x01(\x03R\n" + - "lastSeenMs\x12\x16\n" + - "\x06binary\x18\x10 \x01(\tR\x06binary\x12+\n" + - "\x11validation_result\x18\x11 \x01(\tR\x10validationResult\x12)\n" + - "\x10rejection_reason\x18\x12 \x01(\tR\x0frejectionReason\"\x96\x01\n" + - "\x11DraftPolicyUpdate\x12#\n" + - "\rdraft_version\x18\x01 \x01(\x04R\fdraftVersion\x12\x1d\n" + - "\n" + - "new_chunks\x18\x02 \x01(\rR\tnewChunks\x12#\n" + - "\rtotal_pending\x18\x03 \x01(\rR\ftotalPending\x12\x18\n" + - "\asummary\x18\x04 \x01(\tR\asummary\"\xd5\x01\n" + - "\x1bSubmitPolicyAnalysisRequest\x129\n" + - "\tsummaries\x18\x01 \x03(\v2\x1b.openshell.v1.DenialSummaryR\tsummaries\x12B\n" + - "\x0fproposed_chunks\x18\x02 \x03(\v2\x19.openshell.v1.PolicyChunkR\x0eproposedChunks\x12#\n" + - "\ranalysis_mode\x18\x03 \x01(\tR\fanalysisMode\x12\x12\n" + - "\x04name\x18\x04 \x01(\tR\x04name\"\xcb\x01\n" + - "\x1cSubmitPolicyAnalysisResponse\x12'\n" + - "\x0faccepted_chunks\x18\x01 \x01(\rR\x0eacceptedChunks\x12'\n" + - "\x0frejected_chunks\x18\x02 \x01(\rR\x0erejectedChunks\x12+\n" + - "\x11rejection_reasons\x18\x03 \x03(\tR\x10rejectionReasons\x12,\n" + - "\x12accepted_chunk_ids\x18\x04 \x03(\tR\x10acceptedChunkIds\"P\n" + - "\x15GetDraftPolicyRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12#\n" + - "\rstatus_filter\x18\x02 \x01(\tR\fstatusFilter\"\xc8\x01\n" + - "\x16GetDraftPolicyResponse\x121\n" + - "\x06chunks\x18\x01 \x03(\v2\x19.openshell.v1.PolicyChunkR\x06chunks\x12'\n" + - "\x0frolling_summary\x18\x02 \x01(\tR\x0erollingSummary\x12#\n" + - "\rdraft_version\x18\x03 \x01(\x04R\fdraftVersion\x12-\n" + - "\x13last_analyzed_at_ms\x18\x04 \x01(\x03R\x10lastAnalyzedAtMs\"I\n" + - "\x18ApproveDraftChunkRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12\x19\n" + - "\bchunk_id\x18\x02 \x01(\tR\achunkId\"c\n" + - "\x19ApproveDraftChunkResponse\x12%\n" + - "\x0epolicy_version\x18\x01 \x01(\rR\rpolicyVersion\x12\x1f\n" + - "\vpolicy_hash\x18\x02 \x01(\tR\n" + - "policyHash\"`\n" + - "\x17RejectDraftChunkRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12\x19\n" + - "\bchunk_id\x18\x02 \x01(\tR\achunkId\x12\x16\n" + - "\x06reason\x18\x03 \x01(\tR\x06reason\"\x1a\n" + - "\x18RejectDraftChunkResponse\"l\n" + - "\x1cApproveAllDraftChunksRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x128\n" + - "\x18include_security_flagged\x18\x02 \x01(\bR\x16includeSecurityFlagged\"\xb7\x01\n" + - "\x1dApproveAllDraftChunksResponse\x12%\n" + - "\x0epolicy_version\x18\x01 \x01(\rR\rpolicyVersion\x12\x1f\n" + - "\vpolicy_hash\x18\x02 \x01(\tR\n" + - "policyHash\x12'\n" + - "\x0fchunks_approved\x18\x03 \x01(\rR\x0echunksApproved\x12%\n" + - "\x0echunks_skipped\x18\x04 \x01(\rR\rchunksSkipped\"\x94\x01\n" + - "\x15EditDraftChunkRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12\x19\n" + - "\bchunk_id\x18\x02 \x01(\tR\achunkId\x12L\n" + - "\rproposed_rule\x18\x03 \x01(\v2'.openshell.sandbox.v1.NetworkPolicyRuleR\fproposedRule\"\x18\n" + - "\x16EditDraftChunkResponse\"F\n" + - "\x15UndoDraftChunkRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12\x19\n" + - "\bchunk_id\x18\x02 \x01(\tR\achunkId\"`\n" + - "\x16UndoDraftChunkResponse\x12%\n" + - "\x0epolicy_version\x18\x01 \x01(\rR\rpolicyVersion\x12\x1f\n" + - "\vpolicy_hash\x18\x02 \x01(\tR\n" + - "policyHash\"-\n" + - "\x17ClearDraftChunksRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\"A\n" + - "\x18ClearDraftChunksResponse\x12%\n" + - "\x0echunks_cleared\x18\x01 \x01(\rR\rchunksCleared\",\n" + - "\x16GetDraftHistoryRequest\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\"\x92\x01\n" + - "\x11DraftHistoryEntry\x12!\n" + - "\ftimestamp_ms\x18\x01 \x01(\x03R\vtimestampMs\x12\x1d\n" + - "\n" + - "event_type\x18\x02 \x01(\tR\teventType\x12 \n" + - "\vdescription\x18\x03 \x01(\tR\vdescription\x12\x19\n" + - "\bchunk_id\x18\x04 \x01(\tR\achunkId\"T\n" + - "\x17GetDraftHistoryResponse\x129\n" + - "\aentries\x18\x01 \x03(\v2\x1f.openshell.v1.DraftHistoryEntryR\aentries\"\xa9\x01\n" + - "\x15PolicyRevisionPayload\x12;\n" + - "\x06policy\x18\x01 \x01(\v2#.openshell.sandbox.v1.SandboxPolicyR\x06policy\x12\x12\n" + - "\x04hash\x18\x02 \x01(\tR\x04hash\x12\x1d\n" + - "\n" + - "load_error\x18\x03 \x01(\tR\tloadError\x12 \n" + - "\floaded_at_ms\x18\x04 \x01(\x03R\n" + - "loadedAtMs\"\xc4\x03\n" + - "\x11DraftChunkPayload\x12\x1b\n" + - "\trule_name\x18\x01 \x01(\tR\bruleName\x12L\n" + - "\rproposed_rule\x18\x02 \x01(\v2'.openshell.sandbox.v1.NetworkPolicyRuleR\fproposedRule\x12\x1c\n" + - "\trationale\x18\x03 \x01(\tR\trationale\x12%\n" + - "\x0esecurity_notes\x18\x04 \x01(\tR\rsecurityNotes\x12\x1e\n" + - "\n" + - "confidence\x18\x05 \x01(\x02R\n" + - "confidence\x12\"\n" + - "\rdecided_at_ms\x18\x06 \x01(\x03R\vdecidedAtMs\x12\x12\n" + - "\x04host\x18\a \x01(\tR\x04host\x12\x12\n" + - "\x04port\x18\b \x01(\x05R\x04port\x12\x16\n" + - "\x06binary\x18\t \x01(\tR\x06binary\x12#\n" + - "\rdraft_version\x18\n" + - " \x01(\x03R\fdraftVersion\x12+\n" + - "\x11validation_result\x18\v \x01(\tR\x10validationResult\x12)\n" + - "\x10rejection_reason\x18\f \x01(\tR\x0frejectionReason\"\xce\x02\n" + - "\x14StoredPolicyRevision\x12\x0e\n" + - "\x02id\x18\x01 \x01(\tR\x02id\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x02 \x01(\tR\tsandboxId\x12\x18\n" + - "\aversion\x18\x03 \x01(\x03R\aversion\x12%\n" + - "\x0epolicy_payload\x18\x04 \x01(\fR\rpolicyPayload\x12\x1f\n" + - "\vpolicy_hash\x18\x05 \x01(\tR\n" + - "policyHash\x12\x16\n" + - "\x06status\x18\x06 \x01(\tR\x06status\x12\"\n" + - "\n" + - "load_error\x18\a \x01(\tH\x00R\tloadError\x88\x01\x01\x12\"\n" + - "\rcreated_at_ms\x18\b \x01(\x03R\vcreatedAtMs\x12%\n" + - "\floaded_at_ms\x18\t \x01(\x03H\x01R\n" + - "loadedAtMs\x88\x01\x01B\r\n" + - "\v_load_errorB\x0f\n" + - "\r_loaded_at_ms\"\xff\x04\n" + - "\x10StoredDraftChunk\x12\x0e\n" + - "\x02id\x18\x01 \x01(\tR\x02id\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x02 \x01(\tR\tsandboxId\x12#\n" + - "\rdraft_version\x18\x03 \x01(\x03R\fdraftVersion\x12\x16\n" + - "\x06status\x18\x04 \x01(\tR\x06status\x12\x1b\n" + - "\trule_name\x18\x05 \x01(\tR\bruleName\x12#\n" + - "\rproposed_rule\x18\x06 \x01(\fR\fproposedRule\x12\x1c\n" + - "\trationale\x18\a \x01(\tR\trationale\x12%\n" + - "\x0esecurity_notes\x18\b \x01(\tR\rsecurityNotes\x12\x1e\n" + - "\n" + - "confidence\x18\t \x01(\x01R\n" + - "confidence\x12\"\n" + - "\rcreated_at_ms\x18\n" + - " \x01(\x03R\vcreatedAtMs\x12'\n" + - "\rdecided_at_ms\x18\v \x01(\x03H\x00R\vdecidedAtMs\x88\x01\x01\x12\x12\n" + - "\x04host\x18\f \x01(\tR\x04host\x12\x12\n" + - "\x04port\x18\r \x01(\x05R\x04port\x12\x16\n" + - "\x06binary\x18\x0e \x01(\tR\x06binary\x12\x1b\n" + - "\thit_count\x18\x0f \x01(\x05R\bhitCount\x12\"\n" + - "\rfirst_seen_ms\x18\x10 \x01(\x03R\vfirstSeenMs\x12 \n" + - "\flast_seen_ms\x18\x11 \x01(\x03R\n" + - "lastSeenMs\x12+\n" + - "\x11validation_result\x18\x12 \x01(\tR\x10validationResult\x12)\n" + - "\x10rejection_reason\x18\x13 \x01(\tR\x0frejectionReasonB\x10\n" + - "\x0e_decided_at_ms*\xb6\x01\n" + - "\fSandboxPhase\x12\x1d\n" + - "\x19SANDBOX_PHASE_UNSPECIFIED\x10\x00\x12\x1e\n" + - "\x1aSANDBOX_PHASE_PROVISIONING\x10\x01\x12\x17\n" + - "\x13SANDBOX_PHASE_READY\x10\x02\x12\x17\n" + - "\x13SANDBOX_PHASE_ERROR\x10\x03\x12\x1a\n" + - "\x16SANDBOX_PHASE_DELETING\x10\x04\x12\x19\n" + - "\x15SANDBOX_PHASE_UNKNOWN\x10\x05*\xdb\x02\n" + - "\x17ProviderProfileCategory\x12)\n" + - "%PROVIDER_PROFILE_CATEGORY_UNSPECIFIED\x10\x00\x12#\n" + - "\x1fPROVIDER_PROFILE_CATEGORY_OTHER\x10\x01\x12'\n" + - "#PROVIDER_PROFILE_CATEGORY_INFERENCE\x10\x02\x12#\n" + - "\x1fPROVIDER_PROFILE_CATEGORY_AGENT\x10\x03\x12,\n" + - "(PROVIDER_PROFILE_CATEGORY_SOURCE_CONTROL\x10\x04\x12'\n" + - "#PROVIDER_PROFILE_CATEGORY_MESSAGING\x10\x05\x12\"\n" + - "\x1ePROVIDER_PROFILE_CATEGORY_DATA\x10\x06\x12'\n" + - "#PROVIDER_PROFILE_CATEGORY_KNOWLEDGE\x10\a*\x9a\x01\n" + - "\fPolicyStatus\x12\x1d\n" + - "\x19POLICY_STATUS_UNSPECIFIED\x10\x00\x12\x19\n" + - "\x15POLICY_STATUS_PENDING\x10\x01\x12\x18\n" + - "\x14POLICY_STATUS_LOADED\x10\x02\x12\x18\n" + - "\x14POLICY_STATUS_FAILED\x10\x03\x12\x1c\n" + - "\x18POLICY_STATUS_SUPERSEDED\x10\x04*\x86\x01\n" + - "\rServiceStatus\x12\x1e\n" + - "\x1aSERVICE_STATUS_UNSPECIFIED\x10\x00\x12\x1a\n" + - "\x16SERVICE_STATUS_HEALTHY\x10\x01\x12\x1b\n" + - "\x17SERVICE_STATUS_DEGRADED\x10\x02\x12\x1c\n" + - "\x18SERVICE_STATUS_UNHEALTHY\x10\x032\xcf$\n" + - "\tOpenShell\x12C\n" + - "\x06Health\x12\x1b.openshell.v1.HealthRequest\x1a\x1c.openshell.v1.HealthResponse\x12R\n" + - "\rCreateSandbox\x12\".openshell.v1.CreateSandboxRequest\x1a\x1d.openshell.v1.SandboxResponse\x12L\n" + - "\n" + - "GetSandbox\x12\x1f.openshell.v1.GetSandboxRequest\x1a\x1d.openshell.v1.SandboxResponse\x12X\n" + - "\rListSandboxes\x12\".openshell.v1.ListSandboxesRequest\x1a#.openshell.v1.ListSandboxesResponse\x12m\n" + - "\x14ListSandboxProviders\x12).openshell.v1.ListSandboxProvidersRequest\x1a*.openshell.v1.ListSandboxProvidersResponse\x12p\n" + - "\x15AttachSandboxProvider\x12*.openshell.v1.AttachSandboxProviderRequest\x1a+.openshell.v1.AttachSandboxProviderResponse\x12p\n" + - "\x15DetachSandboxProvider\x12*.openshell.v1.DetachSandboxProviderRequest\x1a+.openshell.v1.DetachSandboxProviderResponse\x12X\n" + - "\rDeleteSandbox\x12\".openshell.v1.DeleteSandboxRequest\x1a#.openshell.v1.DeleteSandboxResponse\x12a\n" + - "\x10CreateSshSession\x12%.openshell.v1.CreateSshSessionRequest\x1a&.openshell.v1.CreateSshSessionResponse\x12Z\n" + - "\rExposeService\x12\".openshell.v1.ExposeServiceRequest\x1a%.openshell.v1.ServiceEndpointResponse\x12T\n" + - "\n" + - "GetService\x12\x1f.openshell.v1.GetServiceRequest\x1a%.openshell.v1.ServiceEndpointResponse\x12U\n" + - "\fListServices\x12!.openshell.v1.ListServicesRequest\x1a\".openshell.v1.ListServicesResponse\x12X\n" + - "\rDeleteService\x12\".openshell.v1.DeleteServiceRequest\x1a#.openshell.v1.DeleteServiceResponse\x12a\n" + - "\x10RevokeSshSession\x12%.openshell.v1.RevokeSshSessionRequest\x1a&.openshell.v1.RevokeSshSessionResponse\x12Q\n" + - "\vExecSandbox\x12 .openshell.v1.ExecSandboxRequest\x1a\x1e.openshell.v1.ExecSandboxEvent0\x01\x12N\n" + - "\n" + - "ForwardTcp\x12\x1d.openshell.v1.TcpForwardFrame\x1a\x1d.openshell.v1.TcpForwardFrame(\x010\x01\x12\\\n" + - "\x16ExecSandboxInteractive\x12\x1e.openshell.v1.ExecSandboxInput\x1a\x1e.openshell.v1.ExecSandboxEvent(\x010\x01\x12U\n" + - "\x0eCreateProvider\x12#.openshell.v1.CreateProviderRequest\x1a\x1e.openshell.v1.ProviderResponse\x12O\n" + - "\vGetProvider\x12 .openshell.v1.GetProviderRequest\x1a\x1e.openshell.v1.ProviderResponse\x12X\n" + - "\rListProviders\x12\".openshell.v1.ListProvidersRequest\x1a#.openshell.v1.ListProvidersResponse\x12m\n" + - "\x14ListProviderProfiles\x12).openshell.v1.ListProviderProfilesRequest\x1a*.openshell.v1.ListProviderProfilesResponse\x12d\n" + - "\x12GetProviderProfile\x12'.openshell.v1.GetProviderProfileRequest\x1a%.openshell.v1.ProviderProfileResponse\x12s\n" + - "\x16ImportProviderProfiles\x12+.openshell.v1.ImportProviderProfilesRequest\x1a,.openshell.v1.ImportProviderProfilesResponse\x12m\n" + - "\x14LintProviderProfiles\x12).openshell.v1.LintProviderProfilesRequest\x1a*.openshell.v1.LintProviderProfilesResponse\x12U\n" + - "\x0eUpdateProvider\x12#.openshell.v1.UpdateProviderRequest\x1a\x1e.openshell.v1.ProviderResponse\x12[\n" + - "\x0eDeleteProvider\x12#.openshell.v1.DeleteProviderRequest\x1a$.openshell.v1.DeleteProviderResponse\x12p\n" + - "\x15DeleteProviderProfile\x12*.openshell.v1.DeleteProviderProfileRequest\x1a+.openshell.v1.DeleteProviderProfileResponse\x12q\n" + - "\x10GetSandboxConfig\x12-.openshell.sandbox.v1.GetSandboxConfigRequest\x1a..openshell.sandbox.v1.GetSandboxConfigResponse\x12q\n" + - "\x10GetGatewayConfig\x12-.openshell.sandbox.v1.GetGatewayConfigRequest\x1a..openshell.sandbox.v1.GetGatewayConfigResponse\x12U\n" + - "\fUpdateConfig\x12!.openshell.v1.UpdateConfigRequest\x1a\".openshell.v1.UpdateConfigResponse\x12s\n" + - "\x16GetSandboxPolicyStatus\x12+.openshell.v1.GetSandboxPolicyStatusRequest\x1a,.openshell.v1.GetSandboxPolicyStatusResponse\x12j\n" + - "\x13ListSandboxPolicies\x12(.openshell.v1.ListSandboxPoliciesRequest\x1a).openshell.v1.ListSandboxPoliciesResponse\x12g\n" + - "\x12ReportPolicyStatus\x12'.openshell.v1.ReportPolicyStatusRequest\x1a(.openshell.v1.ReportPolicyStatusResponse\x12\x88\x01\n" + - "\x1dGetSandboxProviderEnvironment\x122.openshell.v1.GetSandboxProviderEnvironmentRequest\x1a3.openshell.v1.GetSandboxProviderEnvironmentResponse\x12[\n" + - "\x0eGetSandboxLogs\x12#.openshell.v1.GetSandboxLogsRequest\x1a$.openshell.v1.GetSandboxLogsResponse\x12`\n" + - "\x0fPushSandboxLogs\x12$.openshell.v1.PushSandboxLogsRequest\x1a%.openshell.v1.PushSandboxLogsResponse(\x01\x12V\n" + - "\x11ConnectSupervisor\x12\x1f.openshell.v1.SupervisorMessage\x1a\x1c.openshell.v1.GatewayMessage(\x010\x01\x12E\n" + - "\vRelayStream\x12\x18.openshell.v1.RelayFrame\x1a\x18.openshell.v1.RelayFrame(\x010\x01\x12U\n" + - "\fWatchSandbox\x12!.openshell.v1.WatchSandboxRequest\x1a .openshell.v1.SandboxStreamEvent0\x01\x12m\n" + - "\x14SubmitPolicyAnalysis\x12).openshell.v1.SubmitPolicyAnalysisRequest\x1a*.openshell.v1.SubmitPolicyAnalysisResponse\x12[\n" + - "\x0eGetDraftPolicy\x12#.openshell.v1.GetDraftPolicyRequest\x1a$.openshell.v1.GetDraftPolicyResponse\x12d\n" + - "\x11ApproveDraftChunk\x12&.openshell.v1.ApproveDraftChunkRequest\x1a'.openshell.v1.ApproveDraftChunkResponse\x12a\n" + - "\x10RejectDraftChunk\x12%.openshell.v1.RejectDraftChunkRequest\x1a&.openshell.v1.RejectDraftChunkResponse\x12p\n" + - "\x15ApproveAllDraftChunks\x12*.openshell.v1.ApproveAllDraftChunksRequest\x1a+.openshell.v1.ApproveAllDraftChunksResponse\x12[\n" + - "\x0eEditDraftChunk\x12#.openshell.v1.EditDraftChunkRequest\x1a$.openshell.v1.EditDraftChunkResponse\x12[\n" + - "\x0eUndoDraftChunk\x12#.openshell.v1.UndoDraftChunkRequest\x1a$.openshell.v1.UndoDraftChunkResponse\x12a\n" + - "\x10ClearDraftChunks\x12%.openshell.v1.ClearDraftChunksRequest\x1a&.openshell.v1.ClearDraftChunksResponse\x12^\n" + - "\x0fGetDraftHistory\x12$.openshell.v1.GetDraftHistoryRequest\x1a%.openshell.v1.GetDraftHistoryResponseB\xb2\x01\n" + - "\x10com.openshell.v1B\x0eOpenshellProtoP\x01Z=github.com/kagent-dev/kagent/go/api/openshell/gen/openshellv1\xa2\x02\x03OXX\xaa\x02\fOpenshell.V1\xca\x02\fOpenshell\\V1\xe2\x02\x18Openshell\\V1\\GPBMetadata\xea\x02\rOpenshell::V1b\x06proto3" - -var ( - file_openshell_proto_rawDescOnce sync.Once - file_openshell_proto_rawDescData []byte -) - -func file_openshell_proto_rawDescGZIP() []byte { - file_openshell_proto_rawDescOnce.Do(func() { - file_openshell_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_openshell_proto_rawDesc), len(file_openshell_proto_rawDesc))) - }) - return file_openshell_proto_rawDescData -} - -var file_openshell_proto_enumTypes = make([]protoimpl.EnumInfo, 4) -var file_openshell_proto_msgTypes = make([]protoimpl.MessageInfo, 142) -var file_openshell_proto_goTypes = []any{ - (SandboxPhase)(0), // 0: openshell.v1.SandboxPhase - (ProviderProfileCategory)(0), // 1: openshell.v1.ProviderProfileCategory - (PolicyStatus)(0), // 2: openshell.v1.PolicyStatus - (ServiceStatus)(0), // 3: openshell.v1.ServiceStatus - (*HealthRequest)(nil), // 4: openshell.v1.HealthRequest - (*HealthResponse)(nil), // 5: openshell.v1.HealthResponse - (*Sandbox)(nil), // 6: openshell.v1.Sandbox - (*SandboxSpec)(nil), // 7: openshell.v1.SandboxSpec - (*SandboxTemplate)(nil), // 8: openshell.v1.SandboxTemplate - (*SandboxStatus)(nil), // 9: openshell.v1.SandboxStatus - (*SandboxCondition)(nil), // 10: openshell.v1.SandboxCondition - (*PlatformEvent)(nil), // 11: openshell.v1.PlatformEvent - (*CreateSandboxRequest)(nil), // 12: openshell.v1.CreateSandboxRequest - (*GetSandboxRequest)(nil), // 13: openshell.v1.GetSandboxRequest - (*ListSandboxesRequest)(nil), // 14: openshell.v1.ListSandboxesRequest - (*ListSandboxProvidersRequest)(nil), // 15: openshell.v1.ListSandboxProvidersRequest - (*AttachSandboxProviderRequest)(nil), // 16: openshell.v1.AttachSandboxProviderRequest - (*DetachSandboxProviderRequest)(nil), // 17: openshell.v1.DetachSandboxProviderRequest - (*DeleteSandboxRequest)(nil), // 18: openshell.v1.DeleteSandboxRequest - (*SandboxResponse)(nil), // 19: openshell.v1.SandboxResponse - (*ListSandboxesResponse)(nil), // 20: openshell.v1.ListSandboxesResponse - (*ListSandboxProvidersResponse)(nil), // 21: openshell.v1.ListSandboxProvidersResponse - (*AttachSandboxProviderResponse)(nil), // 22: openshell.v1.AttachSandboxProviderResponse - (*DetachSandboxProviderResponse)(nil), // 23: openshell.v1.DetachSandboxProviderResponse - (*DeleteSandboxResponse)(nil), // 24: openshell.v1.DeleteSandboxResponse - (*CreateSshSessionRequest)(nil), // 25: openshell.v1.CreateSshSessionRequest - (*CreateSshSessionResponse)(nil), // 26: openshell.v1.CreateSshSessionResponse - (*ExposeServiceRequest)(nil), // 27: openshell.v1.ExposeServiceRequest - (*GetServiceRequest)(nil), // 28: openshell.v1.GetServiceRequest - (*ListServicesRequest)(nil), // 29: openshell.v1.ListServicesRequest - (*ListServicesResponse)(nil), // 30: openshell.v1.ListServicesResponse - (*DeleteServiceRequest)(nil), // 31: openshell.v1.DeleteServiceRequest - (*DeleteServiceResponse)(nil), // 32: openshell.v1.DeleteServiceResponse - (*ServiceEndpoint)(nil), // 33: openshell.v1.ServiceEndpoint - (*ServiceEndpointResponse)(nil), // 34: openshell.v1.ServiceEndpointResponse - (*RevokeSshSessionRequest)(nil), // 35: openshell.v1.RevokeSshSessionRequest - (*RevokeSshSessionResponse)(nil), // 36: openshell.v1.RevokeSshSessionResponse - (*ExecSandboxRequest)(nil), // 37: openshell.v1.ExecSandboxRequest - (*ExecSandboxStdout)(nil), // 38: openshell.v1.ExecSandboxStdout - (*ExecSandboxStderr)(nil), // 39: openshell.v1.ExecSandboxStderr - (*ExecSandboxExit)(nil), // 40: openshell.v1.ExecSandboxExit - (*ExecSandboxEvent)(nil), // 41: openshell.v1.ExecSandboxEvent - (*TcpForwardInit)(nil), // 42: openshell.v1.TcpForwardInit - (*TcpForwardFrame)(nil), // 43: openshell.v1.TcpForwardFrame - (*ExecSandboxInput)(nil), // 44: openshell.v1.ExecSandboxInput - (*ExecSandboxWindowResize)(nil), // 45: openshell.v1.ExecSandboxWindowResize - (*SshSession)(nil), // 46: openshell.v1.SshSession - (*WatchSandboxRequest)(nil), // 47: openshell.v1.WatchSandboxRequest - (*SandboxStreamEvent)(nil), // 48: openshell.v1.SandboxStreamEvent - (*SandboxLogLine)(nil), // 49: openshell.v1.SandboxLogLine - (*SandboxStreamWarning)(nil), // 50: openshell.v1.SandboxStreamWarning - (*CreateProviderRequest)(nil), // 51: openshell.v1.CreateProviderRequest - (*GetProviderRequest)(nil), // 52: openshell.v1.GetProviderRequest - (*ListProvidersRequest)(nil), // 53: openshell.v1.ListProvidersRequest - (*UpdateProviderRequest)(nil), // 54: openshell.v1.UpdateProviderRequest - (*DeleteProviderRequest)(nil), // 55: openshell.v1.DeleteProviderRequest - (*ProviderResponse)(nil), // 56: openshell.v1.ProviderResponse - (*ListProvidersResponse)(nil), // 57: openshell.v1.ListProvidersResponse - (*ListProviderProfilesRequest)(nil), // 58: openshell.v1.ListProviderProfilesRequest - (*GetProviderProfileRequest)(nil), // 59: openshell.v1.GetProviderProfileRequest - (*ProviderProfileImportItem)(nil), // 60: openshell.v1.ProviderProfileImportItem - (*ProviderProfileDiagnostic)(nil), // 61: openshell.v1.ProviderProfileDiagnostic - (*ProviderProfileCredential)(nil), // 62: openshell.v1.ProviderProfileCredential - (*ProviderProfile)(nil), // 63: openshell.v1.ProviderProfile - (*StoredProviderProfile)(nil), // 64: openshell.v1.StoredProviderProfile - (*ProviderProfileResponse)(nil), // 65: openshell.v1.ProviderProfileResponse - (*ListProviderProfilesResponse)(nil), // 66: openshell.v1.ListProviderProfilesResponse - (*ImportProviderProfilesRequest)(nil), // 67: openshell.v1.ImportProviderProfilesRequest - (*ImportProviderProfilesResponse)(nil), // 68: openshell.v1.ImportProviderProfilesResponse - (*LintProviderProfilesRequest)(nil), // 69: openshell.v1.LintProviderProfilesRequest - (*LintProviderProfilesResponse)(nil), // 70: openshell.v1.LintProviderProfilesResponse - (*DeleteProviderResponse)(nil), // 71: openshell.v1.DeleteProviderResponse - (*DeleteProviderProfileRequest)(nil), // 72: openshell.v1.DeleteProviderProfileRequest - (*DeleteProviderProfileResponse)(nil), // 73: openshell.v1.DeleteProviderProfileResponse - (*GetSandboxProviderEnvironmentRequest)(nil), // 74: openshell.v1.GetSandboxProviderEnvironmentRequest - (*GetSandboxProviderEnvironmentResponse)(nil), // 75: openshell.v1.GetSandboxProviderEnvironmentResponse - (*UpdateConfigRequest)(nil), // 76: openshell.v1.UpdateConfigRequest - (*PolicyMergeOperation)(nil), // 77: openshell.v1.PolicyMergeOperation - (*AddNetworkRule)(nil), // 78: openshell.v1.AddNetworkRule - (*RemoveNetworkEndpoint)(nil), // 79: openshell.v1.RemoveNetworkEndpoint - (*RemoveNetworkRule)(nil), // 80: openshell.v1.RemoveNetworkRule - (*AddDenyRules)(nil), // 81: openshell.v1.AddDenyRules - (*AddAllowRules)(nil), // 82: openshell.v1.AddAllowRules - (*RemoveNetworkBinary)(nil), // 83: openshell.v1.RemoveNetworkBinary - (*UpdateConfigResponse)(nil), // 84: openshell.v1.UpdateConfigResponse - (*GetSandboxPolicyStatusRequest)(nil), // 85: openshell.v1.GetSandboxPolicyStatusRequest - (*GetSandboxPolicyStatusResponse)(nil), // 86: openshell.v1.GetSandboxPolicyStatusResponse - (*ListSandboxPoliciesRequest)(nil), // 87: openshell.v1.ListSandboxPoliciesRequest - (*ListSandboxPoliciesResponse)(nil), // 88: openshell.v1.ListSandboxPoliciesResponse - (*ReportPolicyStatusRequest)(nil), // 89: openshell.v1.ReportPolicyStatusRequest - (*ReportPolicyStatusResponse)(nil), // 90: openshell.v1.ReportPolicyStatusResponse - (*SandboxPolicyRevision)(nil), // 91: openshell.v1.SandboxPolicyRevision - (*GetSandboxLogsRequest)(nil), // 92: openshell.v1.GetSandboxLogsRequest - (*PushSandboxLogsRequest)(nil), // 93: openshell.v1.PushSandboxLogsRequest - (*PushSandboxLogsResponse)(nil), // 94: openshell.v1.PushSandboxLogsResponse - (*GetSandboxLogsResponse)(nil), // 95: openshell.v1.GetSandboxLogsResponse - (*SupervisorMessage)(nil), // 96: openshell.v1.SupervisorMessage - (*GatewayMessage)(nil), // 97: openshell.v1.GatewayMessage - (*SupervisorHello)(nil), // 98: openshell.v1.SupervisorHello - (*SessionAccepted)(nil), // 99: openshell.v1.SessionAccepted - (*SessionRejected)(nil), // 100: openshell.v1.SessionRejected - (*SupervisorHeartbeat)(nil), // 101: openshell.v1.SupervisorHeartbeat - (*GatewayHeartbeat)(nil), // 102: openshell.v1.GatewayHeartbeat - (*RelayOpen)(nil), // 103: openshell.v1.RelayOpen - (*SshRelayTarget)(nil), // 104: openshell.v1.SshRelayTarget - (*TcpRelayTarget)(nil), // 105: openshell.v1.TcpRelayTarget - (*RelayInit)(nil), // 106: openshell.v1.RelayInit - (*RelayFrame)(nil), // 107: openshell.v1.RelayFrame - (*RelayOpenResult)(nil), // 108: openshell.v1.RelayOpenResult - (*RelayClose)(nil), // 109: openshell.v1.RelayClose - (*L7RequestSample)(nil), // 110: openshell.v1.L7RequestSample - (*DenialSummary)(nil), // 111: openshell.v1.DenialSummary - (*PolicyChunk)(nil), // 112: openshell.v1.PolicyChunk - (*DraftPolicyUpdate)(nil), // 113: openshell.v1.DraftPolicyUpdate - (*SubmitPolicyAnalysisRequest)(nil), // 114: openshell.v1.SubmitPolicyAnalysisRequest - (*SubmitPolicyAnalysisResponse)(nil), // 115: openshell.v1.SubmitPolicyAnalysisResponse - (*GetDraftPolicyRequest)(nil), // 116: openshell.v1.GetDraftPolicyRequest - (*GetDraftPolicyResponse)(nil), // 117: openshell.v1.GetDraftPolicyResponse - (*ApproveDraftChunkRequest)(nil), // 118: openshell.v1.ApproveDraftChunkRequest - (*ApproveDraftChunkResponse)(nil), // 119: openshell.v1.ApproveDraftChunkResponse - (*RejectDraftChunkRequest)(nil), // 120: openshell.v1.RejectDraftChunkRequest - (*RejectDraftChunkResponse)(nil), // 121: openshell.v1.RejectDraftChunkResponse - (*ApproveAllDraftChunksRequest)(nil), // 122: openshell.v1.ApproveAllDraftChunksRequest - (*ApproveAllDraftChunksResponse)(nil), // 123: openshell.v1.ApproveAllDraftChunksResponse - (*EditDraftChunkRequest)(nil), // 124: openshell.v1.EditDraftChunkRequest - (*EditDraftChunkResponse)(nil), // 125: openshell.v1.EditDraftChunkResponse - (*UndoDraftChunkRequest)(nil), // 126: openshell.v1.UndoDraftChunkRequest - (*UndoDraftChunkResponse)(nil), // 127: openshell.v1.UndoDraftChunkResponse - (*ClearDraftChunksRequest)(nil), // 128: openshell.v1.ClearDraftChunksRequest - (*ClearDraftChunksResponse)(nil), // 129: openshell.v1.ClearDraftChunksResponse - (*GetDraftHistoryRequest)(nil), // 130: openshell.v1.GetDraftHistoryRequest - (*DraftHistoryEntry)(nil), // 131: openshell.v1.DraftHistoryEntry - (*GetDraftHistoryResponse)(nil), // 132: openshell.v1.GetDraftHistoryResponse - (*PolicyRevisionPayload)(nil), // 133: openshell.v1.PolicyRevisionPayload - (*DraftChunkPayload)(nil), // 134: openshell.v1.DraftChunkPayload - (*StoredPolicyRevision)(nil), // 135: openshell.v1.StoredPolicyRevision - (*StoredDraftChunk)(nil), // 136: openshell.v1.StoredDraftChunk - nil, // 137: openshell.v1.SandboxSpec.EnvironmentEntry - nil, // 138: openshell.v1.SandboxTemplate.LabelsEntry - nil, // 139: openshell.v1.SandboxTemplate.AnnotationsEntry - nil, // 140: openshell.v1.SandboxTemplate.EnvironmentEntry - nil, // 141: openshell.v1.PlatformEvent.MetadataEntry - nil, // 142: openshell.v1.CreateSandboxRequest.LabelsEntry - nil, // 143: openshell.v1.ExecSandboxRequest.EnvironmentEntry - nil, // 144: openshell.v1.SandboxLogLine.FieldsEntry - nil, // 145: openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry - (*datamodelv1.ObjectMeta)(nil), // 146: openshell.datamodel.v1.ObjectMeta - (*sandboxv1.SandboxPolicy)(nil), // 147: openshell.sandbox.v1.SandboxPolicy - (*structpb.Struct)(nil), // 148: google.protobuf.Struct - (*datamodelv1.Provider)(nil), // 149: openshell.datamodel.v1.Provider - (*sandboxv1.NetworkEndpoint)(nil), // 150: openshell.sandbox.v1.NetworkEndpoint - (*sandboxv1.NetworkBinary)(nil), // 151: openshell.sandbox.v1.NetworkBinary - (*sandboxv1.SettingValue)(nil), // 152: openshell.sandbox.v1.SettingValue - (*sandboxv1.NetworkPolicyRule)(nil), // 153: openshell.sandbox.v1.NetworkPolicyRule - (*sandboxv1.L7DenyRule)(nil), // 154: openshell.sandbox.v1.L7DenyRule - (*sandboxv1.L7Rule)(nil), // 155: openshell.sandbox.v1.L7Rule - (*sandboxv1.GetSandboxConfigRequest)(nil), // 156: openshell.sandbox.v1.GetSandboxConfigRequest - (*sandboxv1.GetGatewayConfigRequest)(nil), // 157: openshell.sandbox.v1.GetGatewayConfigRequest - (*sandboxv1.GetSandboxConfigResponse)(nil), // 158: openshell.sandbox.v1.GetSandboxConfigResponse - (*sandboxv1.GetGatewayConfigResponse)(nil), // 159: openshell.sandbox.v1.GetGatewayConfigResponse -} -var file_openshell_proto_depIdxs = []int32{ - 3, // 0: openshell.v1.HealthResponse.status:type_name -> openshell.v1.ServiceStatus - 146, // 1: openshell.v1.Sandbox.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 7, // 2: openshell.v1.Sandbox.spec:type_name -> openshell.v1.SandboxSpec - 9, // 3: openshell.v1.Sandbox.status:type_name -> openshell.v1.SandboxStatus - 0, // 4: openshell.v1.Sandbox.phase:type_name -> openshell.v1.SandboxPhase - 137, // 5: openshell.v1.SandboxSpec.environment:type_name -> openshell.v1.SandboxSpec.EnvironmentEntry - 8, // 6: openshell.v1.SandboxSpec.template:type_name -> openshell.v1.SandboxTemplate - 147, // 7: openshell.v1.SandboxSpec.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 138, // 8: openshell.v1.SandboxTemplate.labels:type_name -> openshell.v1.SandboxTemplate.LabelsEntry - 139, // 9: openshell.v1.SandboxTemplate.annotations:type_name -> openshell.v1.SandboxTemplate.AnnotationsEntry - 140, // 10: openshell.v1.SandboxTemplate.environment:type_name -> openshell.v1.SandboxTemplate.EnvironmentEntry - 148, // 11: openshell.v1.SandboxTemplate.resources:type_name -> google.protobuf.Struct - 148, // 12: openshell.v1.SandboxTemplate.volume_claim_templates:type_name -> google.protobuf.Struct - 10, // 13: openshell.v1.SandboxStatus.conditions:type_name -> openshell.v1.SandboxCondition - 141, // 14: openshell.v1.PlatformEvent.metadata:type_name -> openshell.v1.PlatformEvent.MetadataEntry - 7, // 15: openshell.v1.CreateSandboxRequest.spec:type_name -> openshell.v1.SandboxSpec - 142, // 16: openshell.v1.CreateSandboxRequest.labels:type_name -> openshell.v1.CreateSandboxRequest.LabelsEntry - 6, // 17: openshell.v1.SandboxResponse.sandbox:type_name -> openshell.v1.Sandbox - 6, // 18: openshell.v1.ListSandboxesResponse.sandboxes:type_name -> openshell.v1.Sandbox - 149, // 19: openshell.v1.ListSandboxProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider - 6, // 20: openshell.v1.AttachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox - 6, // 21: openshell.v1.DetachSandboxProviderResponse.sandbox:type_name -> openshell.v1.Sandbox - 34, // 22: openshell.v1.ListServicesResponse.services:type_name -> openshell.v1.ServiceEndpointResponse - 146, // 23: openshell.v1.ServiceEndpoint.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 33, // 24: openshell.v1.ServiceEndpointResponse.endpoint:type_name -> openshell.v1.ServiceEndpoint - 143, // 25: openshell.v1.ExecSandboxRequest.environment:type_name -> openshell.v1.ExecSandboxRequest.EnvironmentEntry - 38, // 26: openshell.v1.ExecSandboxEvent.stdout:type_name -> openshell.v1.ExecSandboxStdout - 39, // 27: openshell.v1.ExecSandboxEvent.stderr:type_name -> openshell.v1.ExecSandboxStderr - 40, // 28: openshell.v1.ExecSandboxEvent.exit:type_name -> openshell.v1.ExecSandboxExit - 104, // 29: openshell.v1.TcpForwardInit.ssh:type_name -> openshell.v1.SshRelayTarget - 105, // 30: openshell.v1.TcpForwardInit.tcp:type_name -> openshell.v1.TcpRelayTarget - 42, // 31: openshell.v1.TcpForwardFrame.init:type_name -> openshell.v1.TcpForwardInit - 37, // 32: openshell.v1.ExecSandboxInput.start:type_name -> openshell.v1.ExecSandboxRequest - 45, // 33: openshell.v1.ExecSandboxInput.resize:type_name -> openshell.v1.ExecSandboxWindowResize - 146, // 34: openshell.v1.SshSession.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 6, // 35: openshell.v1.SandboxStreamEvent.sandbox:type_name -> openshell.v1.Sandbox - 49, // 36: openshell.v1.SandboxStreamEvent.log:type_name -> openshell.v1.SandboxLogLine - 11, // 37: openshell.v1.SandboxStreamEvent.event:type_name -> openshell.v1.PlatformEvent - 50, // 38: openshell.v1.SandboxStreamEvent.warning:type_name -> openshell.v1.SandboxStreamWarning - 113, // 39: openshell.v1.SandboxStreamEvent.draft_policy_update:type_name -> openshell.v1.DraftPolicyUpdate - 144, // 40: openshell.v1.SandboxLogLine.fields:type_name -> openshell.v1.SandboxLogLine.FieldsEntry - 149, // 41: openshell.v1.CreateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider - 149, // 42: openshell.v1.UpdateProviderRequest.provider:type_name -> openshell.datamodel.v1.Provider - 149, // 43: openshell.v1.ProviderResponse.provider:type_name -> openshell.datamodel.v1.Provider - 149, // 44: openshell.v1.ListProvidersResponse.providers:type_name -> openshell.datamodel.v1.Provider - 63, // 45: openshell.v1.ProviderProfileImportItem.profile:type_name -> openshell.v1.ProviderProfile - 1, // 46: openshell.v1.ProviderProfile.category:type_name -> openshell.v1.ProviderProfileCategory - 62, // 47: openshell.v1.ProviderProfile.credentials:type_name -> openshell.v1.ProviderProfileCredential - 150, // 48: openshell.v1.ProviderProfile.endpoints:type_name -> openshell.sandbox.v1.NetworkEndpoint - 151, // 49: openshell.v1.ProviderProfile.binaries:type_name -> openshell.sandbox.v1.NetworkBinary - 146, // 50: openshell.v1.StoredProviderProfile.metadata:type_name -> openshell.datamodel.v1.ObjectMeta - 63, // 51: openshell.v1.StoredProviderProfile.profile:type_name -> openshell.v1.ProviderProfile - 63, // 52: openshell.v1.ProviderProfileResponse.profile:type_name -> openshell.v1.ProviderProfile - 63, // 53: openshell.v1.ListProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile - 60, // 54: openshell.v1.ImportProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem - 61, // 55: openshell.v1.ImportProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 63, // 56: openshell.v1.ImportProviderProfilesResponse.profiles:type_name -> openshell.v1.ProviderProfile - 60, // 57: openshell.v1.LintProviderProfilesRequest.profiles:type_name -> openshell.v1.ProviderProfileImportItem - 61, // 58: openshell.v1.LintProviderProfilesResponse.diagnostics:type_name -> openshell.v1.ProviderProfileDiagnostic - 145, // 59: openshell.v1.GetSandboxProviderEnvironmentResponse.environment:type_name -> openshell.v1.GetSandboxProviderEnvironmentResponse.EnvironmentEntry - 147, // 60: openshell.v1.UpdateConfigRequest.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 152, // 61: openshell.v1.UpdateConfigRequest.setting_value:type_name -> openshell.sandbox.v1.SettingValue - 77, // 62: openshell.v1.UpdateConfigRequest.merge_operations:type_name -> openshell.v1.PolicyMergeOperation - 78, // 63: openshell.v1.PolicyMergeOperation.add_rule:type_name -> openshell.v1.AddNetworkRule - 79, // 64: openshell.v1.PolicyMergeOperation.remove_endpoint:type_name -> openshell.v1.RemoveNetworkEndpoint - 80, // 65: openshell.v1.PolicyMergeOperation.remove_rule:type_name -> openshell.v1.RemoveNetworkRule - 81, // 66: openshell.v1.PolicyMergeOperation.add_deny_rules:type_name -> openshell.v1.AddDenyRules - 82, // 67: openshell.v1.PolicyMergeOperation.add_allow_rules:type_name -> openshell.v1.AddAllowRules - 83, // 68: openshell.v1.PolicyMergeOperation.remove_binary:type_name -> openshell.v1.RemoveNetworkBinary - 153, // 69: openshell.v1.AddNetworkRule.rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 154, // 70: openshell.v1.AddDenyRules.deny_rules:type_name -> openshell.sandbox.v1.L7DenyRule - 155, // 71: openshell.v1.AddAllowRules.rules:type_name -> openshell.sandbox.v1.L7Rule - 91, // 72: openshell.v1.GetSandboxPolicyStatusResponse.revision:type_name -> openshell.v1.SandboxPolicyRevision - 91, // 73: openshell.v1.ListSandboxPoliciesResponse.revisions:type_name -> openshell.v1.SandboxPolicyRevision - 2, // 74: openshell.v1.ReportPolicyStatusRequest.status:type_name -> openshell.v1.PolicyStatus - 2, // 75: openshell.v1.SandboxPolicyRevision.status:type_name -> openshell.v1.PolicyStatus - 147, // 76: openshell.v1.SandboxPolicyRevision.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 49, // 77: openshell.v1.PushSandboxLogsRequest.logs:type_name -> openshell.v1.SandboxLogLine - 49, // 78: openshell.v1.GetSandboxLogsResponse.logs:type_name -> openshell.v1.SandboxLogLine - 98, // 79: openshell.v1.SupervisorMessage.hello:type_name -> openshell.v1.SupervisorHello - 101, // 80: openshell.v1.SupervisorMessage.heartbeat:type_name -> openshell.v1.SupervisorHeartbeat - 108, // 81: openshell.v1.SupervisorMessage.relay_open_result:type_name -> openshell.v1.RelayOpenResult - 109, // 82: openshell.v1.SupervisorMessage.relay_close:type_name -> openshell.v1.RelayClose - 99, // 83: openshell.v1.GatewayMessage.session_accepted:type_name -> openshell.v1.SessionAccepted - 100, // 84: openshell.v1.GatewayMessage.session_rejected:type_name -> openshell.v1.SessionRejected - 102, // 85: openshell.v1.GatewayMessage.heartbeat:type_name -> openshell.v1.GatewayHeartbeat - 103, // 86: openshell.v1.GatewayMessage.relay_open:type_name -> openshell.v1.RelayOpen - 109, // 87: openshell.v1.GatewayMessage.relay_close:type_name -> openshell.v1.RelayClose - 104, // 88: openshell.v1.RelayOpen.ssh:type_name -> openshell.v1.SshRelayTarget - 105, // 89: openshell.v1.RelayOpen.tcp:type_name -> openshell.v1.TcpRelayTarget - 106, // 90: openshell.v1.RelayFrame.init:type_name -> openshell.v1.RelayInit - 110, // 91: openshell.v1.DenialSummary.l7_request_samples:type_name -> openshell.v1.L7RequestSample - 153, // 92: openshell.v1.PolicyChunk.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 111, // 93: openshell.v1.SubmitPolicyAnalysisRequest.summaries:type_name -> openshell.v1.DenialSummary - 112, // 94: openshell.v1.SubmitPolicyAnalysisRequest.proposed_chunks:type_name -> openshell.v1.PolicyChunk - 112, // 95: openshell.v1.GetDraftPolicyResponse.chunks:type_name -> openshell.v1.PolicyChunk - 153, // 96: openshell.v1.EditDraftChunkRequest.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 131, // 97: openshell.v1.GetDraftHistoryResponse.entries:type_name -> openshell.v1.DraftHistoryEntry - 147, // 98: openshell.v1.PolicyRevisionPayload.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 153, // 99: openshell.v1.DraftChunkPayload.proposed_rule:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 4, // 100: openshell.v1.OpenShell.Health:input_type -> openshell.v1.HealthRequest - 12, // 101: openshell.v1.OpenShell.CreateSandbox:input_type -> openshell.v1.CreateSandboxRequest - 13, // 102: openshell.v1.OpenShell.GetSandbox:input_type -> openshell.v1.GetSandboxRequest - 14, // 103: openshell.v1.OpenShell.ListSandboxes:input_type -> openshell.v1.ListSandboxesRequest - 15, // 104: openshell.v1.OpenShell.ListSandboxProviders:input_type -> openshell.v1.ListSandboxProvidersRequest - 16, // 105: openshell.v1.OpenShell.AttachSandboxProvider:input_type -> openshell.v1.AttachSandboxProviderRequest - 17, // 106: openshell.v1.OpenShell.DetachSandboxProvider:input_type -> openshell.v1.DetachSandboxProviderRequest - 18, // 107: openshell.v1.OpenShell.DeleteSandbox:input_type -> openshell.v1.DeleteSandboxRequest - 25, // 108: openshell.v1.OpenShell.CreateSshSession:input_type -> openshell.v1.CreateSshSessionRequest - 27, // 109: openshell.v1.OpenShell.ExposeService:input_type -> openshell.v1.ExposeServiceRequest - 28, // 110: openshell.v1.OpenShell.GetService:input_type -> openshell.v1.GetServiceRequest - 29, // 111: openshell.v1.OpenShell.ListServices:input_type -> openshell.v1.ListServicesRequest - 31, // 112: openshell.v1.OpenShell.DeleteService:input_type -> openshell.v1.DeleteServiceRequest - 35, // 113: openshell.v1.OpenShell.RevokeSshSession:input_type -> openshell.v1.RevokeSshSessionRequest - 37, // 114: openshell.v1.OpenShell.ExecSandbox:input_type -> openshell.v1.ExecSandboxRequest - 43, // 115: openshell.v1.OpenShell.ForwardTcp:input_type -> openshell.v1.TcpForwardFrame - 44, // 116: openshell.v1.OpenShell.ExecSandboxInteractive:input_type -> openshell.v1.ExecSandboxInput - 51, // 117: openshell.v1.OpenShell.CreateProvider:input_type -> openshell.v1.CreateProviderRequest - 52, // 118: openshell.v1.OpenShell.GetProvider:input_type -> openshell.v1.GetProviderRequest - 53, // 119: openshell.v1.OpenShell.ListProviders:input_type -> openshell.v1.ListProvidersRequest - 58, // 120: openshell.v1.OpenShell.ListProviderProfiles:input_type -> openshell.v1.ListProviderProfilesRequest - 59, // 121: openshell.v1.OpenShell.GetProviderProfile:input_type -> openshell.v1.GetProviderProfileRequest - 67, // 122: openshell.v1.OpenShell.ImportProviderProfiles:input_type -> openshell.v1.ImportProviderProfilesRequest - 69, // 123: openshell.v1.OpenShell.LintProviderProfiles:input_type -> openshell.v1.LintProviderProfilesRequest - 54, // 124: openshell.v1.OpenShell.UpdateProvider:input_type -> openshell.v1.UpdateProviderRequest - 55, // 125: openshell.v1.OpenShell.DeleteProvider:input_type -> openshell.v1.DeleteProviderRequest - 72, // 126: openshell.v1.OpenShell.DeleteProviderProfile:input_type -> openshell.v1.DeleteProviderProfileRequest - 156, // 127: openshell.v1.OpenShell.GetSandboxConfig:input_type -> openshell.sandbox.v1.GetSandboxConfigRequest - 157, // 128: openshell.v1.OpenShell.GetGatewayConfig:input_type -> openshell.sandbox.v1.GetGatewayConfigRequest - 76, // 129: openshell.v1.OpenShell.UpdateConfig:input_type -> openshell.v1.UpdateConfigRequest - 85, // 130: openshell.v1.OpenShell.GetSandboxPolicyStatus:input_type -> openshell.v1.GetSandboxPolicyStatusRequest - 87, // 131: openshell.v1.OpenShell.ListSandboxPolicies:input_type -> openshell.v1.ListSandboxPoliciesRequest - 89, // 132: openshell.v1.OpenShell.ReportPolicyStatus:input_type -> openshell.v1.ReportPolicyStatusRequest - 74, // 133: openshell.v1.OpenShell.GetSandboxProviderEnvironment:input_type -> openshell.v1.GetSandboxProviderEnvironmentRequest - 92, // 134: openshell.v1.OpenShell.GetSandboxLogs:input_type -> openshell.v1.GetSandboxLogsRequest - 93, // 135: openshell.v1.OpenShell.PushSandboxLogs:input_type -> openshell.v1.PushSandboxLogsRequest - 96, // 136: openshell.v1.OpenShell.ConnectSupervisor:input_type -> openshell.v1.SupervisorMessage - 107, // 137: openshell.v1.OpenShell.RelayStream:input_type -> openshell.v1.RelayFrame - 47, // 138: openshell.v1.OpenShell.WatchSandbox:input_type -> openshell.v1.WatchSandboxRequest - 114, // 139: openshell.v1.OpenShell.SubmitPolicyAnalysis:input_type -> openshell.v1.SubmitPolicyAnalysisRequest - 116, // 140: openshell.v1.OpenShell.GetDraftPolicy:input_type -> openshell.v1.GetDraftPolicyRequest - 118, // 141: openshell.v1.OpenShell.ApproveDraftChunk:input_type -> openshell.v1.ApproveDraftChunkRequest - 120, // 142: openshell.v1.OpenShell.RejectDraftChunk:input_type -> openshell.v1.RejectDraftChunkRequest - 122, // 143: openshell.v1.OpenShell.ApproveAllDraftChunks:input_type -> openshell.v1.ApproveAllDraftChunksRequest - 124, // 144: openshell.v1.OpenShell.EditDraftChunk:input_type -> openshell.v1.EditDraftChunkRequest - 126, // 145: openshell.v1.OpenShell.UndoDraftChunk:input_type -> openshell.v1.UndoDraftChunkRequest - 128, // 146: openshell.v1.OpenShell.ClearDraftChunks:input_type -> openshell.v1.ClearDraftChunksRequest - 130, // 147: openshell.v1.OpenShell.GetDraftHistory:input_type -> openshell.v1.GetDraftHistoryRequest - 5, // 148: openshell.v1.OpenShell.Health:output_type -> openshell.v1.HealthResponse - 19, // 149: openshell.v1.OpenShell.CreateSandbox:output_type -> openshell.v1.SandboxResponse - 19, // 150: openshell.v1.OpenShell.GetSandbox:output_type -> openshell.v1.SandboxResponse - 20, // 151: openshell.v1.OpenShell.ListSandboxes:output_type -> openshell.v1.ListSandboxesResponse - 21, // 152: openshell.v1.OpenShell.ListSandboxProviders:output_type -> openshell.v1.ListSandboxProvidersResponse - 22, // 153: openshell.v1.OpenShell.AttachSandboxProvider:output_type -> openshell.v1.AttachSandboxProviderResponse - 23, // 154: openshell.v1.OpenShell.DetachSandboxProvider:output_type -> openshell.v1.DetachSandboxProviderResponse - 24, // 155: openshell.v1.OpenShell.DeleteSandbox:output_type -> openshell.v1.DeleteSandboxResponse - 26, // 156: openshell.v1.OpenShell.CreateSshSession:output_type -> openshell.v1.CreateSshSessionResponse - 34, // 157: openshell.v1.OpenShell.ExposeService:output_type -> openshell.v1.ServiceEndpointResponse - 34, // 158: openshell.v1.OpenShell.GetService:output_type -> openshell.v1.ServiceEndpointResponse - 30, // 159: openshell.v1.OpenShell.ListServices:output_type -> openshell.v1.ListServicesResponse - 32, // 160: openshell.v1.OpenShell.DeleteService:output_type -> openshell.v1.DeleteServiceResponse - 36, // 161: openshell.v1.OpenShell.RevokeSshSession:output_type -> openshell.v1.RevokeSshSessionResponse - 41, // 162: openshell.v1.OpenShell.ExecSandbox:output_type -> openshell.v1.ExecSandboxEvent - 43, // 163: openshell.v1.OpenShell.ForwardTcp:output_type -> openshell.v1.TcpForwardFrame - 41, // 164: openshell.v1.OpenShell.ExecSandboxInteractive:output_type -> openshell.v1.ExecSandboxEvent - 56, // 165: openshell.v1.OpenShell.CreateProvider:output_type -> openshell.v1.ProviderResponse - 56, // 166: openshell.v1.OpenShell.GetProvider:output_type -> openshell.v1.ProviderResponse - 57, // 167: openshell.v1.OpenShell.ListProviders:output_type -> openshell.v1.ListProvidersResponse - 66, // 168: openshell.v1.OpenShell.ListProviderProfiles:output_type -> openshell.v1.ListProviderProfilesResponse - 65, // 169: openshell.v1.OpenShell.GetProviderProfile:output_type -> openshell.v1.ProviderProfileResponse - 68, // 170: openshell.v1.OpenShell.ImportProviderProfiles:output_type -> openshell.v1.ImportProviderProfilesResponse - 70, // 171: openshell.v1.OpenShell.LintProviderProfiles:output_type -> openshell.v1.LintProviderProfilesResponse - 56, // 172: openshell.v1.OpenShell.UpdateProvider:output_type -> openshell.v1.ProviderResponse - 71, // 173: openshell.v1.OpenShell.DeleteProvider:output_type -> openshell.v1.DeleteProviderResponse - 73, // 174: openshell.v1.OpenShell.DeleteProviderProfile:output_type -> openshell.v1.DeleteProviderProfileResponse - 158, // 175: openshell.v1.OpenShell.GetSandboxConfig:output_type -> openshell.sandbox.v1.GetSandboxConfigResponse - 159, // 176: openshell.v1.OpenShell.GetGatewayConfig:output_type -> openshell.sandbox.v1.GetGatewayConfigResponse - 84, // 177: openshell.v1.OpenShell.UpdateConfig:output_type -> openshell.v1.UpdateConfigResponse - 86, // 178: openshell.v1.OpenShell.GetSandboxPolicyStatus:output_type -> openshell.v1.GetSandboxPolicyStatusResponse - 88, // 179: openshell.v1.OpenShell.ListSandboxPolicies:output_type -> openshell.v1.ListSandboxPoliciesResponse - 90, // 180: openshell.v1.OpenShell.ReportPolicyStatus:output_type -> openshell.v1.ReportPolicyStatusResponse - 75, // 181: openshell.v1.OpenShell.GetSandboxProviderEnvironment:output_type -> openshell.v1.GetSandboxProviderEnvironmentResponse - 95, // 182: openshell.v1.OpenShell.GetSandboxLogs:output_type -> openshell.v1.GetSandboxLogsResponse - 94, // 183: openshell.v1.OpenShell.PushSandboxLogs:output_type -> openshell.v1.PushSandboxLogsResponse - 97, // 184: openshell.v1.OpenShell.ConnectSupervisor:output_type -> openshell.v1.GatewayMessage - 107, // 185: openshell.v1.OpenShell.RelayStream:output_type -> openshell.v1.RelayFrame - 48, // 186: openshell.v1.OpenShell.WatchSandbox:output_type -> openshell.v1.SandboxStreamEvent - 115, // 187: openshell.v1.OpenShell.SubmitPolicyAnalysis:output_type -> openshell.v1.SubmitPolicyAnalysisResponse - 117, // 188: openshell.v1.OpenShell.GetDraftPolicy:output_type -> openshell.v1.GetDraftPolicyResponse - 119, // 189: openshell.v1.OpenShell.ApproveDraftChunk:output_type -> openshell.v1.ApproveDraftChunkResponse - 121, // 190: openshell.v1.OpenShell.RejectDraftChunk:output_type -> openshell.v1.RejectDraftChunkResponse - 123, // 191: openshell.v1.OpenShell.ApproveAllDraftChunks:output_type -> openshell.v1.ApproveAllDraftChunksResponse - 125, // 192: openshell.v1.OpenShell.EditDraftChunk:output_type -> openshell.v1.EditDraftChunkResponse - 127, // 193: openshell.v1.OpenShell.UndoDraftChunk:output_type -> openshell.v1.UndoDraftChunkResponse - 129, // 194: openshell.v1.OpenShell.ClearDraftChunks:output_type -> openshell.v1.ClearDraftChunksResponse - 132, // 195: openshell.v1.OpenShell.GetDraftHistory:output_type -> openshell.v1.GetDraftHistoryResponse - 148, // [148:196] is the sub-list for method output_type - 100, // [100:148] is the sub-list for method input_type - 100, // [100:100] is the sub-list for extension type_name - 100, // [100:100] is the sub-list for extension extendee - 0, // [0:100] is the sub-list for field type_name -} - -func init() { file_openshell_proto_init() } -func file_openshell_proto_init() { - if File_openshell_proto != nil { - return - } - file_openshell_proto_msgTypes[4].OneofWrappers = []any{} - file_openshell_proto_msgTypes[37].OneofWrappers = []any{ - (*ExecSandboxEvent_Stdout)(nil), - (*ExecSandboxEvent_Stderr)(nil), - (*ExecSandboxEvent_Exit)(nil), - } - file_openshell_proto_msgTypes[38].OneofWrappers = []any{ - (*TcpForwardInit_Ssh)(nil), - (*TcpForwardInit_Tcp)(nil), - } - file_openshell_proto_msgTypes[39].OneofWrappers = []any{ - (*TcpForwardFrame_Init)(nil), - (*TcpForwardFrame_Data)(nil), - } - file_openshell_proto_msgTypes[40].OneofWrappers = []any{ - (*ExecSandboxInput_Start)(nil), - (*ExecSandboxInput_Stdin)(nil), - (*ExecSandboxInput_Resize)(nil), - } - file_openshell_proto_msgTypes[44].OneofWrappers = []any{ - (*SandboxStreamEvent_Sandbox)(nil), - (*SandboxStreamEvent_Log)(nil), - (*SandboxStreamEvent_Event)(nil), - (*SandboxStreamEvent_Warning)(nil), - (*SandboxStreamEvent_DraftPolicyUpdate)(nil), - } - file_openshell_proto_msgTypes[73].OneofWrappers = []any{ - (*PolicyMergeOperation_AddRule)(nil), - (*PolicyMergeOperation_RemoveEndpoint)(nil), - (*PolicyMergeOperation_RemoveRule)(nil), - (*PolicyMergeOperation_AddDenyRules)(nil), - (*PolicyMergeOperation_AddAllowRules)(nil), - (*PolicyMergeOperation_RemoveBinary)(nil), - } - file_openshell_proto_msgTypes[92].OneofWrappers = []any{ - (*SupervisorMessage_Hello)(nil), - (*SupervisorMessage_Heartbeat)(nil), - (*SupervisorMessage_RelayOpenResult)(nil), - (*SupervisorMessage_RelayClose)(nil), - } - file_openshell_proto_msgTypes[93].OneofWrappers = []any{ - (*GatewayMessage_SessionAccepted)(nil), - (*GatewayMessage_SessionRejected)(nil), - (*GatewayMessage_Heartbeat)(nil), - (*GatewayMessage_RelayOpen)(nil), - (*GatewayMessage_RelayClose)(nil), - } - file_openshell_proto_msgTypes[99].OneofWrappers = []any{ - (*RelayOpen_Ssh)(nil), - (*RelayOpen_Tcp)(nil), - } - file_openshell_proto_msgTypes[103].OneofWrappers = []any{ - (*RelayFrame_Init)(nil), - (*RelayFrame_Data)(nil), - } - file_openshell_proto_msgTypes[131].OneofWrappers = []any{} - file_openshell_proto_msgTypes[132].OneofWrappers = []any{} - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_openshell_proto_rawDesc), len(file_openshell_proto_rawDesc)), - NumEnums: 4, - NumMessages: 142, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_openshell_proto_goTypes, - DependencyIndexes: file_openshell_proto_depIdxs, - EnumInfos: file_openshell_proto_enumTypes, - MessageInfos: file_openshell_proto_msgTypes, - }.Build() - File_openshell_proto = out.File - file_openshell_proto_goTypes = nil - file_openshell_proto_depIdxs = nil -} diff --git a/go/api/openshell/gen/openshellv1/openshell_grpc.pb.go b/go/api/openshell/gen/openshellv1/openshell_grpc.pb.go deleted file mode 100644 index bafdbaf728..0000000000 --- a/go/api/openshell/gen/openshellv1/openshell_grpc.pb.go +++ /dev/null @@ -1,2047 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.5.1 -// - protoc (unknown) -// source: openshell.proto - -package openshellv1 - -import ( - context "context" - sandboxv1 "github.com/kagent-dev/kagent/go/api/openshell/gen/sandboxv1" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.64.0 or later. -const _ = grpc.SupportPackageIsVersion9 - -const ( - OpenShell_Health_FullMethodName = "/openshell.v1.OpenShell/Health" - OpenShell_CreateSandbox_FullMethodName = "/openshell.v1.OpenShell/CreateSandbox" - OpenShell_GetSandbox_FullMethodName = "/openshell.v1.OpenShell/GetSandbox" - OpenShell_ListSandboxes_FullMethodName = "/openshell.v1.OpenShell/ListSandboxes" - OpenShell_ListSandboxProviders_FullMethodName = "/openshell.v1.OpenShell/ListSandboxProviders" - OpenShell_AttachSandboxProvider_FullMethodName = "/openshell.v1.OpenShell/AttachSandboxProvider" - OpenShell_DetachSandboxProvider_FullMethodName = "/openshell.v1.OpenShell/DetachSandboxProvider" - OpenShell_DeleteSandbox_FullMethodName = "/openshell.v1.OpenShell/DeleteSandbox" - OpenShell_CreateSshSession_FullMethodName = "/openshell.v1.OpenShell/CreateSshSession" - OpenShell_ExposeService_FullMethodName = "/openshell.v1.OpenShell/ExposeService" - OpenShell_GetService_FullMethodName = "/openshell.v1.OpenShell/GetService" - OpenShell_ListServices_FullMethodName = "/openshell.v1.OpenShell/ListServices" - OpenShell_DeleteService_FullMethodName = "/openshell.v1.OpenShell/DeleteService" - OpenShell_RevokeSshSession_FullMethodName = "/openshell.v1.OpenShell/RevokeSshSession" - OpenShell_ExecSandbox_FullMethodName = "/openshell.v1.OpenShell/ExecSandbox" - OpenShell_ForwardTcp_FullMethodName = "/openshell.v1.OpenShell/ForwardTcp" - OpenShell_ExecSandboxInteractive_FullMethodName = "/openshell.v1.OpenShell/ExecSandboxInteractive" - OpenShell_CreateProvider_FullMethodName = "/openshell.v1.OpenShell/CreateProvider" - OpenShell_GetProvider_FullMethodName = "/openshell.v1.OpenShell/GetProvider" - OpenShell_ListProviders_FullMethodName = "/openshell.v1.OpenShell/ListProviders" - OpenShell_ListProviderProfiles_FullMethodName = "/openshell.v1.OpenShell/ListProviderProfiles" - OpenShell_GetProviderProfile_FullMethodName = "/openshell.v1.OpenShell/GetProviderProfile" - OpenShell_ImportProviderProfiles_FullMethodName = "/openshell.v1.OpenShell/ImportProviderProfiles" - OpenShell_LintProviderProfiles_FullMethodName = "/openshell.v1.OpenShell/LintProviderProfiles" - OpenShell_UpdateProvider_FullMethodName = "/openshell.v1.OpenShell/UpdateProvider" - OpenShell_DeleteProvider_FullMethodName = "/openshell.v1.OpenShell/DeleteProvider" - OpenShell_DeleteProviderProfile_FullMethodName = "/openshell.v1.OpenShell/DeleteProviderProfile" - OpenShell_GetSandboxConfig_FullMethodName = "/openshell.v1.OpenShell/GetSandboxConfig" - OpenShell_GetGatewayConfig_FullMethodName = "/openshell.v1.OpenShell/GetGatewayConfig" - OpenShell_UpdateConfig_FullMethodName = "/openshell.v1.OpenShell/UpdateConfig" - OpenShell_GetSandboxPolicyStatus_FullMethodName = "/openshell.v1.OpenShell/GetSandboxPolicyStatus" - OpenShell_ListSandboxPolicies_FullMethodName = "/openshell.v1.OpenShell/ListSandboxPolicies" - OpenShell_ReportPolicyStatus_FullMethodName = "/openshell.v1.OpenShell/ReportPolicyStatus" - OpenShell_GetSandboxProviderEnvironment_FullMethodName = "/openshell.v1.OpenShell/GetSandboxProviderEnvironment" - OpenShell_GetSandboxLogs_FullMethodName = "/openshell.v1.OpenShell/GetSandboxLogs" - OpenShell_PushSandboxLogs_FullMethodName = "/openshell.v1.OpenShell/PushSandboxLogs" - OpenShell_ConnectSupervisor_FullMethodName = "/openshell.v1.OpenShell/ConnectSupervisor" - OpenShell_RelayStream_FullMethodName = "/openshell.v1.OpenShell/RelayStream" - OpenShell_WatchSandbox_FullMethodName = "/openshell.v1.OpenShell/WatchSandbox" - OpenShell_SubmitPolicyAnalysis_FullMethodName = "/openshell.v1.OpenShell/SubmitPolicyAnalysis" - OpenShell_GetDraftPolicy_FullMethodName = "/openshell.v1.OpenShell/GetDraftPolicy" - OpenShell_ApproveDraftChunk_FullMethodName = "/openshell.v1.OpenShell/ApproveDraftChunk" - OpenShell_RejectDraftChunk_FullMethodName = "/openshell.v1.OpenShell/RejectDraftChunk" - OpenShell_ApproveAllDraftChunks_FullMethodName = "/openshell.v1.OpenShell/ApproveAllDraftChunks" - OpenShell_EditDraftChunk_FullMethodName = "/openshell.v1.OpenShell/EditDraftChunk" - OpenShell_UndoDraftChunk_FullMethodName = "/openshell.v1.OpenShell/UndoDraftChunk" - OpenShell_ClearDraftChunks_FullMethodName = "/openshell.v1.OpenShell/ClearDraftChunks" - OpenShell_GetDraftHistory_FullMethodName = "/openshell.v1.OpenShell/GetDraftHistory" -) - -// OpenShellClient is the client API for OpenShell service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -// -// OpenShell service provides sandbox, provider, and runtime management capabilities. -// -// Conventions: -// - This file owns the public API resource model exposed to OpenShell clients. -// - `Sandbox`, `SandboxSpec`, `SandboxStatus`, and `SandboxPhase` are gateway-owned -// public types. Internal compute drivers must not import or return them directly. -// - The gateway translates internal compute-driver observations into these public -// resource messages before persisting or returning them to clients. -type OpenShellClient interface { - // Check the health of the service. - Health(ctx context.Context, in *HealthRequest, opts ...grpc.CallOption) (*HealthResponse, error) - // Create a new sandbox. - CreateSandbox(ctx context.Context, in *CreateSandboxRequest, opts ...grpc.CallOption) (*SandboxResponse, error) - // Fetch a sandbox by name. - GetSandbox(ctx context.Context, in *GetSandboxRequest, opts ...grpc.CallOption) (*SandboxResponse, error) - // List sandboxes. - ListSandboxes(ctx context.Context, in *ListSandboxesRequest, opts ...grpc.CallOption) (*ListSandboxesResponse, error) - // List provider records attached to a sandbox. - ListSandboxProviders(ctx context.Context, in *ListSandboxProvidersRequest, opts ...grpc.CallOption) (*ListSandboxProvidersResponse, error) - // Attach a provider record to an existing sandbox. - AttachSandboxProvider(ctx context.Context, in *AttachSandboxProviderRequest, opts ...grpc.CallOption) (*AttachSandboxProviderResponse, error) - // Detach a provider record from an existing sandbox. - DetachSandboxProvider(ctx context.Context, in *DetachSandboxProviderRequest, opts ...grpc.CallOption) (*DetachSandboxProviderResponse, error) - // Delete a sandbox by name. - DeleteSandbox(ctx context.Context, in *DeleteSandboxRequest, opts ...grpc.CallOption) (*DeleteSandboxResponse, error) - // Create a short-lived SSH session for a sandbox. - CreateSshSession(ctx context.Context, in *CreateSshSessionRequest, opts ...grpc.CallOption) (*CreateSshSessionResponse, error) - // Create or update a sandbox HTTP service endpoint for local routing. - ExposeService(ctx context.Context, in *ExposeServiceRequest, opts ...grpc.CallOption) (*ServiceEndpointResponse, error) - // Fetch one sandbox HTTP service endpoint. - GetService(ctx context.Context, in *GetServiceRequest, opts ...grpc.CallOption) (*ServiceEndpointResponse, error) - // List sandbox HTTP service endpoints. - ListServices(ctx context.Context, in *ListServicesRequest, opts ...grpc.CallOption) (*ListServicesResponse, error) - // Delete one sandbox HTTP service endpoint. - DeleteService(ctx context.Context, in *DeleteServiceRequest, opts ...grpc.CallOption) (*DeleteServiceResponse, error) - // Revoke a previously issued SSH session. - RevokeSshSession(ctx context.Context, in *RevokeSshSessionRequest, opts ...grpc.CallOption) (*RevokeSshSessionResponse, error) - // Execute a command in a ready sandbox and stream output. - ExecSandbox(ctx context.Context, in *ExecSandboxRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ExecSandboxEvent], error) - // Forward one CLI-side TCP connection to a loopback TCP target in a sandbox. - ForwardTcp(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[TcpForwardFrame, TcpForwardFrame], error) - // Execute an interactive command with bidirectional stdin/stdout streaming. - // The first client message MUST carry an ExecSandboxInput with the start - // variant. Subsequent messages carry stdin bytes or window resize events. - ExecSandboxInteractive(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ExecSandboxInput, ExecSandboxEvent], error) - // Create a provider. - CreateProvider(ctx context.Context, in *CreateProviderRequest, opts ...grpc.CallOption) (*ProviderResponse, error) - // Fetch a provider by name. - GetProvider(ctx context.Context, in *GetProviderRequest, opts ...grpc.CallOption) (*ProviderResponse, error) - // List providers. - ListProviders(ctx context.Context, in *ListProvidersRequest, opts ...grpc.CallOption) (*ListProvidersResponse, error) - // List available provider type profiles. - ListProviderProfiles(ctx context.Context, in *ListProviderProfilesRequest, opts ...grpc.CallOption) (*ListProviderProfilesResponse, error) - // Fetch one provider type profile by id. - GetProviderProfile(ctx context.Context, in *GetProviderProfileRequest, opts ...grpc.CallOption) (*ProviderProfileResponse, error) - // Import custom provider type profiles. - ImportProviderProfiles(ctx context.Context, in *ImportProviderProfilesRequest, opts ...grpc.CallOption) (*ImportProviderProfilesResponse, error) - // Validate provider type profiles without registering them. - LintProviderProfiles(ctx context.Context, in *LintProviderProfilesRequest, opts ...grpc.CallOption) (*LintProviderProfilesResponse, error) - // Update an existing provider by name. - UpdateProvider(ctx context.Context, in *UpdateProviderRequest, opts ...grpc.CallOption) (*ProviderResponse, error) - // Delete a provider by name. - DeleteProvider(ctx context.Context, in *DeleteProviderRequest, opts ...grpc.CallOption) (*DeleteProviderResponse, error) - // Delete a custom provider type profile by id. - DeleteProviderProfile(ctx context.Context, in *DeleteProviderProfileRequest, opts ...grpc.CallOption) (*DeleteProviderProfileResponse, error) - // Get sandbox settings by id (called by sandbox entrypoint and poll loop). - GetSandboxConfig(ctx context.Context, in *sandboxv1.GetSandboxConfigRequest, opts ...grpc.CallOption) (*sandboxv1.GetSandboxConfigResponse, error) - // Get gateway-global settings. - GetGatewayConfig(ctx context.Context, in *sandboxv1.GetGatewayConfigRequest, opts ...grpc.CallOption) (*sandboxv1.GetGatewayConfigResponse, error) - // Update settings or policy at sandbox or global scope. - UpdateConfig(ctx context.Context, in *UpdateConfigRequest, opts ...grpc.CallOption) (*UpdateConfigResponse, error) - // Get the load status of a specific policy version. - GetSandboxPolicyStatus(ctx context.Context, in *GetSandboxPolicyStatusRequest, opts ...grpc.CallOption) (*GetSandboxPolicyStatusResponse, error) - // List policy history for a sandbox. - ListSandboxPolicies(ctx context.Context, in *ListSandboxPoliciesRequest, opts ...grpc.CallOption) (*ListSandboxPoliciesResponse, error) - // Report policy load result (called by sandbox after reload attempt). - ReportPolicyStatus(ctx context.Context, in *ReportPolicyStatusRequest, opts ...grpc.CallOption) (*ReportPolicyStatusResponse, error) - // Get provider environment for a sandbox (called by sandbox supervisor at startup). - GetSandboxProviderEnvironment(ctx context.Context, in *GetSandboxProviderEnvironmentRequest, opts ...grpc.CallOption) (*GetSandboxProviderEnvironmentResponse, error) - // Fetch recent sandbox logs (one-shot). - GetSandboxLogs(ctx context.Context, in *GetSandboxLogsRequest, opts ...grpc.CallOption) (*GetSandboxLogsResponse, error) - // Push sandbox supervisor logs to the server (client-streaming). - PushSandboxLogs(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[PushSandboxLogsRequest, PushSandboxLogsResponse], error) - // Persistent supervisor-to-gateway session (bidirectional streaming). - // - // The supervisor opens this stream at startup and keeps it alive for the - // sandbox lifetime. The gateway uses it to coordinate relay channels for - // SSH connect, ExecSandbox, and targetable sandbox services. Raw service - // bytes flow over RelayStream calls (separate HTTP/2 streams on the same - // connection), not over this stream. - ConnectSupervisor(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[SupervisorMessage, GatewayMessage], error) - // Raw byte relay between supervisor and gateway. - // - // The supervisor initiates this call after receiving a RelayOpen message - // on its ConnectSupervisor stream. The first RelayFrame carries a - // RelayInit with the channel_id to associate the new HTTP/2 stream with - // the pending relay slot on the gateway. Subsequent frames carry raw bytes in either - // direction between the gateway-side waiter (ForwardTcp / exec handler) - // and the supervisor-side target bridge. - // - // This rides the same TCP+TLS+HTTP/2 connection as ConnectSupervisor — - // no new TLS handshake, no reverse HTTP CONNECT. - RelayStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[RelayFrame, RelayFrame], error) - // Watch a sandbox and stream updates. - // - // This stream can include: - // - Sandbox status snapshots (phase/status) - // - OpenShell server process logs correlated by sandbox_id - // - Platform events correlated to the sandbox - WatchSandbox(ctx context.Context, in *WatchSandboxRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SandboxStreamEvent], error) - // Submit denial analysis results from sandbox (summaries + proposed chunks). - SubmitPolicyAnalysis(ctx context.Context, in *SubmitPolicyAnalysisRequest, opts ...grpc.CallOption) (*SubmitPolicyAnalysisResponse, error) - // Get draft policy recommendations for a sandbox. - GetDraftPolicy(ctx context.Context, in *GetDraftPolicyRequest, opts ...grpc.CallOption) (*GetDraftPolicyResponse, error) - // Approve a single draft policy chunk (merges into active policy). - ApproveDraftChunk(ctx context.Context, in *ApproveDraftChunkRequest, opts ...grpc.CallOption) (*ApproveDraftChunkResponse, error) - // Reject a single draft policy chunk. - RejectDraftChunk(ctx context.Context, in *RejectDraftChunkRequest, opts ...grpc.CallOption) (*RejectDraftChunkResponse, error) - // Approve all pending draft chunks (skips security-flagged unless forced). - ApproveAllDraftChunks(ctx context.Context, in *ApproveAllDraftChunksRequest, opts ...grpc.CallOption) (*ApproveAllDraftChunksResponse, error) - // Edit a pending draft chunk in-place (e.g. narrow allowed_ips). - EditDraftChunk(ctx context.Context, in *EditDraftChunkRequest, opts ...grpc.CallOption) (*EditDraftChunkResponse, error) - // Reverse an approval (remove merged rule from active policy). - UndoDraftChunk(ctx context.Context, in *UndoDraftChunkRequest, opts ...grpc.CallOption) (*UndoDraftChunkResponse, error) - // Clear all pending draft chunks for a sandbox. - ClearDraftChunks(ctx context.Context, in *ClearDraftChunksRequest, opts ...grpc.CallOption) (*ClearDraftChunksResponse, error) - // Get decision history for a sandbox's draft policy. - GetDraftHistory(ctx context.Context, in *GetDraftHistoryRequest, opts ...grpc.CallOption) (*GetDraftHistoryResponse, error) -} - -type openShellClient struct { - cc grpc.ClientConnInterface -} - -func NewOpenShellClient(cc grpc.ClientConnInterface) OpenShellClient { - return &openShellClient{cc} -} - -func (c *openShellClient) Health(ctx context.Context, in *HealthRequest, opts ...grpc.CallOption) (*HealthResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(HealthResponse) - err := c.cc.Invoke(ctx, OpenShell_Health_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) CreateSandbox(ctx context.Context, in *CreateSandboxRequest, opts ...grpc.CallOption) (*SandboxResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(SandboxResponse) - err := c.cc.Invoke(ctx, OpenShell_CreateSandbox_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetSandbox(ctx context.Context, in *GetSandboxRequest, opts ...grpc.CallOption) (*SandboxResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(SandboxResponse) - err := c.cc.Invoke(ctx, OpenShell_GetSandbox_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ListSandboxes(ctx context.Context, in *ListSandboxesRequest, opts ...grpc.CallOption) (*ListSandboxesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ListSandboxesResponse) - err := c.cc.Invoke(ctx, OpenShell_ListSandboxes_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ListSandboxProviders(ctx context.Context, in *ListSandboxProvidersRequest, opts ...grpc.CallOption) (*ListSandboxProvidersResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ListSandboxProvidersResponse) - err := c.cc.Invoke(ctx, OpenShell_ListSandboxProviders_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) AttachSandboxProvider(ctx context.Context, in *AttachSandboxProviderRequest, opts ...grpc.CallOption) (*AttachSandboxProviderResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(AttachSandboxProviderResponse) - err := c.cc.Invoke(ctx, OpenShell_AttachSandboxProvider_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) DetachSandboxProvider(ctx context.Context, in *DetachSandboxProviderRequest, opts ...grpc.CallOption) (*DetachSandboxProviderResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(DetachSandboxProviderResponse) - err := c.cc.Invoke(ctx, OpenShell_DetachSandboxProvider_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) DeleteSandbox(ctx context.Context, in *DeleteSandboxRequest, opts ...grpc.CallOption) (*DeleteSandboxResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(DeleteSandboxResponse) - err := c.cc.Invoke(ctx, OpenShell_DeleteSandbox_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) CreateSshSession(ctx context.Context, in *CreateSshSessionRequest, opts ...grpc.CallOption) (*CreateSshSessionResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(CreateSshSessionResponse) - err := c.cc.Invoke(ctx, OpenShell_CreateSshSession_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ExposeService(ctx context.Context, in *ExposeServiceRequest, opts ...grpc.CallOption) (*ServiceEndpointResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ServiceEndpointResponse) - err := c.cc.Invoke(ctx, OpenShell_ExposeService_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetService(ctx context.Context, in *GetServiceRequest, opts ...grpc.CallOption) (*ServiceEndpointResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ServiceEndpointResponse) - err := c.cc.Invoke(ctx, OpenShell_GetService_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ListServices(ctx context.Context, in *ListServicesRequest, opts ...grpc.CallOption) (*ListServicesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ListServicesResponse) - err := c.cc.Invoke(ctx, OpenShell_ListServices_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) DeleteService(ctx context.Context, in *DeleteServiceRequest, opts ...grpc.CallOption) (*DeleteServiceResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(DeleteServiceResponse) - err := c.cc.Invoke(ctx, OpenShell_DeleteService_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) RevokeSshSession(ctx context.Context, in *RevokeSshSessionRequest, opts ...grpc.CallOption) (*RevokeSshSessionResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(RevokeSshSessionResponse) - err := c.cc.Invoke(ctx, OpenShell_RevokeSshSession_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ExecSandbox(ctx context.Context, in *ExecSandboxRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[ExecSandboxEvent], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &OpenShell_ServiceDesc.Streams[0], OpenShell_ExecSandbox_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[ExecSandboxRequest, ExecSandboxEvent]{ClientStream: stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_ExecSandboxClient = grpc.ServerStreamingClient[ExecSandboxEvent] - -func (c *openShellClient) ForwardTcp(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[TcpForwardFrame, TcpForwardFrame], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &OpenShell_ServiceDesc.Streams[1], OpenShell_ForwardTcp_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[TcpForwardFrame, TcpForwardFrame]{ClientStream: stream} - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_ForwardTcpClient = grpc.BidiStreamingClient[TcpForwardFrame, TcpForwardFrame] - -func (c *openShellClient) ExecSandboxInteractive(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[ExecSandboxInput, ExecSandboxEvent], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &OpenShell_ServiceDesc.Streams[2], OpenShell_ExecSandboxInteractive_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[ExecSandboxInput, ExecSandboxEvent]{ClientStream: stream} - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_ExecSandboxInteractiveClient = grpc.BidiStreamingClient[ExecSandboxInput, ExecSandboxEvent] - -func (c *openShellClient) CreateProvider(ctx context.Context, in *CreateProviderRequest, opts ...grpc.CallOption) (*ProviderResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ProviderResponse) - err := c.cc.Invoke(ctx, OpenShell_CreateProvider_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetProvider(ctx context.Context, in *GetProviderRequest, opts ...grpc.CallOption) (*ProviderResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ProviderResponse) - err := c.cc.Invoke(ctx, OpenShell_GetProvider_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ListProviders(ctx context.Context, in *ListProvidersRequest, opts ...grpc.CallOption) (*ListProvidersResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ListProvidersResponse) - err := c.cc.Invoke(ctx, OpenShell_ListProviders_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ListProviderProfiles(ctx context.Context, in *ListProviderProfilesRequest, opts ...grpc.CallOption) (*ListProviderProfilesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ListProviderProfilesResponse) - err := c.cc.Invoke(ctx, OpenShell_ListProviderProfiles_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetProviderProfile(ctx context.Context, in *GetProviderProfileRequest, opts ...grpc.CallOption) (*ProviderProfileResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ProviderProfileResponse) - err := c.cc.Invoke(ctx, OpenShell_GetProviderProfile_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ImportProviderProfiles(ctx context.Context, in *ImportProviderProfilesRequest, opts ...grpc.CallOption) (*ImportProviderProfilesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ImportProviderProfilesResponse) - err := c.cc.Invoke(ctx, OpenShell_ImportProviderProfiles_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) LintProviderProfiles(ctx context.Context, in *LintProviderProfilesRequest, opts ...grpc.CallOption) (*LintProviderProfilesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(LintProviderProfilesResponse) - err := c.cc.Invoke(ctx, OpenShell_LintProviderProfiles_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) UpdateProvider(ctx context.Context, in *UpdateProviderRequest, opts ...grpc.CallOption) (*ProviderResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ProviderResponse) - err := c.cc.Invoke(ctx, OpenShell_UpdateProvider_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) DeleteProvider(ctx context.Context, in *DeleteProviderRequest, opts ...grpc.CallOption) (*DeleteProviderResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(DeleteProviderResponse) - err := c.cc.Invoke(ctx, OpenShell_DeleteProvider_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) DeleteProviderProfile(ctx context.Context, in *DeleteProviderProfileRequest, opts ...grpc.CallOption) (*DeleteProviderProfileResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(DeleteProviderProfileResponse) - err := c.cc.Invoke(ctx, OpenShell_DeleteProviderProfile_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetSandboxConfig(ctx context.Context, in *sandboxv1.GetSandboxConfigRequest, opts ...grpc.CallOption) (*sandboxv1.GetSandboxConfigResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(sandboxv1.GetSandboxConfigResponse) - err := c.cc.Invoke(ctx, OpenShell_GetSandboxConfig_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetGatewayConfig(ctx context.Context, in *sandboxv1.GetGatewayConfigRequest, opts ...grpc.CallOption) (*sandboxv1.GetGatewayConfigResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(sandboxv1.GetGatewayConfigResponse) - err := c.cc.Invoke(ctx, OpenShell_GetGatewayConfig_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) UpdateConfig(ctx context.Context, in *UpdateConfigRequest, opts ...grpc.CallOption) (*UpdateConfigResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(UpdateConfigResponse) - err := c.cc.Invoke(ctx, OpenShell_UpdateConfig_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetSandboxPolicyStatus(ctx context.Context, in *GetSandboxPolicyStatusRequest, opts ...grpc.CallOption) (*GetSandboxPolicyStatusResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetSandboxPolicyStatusResponse) - err := c.cc.Invoke(ctx, OpenShell_GetSandboxPolicyStatus_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ListSandboxPolicies(ctx context.Context, in *ListSandboxPoliciesRequest, opts ...grpc.CallOption) (*ListSandboxPoliciesResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ListSandboxPoliciesResponse) - err := c.cc.Invoke(ctx, OpenShell_ListSandboxPolicies_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ReportPolicyStatus(ctx context.Context, in *ReportPolicyStatusRequest, opts ...grpc.CallOption) (*ReportPolicyStatusResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ReportPolicyStatusResponse) - err := c.cc.Invoke(ctx, OpenShell_ReportPolicyStatus_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetSandboxProviderEnvironment(ctx context.Context, in *GetSandboxProviderEnvironmentRequest, opts ...grpc.CallOption) (*GetSandboxProviderEnvironmentResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetSandboxProviderEnvironmentResponse) - err := c.cc.Invoke(ctx, OpenShell_GetSandboxProviderEnvironment_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetSandboxLogs(ctx context.Context, in *GetSandboxLogsRequest, opts ...grpc.CallOption) (*GetSandboxLogsResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetSandboxLogsResponse) - err := c.cc.Invoke(ctx, OpenShell_GetSandboxLogs_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) PushSandboxLogs(ctx context.Context, opts ...grpc.CallOption) (grpc.ClientStreamingClient[PushSandboxLogsRequest, PushSandboxLogsResponse], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &OpenShell_ServiceDesc.Streams[3], OpenShell_PushSandboxLogs_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[PushSandboxLogsRequest, PushSandboxLogsResponse]{ClientStream: stream} - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_PushSandboxLogsClient = grpc.ClientStreamingClient[PushSandboxLogsRequest, PushSandboxLogsResponse] - -func (c *openShellClient) ConnectSupervisor(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[SupervisorMessage, GatewayMessage], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &OpenShell_ServiceDesc.Streams[4], OpenShell_ConnectSupervisor_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[SupervisorMessage, GatewayMessage]{ClientStream: stream} - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_ConnectSupervisorClient = grpc.BidiStreamingClient[SupervisorMessage, GatewayMessage] - -func (c *openShellClient) RelayStream(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[RelayFrame, RelayFrame], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &OpenShell_ServiceDesc.Streams[5], OpenShell_RelayStream_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[RelayFrame, RelayFrame]{ClientStream: stream} - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_RelayStreamClient = grpc.BidiStreamingClient[RelayFrame, RelayFrame] - -func (c *openShellClient) WatchSandbox(ctx context.Context, in *WatchSandboxRequest, opts ...grpc.CallOption) (grpc.ServerStreamingClient[SandboxStreamEvent], error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - stream, err := c.cc.NewStream(ctx, &OpenShell_ServiceDesc.Streams[6], OpenShell_WatchSandbox_FullMethodName, cOpts...) - if err != nil { - return nil, err - } - x := &grpc.GenericClientStream[WatchSandboxRequest, SandboxStreamEvent]{ClientStream: stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_WatchSandboxClient = grpc.ServerStreamingClient[SandboxStreamEvent] - -func (c *openShellClient) SubmitPolicyAnalysis(ctx context.Context, in *SubmitPolicyAnalysisRequest, opts ...grpc.CallOption) (*SubmitPolicyAnalysisResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(SubmitPolicyAnalysisResponse) - err := c.cc.Invoke(ctx, OpenShell_SubmitPolicyAnalysis_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetDraftPolicy(ctx context.Context, in *GetDraftPolicyRequest, opts ...grpc.CallOption) (*GetDraftPolicyResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetDraftPolicyResponse) - err := c.cc.Invoke(ctx, OpenShell_GetDraftPolicy_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ApproveDraftChunk(ctx context.Context, in *ApproveDraftChunkRequest, opts ...grpc.CallOption) (*ApproveDraftChunkResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ApproveDraftChunkResponse) - err := c.cc.Invoke(ctx, OpenShell_ApproveDraftChunk_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) RejectDraftChunk(ctx context.Context, in *RejectDraftChunkRequest, opts ...grpc.CallOption) (*RejectDraftChunkResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(RejectDraftChunkResponse) - err := c.cc.Invoke(ctx, OpenShell_RejectDraftChunk_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ApproveAllDraftChunks(ctx context.Context, in *ApproveAllDraftChunksRequest, opts ...grpc.CallOption) (*ApproveAllDraftChunksResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ApproveAllDraftChunksResponse) - err := c.cc.Invoke(ctx, OpenShell_ApproveAllDraftChunks_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) EditDraftChunk(ctx context.Context, in *EditDraftChunkRequest, opts ...grpc.CallOption) (*EditDraftChunkResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(EditDraftChunkResponse) - err := c.cc.Invoke(ctx, OpenShell_EditDraftChunk_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) UndoDraftChunk(ctx context.Context, in *UndoDraftChunkRequest, opts ...grpc.CallOption) (*UndoDraftChunkResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(UndoDraftChunkResponse) - err := c.cc.Invoke(ctx, OpenShell_UndoDraftChunk_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) ClearDraftChunks(ctx context.Context, in *ClearDraftChunksRequest, opts ...grpc.CallOption) (*ClearDraftChunksResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(ClearDraftChunksResponse) - err := c.cc.Invoke(ctx, OpenShell_ClearDraftChunks_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *openShellClient) GetDraftHistory(ctx context.Context, in *GetDraftHistoryRequest, opts ...grpc.CallOption) (*GetDraftHistoryResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(GetDraftHistoryResponse) - err := c.cc.Invoke(ctx, OpenShell_GetDraftHistory_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -// OpenShellServer is the server API for OpenShell service. -// All implementations must embed UnimplementedOpenShellServer -// for forward compatibility. -// -// OpenShell service provides sandbox, provider, and runtime management capabilities. -// -// Conventions: -// - This file owns the public API resource model exposed to OpenShell clients. -// - `Sandbox`, `SandboxSpec`, `SandboxStatus`, and `SandboxPhase` are gateway-owned -// public types. Internal compute drivers must not import or return them directly. -// - The gateway translates internal compute-driver observations into these public -// resource messages before persisting or returning them to clients. -type OpenShellServer interface { - // Check the health of the service. - Health(context.Context, *HealthRequest) (*HealthResponse, error) - // Create a new sandbox. - CreateSandbox(context.Context, *CreateSandboxRequest) (*SandboxResponse, error) - // Fetch a sandbox by name. - GetSandbox(context.Context, *GetSandboxRequest) (*SandboxResponse, error) - // List sandboxes. - ListSandboxes(context.Context, *ListSandboxesRequest) (*ListSandboxesResponse, error) - // List provider records attached to a sandbox. - ListSandboxProviders(context.Context, *ListSandboxProvidersRequest) (*ListSandboxProvidersResponse, error) - // Attach a provider record to an existing sandbox. - AttachSandboxProvider(context.Context, *AttachSandboxProviderRequest) (*AttachSandboxProviderResponse, error) - // Detach a provider record from an existing sandbox. - DetachSandboxProvider(context.Context, *DetachSandboxProviderRequest) (*DetachSandboxProviderResponse, error) - // Delete a sandbox by name. - DeleteSandbox(context.Context, *DeleteSandboxRequest) (*DeleteSandboxResponse, error) - // Create a short-lived SSH session for a sandbox. - CreateSshSession(context.Context, *CreateSshSessionRequest) (*CreateSshSessionResponse, error) - // Create or update a sandbox HTTP service endpoint for local routing. - ExposeService(context.Context, *ExposeServiceRequest) (*ServiceEndpointResponse, error) - // Fetch one sandbox HTTP service endpoint. - GetService(context.Context, *GetServiceRequest) (*ServiceEndpointResponse, error) - // List sandbox HTTP service endpoints. - ListServices(context.Context, *ListServicesRequest) (*ListServicesResponse, error) - // Delete one sandbox HTTP service endpoint. - DeleteService(context.Context, *DeleteServiceRequest) (*DeleteServiceResponse, error) - // Revoke a previously issued SSH session. - RevokeSshSession(context.Context, *RevokeSshSessionRequest) (*RevokeSshSessionResponse, error) - // Execute a command in a ready sandbox and stream output. - ExecSandbox(*ExecSandboxRequest, grpc.ServerStreamingServer[ExecSandboxEvent]) error - // Forward one CLI-side TCP connection to a loopback TCP target in a sandbox. - ForwardTcp(grpc.BidiStreamingServer[TcpForwardFrame, TcpForwardFrame]) error - // Execute an interactive command with bidirectional stdin/stdout streaming. - // The first client message MUST carry an ExecSandboxInput with the start - // variant. Subsequent messages carry stdin bytes or window resize events. - ExecSandboxInteractive(grpc.BidiStreamingServer[ExecSandboxInput, ExecSandboxEvent]) error - // Create a provider. - CreateProvider(context.Context, *CreateProviderRequest) (*ProviderResponse, error) - // Fetch a provider by name. - GetProvider(context.Context, *GetProviderRequest) (*ProviderResponse, error) - // List providers. - ListProviders(context.Context, *ListProvidersRequest) (*ListProvidersResponse, error) - // List available provider type profiles. - ListProviderProfiles(context.Context, *ListProviderProfilesRequest) (*ListProviderProfilesResponse, error) - // Fetch one provider type profile by id. - GetProviderProfile(context.Context, *GetProviderProfileRequest) (*ProviderProfileResponse, error) - // Import custom provider type profiles. - ImportProviderProfiles(context.Context, *ImportProviderProfilesRequest) (*ImportProviderProfilesResponse, error) - // Validate provider type profiles without registering them. - LintProviderProfiles(context.Context, *LintProviderProfilesRequest) (*LintProviderProfilesResponse, error) - // Update an existing provider by name. - UpdateProvider(context.Context, *UpdateProviderRequest) (*ProviderResponse, error) - // Delete a provider by name. - DeleteProvider(context.Context, *DeleteProviderRequest) (*DeleteProviderResponse, error) - // Delete a custom provider type profile by id. - DeleteProviderProfile(context.Context, *DeleteProviderProfileRequest) (*DeleteProviderProfileResponse, error) - // Get sandbox settings by id (called by sandbox entrypoint and poll loop). - GetSandboxConfig(context.Context, *sandboxv1.GetSandboxConfigRequest) (*sandboxv1.GetSandboxConfigResponse, error) - // Get gateway-global settings. - GetGatewayConfig(context.Context, *sandboxv1.GetGatewayConfigRequest) (*sandboxv1.GetGatewayConfigResponse, error) - // Update settings or policy at sandbox or global scope. - UpdateConfig(context.Context, *UpdateConfigRequest) (*UpdateConfigResponse, error) - // Get the load status of a specific policy version. - GetSandboxPolicyStatus(context.Context, *GetSandboxPolicyStatusRequest) (*GetSandboxPolicyStatusResponse, error) - // List policy history for a sandbox. - ListSandboxPolicies(context.Context, *ListSandboxPoliciesRequest) (*ListSandboxPoliciesResponse, error) - // Report policy load result (called by sandbox after reload attempt). - ReportPolicyStatus(context.Context, *ReportPolicyStatusRequest) (*ReportPolicyStatusResponse, error) - // Get provider environment for a sandbox (called by sandbox supervisor at startup). - GetSandboxProviderEnvironment(context.Context, *GetSandboxProviderEnvironmentRequest) (*GetSandboxProviderEnvironmentResponse, error) - // Fetch recent sandbox logs (one-shot). - GetSandboxLogs(context.Context, *GetSandboxLogsRequest) (*GetSandboxLogsResponse, error) - // Push sandbox supervisor logs to the server (client-streaming). - PushSandboxLogs(grpc.ClientStreamingServer[PushSandboxLogsRequest, PushSandboxLogsResponse]) error - // Persistent supervisor-to-gateway session (bidirectional streaming). - // - // The supervisor opens this stream at startup and keeps it alive for the - // sandbox lifetime. The gateway uses it to coordinate relay channels for - // SSH connect, ExecSandbox, and targetable sandbox services. Raw service - // bytes flow over RelayStream calls (separate HTTP/2 streams on the same - // connection), not over this stream. - ConnectSupervisor(grpc.BidiStreamingServer[SupervisorMessage, GatewayMessage]) error - // Raw byte relay between supervisor and gateway. - // - // The supervisor initiates this call after receiving a RelayOpen message - // on its ConnectSupervisor stream. The first RelayFrame carries a - // RelayInit with the channel_id to associate the new HTTP/2 stream with - // the pending relay slot on the gateway. Subsequent frames carry raw bytes in either - // direction between the gateway-side waiter (ForwardTcp / exec handler) - // and the supervisor-side target bridge. - // - // This rides the same TCP+TLS+HTTP/2 connection as ConnectSupervisor — - // no new TLS handshake, no reverse HTTP CONNECT. - RelayStream(grpc.BidiStreamingServer[RelayFrame, RelayFrame]) error - // Watch a sandbox and stream updates. - // - // This stream can include: - // - Sandbox status snapshots (phase/status) - // - OpenShell server process logs correlated by sandbox_id - // - Platform events correlated to the sandbox - WatchSandbox(*WatchSandboxRequest, grpc.ServerStreamingServer[SandboxStreamEvent]) error - // Submit denial analysis results from sandbox (summaries + proposed chunks). - SubmitPolicyAnalysis(context.Context, *SubmitPolicyAnalysisRequest) (*SubmitPolicyAnalysisResponse, error) - // Get draft policy recommendations for a sandbox. - GetDraftPolicy(context.Context, *GetDraftPolicyRequest) (*GetDraftPolicyResponse, error) - // Approve a single draft policy chunk (merges into active policy). - ApproveDraftChunk(context.Context, *ApproveDraftChunkRequest) (*ApproveDraftChunkResponse, error) - // Reject a single draft policy chunk. - RejectDraftChunk(context.Context, *RejectDraftChunkRequest) (*RejectDraftChunkResponse, error) - // Approve all pending draft chunks (skips security-flagged unless forced). - ApproveAllDraftChunks(context.Context, *ApproveAllDraftChunksRequest) (*ApproveAllDraftChunksResponse, error) - // Edit a pending draft chunk in-place (e.g. narrow allowed_ips). - EditDraftChunk(context.Context, *EditDraftChunkRequest) (*EditDraftChunkResponse, error) - // Reverse an approval (remove merged rule from active policy). - UndoDraftChunk(context.Context, *UndoDraftChunkRequest) (*UndoDraftChunkResponse, error) - // Clear all pending draft chunks for a sandbox. - ClearDraftChunks(context.Context, *ClearDraftChunksRequest) (*ClearDraftChunksResponse, error) - // Get decision history for a sandbox's draft policy. - GetDraftHistory(context.Context, *GetDraftHistoryRequest) (*GetDraftHistoryResponse, error) - mustEmbedUnimplementedOpenShellServer() -} - -// UnimplementedOpenShellServer must be embedded to have -// forward compatible implementations. -// -// NOTE: this should be embedded by value instead of pointer to avoid a nil -// pointer dereference when methods are called. -type UnimplementedOpenShellServer struct{} - -func (UnimplementedOpenShellServer) Health(context.Context, *HealthRequest) (*HealthResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method Health not implemented") -} -func (UnimplementedOpenShellServer) CreateSandbox(context.Context, *CreateSandboxRequest) (*SandboxResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreateSandbox not implemented") -} -func (UnimplementedOpenShellServer) GetSandbox(context.Context, *GetSandboxRequest) (*SandboxResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetSandbox not implemented") -} -func (UnimplementedOpenShellServer) ListSandboxes(context.Context, *ListSandboxesRequest) (*ListSandboxesResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListSandboxes not implemented") -} -func (UnimplementedOpenShellServer) ListSandboxProviders(context.Context, *ListSandboxProvidersRequest) (*ListSandboxProvidersResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListSandboxProviders not implemented") -} -func (UnimplementedOpenShellServer) AttachSandboxProvider(context.Context, *AttachSandboxProviderRequest) (*AttachSandboxProviderResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method AttachSandboxProvider not implemented") -} -func (UnimplementedOpenShellServer) DetachSandboxProvider(context.Context, *DetachSandboxProviderRequest) (*DetachSandboxProviderResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method DetachSandboxProvider not implemented") -} -func (UnimplementedOpenShellServer) DeleteSandbox(context.Context, *DeleteSandboxRequest) (*DeleteSandboxResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method DeleteSandbox not implemented") -} -func (UnimplementedOpenShellServer) CreateSshSession(context.Context, *CreateSshSessionRequest) (*CreateSshSessionResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreateSshSession not implemented") -} -func (UnimplementedOpenShellServer) ExposeService(context.Context, *ExposeServiceRequest) (*ServiceEndpointResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ExposeService not implemented") -} -func (UnimplementedOpenShellServer) GetService(context.Context, *GetServiceRequest) (*ServiceEndpointResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetService not implemented") -} -func (UnimplementedOpenShellServer) ListServices(context.Context, *ListServicesRequest) (*ListServicesResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListServices not implemented") -} -func (UnimplementedOpenShellServer) DeleteService(context.Context, *DeleteServiceRequest) (*DeleteServiceResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method DeleteService not implemented") -} -func (UnimplementedOpenShellServer) RevokeSshSession(context.Context, *RevokeSshSessionRequest) (*RevokeSshSessionResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method RevokeSshSession not implemented") -} -func (UnimplementedOpenShellServer) ExecSandbox(*ExecSandboxRequest, grpc.ServerStreamingServer[ExecSandboxEvent]) error { - return status.Errorf(codes.Unimplemented, "method ExecSandbox not implemented") -} -func (UnimplementedOpenShellServer) ForwardTcp(grpc.BidiStreamingServer[TcpForwardFrame, TcpForwardFrame]) error { - return status.Errorf(codes.Unimplemented, "method ForwardTcp not implemented") -} -func (UnimplementedOpenShellServer) ExecSandboxInteractive(grpc.BidiStreamingServer[ExecSandboxInput, ExecSandboxEvent]) error { - return status.Errorf(codes.Unimplemented, "method ExecSandboxInteractive not implemented") -} -func (UnimplementedOpenShellServer) CreateProvider(context.Context, *CreateProviderRequest) (*ProviderResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method CreateProvider not implemented") -} -func (UnimplementedOpenShellServer) GetProvider(context.Context, *GetProviderRequest) (*ProviderResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetProvider not implemented") -} -func (UnimplementedOpenShellServer) ListProviders(context.Context, *ListProvidersRequest) (*ListProvidersResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListProviders not implemented") -} -func (UnimplementedOpenShellServer) ListProviderProfiles(context.Context, *ListProviderProfilesRequest) (*ListProviderProfilesResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListProviderProfiles not implemented") -} -func (UnimplementedOpenShellServer) GetProviderProfile(context.Context, *GetProviderProfileRequest) (*ProviderProfileResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetProviderProfile not implemented") -} -func (UnimplementedOpenShellServer) ImportProviderProfiles(context.Context, *ImportProviderProfilesRequest) (*ImportProviderProfilesResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ImportProviderProfiles not implemented") -} -func (UnimplementedOpenShellServer) LintProviderProfiles(context.Context, *LintProviderProfilesRequest) (*LintProviderProfilesResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method LintProviderProfiles not implemented") -} -func (UnimplementedOpenShellServer) UpdateProvider(context.Context, *UpdateProviderRequest) (*ProviderResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method UpdateProvider not implemented") -} -func (UnimplementedOpenShellServer) DeleteProvider(context.Context, *DeleteProviderRequest) (*DeleteProviderResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method DeleteProvider not implemented") -} -func (UnimplementedOpenShellServer) DeleteProviderProfile(context.Context, *DeleteProviderProfileRequest) (*DeleteProviderProfileResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method DeleteProviderProfile not implemented") -} -func (UnimplementedOpenShellServer) GetSandboxConfig(context.Context, *sandboxv1.GetSandboxConfigRequest) (*sandboxv1.GetSandboxConfigResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetSandboxConfig not implemented") -} -func (UnimplementedOpenShellServer) GetGatewayConfig(context.Context, *sandboxv1.GetGatewayConfigRequest) (*sandboxv1.GetGatewayConfigResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetGatewayConfig not implemented") -} -func (UnimplementedOpenShellServer) UpdateConfig(context.Context, *UpdateConfigRequest) (*UpdateConfigResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method UpdateConfig not implemented") -} -func (UnimplementedOpenShellServer) GetSandboxPolicyStatus(context.Context, *GetSandboxPolicyStatusRequest) (*GetSandboxPolicyStatusResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetSandboxPolicyStatus not implemented") -} -func (UnimplementedOpenShellServer) ListSandboxPolicies(context.Context, *ListSandboxPoliciesRequest) (*ListSandboxPoliciesResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ListSandboxPolicies not implemented") -} -func (UnimplementedOpenShellServer) ReportPolicyStatus(context.Context, *ReportPolicyStatusRequest) (*ReportPolicyStatusResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ReportPolicyStatus not implemented") -} -func (UnimplementedOpenShellServer) GetSandboxProviderEnvironment(context.Context, *GetSandboxProviderEnvironmentRequest) (*GetSandboxProviderEnvironmentResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetSandboxProviderEnvironment not implemented") -} -func (UnimplementedOpenShellServer) GetSandboxLogs(context.Context, *GetSandboxLogsRequest) (*GetSandboxLogsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetSandboxLogs not implemented") -} -func (UnimplementedOpenShellServer) PushSandboxLogs(grpc.ClientStreamingServer[PushSandboxLogsRequest, PushSandboxLogsResponse]) error { - return status.Errorf(codes.Unimplemented, "method PushSandboxLogs not implemented") -} -func (UnimplementedOpenShellServer) ConnectSupervisor(grpc.BidiStreamingServer[SupervisorMessage, GatewayMessage]) error { - return status.Errorf(codes.Unimplemented, "method ConnectSupervisor not implemented") -} -func (UnimplementedOpenShellServer) RelayStream(grpc.BidiStreamingServer[RelayFrame, RelayFrame]) error { - return status.Errorf(codes.Unimplemented, "method RelayStream not implemented") -} -func (UnimplementedOpenShellServer) WatchSandbox(*WatchSandboxRequest, grpc.ServerStreamingServer[SandboxStreamEvent]) error { - return status.Errorf(codes.Unimplemented, "method WatchSandbox not implemented") -} -func (UnimplementedOpenShellServer) SubmitPolicyAnalysis(context.Context, *SubmitPolicyAnalysisRequest) (*SubmitPolicyAnalysisResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method SubmitPolicyAnalysis not implemented") -} -func (UnimplementedOpenShellServer) GetDraftPolicy(context.Context, *GetDraftPolicyRequest) (*GetDraftPolicyResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetDraftPolicy not implemented") -} -func (UnimplementedOpenShellServer) ApproveDraftChunk(context.Context, *ApproveDraftChunkRequest) (*ApproveDraftChunkResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ApproveDraftChunk not implemented") -} -func (UnimplementedOpenShellServer) RejectDraftChunk(context.Context, *RejectDraftChunkRequest) (*RejectDraftChunkResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method RejectDraftChunk not implemented") -} -func (UnimplementedOpenShellServer) ApproveAllDraftChunks(context.Context, *ApproveAllDraftChunksRequest) (*ApproveAllDraftChunksResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ApproveAllDraftChunks not implemented") -} -func (UnimplementedOpenShellServer) EditDraftChunk(context.Context, *EditDraftChunkRequest) (*EditDraftChunkResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method EditDraftChunk not implemented") -} -func (UnimplementedOpenShellServer) UndoDraftChunk(context.Context, *UndoDraftChunkRequest) (*UndoDraftChunkResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method UndoDraftChunk not implemented") -} -func (UnimplementedOpenShellServer) ClearDraftChunks(context.Context, *ClearDraftChunksRequest) (*ClearDraftChunksResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method ClearDraftChunks not implemented") -} -func (UnimplementedOpenShellServer) GetDraftHistory(context.Context, *GetDraftHistoryRequest) (*GetDraftHistoryResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetDraftHistory not implemented") -} -func (UnimplementedOpenShellServer) mustEmbedUnimplementedOpenShellServer() {} -func (UnimplementedOpenShellServer) testEmbeddedByValue() {} - -// UnsafeOpenShellServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to OpenShellServer will -// result in compilation errors. -type UnsafeOpenShellServer interface { - mustEmbedUnimplementedOpenShellServer() -} - -func RegisterOpenShellServer(s grpc.ServiceRegistrar, srv OpenShellServer) { - // If the following call pancis, it indicates UnimplementedOpenShellServer was - // embedded by pointer and is nil. This will cause panics if an - // unimplemented method is ever invoked, so we test this at initialization - // time to prevent it from happening at runtime later due to I/O. - if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { - t.testEmbeddedByValue() - } - s.RegisterService(&OpenShell_ServiceDesc, srv) -} - -func _OpenShell_Health_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(HealthRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).Health(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_Health_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).Health(ctx, req.(*HealthRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_CreateSandbox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateSandboxRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).CreateSandbox(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_CreateSandbox_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).CreateSandbox(ctx, req.(*CreateSandboxRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetSandbox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetSandboxRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetSandbox(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetSandbox_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetSandbox(ctx, req.(*GetSandboxRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ListSandboxes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListSandboxesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ListSandboxes(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ListSandboxes_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ListSandboxes(ctx, req.(*ListSandboxesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ListSandboxProviders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListSandboxProvidersRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ListSandboxProviders(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ListSandboxProviders_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ListSandboxProviders(ctx, req.(*ListSandboxProvidersRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_AttachSandboxProvider_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AttachSandboxProviderRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).AttachSandboxProvider(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_AttachSandboxProvider_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).AttachSandboxProvider(ctx, req.(*AttachSandboxProviderRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_DetachSandboxProvider_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DetachSandboxProviderRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).DetachSandboxProvider(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_DetachSandboxProvider_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).DetachSandboxProvider(ctx, req.(*DetachSandboxProviderRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_DeleteSandbox_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteSandboxRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).DeleteSandbox(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_DeleteSandbox_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).DeleteSandbox(ctx, req.(*DeleteSandboxRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_CreateSshSession_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateSshSessionRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).CreateSshSession(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_CreateSshSession_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).CreateSshSession(ctx, req.(*CreateSshSessionRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ExposeService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ExposeServiceRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ExposeService(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ExposeService_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ExposeService(ctx, req.(*ExposeServiceRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetServiceRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetService(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetService_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetService(ctx, req.(*GetServiceRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ListServices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListServicesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ListServices(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ListServices_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ListServices(ctx, req.(*ListServicesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_DeleteService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteServiceRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).DeleteService(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_DeleteService_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).DeleteService(ctx, req.(*DeleteServiceRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_RevokeSshSession_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RevokeSshSessionRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).RevokeSshSession(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_RevokeSshSession_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).RevokeSshSession(ctx, req.(*RevokeSshSessionRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ExecSandbox_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(ExecSandboxRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(OpenShellServer).ExecSandbox(m, &grpc.GenericServerStream[ExecSandboxRequest, ExecSandboxEvent]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_ExecSandboxServer = grpc.ServerStreamingServer[ExecSandboxEvent] - -func _OpenShell_ForwardTcp_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(OpenShellServer).ForwardTcp(&grpc.GenericServerStream[TcpForwardFrame, TcpForwardFrame]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_ForwardTcpServer = grpc.BidiStreamingServer[TcpForwardFrame, TcpForwardFrame] - -func _OpenShell_ExecSandboxInteractive_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(OpenShellServer).ExecSandboxInteractive(&grpc.GenericServerStream[ExecSandboxInput, ExecSandboxEvent]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_ExecSandboxInteractiveServer = grpc.BidiStreamingServer[ExecSandboxInput, ExecSandboxEvent] - -func _OpenShell_CreateProvider_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateProviderRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).CreateProvider(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_CreateProvider_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).CreateProvider(ctx, req.(*CreateProviderRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetProvider_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetProviderRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetProvider(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetProvider_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetProvider(ctx, req.(*GetProviderRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ListProviders_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListProvidersRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ListProviders(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ListProviders_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ListProviders(ctx, req.(*ListProvidersRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ListProviderProfiles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListProviderProfilesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ListProviderProfiles(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ListProviderProfiles_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ListProviderProfiles(ctx, req.(*ListProviderProfilesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetProviderProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetProviderProfileRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetProviderProfile(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetProviderProfile_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetProviderProfile(ctx, req.(*GetProviderProfileRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ImportProviderProfiles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ImportProviderProfilesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ImportProviderProfiles(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ImportProviderProfiles_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ImportProviderProfiles(ctx, req.(*ImportProviderProfilesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_LintProviderProfiles_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(LintProviderProfilesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).LintProviderProfiles(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_LintProviderProfiles_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).LintProviderProfiles(ctx, req.(*LintProviderProfilesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_UpdateProvider_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateProviderRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).UpdateProvider(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_UpdateProvider_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).UpdateProvider(ctx, req.(*UpdateProviderRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_DeleteProvider_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteProviderRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).DeleteProvider(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_DeleteProvider_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).DeleteProvider(ctx, req.(*DeleteProviderRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_DeleteProviderProfile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteProviderProfileRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).DeleteProviderProfile(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_DeleteProviderProfile_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).DeleteProviderProfile(ctx, req.(*DeleteProviderProfileRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetSandboxConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(sandboxv1.GetSandboxConfigRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetSandboxConfig(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetSandboxConfig_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetSandboxConfig(ctx, req.(*sandboxv1.GetSandboxConfigRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetGatewayConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(sandboxv1.GetGatewayConfigRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetGatewayConfig(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetGatewayConfig_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetGatewayConfig(ctx, req.(*sandboxv1.GetGatewayConfigRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_UpdateConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateConfigRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).UpdateConfig(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_UpdateConfig_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).UpdateConfig(ctx, req.(*UpdateConfigRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetSandboxPolicyStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetSandboxPolicyStatusRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetSandboxPolicyStatus(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetSandboxPolicyStatus_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetSandboxPolicyStatus(ctx, req.(*GetSandboxPolicyStatusRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ListSandboxPolicies_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListSandboxPoliciesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ListSandboxPolicies(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ListSandboxPolicies_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ListSandboxPolicies(ctx, req.(*ListSandboxPoliciesRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ReportPolicyStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ReportPolicyStatusRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ReportPolicyStatus(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ReportPolicyStatus_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ReportPolicyStatus(ctx, req.(*ReportPolicyStatusRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetSandboxProviderEnvironment_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetSandboxProviderEnvironmentRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetSandboxProviderEnvironment(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetSandboxProviderEnvironment_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetSandboxProviderEnvironment(ctx, req.(*GetSandboxProviderEnvironmentRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetSandboxLogs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetSandboxLogsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetSandboxLogs(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetSandboxLogs_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetSandboxLogs(ctx, req.(*GetSandboxLogsRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_PushSandboxLogs_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(OpenShellServer).PushSandboxLogs(&grpc.GenericServerStream[PushSandboxLogsRequest, PushSandboxLogsResponse]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_PushSandboxLogsServer = grpc.ClientStreamingServer[PushSandboxLogsRequest, PushSandboxLogsResponse] - -func _OpenShell_ConnectSupervisor_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(OpenShellServer).ConnectSupervisor(&grpc.GenericServerStream[SupervisorMessage, GatewayMessage]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_ConnectSupervisorServer = grpc.BidiStreamingServer[SupervisorMessage, GatewayMessage] - -func _OpenShell_RelayStream_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(OpenShellServer).RelayStream(&grpc.GenericServerStream[RelayFrame, RelayFrame]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_RelayStreamServer = grpc.BidiStreamingServer[RelayFrame, RelayFrame] - -func _OpenShell_WatchSandbox_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(WatchSandboxRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(OpenShellServer).WatchSandbox(m, &grpc.GenericServerStream[WatchSandboxRequest, SandboxStreamEvent]{ServerStream: stream}) -} - -// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. -type OpenShell_WatchSandboxServer = grpc.ServerStreamingServer[SandboxStreamEvent] - -func _OpenShell_SubmitPolicyAnalysis_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SubmitPolicyAnalysisRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).SubmitPolicyAnalysis(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_SubmitPolicyAnalysis_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).SubmitPolicyAnalysis(ctx, req.(*SubmitPolicyAnalysisRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetDraftPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetDraftPolicyRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetDraftPolicy(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetDraftPolicy_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetDraftPolicy(ctx, req.(*GetDraftPolicyRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ApproveDraftChunk_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ApproveDraftChunkRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ApproveDraftChunk(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ApproveDraftChunk_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ApproveDraftChunk(ctx, req.(*ApproveDraftChunkRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_RejectDraftChunk_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(RejectDraftChunkRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).RejectDraftChunk(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_RejectDraftChunk_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).RejectDraftChunk(ctx, req.(*RejectDraftChunkRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ApproveAllDraftChunks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ApproveAllDraftChunksRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ApproveAllDraftChunks(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ApproveAllDraftChunks_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ApproveAllDraftChunks(ctx, req.(*ApproveAllDraftChunksRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_EditDraftChunk_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(EditDraftChunkRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).EditDraftChunk(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_EditDraftChunk_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).EditDraftChunk(ctx, req.(*EditDraftChunkRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_UndoDraftChunk_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UndoDraftChunkRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).UndoDraftChunk(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_UndoDraftChunk_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).UndoDraftChunk(ctx, req.(*UndoDraftChunkRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_ClearDraftChunks_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ClearDraftChunksRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).ClearDraftChunks(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_ClearDraftChunks_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).ClearDraftChunks(ctx, req.(*ClearDraftChunksRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _OpenShell_GetDraftHistory_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetDraftHistoryRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(OpenShellServer).GetDraftHistory(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: OpenShell_GetDraftHistory_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(OpenShellServer).GetDraftHistory(ctx, req.(*GetDraftHistoryRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// OpenShell_ServiceDesc is the grpc.ServiceDesc for OpenShell service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var OpenShell_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "openshell.v1.OpenShell", - HandlerType: (*OpenShellServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Health", - Handler: _OpenShell_Health_Handler, - }, - { - MethodName: "CreateSandbox", - Handler: _OpenShell_CreateSandbox_Handler, - }, - { - MethodName: "GetSandbox", - Handler: _OpenShell_GetSandbox_Handler, - }, - { - MethodName: "ListSandboxes", - Handler: _OpenShell_ListSandboxes_Handler, - }, - { - MethodName: "ListSandboxProviders", - Handler: _OpenShell_ListSandboxProviders_Handler, - }, - { - MethodName: "AttachSandboxProvider", - Handler: _OpenShell_AttachSandboxProvider_Handler, - }, - { - MethodName: "DetachSandboxProvider", - Handler: _OpenShell_DetachSandboxProvider_Handler, - }, - { - MethodName: "DeleteSandbox", - Handler: _OpenShell_DeleteSandbox_Handler, - }, - { - MethodName: "CreateSshSession", - Handler: _OpenShell_CreateSshSession_Handler, - }, - { - MethodName: "ExposeService", - Handler: _OpenShell_ExposeService_Handler, - }, - { - MethodName: "GetService", - Handler: _OpenShell_GetService_Handler, - }, - { - MethodName: "ListServices", - Handler: _OpenShell_ListServices_Handler, - }, - { - MethodName: "DeleteService", - Handler: _OpenShell_DeleteService_Handler, - }, - { - MethodName: "RevokeSshSession", - Handler: _OpenShell_RevokeSshSession_Handler, - }, - { - MethodName: "CreateProvider", - Handler: _OpenShell_CreateProvider_Handler, - }, - { - MethodName: "GetProvider", - Handler: _OpenShell_GetProvider_Handler, - }, - { - MethodName: "ListProviders", - Handler: _OpenShell_ListProviders_Handler, - }, - { - MethodName: "ListProviderProfiles", - Handler: _OpenShell_ListProviderProfiles_Handler, - }, - { - MethodName: "GetProviderProfile", - Handler: _OpenShell_GetProviderProfile_Handler, - }, - { - MethodName: "ImportProviderProfiles", - Handler: _OpenShell_ImportProviderProfiles_Handler, - }, - { - MethodName: "LintProviderProfiles", - Handler: _OpenShell_LintProviderProfiles_Handler, - }, - { - MethodName: "UpdateProvider", - Handler: _OpenShell_UpdateProvider_Handler, - }, - { - MethodName: "DeleteProvider", - Handler: _OpenShell_DeleteProvider_Handler, - }, - { - MethodName: "DeleteProviderProfile", - Handler: _OpenShell_DeleteProviderProfile_Handler, - }, - { - MethodName: "GetSandboxConfig", - Handler: _OpenShell_GetSandboxConfig_Handler, - }, - { - MethodName: "GetGatewayConfig", - Handler: _OpenShell_GetGatewayConfig_Handler, - }, - { - MethodName: "UpdateConfig", - Handler: _OpenShell_UpdateConfig_Handler, - }, - { - MethodName: "GetSandboxPolicyStatus", - Handler: _OpenShell_GetSandboxPolicyStatus_Handler, - }, - { - MethodName: "ListSandboxPolicies", - Handler: _OpenShell_ListSandboxPolicies_Handler, - }, - { - MethodName: "ReportPolicyStatus", - Handler: _OpenShell_ReportPolicyStatus_Handler, - }, - { - MethodName: "GetSandboxProviderEnvironment", - Handler: _OpenShell_GetSandboxProviderEnvironment_Handler, - }, - { - MethodName: "GetSandboxLogs", - Handler: _OpenShell_GetSandboxLogs_Handler, - }, - { - MethodName: "SubmitPolicyAnalysis", - Handler: _OpenShell_SubmitPolicyAnalysis_Handler, - }, - { - MethodName: "GetDraftPolicy", - Handler: _OpenShell_GetDraftPolicy_Handler, - }, - { - MethodName: "ApproveDraftChunk", - Handler: _OpenShell_ApproveDraftChunk_Handler, - }, - { - MethodName: "RejectDraftChunk", - Handler: _OpenShell_RejectDraftChunk_Handler, - }, - { - MethodName: "ApproveAllDraftChunks", - Handler: _OpenShell_ApproveAllDraftChunks_Handler, - }, - { - MethodName: "EditDraftChunk", - Handler: _OpenShell_EditDraftChunk_Handler, - }, - { - MethodName: "UndoDraftChunk", - Handler: _OpenShell_UndoDraftChunk_Handler, - }, - { - MethodName: "ClearDraftChunks", - Handler: _OpenShell_ClearDraftChunks_Handler, - }, - { - MethodName: "GetDraftHistory", - Handler: _OpenShell_GetDraftHistory_Handler, - }, - }, - Streams: []grpc.StreamDesc{ - { - StreamName: "ExecSandbox", - Handler: _OpenShell_ExecSandbox_Handler, - ServerStreams: true, - }, - { - StreamName: "ForwardTcp", - Handler: _OpenShell_ForwardTcp_Handler, - ServerStreams: true, - ClientStreams: true, - }, - { - StreamName: "ExecSandboxInteractive", - Handler: _OpenShell_ExecSandboxInteractive_Handler, - ServerStreams: true, - ClientStreams: true, - }, - { - StreamName: "PushSandboxLogs", - Handler: _OpenShell_PushSandboxLogs_Handler, - ClientStreams: true, - }, - { - StreamName: "ConnectSupervisor", - Handler: _OpenShell_ConnectSupervisor_Handler, - ServerStreams: true, - ClientStreams: true, - }, - { - StreamName: "RelayStream", - Handler: _OpenShell_RelayStream_Handler, - ServerStreams: true, - ClientStreams: true, - }, - { - StreamName: "WatchSandbox", - Handler: _OpenShell_WatchSandbox_Handler, - ServerStreams: true, - }, - }, - Metadata: "openshell.proto", -} diff --git a/go/api/openshell/gen/sandboxv1/sandbox.pb.go b/go/api/openshell/gen/sandboxv1/sandbox.pb.go deleted file mode 100644 index 7a14bc5214..0000000000 --- a/go/api/openshell/gen/sandboxv1/sandbox.pb.go +++ /dev/null @@ -1,1742 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.36.11 -// protoc (unknown) -// source: sandbox.proto - -package sandboxv1 - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" - unsafe "unsafe" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -// Scope that currently controls a setting. -type SettingScope int32 - -const ( - SettingScope_SETTING_SCOPE_UNSPECIFIED SettingScope = 0 - SettingScope_SETTING_SCOPE_SANDBOX SettingScope = 1 - SettingScope_SETTING_SCOPE_GLOBAL SettingScope = 2 -) - -// Enum value maps for SettingScope. -var ( - SettingScope_name = map[int32]string{ - 0: "SETTING_SCOPE_UNSPECIFIED", - 1: "SETTING_SCOPE_SANDBOX", - 2: "SETTING_SCOPE_GLOBAL", - } - SettingScope_value = map[string]int32{ - "SETTING_SCOPE_UNSPECIFIED": 0, - "SETTING_SCOPE_SANDBOX": 1, - "SETTING_SCOPE_GLOBAL": 2, - } -) - -func (x SettingScope) Enum() *SettingScope { - p := new(SettingScope) - *p = x - return p -} - -func (x SettingScope) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (SettingScope) Descriptor() protoreflect.EnumDescriptor { - return file_sandbox_proto_enumTypes[0].Descriptor() -} - -func (SettingScope) Type() protoreflect.EnumType { - return &file_sandbox_proto_enumTypes[0] -} - -func (x SettingScope) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use SettingScope.Descriptor instead. -func (SettingScope) EnumDescriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{0} -} - -// Source used for the policy payload in GetSandboxConfigResponse. -type PolicySource int32 - -const ( - PolicySource_POLICY_SOURCE_UNSPECIFIED PolicySource = 0 - PolicySource_POLICY_SOURCE_SANDBOX PolicySource = 1 - PolicySource_POLICY_SOURCE_GLOBAL PolicySource = 2 -) - -// Enum value maps for PolicySource. -var ( - PolicySource_name = map[int32]string{ - 0: "POLICY_SOURCE_UNSPECIFIED", - 1: "POLICY_SOURCE_SANDBOX", - 2: "POLICY_SOURCE_GLOBAL", - } - PolicySource_value = map[string]int32{ - "POLICY_SOURCE_UNSPECIFIED": 0, - "POLICY_SOURCE_SANDBOX": 1, - "POLICY_SOURCE_GLOBAL": 2, - } -) - -func (x PolicySource) Enum() *PolicySource { - p := new(PolicySource) - *p = x - return p -} - -func (x PolicySource) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (PolicySource) Descriptor() protoreflect.EnumDescriptor { - return file_sandbox_proto_enumTypes[1].Descriptor() -} - -func (PolicySource) Type() protoreflect.EnumType { - return &file_sandbox_proto_enumTypes[1] -} - -func (x PolicySource) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use PolicySource.Descriptor instead. -func (PolicySource) EnumDescriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{1} -} - -// Sandbox security policy configuration. -type SandboxPolicy struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Policy version. - Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - // Filesystem access policy. - Filesystem *FilesystemPolicy `protobuf:"bytes,2,opt,name=filesystem,proto3" json:"filesystem,omitempty"` - // Landlock configuration. - Landlock *LandlockPolicy `protobuf:"bytes,3,opt,name=landlock,proto3" json:"landlock,omitempty"` - // Process execution policy. - Process *ProcessPolicy `protobuf:"bytes,4,opt,name=process,proto3" json:"process,omitempty"` - // Network access policies keyed by name (e.g. "claude_code", "gitlab"). - NetworkPolicies map[string]*NetworkPolicyRule `protobuf:"bytes,5,rep,name=network_policies,json=networkPolicies,proto3" json:"network_policies,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SandboxPolicy) Reset() { - *x = SandboxPolicy{} - mi := &file_sandbox_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SandboxPolicy) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SandboxPolicy) ProtoMessage() {} - -func (x *SandboxPolicy) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[0] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SandboxPolicy.ProtoReflect.Descriptor instead. -func (*SandboxPolicy) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{0} -} - -func (x *SandboxPolicy) GetVersion() uint32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *SandboxPolicy) GetFilesystem() *FilesystemPolicy { - if x != nil { - return x.Filesystem - } - return nil -} - -func (x *SandboxPolicy) GetLandlock() *LandlockPolicy { - if x != nil { - return x.Landlock - } - return nil -} - -func (x *SandboxPolicy) GetProcess() *ProcessPolicy { - if x != nil { - return x.Process - } - return nil -} - -func (x *SandboxPolicy) GetNetworkPolicies() map[string]*NetworkPolicyRule { - if x != nil { - return x.NetworkPolicies - } - return nil -} - -// Filesystem access policy. -type FilesystemPolicy struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Automatically include the workdir as read-write. - IncludeWorkdir bool `protobuf:"varint,1,opt,name=include_workdir,json=includeWorkdir,proto3" json:"include_workdir,omitempty"` - // Read-only directory allow list. - ReadOnly []string `protobuf:"bytes,2,rep,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"` - // Read-write directory allow list. - ReadWrite []string `protobuf:"bytes,3,rep,name=read_write,json=readWrite,proto3" json:"read_write,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *FilesystemPolicy) Reset() { - *x = FilesystemPolicy{} - mi := &file_sandbox_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *FilesystemPolicy) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FilesystemPolicy) ProtoMessage() {} - -func (x *FilesystemPolicy) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[1] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FilesystemPolicy.ProtoReflect.Descriptor instead. -func (*FilesystemPolicy) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{1} -} - -func (x *FilesystemPolicy) GetIncludeWorkdir() bool { - if x != nil { - return x.IncludeWorkdir - } - return false -} - -func (x *FilesystemPolicy) GetReadOnly() []string { - if x != nil { - return x.ReadOnly - } - return nil -} - -func (x *FilesystemPolicy) GetReadWrite() []string { - if x != nil { - return x.ReadWrite - } - return nil -} - -// Landlock policy configuration. -type LandlockPolicy struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Compatibility mode (e.g. "best_effort", "hard_requirement"). - Compatibility string `protobuf:"bytes,1,opt,name=compatibility,proto3" json:"compatibility,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *LandlockPolicy) Reset() { - *x = LandlockPolicy{} - mi := &file_sandbox_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *LandlockPolicy) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*LandlockPolicy) ProtoMessage() {} - -func (x *LandlockPolicy) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[2] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use LandlockPolicy.ProtoReflect.Descriptor instead. -func (*LandlockPolicy) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{2} -} - -func (x *LandlockPolicy) GetCompatibility() string { - if x != nil { - return x.Compatibility - } - return "" -} - -// Process execution policy. -type ProcessPolicy struct { - state protoimpl.MessageState `protogen:"open.v1"` - // User name to run the sandboxed process as. - RunAsUser string `protobuf:"bytes,1,opt,name=run_as_user,json=runAsUser,proto3" json:"run_as_user,omitempty"` - // Group name to run the sandboxed process as. - RunAsGroup string `protobuf:"bytes,2,opt,name=run_as_group,json=runAsGroup,proto3" json:"run_as_group,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *ProcessPolicy) Reset() { - *x = ProcessPolicy{} - mi := &file_sandbox_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *ProcessPolicy) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ProcessPolicy) ProtoMessage() {} - -func (x *ProcessPolicy) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[3] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use ProcessPolicy.ProtoReflect.Descriptor instead. -func (*ProcessPolicy) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{3} -} - -func (x *ProcessPolicy) GetRunAsUser() string { - if x != nil { - return x.RunAsUser - } - return "" -} - -func (x *ProcessPolicy) GetRunAsGroup() string { - if x != nil { - return x.RunAsGroup - } - return "" -} - -// A named network access policy rule. -type NetworkPolicyRule struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Human-readable name for this policy rule. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Allowed endpoint (host:port) pairs. - Endpoints []*NetworkEndpoint `protobuf:"bytes,2,rep,name=endpoints,proto3" json:"endpoints,omitempty"` - // Allowed binary identities. - Binaries []*NetworkBinary `protobuf:"bytes,3,rep,name=binaries,proto3" json:"binaries,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *NetworkPolicyRule) Reset() { - *x = NetworkPolicyRule{} - mi := &file_sandbox_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *NetworkPolicyRule) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*NetworkPolicyRule) ProtoMessage() {} - -func (x *NetworkPolicyRule) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[4] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use NetworkPolicyRule.ProtoReflect.Descriptor instead. -func (*NetworkPolicyRule) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{4} -} - -func (x *NetworkPolicyRule) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *NetworkPolicyRule) GetEndpoints() []*NetworkEndpoint { - if x != nil { - return x.Endpoints - } - return nil -} - -func (x *NetworkPolicyRule) GetBinaries() []*NetworkBinary { - if x != nil { - return x.Binaries - } - return nil -} - -// A network endpoint (host + port) with optional L7 inspection config. -type NetworkEndpoint struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Hostname or host glob pattern. Exact match is case-insensitive. - // Glob patterns use "." as delimiter: "*.example.com" matches a single - // subdomain label, "**.example.com" matches across labels. - Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"` - // Single port (backwards compat). Use `ports` for multiple ports. - // Mutually exclusive with `ports` — if both are set, `ports` takes precedence. - Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` - // Application protocol for L7 inspection: "rest", "websocket", "graphql", "sql", or "" (L4-only). - Protocol string `protobuf:"bytes,3,opt,name=protocol,proto3" json:"protocol,omitempty"` - // TLS handling: "terminate" or "passthrough" (default). - Tls string `protobuf:"bytes,4,opt,name=tls,proto3" json:"tls,omitempty"` - // Enforcement mode: "enforce" or "audit" (default). - Enforcement string `protobuf:"bytes,5,opt,name=enforcement,proto3" json:"enforcement,omitempty"` - // Access preset shorthand: "read-only", "read-write", "full". - // Mutually exclusive with rules. - Access string `protobuf:"bytes,6,opt,name=access,proto3" json:"access,omitempty"` - // Explicit L7 rules (mutually exclusive with access). - Rules []*L7Rule `protobuf:"bytes,7,rep,name=rules,proto3" json:"rules,omitempty"` - // Allowed resolved IP addresses or CIDR ranges for this endpoint. - // When non-empty, the SSRF internal-IP check is replaced by an allowlist check: - // - If host is also set: domain must resolve to an IP in this list. - // - If host is empty: any domain is allowed as long as it resolves to an IP in this list. - // - // Supports exact IPs ("10.0.5.20") and CIDR notation ("10.0.5.0/24"). - // Loopback (127.0.0.0/8) and link-local (169.254.0.0/16) are always blocked - // regardless of this field. - AllowedIps []string `protobuf:"bytes,8,rep,name=allowed_ips,json=allowedIps,proto3" json:"allowed_ips,omitempty"` - // Multiple ports. When non-empty, this endpoint covers all listed ports. - // If `port` is set and `ports` is empty, `port` is normalized to `ports: [port]`. - // If both are set, `ports` takes precedence. - Ports []uint32 `protobuf:"varint,9,rep,packed,name=ports,proto3" json:"ports,omitempty"` - // Explicit L7 deny rules. When present, requests matching any deny rule - // are blocked even if they match an allow rule or access preset. - // Deny rules take precedence over allow rules. - DenyRules []*L7DenyRule `protobuf:"bytes,10,rep,name=deny_rules,json=denyRules,proto3" json:"deny_rules,omitempty"` - // When true, percent-encoded '/' (%2F) is preserved in path segments - // rather than rejected by the L7 path canonicalizer. Required for - // upstreams like GitLab that embed %2F in namespaced resource paths. - // Defaults to false (strict). - AllowEncodedSlash bool `protobuf:"varint,11,opt,name=allow_encoded_slash,json=allowEncodedSlash,proto3" json:"allow_encoded_slash,omitempty"` - // GraphQL persisted-query behavior for hash-only/saved-query requests: - // "deny" (default) or "allow_registered". - PersistedQueries string `protobuf:"bytes,12,opt,name=persisted_queries,json=persistedQueries,proto3" json:"persisted_queries,omitempty"` - // Trusted GraphQL persisted-query registry keyed by hash or service-specific ID. - // Only used when persisted_queries is "allow_registered". - GraphqlPersistedQueries map[string]*GraphqlOperation `protobuf:"bytes,13,rep,name=graphql_persisted_queries,json=graphqlPersistedQueries,proto3" json:"graphql_persisted_queries,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Maximum GraphQL request body bytes to buffer for inspection. - // Defaults to 65536 when unset. - GraphqlMaxBodyBytes uint32 `protobuf:"varint,14,opt,name=graphql_max_body_bytes,json=graphqlMaxBodyBytes,proto3" json:"graphql_max_body_bytes,omitempty"` - // Optional HTTP path glob that scopes this L7 endpoint on shared host:port APIs. - // Example: use path "/graphql" for protocol "graphql" and "/repos/**" for - // protocol "rest" when both surfaces live under api.example.com:443. - // Empty means all paths. - Path string `protobuf:"bytes,15,opt,name=path,proto3" json:"path,omitempty"` - // When true on a "rest" endpoint, OpenShell rewrites credential placeholders - // inside client-to-server WebSocket text messages after an allowed HTTP 101 - // upgrade. Defaults to false. - WebsocketCredentialRewrite bool `protobuf:"varint,16,opt,name=websocket_credential_rewrite,json=websocketCredentialRewrite,proto3" json:"websocket_credential_rewrite,omitempty"` - // When true on a "rest" endpoint, OpenShell rewrites credential placeholders - // inside supported textual HTTP request bodies before forwarding upstream. - // Defaults to false. - RequestBodyCredentialRewrite bool `protobuf:"varint,17,opt,name=request_body_credential_rewrite,json=requestBodyCredentialRewrite,proto3" json:"request_body_credential_rewrite,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *NetworkEndpoint) Reset() { - *x = NetworkEndpoint{} - mi := &file_sandbox_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *NetworkEndpoint) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*NetworkEndpoint) ProtoMessage() {} - -func (x *NetworkEndpoint) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[5] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use NetworkEndpoint.ProtoReflect.Descriptor instead. -func (*NetworkEndpoint) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{5} -} - -func (x *NetworkEndpoint) GetHost() string { - if x != nil { - return x.Host - } - return "" -} - -func (x *NetworkEndpoint) GetPort() uint32 { - if x != nil { - return x.Port - } - return 0 -} - -func (x *NetworkEndpoint) GetProtocol() string { - if x != nil { - return x.Protocol - } - return "" -} - -func (x *NetworkEndpoint) GetTls() string { - if x != nil { - return x.Tls - } - return "" -} - -func (x *NetworkEndpoint) GetEnforcement() string { - if x != nil { - return x.Enforcement - } - return "" -} - -func (x *NetworkEndpoint) GetAccess() string { - if x != nil { - return x.Access - } - return "" -} - -func (x *NetworkEndpoint) GetRules() []*L7Rule { - if x != nil { - return x.Rules - } - return nil -} - -func (x *NetworkEndpoint) GetAllowedIps() []string { - if x != nil { - return x.AllowedIps - } - return nil -} - -func (x *NetworkEndpoint) GetPorts() []uint32 { - if x != nil { - return x.Ports - } - return nil -} - -func (x *NetworkEndpoint) GetDenyRules() []*L7DenyRule { - if x != nil { - return x.DenyRules - } - return nil -} - -func (x *NetworkEndpoint) GetAllowEncodedSlash() bool { - if x != nil { - return x.AllowEncodedSlash - } - return false -} - -func (x *NetworkEndpoint) GetPersistedQueries() string { - if x != nil { - return x.PersistedQueries - } - return "" -} - -func (x *NetworkEndpoint) GetGraphqlPersistedQueries() map[string]*GraphqlOperation { - if x != nil { - return x.GraphqlPersistedQueries - } - return nil -} - -func (x *NetworkEndpoint) GetGraphqlMaxBodyBytes() uint32 { - if x != nil { - return x.GraphqlMaxBodyBytes - } - return 0 -} - -func (x *NetworkEndpoint) GetPath() string { - if x != nil { - return x.Path - } - return "" -} - -func (x *NetworkEndpoint) GetWebsocketCredentialRewrite() bool { - if x != nil { - return x.WebsocketCredentialRewrite - } - return false -} - -func (x *NetworkEndpoint) GetRequestBodyCredentialRewrite() bool { - if x != nil { - return x.RequestBodyCredentialRewrite - } - return false -} - -// Trusted GraphQL operation classification. -type GraphqlOperation struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Operation type: "query", "mutation", or "subscription". - OperationType string `protobuf:"bytes,1,opt,name=operation_type,json=operationType,proto3" json:"operation_type,omitempty"` - // Operation name, if known. - OperationName string `protobuf:"bytes,2,opt,name=operation_name,json=operationName,proto3" json:"operation_name,omitempty"` - // Root field names selected by the operation. - Fields []string `protobuf:"bytes,3,rep,name=fields,proto3" json:"fields,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GraphqlOperation) Reset() { - *x = GraphqlOperation{} - mi := &file_sandbox_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GraphqlOperation) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GraphqlOperation) ProtoMessage() {} - -func (x *GraphqlOperation) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[6] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GraphqlOperation.ProtoReflect.Descriptor instead. -func (*GraphqlOperation) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{6} -} - -func (x *GraphqlOperation) GetOperationType() string { - if x != nil { - return x.OperationType - } - return "" -} - -func (x *GraphqlOperation) GetOperationName() string { - if x != nil { - return x.OperationName - } - return "" -} - -func (x *GraphqlOperation) GetFields() []string { - if x != nil { - return x.Fields - } - return nil -} - -// An L7 deny rule that blocks specific requests. -// Mirrors L7Allow — same fields, same matching semantics, inverted effect. -// Deny rules are evaluated after allow rules and take precedence. -type L7DenyRule struct { - state protoimpl.MessageState `protogen:"open.v1"` - // HTTP method (REST): GET, POST, etc. or "*" for any. - Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` - // URL path glob pattern (REST): "/repos/*/pulls/*/reviews", "**" for any. - Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` - // SQL command (SQL): SELECT, INSERT, etc. or "*" for any. - Command string `protobuf:"bytes,3,opt,name=command,proto3" json:"command,omitempty"` - // Query parameter matcher map (REST). - // Same semantics as L7Allow.query. - Query map[string]*L7QueryMatcher `protobuf:"bytes,4,rep,name=query,proto3" json:"query,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // GraphQL operation type: "query", "mutation", "subscription", or "*" for any. - OperationType string `protobuf:"bytes,5,opt,name=operation_type,json=operationType,proto3" json:"operation_type,omitempty"` - // GraphQL operation name glob. "*" matches any operation name. - OperationName string `protobuf:"bytes,6,opt,name=operation_name,json=operationName,proto3" json:"operation_name,omitempty"` - // GraphQL root field globs. Deny rules match when any selected root field - // matches any configured glob. - Fields []string `protobuf:"bytes,7,rep,name=fields,proto3" json:"fields,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *L7DenyRule) Reset() { - *x = L7DenyRule{} - mi := &file_sandbox_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *L7DenyRule) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*L7DenyRule) ProtoMessage() {} - -func (x *L7DenyRule) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[7] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use L7DenyRule.ProtoReflect.Descriptor instead. -func (*L7DenyRule) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{7} -} - -func (x *L7DenyRule) GetMethod() string { - if x != nil { - return x.Method - } - return "" -} - -func (x *L7DenyRule) GetPath() string { - if x != nil { - return x.Path - } - return "" -} - -func (x *L7DenyRule) GetCommand() string { - if x != nil { - return x.Command - } - return "" -} - -func (x *L7DenyRule) GetQuery() map[string]*L7QueryMatcher { - if x != nil { - return x.Query - } - return nil -} - -func (x *L7DenyRule) GetOperationType() string { - if x != nil { - return x.OperationType - } - return "" -} - -func (x *L7DenyRule) GetOperationName() string { - if x != nil { - return x.OperationName - } - return "" -} - -func (x *L7DenyRule) GetFields() []string { - if x != nil { - return x.Fields - } - return nil -} - -// An L7 policy rule (allow-only). -type L7Rule struct { - state protoimpl.MessageState `protogen:"open.v1"` - Allow *L7Allow `protobuf:"bytes,1,opt,name=allow,proto3" json:"allow,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *L7Rule) Reset() { - *x = L7Rule{} - mi := &file_sandbox_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *L7Rule) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*L7Rule) ProtoMessage() {} - -func (x *L7Rule) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[8] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use L7Rule.ProtoReflect.Descriptor instead. -func (*L7Rule) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{8} -} - -func (x *L7Rule) GetAllow() *L7Allow { - if x != nil { - return x.Allow - } - return nil -} - -// Allowed action definition for L7 rules. -type L7Allow struct { - state protoimpl.MessageState `protogen:"open.v1"` - // HTTP method (REST): GET, POST, etc. or "*" for any. - Method string `protobuf:"bytes,1,opt,name=method,proto3" json:"method,omitempty"` - // URL path glob pattern (REST): "/repos/**", "**" for any. - Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"` - // SQL command (SQL): SELECT, INSERT, etc. or "*" for any. - Command string `protobuf:"bytes,3,opt,name=command,proto3" json:"command,omitempty"` - // Query parameter matcher map (REST). - // Key is the decoded query parameter name (case-sensitive). - // Value supports either a single glob (`glob`) or a list (`any`). - Query map[string]*L7QueryMatcher `protobuf:"bytes,4,rep,name=query,proto3" json:"query,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // GraphQL operation type: "query", "mutation", "subscription", or "*" for any. - OperationType string `protobuf:"bytes,5,opt,name=operation_type,json=operationType,proto3" json:"operation_type,omitempty"` - // GraphQL operation name glob. "*" matches any operation name. - OperationName string `protobuf:"bytes,6,opt,name=operation_name,json=operationName,proto3" json:"operation_name,omitempty"` - // GraphQL root field globs. Allow rules match only when every selected root - // field matches one of the configured globs. Omit to match all fields. - Fields []string `protobuf:"bytes,7,rep,name=fields,proto3" json:"fields,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *L7Allow) Reset() { - *x = L7Allow{} - mi := &file_sandbox_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *L7Allow) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*L7Allow) ProtoMessage() {} - -func (x *L7Allow) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[9] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use L7Allow.ProtoReflect.Descriptor instead. -func (*L7Allow) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{9} -} - -func (x *L7Allow) GetMethod() string { - if x != nil { - return x.Method - } - return "" -} - -func (x *L7Allow) GetPath() string { - if x != nil { - return x.Path - } - return "" -} - -func (x *L7Allow) GetCommand() string { - if x != nil { - return x.Command - } - return "" -} - -func (x *L7Allow) GetQuery() map[string]*L7QueryMatcher { - if x != nil { - return x.Query - } - return nil -} - -func (x *L7Allow) GetOperationType() string { - if x != nil { - return x.OperationType - } - return "" -} - -func (x *L7Allow) GetOperationName() string { - if x != nil { - return x.OperationName - } - return "" -} - -func (x *L7Allow) GetFields() []string { - if x != nil { - return x.Fields - } - return nil -} - -// Query value matcher for one query parameter key. -type L7QueryMatcher struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Single glob pattern. - Glob string `protobuf:"bytes,1,opt,name=glob,proto3" json:"glob,omitempty"` - // Any-of glob patterns. - Any []string `protobuf:"bytes,2,rep,name=any,proto3" json:"any,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *L7QueryMatcher) Reset() { - *x = L7QueryMatcher{} - mi := &file_sandbox_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *L7QueryMatcher) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*L7QueryMatcher) ProtoMessage() {} - -func (x *L7QueryMatcher) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[10] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use L7QueryMatcher.ProtoReflect.Descriptor instead. -func (*L7QueryMatcher) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{10} -} - -func (x *L7QueryMatcher) GetGlob() string { - if x != nil { - return x.Glob - } - return "" -} - -func (x *L7QueryMatcher) GetAny() []string { - if x != nil { - return x.Any - } - return nil -} - -// A binary identity for network policy matching. -type NetworkBinary struct { - state protoimpl.MessageState `protogen:"open.v1"` - Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` - // Deprecated: the harness concept has been removed. This field is ignored. - // - // Deprecated: Marked as deprecated in sandbox.proto. - Harness bool `protobuf:"varint,2,opt,name=harness,proto3" json:"harness,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *NetworkBinary) Reset() { - *x = NetworkBinary{} - mi := &file_sandbox_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *NetworkBinary) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*NetworkBinary) ProtoMessage() {} - -func (x *NetworkBinary) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[11] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use NetworkBinary.ProtoReflect.Descriptor instead. -func (*NetworkBinary) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{11} -} - -func (x *NetworkBinary) GetPath() string { - if x != nil { - return x.Path - } - return "" -} - -// Deprecated: Marked as deprecated in sandbox.proto. -func (x *NetworkBinary) GetHarness() bool { - if x != nil { - return x.Harness - } - return false -} - -// Request to get sandbox settings by sandbox ID. -type GetSandboxConfigRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The sandbox ID. - SandboxId string `protobuf:"bytes,1,opt,name=sandbox_id,json=sandboxId,proto3" json:"sandbox_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxConfigRequest) Reset() { - *x = GetSandboxConfigRequest{} - mi := &file_sandbox_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxConfigRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxConfigRequest) ProtoMessage() {} - -func (x *GetSandboxConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[12] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxConfigRequest.ProtoReflect.Descriptor instead. -func (*GetSandboxConfigRequest) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{12} -} - -func (x *GetSandboxConfigRequest) GetSandboxId() string { - if x != nil { - return x.SandboxId - } - return "" -} - -// Request to get gateway-global settings. -type GetGatewayConfigRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetGatewayConfigRequest) Reset() { - *x = GetGatewayConfigRequest{} - mi := &file_sandbox_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetGatewayConfigRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetGatewayConfigRequest) ProtoMessage() {} - -func (x *GetGatewayConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[13] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetGatewayConfigRequest.ProtoReflect.Descriptor instead. -func (*GetGatewayConfigRequest) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{13} -} - -// Response containing gateway-global settings. -type GetGatewayConfigResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Gateway-global settings map excluding the reserved policy key. - // Registered keys without a configured value are returned with an empty SettingValue. - Settings map[string]*SettingValue `protobuf:"bytes,1,rep,name=settings,proto3" json:"settings,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Monotonically increasing revision for gateway-global settings. - SettingsRevision uint64 `protobuf:"varint,2,opt,name=settings_revision,json=settingsRevision,proto3" json:"settings_revision,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetGatewayConfigResponse) Reset() { - *x = GetGatewayConfigResponse{} - mi := &file_sandbox_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetGatewayConfigResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetGatewayConfigResponse) ProtoMessage() {} - -func (x *GetGatewayConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[14] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetGatewayConfigResponse.ProtoReflect.Descriptor instead. -func (*GetGatewayConfigResponse) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{14} -} - -func (x *GetGatewayConfigResponse) GetSettings() map[string]*SettingValue { - if x != nil { - return x.Settings - } - return nil -} - -func (x *GetGatewayConfigResponse) GetSettingsRevision() uint64 { - if x != nil { - return x.SettingsRevision - } - return 0 -} - -// Type-aware setting value for sandbox/gateway settings. -type SettingValue struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Types that are valid to be assigned to Value: - // - // *SettingValue_StringValue - // *SettingValue_BoolValue - // *SettingValue_IntValue - // *SettingValue_BytesValue - Value isSettingValue_Value `protobuf_oneof:"value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *SettingValue) Reset() { - *x = SettingValue{} - mi := &file_sandbox_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *SettingValue) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SettingValue) ProtoMessage() {} - -func (x *SettingValue) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[15] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use SettingValue.ProtoReflect.Descriptor instead. -func (*SettingValue) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{15} -} - -func (x *SettingValue) GetValue() isSettingValue_Value { - if x != nil { - return x.Value - } - return nil -} - -func (x *SettingValue) GetStringValue() string { - if x != nil { - if x, ok := x.Value.(*SettingValue_StringValue); ok { - return x.StringValue - } - } - return "" -} - -func (x *SettingValue) GetBoolValue() bool { - if x != nil { - if x, ok := x.Value.(*SettingValue_BoolValue); ok { - return x.BoolValue - } - } - return false -} - -func (x *SettingValue) GetIntValue() int64 { - if x != nil { - if x, ok := x.Value.(*SettingValue_IntValue); ok { - return x.IntValue - } - } - return 0 -} - -func (x *SettingValue) GetBytesValue() []byte { - if x != nil { - if x, ok := x.Value.(*SettingValue_BytesValue); ok { - return x.BytesValue - } - } - return nil -} - -type isSettingValue_Value interface { - isSettingValue_Value() -} - -type SettingValue_StringValue struct { - StringValue string `protobuf:"bytes,1,opt,name=string_value,json=stringValue,proto3,oneof"` -} - -type SettingValue_BoolValue struct { - BoolValue bool `protobuf:"varint,2,opt,name=bool_value,json=boolValue,proto3,oneof"` -} - -type SettingValue_IntValue struct { - IntValue int64 `protobuf:"varint,3,opt,name=int_value,json=intValue,proto3,oneof"` -} - -type SettingValue_BytesValue struct { - BytesValue []byte `protobuf:"bytes,4,opt,name=bytes_value,json=bytesValue,proto3,oneof"` -} - -func (*SettingValue_StringValue) isSettingValue_Value() {} - -func (*SettingValue_BoolValue) isSettingValue_Value() {} - -func (*SettingValue_IntValue) isSettingValue_Value() {} - -func (*SettingValue_BytesValue) isSettingValue_Value() {} - -// Effective setting value and the scope it was resolved from. -type EffectiveSetting struct { - state protoimpl.MessageState `protogen:"open.v1"` - Value *SettingValue `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"` - Scope SettingScope `protobuf:"varint,2,opt,name=scope,proto3,enum=openshell.sandbox.v1.SettingScope" json:"scope,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *EffectiveSetting) Reset() { - *x = EffectiveSetting{} - mi := &file_sandbox_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *EffectiveSetting) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*EffectiveSetting) ProtoMessage() {} - -func (x *EffectiveSetting) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[16] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use EffectiveSetting.ProtoReflect.Descriptor instead. -func (*EffectiveSetting) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{16} -} - -func (x *EffectiveSetting) GetValue() *SettingValue { - if x != nil { - return x.Value - } - return nil -} - -func (x *EffectiveSetting) GetScope() SettingScope { - if x != nil { - return x.Scope - } - return SettingScope_SETTING_SCOPE_UNSPECIFIED -} - -// Response containing effective sandbox settings and policy. -type GetSandboxConfigResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The sandbox policy configuration. - Policy *SandboxPolicy `protobuf:"bytes,1,opt,name=policy,proto3" json:"policy,omitempty"` - // Current policy version (monotonically increasing per sandbox). - Version uint32 `protobuf:"varint,2,opt,name=version,proto3" json:"version,omitempty"` - // SHA-256 hash of the serialized policy payload. - PolicyHash string `protobuf:"bytes,3,opt,name=policy_hash,json=policyHash,proto3" json:"policy_hash,omitempty"` - // Effective settings resolved for this sandbox, excluding the reserved policy key. - // Registered keys without a configured value are returned with an empty EffectiveSetting.value. - Settings map[string]*EffectiveSetting `protobuf:"bytes,4,rep,name=settings,proto3" json:"settings,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // Fingerprint for effective config (policy + settings). Changes when any effective input changes. - ConfigRevision uint64 `protobuf:"varint,5,opt,name=config_revision,json=configRevision,proto3" json:"config_revision,omitempty"` - // Source of the policy payload for this response. - PolicySource PolicySource `protobuf:"varint,6,opt,name=policy_source,json=policySource,proto3,enum=openshell.sandbox.v1.PolicySource" json:"policy_source,omitempty"` - // When policy_source is GLOBAL, the version of the global policy revision. - // Zero when no global policy is active or when policy_source is SANDBOX. - GlobalPolicyVersion uint32 `protobuf:"varint,7,opt,name=global_policy_version,json=globalPolicyVersion,proto3" json:"global_policy_version,omitempty"` - // Fingerprint for provider credential inputs attached to this sandbox. - // Changes when attached provider names or attached provider records change. - ProviderEnvRevision uint64 `protobuf:"varint,8,opt,name=provider_env_revision,json=providerEnvRevision,proto3" json:"provider_env_revision,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *GetSandboxConfigResponse) Reset() { - *x = GetSandboxConfigResponse{} - mi := &file_sandbox_proto_msgTypes[17] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *GetSandboxConfigResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSandboxConfigResponse) ProtoMessage() {} - -func (x *GetSandboxConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_sandbox_proto_msgTypes[17] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSandboxConfigResponse.ProtoReflect.Descriptor instead. -func (*GetSandboxConfigResponse) Descriptor() ([]byte, []int) { - return file_sandbox_proto_rawDescGZIP(), []int{17} -} - -func (x *GetSandboxConfigResponse) GetPolicy() *SandboxPolicy { - if x != nil { - return x.Policy - } - return nil -} - -func (x *GetSandboxConfigResponse) GetVersion() uint32 { - if x != nil { - return x.Version - } - return 0 -} - -func (x *GetSandboxConfigResponse) GetPolicyHash() string { - if x != nil { - return x.PolicyHash - } - return "" -} - -func (x *GetSandboxConfigResponse) GetSettings() map[string]*EffectiveSetting { - if x != nil { - return x.Settings - } - return nil -} - -func (x *GetSandboxConfigResponse) GetConfigRevision() uint64 { - if x != nil { - return x.ConfigRevision - } - return 0 -} - -func (x *GetSandboxConfigResponse) GetPolicySource() PolicySource { - if x != nil { - return x.PolicySource - } - return PolicySource_POLICY_SOURCE_UNSPECIFIED -} - -func (x *GetSandboxConfigResponse) GetGlobalPolicyVersion() uint32 { - if x != nil { - return x.GlobalPolicyVersion - } - return 0 -} - -func (x *GetSandboxConfigResponse) GetProviderEnvRevision() uint64 { - if x != nil { - return x.ProviderEnvRevision - } - return 0 -} - -var File_sandbox_proto protoreflect.FileDescriptor - -const file_sandbox_proto_rawDesc = "" + - "\n" + - "\rsandbox.proto\x12\x14openshell.sandbox.v1\"\xc4\x03\n" + - "\rSandboxPolicy\x12\x18\n" + - "\aversion\x18\x01 \x01(\rR\aversion\x12F\n" + - "\n" + - "filesystem\x18\x02 \x01(\v2&.openshell.sandbox.v1.FilesystemPolicyR\n" + - "filesystem\x12@\n" + - "\blandlock\x18\x03 \x01(\v2$.openshell.sandbox.v1.LandlockPolicyR\blandlock\x12=\n" + - "\aprocess\x18\x04 \x01(\v2#.openshell.sandbox.v1.ProcessPolicyR\aprocess\x12c\n" + - "\x10network_policies\x18\x05 \x03(\v28.openshell.sandbox.v1.SandboxPolicy.NetworkPoliciesEntryR\x0fnetworkPolicies\x1ak\n" + - "\x14NetworkPoliciesEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12=\n" + - "\x05value\x18\x02 \x01(\v2'.openshell.sandbox.v1.NetworkPolicyRuleR\x05value:\x028\x01\"w\n" + - "\x10FilesystemPolicy\x12'\n" + - "\x0finclude_workdir\x18\x01 \x01(\bR\x0eincludeWorkdir\x12\x1b\n" + - "\tread_only\x18\x02 \x03(\tR\breadOnly\x12\x1d\n" + - "\n" + - "read_write\x18\x03 \x03(\tR\treadWrite\"6\n" + - "\x0eLandlockPolicy\x12$\n" + - "\rcompatibility\x18\x01 \x01(\tR\rcompatibility\"Q\n" + - "\rProcessPolicy\x12\x1e\n" + - "\vrun_as_user\x18\x01 \x01(\tR\trunAsUser\x12 \n" + - "\frun_as_group\x18\x02 \x01(\tR\n" + - "runAsGroup\"\xad\x01\n" + - "\x11NetworkPolicyRule\x12\x12\n" + - "\x04name\x18\x01 \x01(\tR\x04name\x12C\n" + - "\tendpoints\x18\x02 \x03(\v2%.openshell.sandbox.v1.NetworkEndpointR\tendpoints\x12?\n" + - "\bbinaries\x18\x03 \x03(\v2#.openshell.sandbox.v1.NetworkBinaryR\bbinaries\"\xf0\x06\n" + - "\x0fNetworkEndpoint\x12\x12\n" + - "\x04host\x18\x01 \x01(\tR\x04host\x12\x12\n" + - "\x04port\x18\x02 \x01(\rR\x04port\x12\x1a\n" + - "\bprotocol\x18\x03 \x01(\tR\bprotocol\x12\x10\n" + - "\x03tls\x18\x04 \x01(\tR\x03tls\x12 \n" + - "\venforcement\x18\x05 \x01(\tR\venforcement\x12\x16\n" + - "\x06access\x18\x06 \x01(\tR\x06access\x122\n" + - "\x05rules\x18\a \x03(\v2\x1c.openshell.sandbox.v1.L7RuleR\x05rules\x12\x1f\n" + - "\vallowed_ips\x18\b \x03(\tR\n" + - "allowedIps\x12\x14\n" + - "\x05ports\x18\t \x03(\rR\x05ports\x12?\n" + - "\n" + - "deny_rules\x18\n" + - " \x03(\v2 .openshell.sandbox.v1.L7DenyRuleR\tdenyRules\x12.\n" + - "\x13allow_encoded_slash\x18\v \x01(\bR\x11allowEncodedSlash\x12+\n" + - "\x11persisted_queries\x18\f \x01(\tR\x10persistedQueries\x12~\n" + - "\x19graphql_persisted_queries\x18\r \x03(\v2B.openshell.sandbox.v1.NetworkEndpoint.GraphqlPersistedQueriesEntryR\x17graphqlPersistedQueries\x123\n" + - "\x16graphql_max_body_bytes\x18\x0e \x01(\rR\x13graphqlMaxBodyBytes\x12\x12\n" + - "\x04path\x18\x0f \x01(\tR\x04path\x12@\n" + - "\x1cwebsocket_credential_rewrite\x18\x10 \x01(\bR\x1awebsocketCredentialRewrite\x12E\n" + - "\x1frequest_body_credential_rewrite\x18\x11 \x01(\bR\x1crequestBodyCredentialRewrite\x1ar\n" + - "\x1cGraphqlPersistedQueriesEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12<\n" + - "\x05value\x18\x02 \x01(\v2&.openshell.sandbox.v1.GraphqlOperationR\x05value:\x028\x01\"x\n" + - "\x10GraphqlOperation\x12%\n" + - "\x0eoperation_type\x18\x01 \x01(\tR\roperationType\x12%\n" + - "\x0eoperation_name\x18\x02 \x01(\tR\roperationName\x12\x16\n" + - "\x06fields\x18\x03 \x03(\tR\x06fields\"\xdb\x02\n" + - "\n" + - "L7DenyRule\x12\x16\n" + - "\x06method\x18\x01 \x01(\tR\x06method\x12\x12\n" + - "\x04path\x18\x02 \x01(\tR\x04path\x12\x18\n" + - "\acommand\x18\x03 \x01(\tR\acommand\x12A\n" + - "\x05query\x18\x04 \x03(\v2+.openshell.sandbox.v1.L7DenyRule.QueryEntryR\x05query\x12%\n" + - "\x0eoperation_type\x18\x05 \x01(\tR\roperationType\x12%\n" + - "\x0eoperation_name\x18\x06 \x01(\tR\roperationName\x12\x16\n" + - "\x06fields\x18\a \x03(\tR\x06fields\x1a^\n" + - "\n" + - "QueryEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12:\n" + - "\x05value\x18\x02 \x01(\v2$.openshell.sandbox.v1.L7QueryMatcherR\x05value:\x028\x01\"=\n" + - "\x06L7Rule\x123\n" + - "\x05allow\x18\x01 \x01(\v2\x1d.openshell.sandbox.v1.L7AllowR\x05allow\"\xd5\x02\n" + - "\aL7Allow\x12\x16\n" + - "\x06method\x18\x01 \x01(\tR\x06method\x12\x12\n" + - "\x04path\x18\x02 \x01(\tR\x04path\x12\x18\n" + - "\acommand\x18\x03 \x01(\tR\acommand\x12>\n" + - "\x05query\x18\x04 \x03(\v2(.openshell.sandbox.v1.L7Allow.QueryEntryR\x05query\x12%\n" + - "\x0eoperation_type\x18\x05 \x01(\tR\roperationType\x12%\n" + - "\x0eoperation_name\x18\x06 \x01(\tR\roperationName\x12\x16\n" + - "\x06fields\x18\a \x03(\tR\x06fields\x1a^\n" + - "\n" + - "QueryEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12:\n" + - "\x05value\x18\x02 \x01(\v2$.openshell.sandbox.v1.L7QueryMatcherR\x05value:\x028\x01\"6\n" + - "\x0eL7QueryMatcher\x12\x12\n" + - "\x04glob\x18\x01 \x01(\tR\x04glob\x12\x10\n" + - "\x03any\x18\x02 \x03(\tR\x03any\"A\n" + - "\rNetworkBinary\x12\x12\n" + - "\x04path\x18\x01 \x01(\tR\x04path\x12\x1c\n" + - "\aharness\x18\x02 \x01(\bB\x02\x18\x01R\aharness\"8\n" + - "\x17GetSandboxConfigRequest\x12\x1d\n" + - "\n" + - "sandbox_id\x18\x01 \x01(\tR\tsandboxId\"\x19\n" + - "\x17GetGatewayConfigRequest\"\x82\x02\n" + - "\x18GetGatewayConfigResponse\x12X\n" + - "\bsettings\x18\x01 \x03(\v2<.openshell.sandbox.v1.GetGatewayConfigResponse.SettingsEntryR\bsettings\x12+\n" + - "\x11settings_revision\x18\x02 \x01(\x04R\x10settingsRevision\x1a_\n" + - "\rSettingsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x128\n" + - "\x05value\x18\x02 \x01(\v2\".openshell.sandbox.v1.SettingValueR\x05value:\x028\x01\"\x9f\x01\n" + - "\fSettingValue\x12#\n" + - "\fstring_value\x18\x01 \x01(\tH\x00R\vstringValue\x12\x1f\n" + - "\n" + - "bool_value\x18\x02 \x01(\bH\x00R\tboolValue\x12\x1d\n" + - "\tint_value\x18\x03 \x01(\x03H\x00R\bintValue\x12!\n" + - "\vbytes_value\x18\x04 \x01(\fH\x00R\n" + - "bytesValueB\a\n" + - "\x05value\"\x86\x01\n" + - "\x10EffectiveSetting\x128\n" + - "\x05value\x18\x01 \x01(\v2\".openshell.sandbox.v1.SettingValueR\x05value\x128\n" + - "\x05scope\x18\x02 \x01(\x0e2\".openshell.sandbox.v1.SettingScopeR\x05scope\"\xab\x04\n" + - "\x18GetSandboxConfigResponse\x12;\n" + - "\x06policy\x18\x01 \x01(\v2#.openshell.sandbox.v1.SandboxPolicyR\x06policy\x12\x18\n" + - "\aversion\x18\x02 \x01(\rR\aversion\x12\x1f\n" + - "\vpolicy_hash\x18\x03 \x01(\tR\n" + - "policyHash\x12X\n" + - "\bsettings\x18\x04 \x03(\v2<.openshell.sandbox.v1.GetSandboxConfigResponse.SettingsEntryR\bsettings\x12'\n" + - "\x0fconfig_revision\x18\x05 \x01(\x04R\x0econfigRevision\x12G\n" + - "\rpolicy_source\x18\x06 \x01(\x0e2\".openshell.sandbox.v1.PolicySourceR\fpolicySource\x122\n" + - "\x15global_policy_version\x18\a \x01(\rR\x13globalPolicyVersion\x122\n" + - "\x15provider_env_revision\x18\b \x01(\x04R\x13providerEnvRevision\x1ac\n" + - "\rSettingsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12<\n" + - "\x05value\x18\x02 \x01(\v2&.openshell.sandbox.v1.EffectiveSettingR\x05value:\x028\x01*b\n" + - "\fSettingScope\x12\x1d\n" + - "\x19SETTING_SCOPE_UNSPECIFIED\x10\x00\x12\x19\n" + - "\x15SETTING_SCOPE_SANDBOX\x10\x01\x12\x18\n" + - "\x14SETTING_SCOPE_GLOBAL\x10\x02*b\n" + - "\fPolicySource\x12\x1d\n" + - "\x19POLICY_SOURCE_UNSPECIFIED\x10\x00\x12\x19\n" + - "\x15POLICY_SOURCE_SANDBOX\x10\x01\x12\x18\n" + - "\x14POLICY_SOURCE_GLOBAL\x10\x02B\xd7\x01\n" + - "\x18com.openshell.sandbox.v1B\fSandboxProtoP\x01Z;github.com/kagent-dev/kagent/go/api/openshell/gen/sandboxv1\xa2\x02\x03OSX\xaa\x02\x14Openshell.Sandbox.V1\xca\x02\x14Openshell\\Sandbox\\V1\xe2\x02 Openshell\\Sandbox\\V1\\GPBMetadata\xea\x02\x16Openshell::Sandbox::V1b\x06proto3" - -var ( - file_sandbox_proto_rawDescOnce sync.Once - file_sandbox_proto_rawDescData []byte -) - -func file_sandbox_proto_rawDescGZIP() []byte { - file_sandbox_proto_rawDescOnce.Do(func() { - file_sandbox_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_sandbox_proto_rawDesc), len(file_sandbox_proto_rawDesc))) - }) - return file_sandbox_proto_rawDescData -} - -var file_sandbox_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_sandbox_proto_msgTypes = make([]protoimpl.MessageInfo, 24) -var file_sandbox_proto_goTypes = []any{ - (SettingScope)(0), // 0: openshell.sandbox.v1.SettingScope - (PolicySource)(0), // 1: openshell.sandbox.v1.PolicySource - (*SandboxPolicy)(nil), // 2: openshell.sandbox.v1.SandboxPolicy - (*FilesystemPolicy)(nil), // 3: openshell.sandbox.v1.FilesystemPolicy - (*LandlockPolicy)(nil), // 4: openshell.sandbox.v1.LandlockPolicy - (*ProcessPolicy)(nil), // 5: openshell.sandbox.v1.ProcessPolicy - (*NetworkPolicyRule)(nil), // 6: openshell.sandbox.v1.NetworkPolicyRule - (*NetworkEndpoint)(nil), // 7: openshell.sandbox.v1.NetworkEndpoint - (*GraphqlOperation)(nil), // 8: openshell.sandbox.v1.GraphqlOperation - (*L7DenyRule)(nil), // 9: openshell.sandbox.v1.L7DenyRule - (*L7Rule)(nil), // 10: openshell.sandbox.v1.L7Rule - (*L7Allow)(nil), // 11: openshell.sandbox.v1.L7Allow - (*L7QueryMatcher)(nil), // 12: openshell.sandbox.v1.L7QueryMatcher - (*NetworkBinary)(nil), // 13: openshell.sandbox.v1.NetworkBinary - (*GetSandboxConfigRequest)(nil), // 14: openshell.sandbox.v1.GetSandboxConfigRequest - (*GetGatewayConfigRequest)(nil), // 15: openshell.sandbox.v1.GetGatewayConfigRequest - (*GetGatewayConfigResponse)(nil), // 16: openshell.sandbox.v1.GetGatewayConfigResponse - (*SettingValue)(nil), // 17: openshell.sandbox.v1.SettingValue - (*EffectiveSetting)(nil), // 18: openshell.sandbox.v1.EffectiveSetting - (*GetSandboxConfigResponse)(nil), // 19: openshell.sandbox.v1.GetSandboxConfigResponse - nil, // 20: openshell.sandbox.v1.SandboxPolicy.NetworkPoliciesEntry - nil, // 21: openshell.sandbox.v1.NetworkEndpoint.GraphqlPersistedQueriesEntry - nil, // 22: openshell.sandbox.v1.L7DenyRule.QueryEntry - nil, // 23: openshell.sandbox.v1.L7Allow.QueryEntry - nil, // 24: openshell.sandbox.v1.GetGatewayConfigResponse.SettingsEntry - nil, // 25: openshell.sandbox.v1.GetSandboxConfigResponse.SettingsEntry -} -var file_sandbox_proto_depIdxs = []int32{ - 3, // 0: openshell.sandbox.v1.SandboxPolicy.filesystem:type_name -> openshell.sandbox.v1.FilesystemPolicy - 4, // 1: openshell.sandbox.v1.SandboxPolicy.landlock:type_name -> openshell.sandbox.v1.LandlockPolicy - 5, // 2: openshell.sandbox.v1.SandboxPolicy.process:type_name -> openshell.sandbox.v1.ProcessPolicy - 20, // 3: openshell.sandbox.v1.SandboxPolicy.network_policies:type_name -> openshell.sandbox.v1.SandboxPolicy.NetworkPoliciesEntry - 7, // 4: openshell.sandbox.v1.NetworkPolicyRule.endpoints:type_name -> openshell.sandbox.v1.NetworkEndpoint - 13, // 5: openshell.sandbox.v1.NetworkPolicyRule.binaries:type_name -> openshell.sandbox.v1.NetworkBinary - 10, // 6: openshell.sandbox.v1.NetworkEndpoint.rules:type_name -> openshell.sandbox.v1.L7Rule - 9, // 7: openshell.sandbox.v1.NetworkEndpoint.deny_rules:type_name -> openshell.sandbox.v1.L7DenyRule - 21, // 8: openshell.sandbox.v1.NetworkEndpoint.graphql_persisted_queries:type_name -> openshell.sandbox.v1.NetworkEndpoint.GraphqlPersistedQueriesEntry - 22, // 9: openshell.sandbox.v1.L7DenyRule.query:type_name -> openshell.sandbox.v1.L7DenyRule.QueryEntry - 11, // 10: openshell.sandbox.v1.L7Rule.allow:type_name -> openshell.sandbox.v1.L7Allow - 23, // 11: openshell.sandbox.v1.L7Allow.query:type_name -> openshell.sandbox.v1.L7Allow.QueryEntry - 24, // 12: openshell.sandbox.v1.GetGatewayConfigResponse.settings:type_name -> openshell.sandbox.v1.GetGatewayConfigResponse.SettingsEntry - 17, // 13: openshell.sandbox.v1.EffectiveSetting.value:type_name -> openshell.sandbox.v1.SettingValue - 0, // 14: openshell.sandbox.v1.EffectiveSetting.scope:type_name -> openshell.sandbox.v1.SettingScope - 2, // 15: openshell.sandbox.v1.GetSandboxConfigResponse.policy:type_name -> openshell.sandbox.v1.SandboxPolicy - 25, // 16: openshell.sandbox.v1.GetSandboxConfigResponse.settings:type_name -> openshell.sandbox.v1.GetSandboxConfigResponse.SettingsEntry - 1, // 17: openshell.sandbox.v1.GetSandboxConfigResponse.policy_source:type_name -> openshell.sandbox.v1.PolicySource - 6, // 18: openshell.sandbox.v1.SandboxPolicy.NetworkPoliciesEntry.value:type_name -> openshell.sandbox.v1.NetworkPolicyRule - 8, // 19: openshell.sandbox.v1.NetworkEndpoint.GraphqlPersistedQueriesEntry.value:type_name -> openshell.sandbox.v1.GraphqlOperation - 12, // 20: openshell.sandbox.v1.L7DenyRule.QueryEntry.value:type_name -> openshell.sandbox.v1.L7QueryMatcher - 12, // 21: openshell.sandbox.v1.L7Allow.QueryEntry.value:type_name -> openshell.sandbox.v1.L7QueryMatcher - 17, // 22: openshell.sandbox.v1.GetGatewayConfigResponse.SettingsEntry.value:type_name -> openshell.sandbox.v1.SettingValue - 18, // 23: openshell.sandbox.v1.GetSandboxConfigResponse.SettingsEntry.value:type_name -> openshell.sandbox.v1.EffectiveSetting - 24, // [24:24] is the sub-list for method output_type - 24, // [24:24] is the sub-list for method input_type - 24, // [24:24] is the sub-list for extension type_name - 24, // [24:24] is the sub-list for extension extendee - 0, // [0:24] is the sub-list for field type_name -} - -func init() { file_sandbox_proto_init() } -func file_sandbox_proto_init() { - if File_sandbox_proto != nil { - return - } - file_sandbox_proto_msgTypes[15].OneofWrappers = []any{ - (*SettingValue_StringValue)(nil), - (*SettingValue_BoolValue)(nil), - (*SettingValue_IntValue)(nil), - (*SettingValue_BytesValue)(nil), - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: unsafe.Slice(unsafe.StringData(file_sandbox_proto_rawDesc), len(file_sandbox_proto_rawDesc)), - NumEnums: 2, - NumMessages: 24, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_sandbox_proto_goTypes, - DependencyIndexes: file_sandbox_proto_depIdxs, - EnumInfos: file_sandbox_proto_enumTypes, - MessageInfos: file_sandbox_proto_msgTypes, - }.Build() - File_sandbox_proto = out.File - file_sandbox_proto_goTypes = nil - file_sandbox_proto_depIdxs = nil -} diff --git a/go/api/openshell/proto/buf.gen.yaml b/go/api/openshell/proto/buf.gen.yaml deleted file mode 100644 index c676a3daf2..0000000000 --- a/go/api/openshell/proto/buf.gen.yaml +++ /dev/null @@ -1,45 +0,0 @@ -version: v2 -managed: - enabled: true - override: - - file_option: go_package - path: openshell.proto - value: github.com/kagent-dev/kagent/go/api/openshell/gen/openshellv1 - - file_option: go_package - path: sandbox.proto - value: github.com/kagent-dev/kagent/go/api/openshell/gen/sandboxv1 - - file_option: go_package - path: datamodel.proto - value: github.com/kagent-dev/kagent/go/api/openshell/gen/datamodelv1 - - file_option: go_package - path: compute_driver.proto - value: github.com/kagent-dev/kagent/go/api/openshell/gen/computev1 - - file_option: go_package - path: inference.proto - value: github.com/kagent-dev/kagent/go/api/openshell/gen/inferencev1 - - file_option: go_package - path: test.proto - value: github.com/kagent-dev/kagent/go/api/openshell/gen/openshelltestv1 -plugins: - - local: protoc-gen-go - out: ../gen - opt: - - module=github.com/kagent-dev/kagent/go/api/openshell/gen - - Mopenshell.proto=github.com/kagent-dev/kagent/go/api/openshell/gen/openshellv1 - - Msandbox.proto=github.com/kagent-dev/kagent/go/api/openshell/gen/sandboxv1 - - Mdatamodel.proto=github.com/kagent-dev/kagent/go/api/openshell/gen/datamodelv1 - - Mcompute_driver.proto=github.com/kagent-dev/kagent/go/api/openshell/gen/computev1 - - Minference.proto=github.com/kagent-dev/kagent/go/api/openshell/gen/inferencev1 - - Mtest.proto=github.com/kagent-dev/kagent/go/api/openshell/gen/openshelltestv1 - - local: protoc-gen-go-grpc - out: ../gen - opt: - - module=github.com/kagent-dev/kagent/go/api/openshell/gen - - Mopenshell.proto=github.com/kagent-dev/kagent/go/api/openshell/gen/openshellv1 - - Msandbox.proto=github.com/kagent-dev/kagent/go/api/openshell/gen/sandboxv1 - - Mdatamodel.proto=github.com/kagent-dev/kagent/go/api/openshell/gen/datamodelv1 - - Mcompute_driver.proto=github.com/kagent-dev/kagent/go/api/openshell/gen/computev1 - - Minference.proto=github.com/kagent-dev/kagent/go/api/openshell/gen/inferencev1 - - Mtest.proto=github.com/kagent-dev/kagent/go/api/openshell/gen/openshelltestv1 -inputs: - - directory: . diff --git a/go/api/openshell/proto/buf.yaml b/go/api/openshell/proto/buf.yaml deleted file mode 100644 index aa75e19f4a..0000000000 --- a/go/api/openshell/proto/buf.yaml +++ /dev/null @@ -1,10 +0,0 @@ -version: v2 -modules: - - path: . -deps: [] -lint: - use: - - STANDARD -breaking: - use: - - FILE diff --git a/go/api/openshell/proto/compute_driver.proto b/go/api/openshell/proto/compute_driver.proto deleted file mode 100644 index 3c4308f3f0..0000000000 --- a/go/api/openshell/proto/compute_driver.proto +++ /dev/null @@ -1,271 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -syntax = "proto3"; - -package openshell.compute.v1; - -import "google/protobuf/struct.proto"; - -// Internal compute-driver contract used by the gateway. -// -// Conventions: -// - This file owns driver-native request, response, and observation types. -// - Compute drivers must not import or return the public `openshell.v1.Sandbox` -// resource model. -// - The gateway translates between these internal driver-native messages and -// the public OpenShell API resource model. -service ComputeDriver { - // Report driver capabilities and defaults. - rpc GetCapabilities(GetCapabilitiesRequest) returns (GetCapabilitiesResponse); - - // Validate a sandbox before create-time provisioning. - rpc ValidateSandboxCreate(ValidateSandboxCreateRequest) - returns (ValidateSandboxCreateResponse); - - // Fetch the platform-observed sandbox state for one sandbox. - rpc GetSandbox(GetSandboxRequest) returns (GetSandboxResponse); - - // List platform-observed sandbox state for all sandboxes. - rpc ListSandboxes(ListSandboxesRequest) returns (ListSandboxesResponse); - - // Provision platform resources for a sandbox. - rpc CreateSandbox(CreateSandboxRequest) returns (CreateSandboxResponse); - - // Stop platform resources for a sandbox without deleting its record. - rpc StopSandbox(StopSandboxRequest) returns (StopSandboxResponse); - - // Tear down platform resources for a sandbox. - rpc DeleteSandbox(DeleteSandboxRequest) returns (DeleteSandboxResponse); - - // Stream sandbox observations from the platform. - rpc WatchSandboxes(WatchSandboxesRequest) returns (stream WatchSandboxesEvent); -} - -message GetCapabilitiesRequest {} - -message GetCapabilitiesResponse { - // Human-readable driver name. - string driver_name = 1; - // Driver implementation version string. - string driver_version = 2; - // Default sandbox image recommended by the driver. - string default_image = 3; - // True when the driver can provision GPU-backed sandboxes. - bool supports_gpu = 4; - // Number of GPUs available for sandbox assignment. - uint32 gpu_count = 5; -} - -// Driver-owned sandbox model used for create requests and platform observations. -// -// This intentionally omits gateway-owned lifecycle fields such as the public -// `openshell.v1.SandboxPhase` and persisted metadata. The gateway derives and -// stores those fields after translating driver observations. -message DriverSandbox { - // Stable sandbox ID assigned by the gateway. - string id = 1; - // Compute-runtime sandbox name. - string name = 2; - // Compute-platform namespace or equivalent tenancy boundary. - string namespace = 3; - // Provisioning input supplied by the gateway. Drivers may omit this in - // observed snapshots returned by Get/List/Watch. - DriverSandboxSpec spec = 4; - // Raw platform-observed status. - DriverSandboxStatus status = 5; -} - -// Driver-owned provisioning inputs required to create a sandbox. -message DriverSandboxSpec { - // Log level exposed to processes running inside the sandbox. - string log_level = 1; - // Environment variables injected into the sandbox runtime. - map environment = 5; - // Runtime template consumed by the driver during provisioning. - DriverSandboxTemplate template = 6; - // Request NVIDIA GPU resources for this sandbox. - bool gpu = 9; - // Optional PCI BDF address (e.g. "0000:2d:00.0") or device index - // (e.g. "0", "1"). When empty with gpu=true, the driver assigns the - // first available GPU. - string gpu_device = 10; -} - -// Driver-owned runtime template consumed by the compute platform. -// -// This message describes the sandbox workload in backend-neutral terms. -// Platform-specific knobs (Kubernetes runtimeClassName, annotations, -// volumeClaimTemplates, etc.) belong in `platform_config`. -message DriverSandboxTemplate { - // Fully-qualified OCI image reference used to boot the sandbox. - string image = 1; - // Socket path inside the sandbox where the agent service listens. - string agent_socket_path = 3; - // Metadata labels applied to compute-platform resources. - // Drivers map these to the platform's native tagging mechanism - // (Kubernetes labels, cloud instance tags, etc.). - map labels = 4; - // Additional environment variables injected into the sandbox runtime. - map environment = 6; - // Typed compute-resource requirements for the sandbox workload. - DriverResourceRequirements resources = 10; - // Opaque, platform-specific configuration passed through to the driver. - // The gateway does not inspect this; each driver defines its own schema. - // For the Kubernetes driver this carries fields such as runtimeClassName, - // annotations, and volumeClaimTemplates. - google.protobuf.Struct platform_config = 11; -} - -// Typed compute-resource requirements. -// -// Values use Kubernetes-style quantity strings (e.g. "500m", "2", "4Gi") -// because they are a well-known, widely-adopted notation. Drivers for -// non-Kubernetes platforms must parse these strings into their native units. -message DriverResourceRequirements { - // Minimum CPU cores requested (e.g. "500m", "2"). - string cpu_request = 1; - // Maximum CPU cores allowed (e.g. "500m", "4"). - string cpu_limit = 2; - // Minimum memory requested (e.g. "256Mi", "4Gi"). - string memory_request = 3; - // Maximum memory allowed (e.g. "512Mi", "8Gi"). - string memory_limit = 4; -} - -// Raw status observed directly from the compute platform. -// -// The gateway derives the public `openshell.v1.SandboxPhase` from these -// conditions plus `deleting`. -message DriverSandboxStatus { - // Compute-platform sandbox object name. - string sandbox_name = 1; - // Platform-assigned instance identifier for the compute unit running the - // sandbox agent (e.g. Kubernetes pod name, VM instance ID, hostname). - // The gateway uses this to correlate incoming connections back to a sandbox. - string instance_id = 2; - // File descriptor or address for reaching the agent service inside the - // sandbox, when available. - string agent_fd = 3; - // File descriptor or address for reaching the sandbox supervisor service, - // when available. - string sandbox_fd = 4; - // Raw readiness and lifecycle conditions reported by the platform. - repeated DriverCondition conditions = 5; - // True when the compute platform has begun deleting this sandbox. - bool deleting = 6; -} - -// Raw compute-platform condition. -message DriverCondition { - // Condition class reported by the compute platform. - string type = 1; - // Condition status value such as `True`, `False`, or `Unknown`. - string status = 2; - // Short machine-readable reason associated with the condition. - string reason = 3; - // Human-readable condition message. - string message = 4; - // Timestamp reported by the platform for the last transition. - string last_transition_time = 5; -} - -// Raw compute-platform event correlated to a sandbox. -message DriverPlatformEvent { - // Event timestamp in milliseconds since epoch. - int64 timestamp_ms = 1; - // Event source (for example `kubernetes`). - string source = 2; - // Event type or severity (for example `Normal` or `Warning`). - string type = 3; - // Short machine-readable reason code. - string reason = 4; - // Human-readable event message. - string message = 5; - // Optional platform-specific metadata attached to the event. - map metadata = 6; -} - -message ValidateSandboxCreateRequest { - // Proposed sandbox configuration to validate before provisioning. - DriverSandbox sandbox = 1; -} - -message ValidateSandboxCreateResponse {} - -message GetSandboxRequest { - // Stable sandbox ID stored by the gateway. - string sandbox_id = 1; - // Compute-runtime name used by the driver. - string sandbox_name = 2; -} - -message GetSandboxResponse { - // Platform-observed sandbox snapshot returned by the driver. - DriverSandbox sandbox = 1; -} - -message ListSandboxesRequest {} - -message ListSandboxesResponse { - // Platform-observed sandbox snapshots returned by the driver. - repeated DriverSandbox sandboxes = 1; -} - -message CreateSandboxRequest { - // Sandbox configuration to provision on the compute platform. - DriverSandbox sandbox = 1; -} - -message CreateSandboxResponse {} - -message StopSandboxRequest { - // Stable sandbox ID stored by the gateway. - string sandbox_id = 1; - // Compute-runtime name used by the driver. - string sandbox_name = 2; -} - -message StopSandboxResponse {} - -message DeleteSandboxRequest { - // Stable sandbox ID stored by the gateway. - string sandbox_id = 1; - // Compute-runtime name used by the driver. - string sandbox_name = 2; -} - -message DeleteSandboxResponse { - // True when a platform resource was deleted by this request. - bool deleted = 1; -} - -message WatchSandboxesRequest {} - -message WatchSandboxesSandboxEvent { - // Updated driver-native snapshot for one sandbox. - DriverSandbox sandbox = 1; -} - -message WatchSandboxesDeletedEvent { - // Sandbox ID removed from the compute platform. - string sandbox_id = 1; -} - -message WatchSandboxesPlatformEvent { - // Sandbox ID correlated to the platform event. - string sandbox_id = 1; - // Raw platform event emitted for the sandbox. - DriverPlatformEvent event = 2; -} - -message WatchSandboxesEvent { - oneof payload { - // Updated or newly observed sandbox snapshot. - WatchSandboxesSandboxEvent sandbox = 1; - // Sandbox deletion observation. - WatchSandboxesDeletedEvent deleted = 2; - // Raw platform event correlated to a sandbox. - WatchSandboxesPlatformEvent platform_event = 3; - } -} diff --git a/go/api/openshell/proto/datamodel.proto b/go/api/openshell/proto/datamodel.proto deleted file mode 100644 index 4620881246..0000000000 --- a/go/api/openshell/proto/datamodel.proto +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -syntax = "proto3"; - -package openshell.datamodel.v1; - -// Kubernetes-style metadata shared by all top-level OpenShell domain objects. -// -// This structure provides consistent metadata (identity, labels, timestamps, -// resource versioning) across Sandbox, Provider, SshSession, and other resources. -message ObjectMeta { - // Stable object ID generated by the gateway. - string id = 1; - - // Human-readable object name (unique per object type). - string name = 2; - - // Milliseconds since Unix epoch when the object was created. - int64 created_at_ms = 3; - - // Key-value labels for filtering and organization. - // Labels must follow Kubernetes conventions: alphanumeric + `-._/`, max 63 chars per segment. - map labels = 4; - - // Optimistic concurrency control version. - // Incremented by the gateway on each update. Clients can use this for compare-and-swap operations. - uint64 resource_version = 5; -} - -// Provider model stored by OpenShell. -message Provider { - // Kubernetes-style metadata (id, name, labels, timestamps, resource version). - ObjectMeta metadata = 1; - // Canonical provider type slug (for example: "claude", "gitlab"). - string type = 2; - // Secret values used for authentication. - map credentials = 3; - // Non-secret provider configuration. - map config = 4; -} diff --git a/go/api/openshell/proto/inference.proto b/go/api/openshell/proto/inference.proto deleted file mode 100644 index 743f245f9f..0000000000 --- a/go/api/openshell/proto/inference.proto +++ /dev/null @@ -1,120 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -syntax = "proto3"; - -package openshell.inference.v1; - -import "datamodel.proto"; - -// Inference service provides cluster inference configuration and bundle delivery. -service Inference { - // Return the resolved inference route bundle for sandbox-local execution. - rpc GetInferenceBundle(GetInferenceBundleRequest) - returns (GetInferenceBundleResponse); - - // Set cluster-level inference configuration. - // - // This controls how requests sent to `inference.local` are routed. - rpc SetClusterInference(SetClusterInferenceRequest) - returns (SetClusterInferenceResponse); - - // Get cluster-level inference configuration. - rpc GetClusterInference(GetClusterInferenceRequest) - returns (GetClusterInferenceResponse); - -} - -// Persisted cluster inference configuration. -// -// Only `provider_name` and `model_id` are stored; endpoint, protocols, -// credentials, and auth style are resolved from the provider at bundle time. -message ClusterInferenceConfig { - // Provider record name backing this route. - string provider_name = 1; - // Model identifier to force on generation calls. - string model_id = 2; - // Per-route request timeout in seconds. 0 means use default (60s). - uint64 timeout_secs = 3; -} - -// Storage envelope for the managed cluster inference route. -message InferenceRoute { - openshell.datamodel.v1.ObjectMeta metadata = 1; - ClusterInferenceConfig config = 2; - // Monotonic version incremented on every update. - uint64 version = 3; -} - -message SetClusterInferenceRequest { - // Provider record name to use for credentials + endpoint mapping. - string provider_name = 1; - // Model identifier to force on generation calls. - string model_id = 2; - // Route name to target. Empty string defaults to "inference.local" (user-facing). - // Use "sandbox-system" for the sandbox system-level inference route. - string route_name = 3; - // Verify the resolved upstream endpoint synchronously before persistence. - bool verify = 4; - // Skip synchronous endpoint validation before persistence. - bool no_verify = 5; - // Per-route request timeout in seconds. 0 means use default (60s). - uint64 timeout_secs = 6; -} - -message ValidatedEndpoint { - string url = 1; - string protocol = 2; -} - -message SetClusterInferenceResponse { - string provider_name = 1; - string model_id = 2; - uint64 version = 3; - // Route name that was configured. - string route_name = 4; - // Whether endpoint verification ran as part of this request. - bool validation_performed = 5; - // The concrete endpoints that were probed during validation, when available. - repeated ValidatedEndpoint validated_endpoints = 6; - // Per-route request timeout in seconds that was persisted. - uint64 timeout_secs = 7; -} - -message GetClusterInferenceRequest { - // Route name to query. Empty string defaults to "inference.local" (user-facing). - // Use "sandbox-system" for the sandbox system-level inference route. - string route_name = 1; -} - -message GetClusterInferenceResponse { - string provider_name = 1; - string model_id = 2; - uint64 version = 3; - // Route name that was queried. - string route_name = 4; - // Per-route request timeout in seconds. 0 means default (60s). - uint64 timeout_secs = 5; -} - -message GetInferenceBundleRequest {} - -// A single resolved route ready for sandbox-local execution. -message ResolvedRoute { - string name = 1; - string base_url = 2; - repeated string protocols = 3; - string api_key = 4; - string model_id = 5; - string provider_type = 6; - // Per-route request timeout in seconds. 0 means use default (60s). - uint64 timeout_secs = 7; -} - -message GetInferenceBundleResponse { - repeated ResolvedRoute routes = 1; - // Opaque revision tag for cache freshness checks. - string revision = 2; - // Timestamp (epoch ms) when this bundle was generated. - int64 generated_at_ms = 3; -} diff --git a/go/api/openshell/proto/openshell.proto b/go/api/openshell/proto/openshell.proto deleted file mode 100644 index 990698d9b1..0000000000 --- a/go/api/openshell/proto/openshell.proto +++ /dev/null @@ -1,1603 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -syntax = "proto3"; - -package openshell.v1; - -import "datamodel.proto"; -import "google/protobuf/struct.proto"; -import "sandbox.proto"; - -// OpenShell service provides sandbox, provider, and runtime management capabilities. -// -// Conventions: -// - This file owns the public API resource model exposed to OpenShell clients. -// - `Sandbox`, `SandboxSpec`, `SandboxStatus`, and `SandboxPhase` are gateway-owned -// public types. Internal compute drivers must not import or return them directly. -// - The gateway translates internal compute-driver observations into these public -// resource messages before persisting or returning them to clients. -service OpenShell { - // Check the health of the service. - rpc Health(HealthRequest) returns (HealthResponse); - - // Create a new sandbox. - rpc CreateSandbox(CreateSandboxRequest) returns (SandboxResponse); - - // Fetch a sandbox by name. - rpc GetSandbox(GetSandboxRequest) returns (SandboxResponse); - - // List sandboxes. - rpc ListSandboxes(ListSandboxesRequest) returns (ListSandboxesResponse); - - // List provider records attached to a sandbox. - rpc ListSandboxProviders(ListSandboxProvidersRequest) - returns (ListSandboxProvidersResponse); - - // Attach a provider record to an existing sandbox. - rpc AttachSandboxProvider(AttachSandboxProviderRequest) - returns (AttachSandboxProviderResponse); - - // Detach a provider record from an existing sandbox. - rpc DetachSandboxProvider(DetachSandboxProviderRequest) - returns (DetachSandboxProviderResponse); - - // Delete a sandbox by name. - rpc DeleteSandbox(DeleteSandboxRequest) returns (DeleteSandboxResponse); - - // Create a short-lived SSH session for a sandbox. - rpc CreateSshSession(CreateSshSessionRequest) returns (CreateSshSessionResponse); - - // Create or update a sandbox HTTP service endpoint for local routing. - rpc ExposeService(ExposeServiceRequest) returns (ServiceEndpointResponse); - - // Fetch one sandbox HTTP service endpoint. - rpc GetService(GetServiceRequest) returns (ServiceEndpointResponse); - - // List sandbox HTTP service endpoints. - rpc ListServices(ListServicesRequest) returns (ListServicesResponse); - - // Delete one sandbox HTTP service endpoint. - rpc DeleteService(DeleteServiceRequest) returns (DeleteServiceResponse); - - // Revoke a previously issued SSH session. - rpc RevokeSshSession(RevokeSshSessionRequest) returns (RevokeSshSessionResponse); - - // Execute a command in a ready sandbox and stream output. - rpc ExecSandbox(ExecSandboxRequest) returns (stream ExecSandboxEvent); - - // Forward one CLI-side TCP connection to a loopback TCP target in a sandbox. - rpc ForwardTcp(stream TcpForwardFrame) returns (stream TcpForwardFrame); - - // Execute an interactive command with bidirectional stdin/stdout streaming. - // The first client message MUST carry an ExecSandboxInput with the start - // variant. Subsequent messages carry stdin bytes or window resize events. - rpc ExecSandboxInteractive(stream ExecSandboxInput) returns (stream ExecSandboxEvent); - - // Create a provider. - rpc CreateProvider(CreateProviderRequest) returns (ProviderResponse); - - // Fetch a provider by name. - rpc GetProvider(GetProviderRequest) returns (ProviderResponse); - - // List providers. - rpc ListProviders(ListProvidersRequest) returns (ListProvidersResponse); - - // List available provider type profiles. - rpc ListProviderProfiles(ListProviderProfilesRequest) - returns (ListProviderProfilesResponse); - - // Fetch one provider type profile by id. - rpc GetProviderProfile(GetProviderProfileRequest) - returns (ProviderProfileResponse); - - // Import custom provider type profiles. - rpc ImportProviderProfiles(ImportProviderProfilesRequest) - returns (ImportProviderProfilesResponse); - - // Validate provider type profiles without registering them. - rpc LintProviderProfiles(LintProviderProfilesRequest) - returns (LintProviderProfilesResponse); - - // Update an existing provider by name. - rpc UpdateProvider(UpdateProviderRequest) returns (ProviderResponse); - - // Delete a provider by name. - rpc DeleteProvider(DeleteProviderRequest) returns (DeleteProviderResponse); - - // Delete a custom provider type profile by id. - rpc DeleteProviderProfile(DeleteProviderProfileRequest) - returns (DeleteProviderProfileResponse); - - // Get sandbox settings by id (called by sandbox entrypoint and poll loop). - rpc GetSandboxConfig(openshell.sandbox.v1.GetSandboxConfigRequest) - returns (openshell.sandbox.v1.GetSandboxConfigResponse); - - // Get gateway-global settings. - rpc GetGatewayConfig(openshell.sandbox.v1.GetGatewayConfigRequest) - returns (openshell.sandbox.v1.GetGatewayConfigResponse); - - // Update settings or policy at sandbox or global scope. - rpc UpdateConfig(UpdateConfigRequest) - returns (UpdateConfigResponse); - - // Get the load status of a specific policy version. - rpc GetSandboxPolicyStatus(GetSandboxPolicyStatusRequest) - returns (GetSandboxPolicyStatusResponse); - - // List policy history for a sandbox. - rpc ListSandboxPolicies(ListSandboxPoliciesRequest) - returns (ListSandboxPoliciesResponse); - - // Report policy load result (called by sandbox after reload attempt). - rpc ReportPolicyStatus(ReportPolicyStatusRequest) - returns (ReportPolicyStatusResponse); - - // Get provider environment for a sandbox (called by sandbox supervisor at startup). - rpc GetSandboxProviderEnvironment(GetSandboxProviderEnvironmentRequest) - returns (GetSandboxProviderEnvironmentResponse); - - // Fetch recent sandbox logs (one-shot). - rpc GetSandboxLogs(GetSandboxLogsRequest) returns (GetSandboxLogsResponse); - - // Push sandbox supervisor logs to the server (client-streaming). - rpc PushSandboxLogs(stream PushSandboxLogsRequest) returns (PushSandboxLogsResponse); - - // Persistent supervisor-to-gateway session (bidirectional streaming). - // - // The supervisor opens this stream at startup and keeps it alive for the - // sandbox lifetime. The gateway uses it to coordinate relay channels for - // SSH connect, ExecSandbox, and targetable sandbox services. Raw service - // bytes flow over RelayStream calls (separate HTTP/2 streams on the same - // connection), not over this stream. - rpc ConnectSupervisor(stream SupervisorMessage) returns (stream GatewayMessage); - - // Raw byte relay between supervisor and gateway. - // - // The supervisor initiates this call after receiving a RelayOpen message - // on its ConnectSupervisor stream. The first RelayFrame carries a - // RelayInit with the channel_id to associate the new HTTP/2 stream with - // the pending relay slot on the gateway. Subsequent frames carry raw bytes in either - // direction between the gateway-side waiter (ForwardTcp / exec handler) - // and the supervisor-side target bridge. - // - // This rides the same TCP+TLS+HTTP/2 connection as ConnectSupervisor — - // no new TLS handshake, no reverse HTTP CONNECT. - rpc RelayStream(stream RelayFrame) returns (stream RelayFrame); - - // Watch a sandbox and stream updates. - // - // This stream can include: - // - Sandbox status snapshots (phase/status) - // - OpenShell server process logs correlated by sandbox_id - // - Platform events correlated to the sandbox - rpc WatchSandbox(WatchSandboxRequest) returns (stream SandboxStreamEvent); - - // --------------------------------------------------------------------------- - // Draft policy recommendation RPCs - // --------------------------------------------------------------------------- - - // Submit denial analysis results from sandbox (summaries + proposed chunks). - rpc SubmitPolicyAnalysis(SubmitPolicyAnalysisRequest) - returns (SubmitPolicyAnalysisResponse); - - // Get draft policy recommendations for a sandbox. - rpc GetDraftPolicy(GetDraftPolicyRequest) returns (GetDraftPolicyResponse); - - // Approve a single draft policy chunk (merges into active policy). - rpc ApproveDraftChunk(ApproveDraftChunkRequest) - returns (ApproveDraftChunkResponse); - - // Reject a single draft policy chunk. - rpc RejectDraftChunk(RejectDraftChunkRequest) - returns (RejectDraftChunkResponse); - - // Approve all pending draft chunks (skips security-flagged unless forced). - rpc ApproveAllDraftChunks(ApproveAllDraftChunksRequest) - returns (ApproveAllDraftChunksResponse); - - // Edit a pending draft chunk in-place (e.g. narrow allowed_ips). - rpc EditDraftChunk(EditDraftChunkRequest) returns (EditDraftChunkResponse); - - // Reverse an approval (remove merged rule from active policy). - rpc UndoDraftChunk(UndoDraftChunkRequest) returns (UndoDraftChunkResponse); - - // Clear all pending draft chunks for a sandbox. - rpc ClearDraftChunks(ClearDraftChunksRequest) - returns (ClearDraftChunksResponse); - - // Get decision history for a sandbox's draft policy. - rpc GetDraftHistory(GetDraftHistoryRequest) returns (GetDraftHistoryResponse); -} - -// Health check request. -message HealthRequest {} - -// Health check response. -message HealthResponse { - // Service status. - ServiceStatus status = 1; - - // Service version. - string version = 2; -} - -// Public sandbox resource exposed by the OpenShell API. -// -// This is the canonical gateway-owned view of a sandbox. It merges user intent -// (`spec`) with gateway-managed metadata and status derived from internal -// compute-driver observations. -// -// Note: The `namespace` field has been removed from the public API. It remains -// in the internal `DriverSandbox` message as a compute-driver implementation detail. -message Sandbox { - // Kubernetes-style metadata (id, name, labels, timestamps, resource version). - openshell.datamodel.v1.ObjectMeta metadata = 1; - // Desired sandbox configuration submitted through the API. - SandboxSpec spec = 2; - // Latest user-facing observed status derived by the gateway. - SandboxStatus status = 3; - // Gateway-derived lifecycle summary. - SandboxPhase phase = 4; - // Currently active policy version (updated when sandbox reports loaded). - uint32 current_policy_version = 5; -} - -// Desired sandbox configuration provided through the public API. -message SandboxSpec { - // Log level exposed to processes running inside the sandbox. - string log_level = 1; - // Environment variables injected into the sandbox runtime. - map environment = 5; - // Container or VM template used to provision the sandbox. - SandboxTemplate template = 6; - // Required sandbox policy configuration. - openshell.sandbox.v1.SandboxPolicy policy = 7; - // Provider names to attach to this sandbox. - repeated string providers = 8; - // Request NVIDIA GPU resources for this sandbox. - bool gpu = 9; - // Optional PCI BDF address (e.g. "0000:2d:00.0") or device index - // (e.g. "0", "1"). When empty with gpu=true, the driver assigns the - // first available GPU. - string gpu_device = 10; -} - -// Public sandbox template mapped onto compute-driver template inputs. -message SandboxTemplate { - // Fully-qualified OCI image reference used to boot the sandbox. - string image = 1; - // Optional runtime class name requested from the compute platform. - string runtime_class_name = 2; - // Optional agent socket path exposed to the workload. - string agent_socket = 3; - // Labels applied to compute-platform resources for this sandbox. - map labels = 4; - // Annotations applied to compute-platform resources for this sandbox. - map annotations = 5; - // Additional environment variables injected by the template. - map environment = 6; - // Platform-specific compute resource requirements and limits. - google.protobuf.Struct resources = 7; - // Optional platform-specific volume claim templates. - google.protobuf.Struct volume_claim_templates = 9; - // Enable Kubernetes user namespace isolation (hostUsers: false). - // When true, container UID 0 maps to a non-root host UID and capabilities - // become namespaced. Requires Kubernetes 1.33+ with user namespace support - // available (beta through 1.35, GA in 1.36+) and a supporting runtime. - // When unset, the cluster-wide default is used. - optional bool user_namespaces = 10; -} - -// User-facing sandbox status derived by the gateway from compute-driver observations. -// -// Lifecycle summary is exposed separately as `Sandbox.phase`. Public status does -// not embed driver-only flags such as `deleting`. -message SandboxStatus { - // Compute-platform sandbox object name. - string sandbox_name = 1; - // Name of the agent pod or equivalent runtime instance. - string agent_pod = 2; - // File descriptor or endpoint for reaching the agent service, when available. - string agent_fd = 3; - // File descriptor or endpoint for reaching the sandbox service, when available. - string sandbox_fd = 4; - // Latest user-facing readiness and lifecycle conditions. - repeated SandboxCondition conditions = 5; -} - -// User-facing sandbox condition derived from driver-native conditions. -message SandboxCondition { - // Condition class, typically mirroring the underlying platform condition type. - string type = 1; - // Condition status value such as `True`, `False`, or `Unknown`. - string status = 2; - // Short machine-readable reason associated with the condition. - string reason = 3; - // Human-readable condition message. - string message = 4; - // Timestamp reported by the underlying platform for the last transition. - string last_transition_time = 5; -} - -// High-level sandbox lifecycle phase derived by the gateway. -// -// Clients should rely on this normalized lifecycle summary for readiness and -// deletion decisions instead of interpreting raw conditions. -enum SandboxPhase { - SANDBOX_PHASE_UNSPECIFIED = 0; - SANDBOX_PHASE_PROVISIONING = 1; - SANDBOX_PHASE_READY = 2; - SANDBOX_PHASE_ERROR = 3; - SANDBOX_PHASE_DELETING = 4; - SANDBOX_PHASE_UNKNOWN = 5; -} - -// Public platform event exposed on the sandbox watch stream. -message PlatformEvent { - // Event timestamp in milliseconds since epoch. - int64 timestamp_ms = 1; - // Event source (e.g. "kubernetes", "docker", "process"). - string source = 2; - // Event type/severity (e.g. "Normal", "Warning"). - string type = 3; - // Short reason code (e.g. "Started", "Pulled", "Failed"). - string reason = 4; - // Human-readable event message. - string message = 5; - // Optional metadata as key-value pairs. - map metadata = 6; -} - -// Create sandbox request. -message CreateSandboxRequest { - SandboxSpec spec = 1; - // Optional user-supplied sandbox name. When empty the server generates one. - string name = 2; - // Optional labels for the sandbox (key-value metadata). - map labels = 3; -} - -// Get sandbox request. -message GetSandboxRequest { - // Sandbox name (canonical lookup key). - string name = 1; -} - -// List sandboxes request. -message ListSandboxesRequest { - uint32 limit = 1; - uint32 offset = 2; - // Optional label selector for filtering (format: "key1=value1,key2=value2"). - string label_selector = 3; -} - -// List providers attached to a sandbox request. -message ListSandboxProvidersRequest { - // Sandbox name (canonical lookup key). - string sandbox_name = 1; -} - -// Attach provider to sandbox request. -message AttachSandboxProviderRequest { - // Sandbox name (canonical lookup key). - string sandbox_name = 1; - // Provider name to attach. - string provider_name = 2; - // Expected resource version for optimistic concurrency control. - // If 0, the server uses the current version (backward compatibility). - // If non-zero, the server validates that the sandbox's current resource_version - // matches this value before applying the mutation, returning ABORTED on mismatch. - uint64 expected_resource_version = 3; -} - -// Detach provider from sandbox request. -message DetachSandboxProviderRequest { - // Sandbox name (canonical lookup key). - string sandbox_name = 1; - // Provider name to detach. - string provider_name = 2; - // Expected resource version for optimistic concurrency control. - // If 0, the server uses the current version (backward compatibility). - // If non-zero, the server validates that the sandbox's current resource_version - // matches this value before applying the mutation, returning ABORTED on mismatch. - uint64 expected_resource_version = 3; -} - -// Delete sandbox request. -message DeleteSandboxRequest { - // Sandbox name (canonical lookup key). - string name = 1; -} - -// Sandbox response. -message SandboxResponse { - Sandbox sandbox = 1; -} - -// List sandboxes response. -message ListSandboxesResponse { - repeated Sandbox sandboxes = 1; -} - -// List providers attached to a sandbox response. -message ListSandboxProvidersResponse { - repeated openshell.datamodel.v1.Provider providers = 1; -} - -// Attach provider to sandbox response. -message AttachSandboxProviderResponse { - Sandbox sandbox = 1; - // True when the provider was newly attached. False means it was already attached. - bool attached = 2; -} - -// Detach provider from sandbox response. -message DetachSandboxProviderResponse { - Sandbox sandbox = 1; - // True when the provider was removed. False means it was not attached. - bool detached = 2; -} - -// Delete sandbox response. -message DeleteSandboxResponse { - bool deleted = 1; -} - -// Create SSH session request. -message CreateSshSessionRequest { - // Sandbox id. - string sandbox_id = 1; -} - -// Create SSH session response. -// -// Fields are interpolated into an SSH `ProxyCommand` string that OpenSSH -// executes through `/bin/sh -c` on the caller's workstation. Servers MUST -// uphold the charset contract below; clients MUST reject responses that -// violate it. The client's own escaping provides defense-in-depth, but -// narrow charsets close injection vectors at the trust boundary. -message CreateSshSessionResponse { - // Sandbox id. [A-Za-z0-9._-]{1,128}. - string sandbox_id = 1; - - // Session token for the gateway tunnel. URL-safe ASCII - // ([A-Za-z0-9._~+/=-]) up to 4096 bytes. No shell metacharacters or - // whitespace. - string token = 2; - - // Gateway host for SSH proxy connection. IPv4 address, bracketed IPv6 - // address, or DNS hostname (Punycode-encoded for IDN). Alphanumeric plus - // `.-:[]` only, up to 253 bytes. - string gateway_host = 3; - - // Gateway port for SSH proxy connection. Must be in range 1..=65535. - uint32 gateway_port = 4; - - // Gateway scheme. Must be exactly "http" or "https". - string gateway_scheme = 5; - - // Optional host key fingerprint. If non-empty, [A-Za-z0-9:+/=-] only. - string host_key_fingerprint = 7; - - // Expiry timestamp in milliseconds since epoch. 0 means no expiry. - int64 expires_at_ms = 8; -} - -// Request to expose an HTTP service running inside a sandbox. -message ExposeServiceRequest { - // Sandbox name. - string sandbox = 1; - // Service name within the sandbox. - string service = 2; - // Loopback TCP port inside the sandbox. - uint32 target_port = 3; - // Whether to print/use the browser-facing service URL. - bool domain = 4; -} - -// Request to fetch an exposed sandbox service endpoint. -message GetServiceRequest { - // Sandbox name. - string sandbox = 1; - // Service name within the sandbox. Empty selects the unnamed endpoint. - string service = 2; -} - -// Request to list exposed sandbox service endpoints. -message ListServicesRequest { - // Optional sandbox name. Empty lists endpoints for all sandboxes. - string sandbox = 1; - // Page size. Zero uses the server default. - uint32 limit = 2; - // Page offset. - uint32 offset = 3; -} - -// Response containing exposed sandbox service endpoints. -message ListServicesResponse { - repeated ServiceEndpointResponse services = 1; -} - -// Request to delete an exposed sandbox service endpoint. -message DeleteServiceRequest { - // Sandbox name. - string sandbox = 1; - // Service name within the sandbox. Empty selects the unnamed endpoint. - string service = 2; -} - -// Response for deleting an exposed sandbox service endpoint. -message DeleteServiceResponse { - // True when an endpoint existed and was deleted. - bool deleted = 1; -} - -// Persisted sandbox service endpoint. -message ServiceEndpoint { - // Kubernetes-style metadata. - openshell.datamodel.v1.ObjectMeta metadata = 1; - // Sandbox object ID. - string sandbox_id = 2; - // Sandbox name. - string sandbox_name = 3; - // Service name within the sandbox. - string service_name = 4; - // Loopback TCP port inside the sandbox. - uint32 target_port = 5; - // Whether browser-facing service routing is enabled for this endpoint. - bool domain = 6; -} - -// Response containing a service endpoint and, when available, its local URL. -message ServiceEndpointResponse { - ServiceEndpoint endpoint = 1; - string url = 2; -} - -// Revoke SSH session request. -message RevokeSshSessionRequest { - // Session token to revoke. - string token = 1; -} - -// Revoke SSH session response. -message RevokeSshSessionResponse { - // True when a session was revoked. - bool revoked = 1; -} - -// Execute command request. -message ExecSandboxRequest { - // Sandbox id. - string sandbox_id = 1; - - // Command and arguments. - repeated string command = 2; - - // Optional working directory. - string workdir = 3; - - // Optional environment overrides. - map environment = 4; - - // Optional timeout in seconds. 0 means no timeout. - uint32 timeout_seconds = 5; - - // Optional stdin payload passed to the command. - bytes stdin = 6; - - // Request a pseudo-terminal for the remote command. - bool tty = 7; - - // Initial terminal columns (used when tty=true, 0 = use default). - uint32 cols = 8; - - // Initial terminal rows (used when tty=true, 0 = use default). - uint32 rows = 9; -} - -// One stdout chunk from a sandbox exec. -message ExecSandboxStdout { - bytes data = 1; -} - -// One stderr chunk from a sandbox exec. -message ExecSandboxStderr { - bytes data = 1; -} - -// Final exit status for a sandbox exec. -message ExecSandboxExit { - int32 exit_code = 1; -} - -// One event in a sandbox exec stream. -message ExecSandboxEvent { - oneof payload { - ExecSandboxStdout stdout = 1; - ExecSandboxStderr stderr = 2; - ExecSandboxExit exit = 3; - } -} - -// Initial frame for one TCP forward stream. -message TcpForwardInit { - // Sandbox id. - string sandbox_id = 1; - // Optional service identifier for audit/correlation. - string service_id = 4; - // Target the gateway should request from the supervisor. - oneof target { - SshRelayTarget ssh = 5; - TcpRelayTarget tcp = 6; - } - // Optional target-specific authorization token. SSH targets use this as the - // short-lived SSH session token issued by CreateSshSession. - string authorization_token = 7; -} - -// A single frame on the CLI-to-gateway TCP forward stream. -message TcpForwardFrame { - oneof payload { - TcpForwardInit init = 1; - bytes data = 2; - } -} - -// Client-to-server message for interactive exec. -message ExecSandboxInput { - oneof payload { - // First message: exec request metadata. - ExecSandboxRequest start = 1; - // Subsequent messages: raw stdin bytes. - bytes stdin = 2; - // Terminal window size change. - ExecSandboxWindowResize resize = 3; - } -} - -// Terminal window resize event for interactive exec. -message ExecSandboxWindowResize { - uint32 cols = 1; - uint32 rows = 2; -} - - -// SSH session record stored in persistence. -message SshSession { - // Kubernetes-style metadata (id, name, labels, timestamps, resource version). - openshell.datamodel.v1.ObjectMeta metadata = 1; - - // Sandbox id. - string sandbox_id = 2; - - // Session token. - string token = 3; - - // Expiry timestamp in milliseconds since epoch. 0 means no expiry - // (backward-compatible default for sessions created before this field existed). - int64 expires_at_ms = 4; - - // Revoked flag. - bool revoked = 5; -} - -// Watch sandbox request. -message WatchSandboxRequest { - // Sandbox id. - string id = 1; - - // Stream sandbox status snapshots. - bool follow_status = 2; - - // Stream openshell-server process logs correlated to this sandbox. - bool follow_logs = 3; - - // Stream platform events correlated to this sandbox. - bool follow_events = 4; - - // Replay the last N log lines (best-effort) before following. - uint32 log_tail_lines = 5; - - // Replay the last N platform events (best-effort) before following. - uint32 event_tail = 6; - - // Stop streaming once the sandbox reaches a terminal phase (READY or ERROR). - bool stop_on_terminal = 7; - - // Only include log lines with timestamp >= this value (milliseconds since epoch). - // 0 means no time filter. Applies to both tail replay and live streaming. - int64 log_since_ms = 8; - - // Filter by log source (e.g. "gateway", "sandbox"). Empty means all sources. - repeated string log_sources = 9; - - // Minimum log level to include (e.g. "INFO", "WARN", "ERROR"). Empty means all levels. - string log_min_level = 10; -} - -// One event in a sandbox watch stream. -message SandboxStreamEvent { - oneof payload { - // Latest sandbox snapshot. - Sandbox sandbox = 1; - // One server log line/event. - SandboxLogLine log = 2; - // One platform event. - PlatformEvent event = 3; - // Warning from the server (e.g. missed messages due to lag). - SandboxStreamWarning warning = 4; - // Draft policy update notification. - DraftPolicyUpdate draft_policy_update = 5; - } -} - -// Log line correlated to a sandbox. -message SandboxLogLine { - string sandbox_id = 1; - int64 timestamp_ms = 2; - string level = 3; - string target = 4; - string message = 5; - // Log source: "gateway" (server-side) or "sandbox" (supervisor). - // Empty is treated as "gateway" for backward compatibility. - string source = 6; - // Structured key-value fields from the tracing event (e.g. dst_host, action). - map fields = 7; -} - -message SandboxStreamWarning { - string message = 1; -} - -// Create provider request. -message CreateProviderRequest { - openshell.datamodel.v1.Provider provider = 1; -} - -// Get provider request. -message GetProviderRequest { - string name = 1; -} - -// List providers request. -message ListProvidersRequest { - uint32 limit = 1; - uint32 offset = 2; -} - -// Update provider request. -message UpdateProviderRequest { - openshell.datamodel.v1.Provider provider = 1; -} - -// Delete provider request. -message DeleteProviderRequest { - string name = 1; -} - -// Provider response. -message ProviderResponse { - openshell.datamodel.v1.Provider provider = 1; -} - -// List providers response. -message ListProvidersResponse { - repeated openshell.datamodel.v1.Provider providers = 1; -} - -// List provider type profiles request. -message ListProviderProfilesRequest { - uint32 limit = 1; - uint32 offset = 2; -} - -// Fetch provider type profile request. -message GetProviderProfileRequest { - string id = 1; -} - -// Provider profile payload with optional source metadata for diagnostics. -message ProviderProfileImportItem { - ProviderProfile profile = 1; - string source = 2; -} - -// Provider profile validation diagnostic. -message ProviderProfileDiagnostic { - string source = 1; - string profile_id = 2; - string field = 3; - string message = 4; - string severity = 5; -} - -// Provider credential declaration. -message ProviderProfileCredential { - string name = 1; - string description = 2; - repeated string env_vars = 3; - bool required = 4; - string auth_style = 5; - string header_name = 6; - string query_param = 7; -} - -// Stable provider profile categories used by clients for grouping and filtering. -enum ProviderProfileCategory { - PROVIDER_PROFILE_CATEGORY_UNSPECIFIED = 0; - PROVIDER_PROFILE_CATEGORY_OTHER = 1; - PROVIDER_PROFILE_CATEGORY_INFERENCE = 2; - PROVIDER_PROFILE_CATEGORY_AGENT = 3; - PROVIDER_PROFILE_CATEGORY_SOURCE_CONTROL = 4; - PROVIDER_PROFILE_CATEGORY_MESSAGING = 5; - PROVIDER_PROFILE_CATEGORY_DATA = 6; - PROVIDER_PROFILE_CATEGORY_KNOWLEDGE = 7; -} - -// Provider type profile metadata exposed to clients. -message ProviderProfile { - string id = 1; - string display_name = 2; - string description = 3; - ProviderProfileCategory category = 4; - repeated ProviderProfileCredential credentials = 5; - repeated openshell.sandbox.v1.NetworkEndpoint endpoints = 6; - repeated openshell.sandbox.v1.NetworkBinary binaries = 7; - bool inference_capable = 8; -} - -// Stored custom provider profile object. -message StoredProviderProfile { - openshell.datamodel.v1.ObjectMeta metadata = 1; - ProviderProfile profile = 2; -} - -// Provider profile response. -message ProviderProfileResponse { - ProviderProfile profile = 1; -} - -// List provider profiles response. -message ListProviderProfilesResponse { - repeated ProviderProfile profiles = 1; -} - -// Import custom provider profiles request. -message ImportProviderProfilesRequest { - repeated ProviderProfileImportItem profiles = 1; -} - -// Import custom provider profiles response. -message ImportProviderProfilesResponse { - repeated ProviderProfileDiagnostic diagnostics = 1; - repeated ProviderProfile profiles = 2; - bool imported = 3; -} - -// Lint provider profiles request. -message LintProviderProfilesRequest { - repeated ProviderProfileImportItem profiles = 1; -} - -// Lint provider profiles response. -message LintProviderProfilesResponse { - repeated ProviderProfileDiagnostic diagnostics = 1; - bool valid = 2; -} - -// Delete provider response. -message DeleteProviderResponse { - bool deleted = 1; -} - -// Delete custom provider profile request. -message DeleteProviderProfileRequest { - string id = 1; -} - -// Delete custom provider profile response. -message DeleteProviderProfileResponse { - bool deleted = 1; -} - -// Get sandbox provider environment request. -message GetSandboxProviderEnvironmentRequest { - // The sandbox ID. - string sandbox_id = 1; -} - -// Get sandbox provider environment response. -message GetSandboxProviderEnvironmentResponse { - // Provider credential environment variables. - map environment = 1; - // Fingerprint for the provider credential inputs that produced environment. - uint64 provider_env_revision = 2; -} - -// --------------------------------------------------------------------------- -// Policy update messages -// --------------------------------------------------------------------------- - -// Update sandbox policy request. -message UpdateConfigRequest { - // Sandbox name (canonical lookup key). Required for sandbox-scoped updates. - // Not required when `global=true`. - string name = 1; - // The new policy to apply. - // - // Sandbox scope (`global=false`): - // - only network_policies and inference fields may differ from create-time - // policy; static fields must match version 1. - // - // Global scope (`global=true`): - // - applies to all sandboxes in full (no merge). - openshell.sandbox.v1.SandboxPolicy policy = 2; - // Optional single setting key to mutate. - string setting_key = 3; - // Setting value for upsert operations. - openshell.sandbox.v1.SettingValue setting_value = 4; - // Delete the setting key from scope. - // Sandbox-scoped deletes are rejected; only global delete is supported. - bool delete_setting = 5; - // Apply mutation at gateway-global scope. - bool global = 6; - // Batched incremental policy merge operations. Sandbox-scoped only. - repeated PolicyMergeOperation merge_operations = 7; - // Expected resource version for optimistic concurrency control (sandbox-scoped only). - // If 0, the server uses the current version (backward compatibility). - // If non-zero, the server validates that the sandbox's current resource_version - // matches this value before applying the mutation, returning ABORTED on mismatch. - // Ignored for global-scoped updates. - uint64 expected_resource_version = 8; -} - -message PolicyMergeOperation { - oneof operation { - AddNetworkRule add_rule = 1; - RemoveNetworkEndpoint remove_endpoint = 2; - RemoveNetworkRule remove_rule = 3; - AddDenyRules add_deny_rules = 4; - AddAllowRules add_allow_rules = 5; - RemoveNetworkBinary remove_binary = 6; - } -} - -message AddNetworkRule { - string rule_name = 1; - openshell.sandbox.v1.NetworkPolicyRule rule = 2; -} - -message RemoveNetworkEndpoint { - string rule_name = 1; - string host = 2; - uint32 port = 3; -} - -message RemoveNetworkRule { - string rule_name = 1; -} - -message AddDenyRules { - string host = 1; - uint32 port = 2; - repeated openshell.sandbox.v1.L7DenyRule deny_rules = 3; -} - -message AddAllowRules { - string host = 1; - uint32 port = 2; - repeated openshell.sandbox.v1.L7Rule rules = 3; -} - -message RemoveNetworkBinary { - string rule_name = 1; - string binary_path = 2; -} - -// Update sandbox policy response. -message UpdateConfigResponse { - // Assigned policy version (monotonically increasing per sandbox). - uint32 version = 1; - // SHA-256 hash of the serialized policy payload. - string policy_hash = 2; - // Settings revision for the scope that was modified. - uint64 settings_revision = 3; - // True when a setting delete operation removed an existing key. - bool deleted = 4; -} - -// Get sandbox policy status request. -message GetSandboxPolicyStatusRequest { - // Sandbox name (canonical lookup key). Ignored when global is true. - string name = 1; - // The specific policy version to query. 0 means latest. - uint32 version = 2; - // Query global policy revisions instead of a sandbox-scoped one. - bool global = 3; -} - -// Get sandbox policy status response. -message GetSandboxPolicyStatusResponse { - // The queried policy revision. - SandboxPolicyRevision revision = 1; - // The currently active (loaded) policy version for this sandbox. - uint32 active_version = 2; -} - -// List sandbox policies request. -message ListSandboxPoliciesRequest { - // Sandbox name (canonical lookup key). Ignored when global is true. - string name = 1; - uint32 limit = 2; - uint32 offset = 3; - // List global policy revisions instead of sandbox-scoped ones. - bool global = 4; -} - -// List sandbox policies response. -message ListSandboxPoliciesResponse { - repeated SandboxPolicyRevision revisions = 1; -} - -// Report policy load status (called by sandbox runtime after reload attempt). -message ReportPolicyStatusRequest { - // Sandbox id. - string sandbox_id = 1; - // The policy version that was attempted. - uint32 version = 2; - // Load result status. - PolicyStatus status = 3; - // Error message if status is FAILED. - string load_error = 4; -} - -// Report policy status response. -message ReportPolicyStatusResponse {} - -// A versioned policy revision with metadata. -message SandboxPolicyRevision { - // Policy version (monotonically increasing per sandbox). - uint32 version = 1; - // SHA-256 hash of the serialized policy payload. - string policy_hash = 2; - // Load status of this revision. - PolicyStatus status = 3; - // Error message if status is FAILED. - string load_error = 4; - // Milliseconds since epoch when this revision was created. - int64 created_at_ms = 5; - // Milliseconds since epoch when this revision was loaded by the sandbox. - int64 loaded_at_ms = 6; - // The full policy (only populated when explicitly requested). - openshell.sandbox.v1.SandboxPolicy policy = 7; -} - -// Policy load status. -enum PolicyStatus { - POLICY_STATUS_UNSPECIFIED = 0; - // Server received the update; sandbox has not yet loaded it. - POLICY_STATUS_PENDING = 1; - // Sandbox successfully applied this policy version. - POLICY_STATUS_LOADED = 2; - // Sandbox attempted to apply but failed; LKG policy remains active. - POLICY_STATUS_FAILED = 3; - // A newer version was persisted before the sandbox loaded this one. - POLICY_STATUS_SUPERSEDED = 4; -} - -// --------------------------------------------------------------------------- -// Sandbox logs messages -// --------------------------------------------------------------------------- - -// Get sandbox logs request (one-shot fetch). -message GetSandboxLogsRequest { - // Sandbox id. - string sandbox_id = 1; - // Maximum number of log lines to return. 0 means use default (2000). - uint32 lines = 2; - // Only include logs with timestamp >= this value (ms since epoch). 0 means no filter. - int64 since_ms = 3; - // Filter by log source (e.g. "gateway", "sandbox"). Empty means all sources. - repeated string sources = 4; - // Minimum log level to include (e.g. "INFO", "WARN", "ERROR"). Empty means all levels. - string min_level = 5; -} - -// Batch of log lines pushed from sandbox to server. -message PushSandboxLogsRequest { - // The sandbox ID. - string sandbox_id = 1; - // Log lines to ingest. - repeated SandboxLogLine logs = 2; -} - -// Push sandbox logs response. -message PushSandboxLogsResponse {} - -// Get sandbox logs response. -message GetSandboxLogsResponse { - // Log lines in chronological order. - repeated SandboxLogLine logs = 1; - // Total number of lines in the server's buffer for this sandbox. - uint32 buffer_total = 2; -} - -// --------------------------------------------------------------------------- -// Supervisor session messages -// --------------------------------------------------------------------------- - -// Envelope for supervisor-to-gateway messages on the ConnectSupervisor stream. -message SupervisorMessage { - oneof payload { - SupervisorHello hello = 1; - SupervisorHeartbeat heartbeat = 2; - RelayOpenResult relay_open_result = 3; - RelayClose relay_close = 4; - } -} - -// Envelope for gateway-to-supervisor messages on the ConnectSupervisor stream. -message GatewayMessage { - oneof payload { - SessionAccepted session_accepted = 1; - SessionRejected session_rejected = 2; - GatewayHeartbeat heartbeat = 3; - RelayOpen relay_open = 4; - RelayClose relay_close = 5; - } -} - -// Supervisor identifies itself and the sandbox it manages. -message SupervisorHello { - // Sandbox ID this supervisor manages. - string sandbox_id = 1; - // Supervisor instance ID (e.g. boot id or process epoch). - string instance_id = 2; -} - -// Gateway accepts the supervisor session. -message SessionAccepted { - // Gateway-assigned session ID for this connection. - string session_id = 1; - // Recommended heartbeat interval in seconds. - uint32 heartbeat_interval_secs = 2; -} - -// Gateway rejects the supervisor session. -message SessionRejected { - // Human-readable rejection reason. - string reason = 1; -} - -// Supervisor heartbeat. -message SupervisorHeartbeat {} - -// Gateway heartbeat. -message GatewayHeartbeat {} - -// Gateway requests the supervisor to open a relay channel. -// -// On receiving this, the supervisor should initiate a RelayStream RPC to -// the gateway, sending a RelayInit in the first RelayFrame to associate -// the new HTTP/2 stream with the pending relay slot. The supervisor -// bridges that stream to the requested local target. -message RelayOpen { - // Gateway-allocated channel identifier (UUID). - string channel_id = 1; - // Target the supervisor should dial inside the sandbox. - // If absent, supervisors treat the relay as SSH for compatibility. - oneof target { - SshRelayTarget ssh = 2; - TcpRelayTarget tcp = 3; - } - // Optional service identifier for audit/correlation. - string service_id = 5; -} - -// Built-in SSH relay target. -message SshRelayTarget {} - -// TCP target dialed by the supervisor from inside the sandbox. -message TcpRelayTarget { - // Phase 1 accepts loopback only: 127.0.0.1, ::1, or localhost. - string host = 1; - // Target port. Must fit in u16 and be non-zero. - uint32 port = 2; -} - -// Initial RelayStream frame sent by the supervisor to claim a pending relay. -message RelayInit { - // Gateway-allocated channel identifier (UUID). - string channel_id = 1; -} - -// A single frame on the RelayStream RPC. -// -// The supervisor MUST send `init` as the first frame. All subsequent frames -// in either direction carry raw bytes in `data`. -message RelayFrame { - oneof payload { - RelayInit init = 1; - bytes data = 2; - } -} - -// Supervisor reports the result of a relay open request. -message RelayOpenResult { - // Channel identifier from the RelayOpen request. - string channel_id = 1; - // True if the relay was successfully established. - bool success = 2; - // Error message if success is false. - string error = 3; -} - -// Either side requests closure of a relay channel. -message RelayClose { - // Channel identifier to close. - string channel_id = 1; - // Optional reason for closure. - string reason = 2; -} - -// --------------------------------------------------------------------------- -// Service status -// --------------------------------------------------------------------------- - -// Service status enum. -enum ServiceStatus { - SERVICE_STATUS_UNSPECIFIED = 0; - SERVICE_STATUS_HEALTHY = 1; - SERVICE_STATUS_DEGRADED = 2; - SERVICE_STATUS_UNHEALTHY = 3; -} - -// --------------------------------------------------------------------------- -// Draft policy recommendation messages -// --------------------------------------------------------------------------- - -// Observed HTTP method+path pattern from L7 inspection. -message L7RequestSample { - // HTTP method: GET, POST, PUT, DELETE, etc. - string method = 1; - // HTTP path: /v1/models, /repos/myorg/issues - string path = 2; - // L7 decision: "audit" or "deny" (allowed requests not collected). - string decision = 3; - // Number of times this (method, path) was observed. - uint32 count = 4; -} - -// Structured denial summary from sandbox aggregator. -message DenialSummary { - // Sandbox ID that produced this summary. - string sandbox_id = 1; - // Denied destination host. - string host = 2; - // Denied destination port. - uint32 port = 3; - // Binary that attempted the connection. - string binary = 4; - // Process ancestor chain. - repeated string ancestors = 5; - // Denial reason from OPA evaluation. - string deny_reason = 6; - // First denial timestamp (ms since epoch). - int64 first_seen_ms = 7; - // Most recent denial timestamp (ms since epoch). - int64 last_seen_ms = 8; - // Number of denials in the current window. - uint32 count = 9; - // Events dropped during aggregator cooldown. - uint32 suppressed_count = 10; - // Cumulative lifetime count (never resets). - uint32 total_count = 11; - // Distinct cmdline strings observed (sanitized of credentials). - repeated string sample_cmdlines = 12; - // SHA-256 of the binary for audit trail. - string binary_sha256 = 13; - // True if emitted by stale-flush rather than threshold. - bool persistent = 14; - // Denial category: "l4_deny", "l7_deny", "l7_audit", "ssrf". - string denial_stage = 15; - // Observed HTTP request patterns (from L7 inspection). - repeated L7RequestSample l7_request_samples = 16; - // True if L7 inspection was active during observation window. - bool l7_inspection_active = 17; -} - -// A proposed policy rule with rationale and approval status. -message PolicyChunk { - // Unique chunk identifier. - string id = 1; - // Approval status: "pending", "approved", "rejected". - string status = 2; - // Proposed network_policies map key. - string rule_name = 3; - // The proposed network policy rule. - openshell.sandbox.v1.NetworkPolicyRule proposed_rule = 4; - // Human-readable explanation of why this rule is proposed. - string rationale = 5; - // Security concerns flagged by analysis (empty if none). - string security_notes = 6; - // Analysis confidence (0.0-1.0). 0 for mechanistic mode. - float confidence = 7; - // IDs of denial summaries that led to this chunk. - repeated string denial_summary_ids = 8; - // Creation timestamp (ms since epoch). - int64 created_at_ms = 9; - // When the user approved/rejected (ms since epoch). 0 if undecided. - int64 decided_at_ms = 10; - // Recommendation stage: "initial" or "refined" (progressive L7 visibility). - string stage = 11; - // For stage="refined": the initial chunk this replaces. - string supersedes_chunk_id = 12; - // How many times this endpoint has been seen across denial flush cycles. - int32 hit_count = 13; - // First time this endpoint was proposed (ms since epoch). - int64 first_seen_ms = 14; - // Most recent time this endpoint was re-proposed (ms since epoch). - int64 last_seen_ms = 15; - // Binary path that triggered the denial (denormalized for display convenience). - string binary = 16; - // Validation verdict from gateway-side static checks (prover output). - // Free-form summary string for human consumption in the inbox card. - // Empty until the prover has run for this chunk. - string validation_result = 17; - // Operator-supplied free-form text accompanying a rejection. Populated - // when the reviewer rejects via `RejectDraftChunkRequest.reason`; surfaced - // back to the in-sandbox agent so it can revise the proposal. - // Empty for non-rejected chunks. - string rejection_reason = 18; -} - -// Notification that the draft policy was updated. -message DraftPolicyUpdate { - // Current draft version. - uint64 draft_version = 1; - // Number of new chunks added in this update. - uint32 new_chunks = 2; - // Total pending chunks awaiting approval. - uint32 total_pending = 3; - // Brief description of what changed. - string summary = 4; -} - -// Submit analysis results from sandbox to gateway. -message SubmitPolicyAnalysisRequest { - // Aggregated denial summaries. - repeated DenialSummary summaries = 1; - // Proposed policy chunks (validated by sandbox OPA engine). - repeated PolicyChunk proposed_chunks = 2; - // Analysis mode. `mechanistic` is the observation-driven path from the - // denial aggregator — chunks targeting the same host|port|binary fold - // into one row with hit_count incremented. `agent_authored` is an - // intentional proposal from an in-sandbox agent — each submission lands - // as its own chunk so the redraft-after-rejection loop has a stable id - // to watch. Other values are treated as agent-style (no dedup) so a new - // mode does not silently collapse proposals. - string analysis_mode = 3; - // Sandbox name. - string name = 4; -} - -message SubmitPolicyAnalysisResponse { - // Number of chunks accepted by the gateway. - uint32 accepted_chunks = 1; - // Number of chunks rejected by gateway validation. - uint32 rejected_chunks = 2; - // Reasons for each rejected chunk. - repeated string rejection_reasons = 3; - // Server-assigned chunk IDs for the accepted chunks, in submission order. - // Agents use these to watch proposal state via policy.local's - // GET /v1/proposals/{id} and /wait endpoints. - repeated string accepted_chunk_ids = 4; -} - -// Get draft policy for a sandbox. -message GetDraftPolicyRequest { - // Sandbox name. - string name = 1; - // Optional status filter: "pending", "approved", "rejected", or "" for all. - string status_filter = 2; -} - -message GetDraftPolicyResponse { - // Draft policy chunks. - repeated PolicyChunk chunks = 1; - // LLM-generated summary of all analysis (empty in mechanistic mode). - string rolling_summary = 2; - // Current draft version. - uint64 draft_version = 3; - // When the last analysis completed (ms since epoch). - int64 last_analyzed_at_ms = 4; -} - -// Approve a single draft chunk. -message ApproveDraftChunkRequest { - // Sandbox name. - string name = 1; - // Chunk ID to approve. - string chunk_id = 2; -} - -message ApproveDraftChunkResponse { - // New policy version after merge. - uint32 policy_version = 1; - // SHA-256 hash of the new policy. - string policy_hash = 2; -} - -// Reject a single draft chunk. -message RejectDraftChunkRequest { - // Sandbox name. - string name = 1; - // Chunk ID to reject. - string chunk_id = 2; - // Optional reason for rejection (fed to LLM context in future analysis). - string reason = 3; -} - -message RejectDraftChunkResponse {} - -// Approve all pending chunks. -message ApproveAllDraftChunksRequest { - // Sandbox name. - string name = 1; - // Include chunks with security_notes (default false: skips them). - bool include_security_flagged = 2; -} - -message ApproveAllDraftChunksResponse { - // New policy version after merge. - uint32 policy_version = 1; - // SHA-256 hash of the new policy. - string policy_hash = 2; - // Number of chunks approved. - uint32 chunks_approved = 3; - // Number of chunks skipped (security-flagged). - uint32 chunks_skipped = 4; -} - -// Edit a pending chunk in-place. -message EditDraftChunkRequest { - // Sandbox name. - string name = 1; - // Chunk ID to edit. - string chunk_id = 2; - // The modified rule (replaces existing proposed_rule). - openshell.sandbox.v1.NetworkPolicyRule proposed_rule = 3; -} - -message EditDraftChunkResponse {} - -// Reverse an approval (remove merged rule from active policy). -message UndoDraftChunkRequest { - // Sandbox name. - string name = 1; - // Chunk ID to undo. - string chunk_id = 2; -} - -message UndoDraftChunkResponse { - // New policy version after removal. - uint32 policy_version = 1; - // SHA-256 hash of the updated policy. - string policy_hash = 2; -} - -// Clear all pending draft chunks for a sandbox. -message ClearDraftChunksRequest { - // Sandbox name. - string name = 1; -} - -message ClearDraftChunksResponse { - // Number of chunks cleared. - uint32 chunks_cleared = 1; -} - -// Get decision history for a sandbox's draft policy. -message GetDraftHistoryRequest { - // Sandbox name. - string name = 1; -} - -message DraftHistoryEntry { - // Event timestamp (ms since epoch). - int64 timestamp_ms = 1; - // Event type: "denial_detected", "analysis_cycle", "approved", - // "rejected", "edited", "undone", "cleared". - string event_type = 2; - // Human-readable description. - string description = 3; - // Associated chunk ID (if applicable). - string chunk_id = 4; -} - -message GetDraftHistoryResponse { - // Chronological decision history. - repeated DraftHistoryEntry entries = 1; -} - -// Stored payload for a policy revision row in the generic objects table. -message PolicyRevisionPayload { - // Serialized policy contents. - openshell.sandbox.v1.SandboxPolicy policy = 1; - // Deterministic hash of the policy payload. - string hash = 2; - // Load error reported by the sandbox, if any. - string load_error = 3; - // When the policy version was reported as loaded (ms since epoch). 0 if unset. - int64 loaded_at_ms = 4; -} - -// Stored payload for a draft policy chunk row in the generic objects table. -message DraftChunkPayload { - // Proposed network_policies map key. - string rule_name = 1; - // Proposed network policy rule. - openshell.sandbox.v1.NetworkPolicyRule proposed_rule = 2; - // Human-readable explanation of why this rule is proposed. - string rationale = 3; - // Security concerns flagged by analysis (empty if none). - string security_notes = 4; - // Analysis confidence (0.0-1.0). 0 for mechanistic mode. - float confidence = 5; - // When the user approved/rejected (ms since epoch). 0 if undecided. - int64 decided_at_ms = 6; - // Denormalized endpoint host for dedup and display. - string host = 7; - // Denormalized endpoint port for dedup and display. - int32 port = 8; - // Binary path that triggered the denial. - string binary = 9; - // Current draft version for the owning sandbox. - int64 draft_version = 10; - // Gateway prover verdict for this chunk; empty until prover runs. - // Mirrors PolicyChunk.validation_result. - string validation_result = 11; - // Operator-supplied free-form rejection text; empty for non-rejected - // chunks. Mirrors PolicyChunk.rejection_reason. - string rejection_reason = 12; -} - -// Internal stored policy revision row materialized from the generic objects table. -message StoredPolicyRevision { - string id = 1; - string sandbox_id = 2; - int64 version = 3; - bytes policy_payload = 4; - string policy_hash = 5; - string status = 6; - optional string load_error = 7; - int64 created_at_ms = 8; - optional int64 loaded_at_ms = 9; -} - -// Internal stored draft chunk row materialized from the generic objects table. -message StoredDraftChunk { - string id = 1; - string sandbox_id = 2; - int64 draft_version = 3; - string status = 4; - string rule_name = 5; - bytes proposed_rule = 6; - string rationale = 7; - string security_notes = 8; - double confidence = 9; - int64 created_at_ms = 10; - optional int64 decided_at_ms = 11; - string host = 12; - int32 port = 13; - string binary = 14; - int32 hit_count = 15; - int64 first_seen_ms = 16; - int64 last_seen_ms = 17; - // Gateway prover verdict; empty until the prover runs. See PolicyChunk. - string validation_result = 18; - // Operator-supplied free-form rejection text. See PolicyChunk. - string rejection_reason = 19; -} diff --git a/go/api/openshell/proto/sandbox.proto b/go/api/openshell/proto/sandbox.proto deleted file mode 100644 index b40d95cb12..0000000000 --- a/go/api/openshell/proto/sandbox.proto +++ /dev/null @@ -1,271 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -syntax = "proto3"; - -package openshell.sandbox.v1; - -// Sandbox-supervisor configuration and policy messages. -// -// Conventions: -// - This file owns messages exchanged between the gateway and the sandbox -// supervisor/runtime. -// - Public sandbox resource types live in `openshell.proto`. -// - Internal compute-driver sandbox observation types live in `compute_driver.proto`. - -// Sandbox security policy configuration. -message SandboxPolicy { - // Policy version. - uint32 version = 1; - // Filesystem access policy. - FilesystemPolicy filesystem = 2; - // Landlock configuration. - LandlockPolicy landlock = 3; - // Process execution policy. - ProcessPolicy process = 4; - // Network access policies keyed by name (e.g. "claude_code", "gitlab"). - map network_policies = 5; -} - -// Filesystem access policy. -message FilesystemPolicy { - // Automatically include the workdir as read-write. - bool include_workdir = 1; - // Read-only directory allow list. - repeated string read_only = 2; - // Read-write directory allow list. - repeated string read_write = 3; -} - -// Landlock policy configuration. -message LandlockPolicy { - // Compatibility mode (e.g. "best_effort", "hard_requirement"). - string compatibility = 1; -} - -// Process execution policy. -message ProcessPolicy { - // User name to run the sandboxed process as. - string run_as_user = 1; - // Group name to run the sandboxed process as. - string run_as_group = 2; -} - -// A named network access policy rule. -message NetworkPolicyRule { - // Human-readable name for this policy rule. - string name = 1; - // Allowed endpoint (host:port) pairs. - repeated NetworkEndpoint endpoints = 2; - // Allowed binary identities. - repeated NetworkBinary binaries = 3; -} - -// A network endpoint (host + port) with optional L7 inspection config. -message NetworkEndpoint { - // Hostname or host glob pattern. Exact match is case-insensitive. - // Glob patterns use "." as delimiter: "*.example.com" matches a single - // subdomain label, "**.example.com" matches across labels. - string host = 1; - // Single port (backwards compat). Use `ports` for multiple ports. - // Mutually exclusive with `ports` — if both are set, `ports` takes precedence. - uint32 port = 2; - // Application protocol for L7 inspection: "rest", "websocket", "graphql", "sql", or "" (L4-only). - string protocol = 3; - // TLS handling: "terminate" or "passthrough" (default). - string tls = 4; - // Enforcement mode: "enforce" or "audit" (default). - string enforcement = 5; - // Access preset shorthand: "read-only", "read-write", "full". - // Mutually exclusive with rules. - string access = 6; - // Explicit L7 rules (mutually exclusive with access). - repeated L7Rule rules = 7; - // Allowed resolved IP addresses or CIDR ranges for this endpoint. - // When non-empty, the SSRF internal-IP check is replaced by an allowlist check: - // - If host is also set: domain must resolve to an IP in this list. - // - If host is empty: any domain is allowed as long as it resolves to an IP in this list. - // Supports exact IPs ("10.0.5.20") and CIDR notation ("10.0.5.0/24"). - // Loopback (127.0.0.0/8) and link-local (169.254.0.0/16) are always blocked - // regardless of this field. - repeated string allowed_ips = 8; - // Multiple ports. When non-empty, this endpoint covers all listed ports. - // If `port` is set and `ports` is empty, `port` is normalized to `ports: [port]`. - // If both are set, `ports` takes precedence. - repeated uint32 ports = 9; - // Explicit L7 deny rules. When present, requests matching any deny rule - // are blocked even if they match an allow rule or access preset. - // Deny rules take precedence over allow rules. - repeated L7DenyRule deny_rules = 10; - // When true, percent-encoded '/' (%2F) is preserved in path segments - // rather than rejected by the L7 path canonicalizer. Required for - // upstreams like GitLab that embed %2F in namespaced resource paths. - // Defaults to false (strict). - bool allow_encoded_slash = 11; - // GraphQL persisted-query behavior for hash-only/saved-query requests: - // "deny" (default) or "allow_registered". - string persisted_queries = 12; - // Trusted GraphQL persisted-query registry keyed by hash or service-specific ID. - // Only used when persisted_queries is "allow_registered". - map graphql_persisted_queries = 13; - // Maximum GraphQL request body bytes to buffer for inspection. - // Defaults to 65536 when unset. - uint32 graphql_max_body_bytes = 14; - // Optional HTTP path glob that scopes this L7 endpoint on shared host:port APIs. - // Example: use path "/graphql" for protocol "graphql" and "/repos/**" for - // protocol "rest" when both surfaces live under api.example.com:443. - // Empty means all paths. - string path = 15; - // When true on a "rest" endpoint, OpenShell rewrites credential placeholders - // inside client-to-server WebSocket text messages after an allowed HTTP 101 - // upgrade. Defaults to false. - bool websocket_credential_rewrite = 16; - // When true on a "rest" endpoint, OpenShell rewrites credential placeholders - // inside supported textual HTTP request bodies before forwarding upstream. - // Defaults to false. - bool request_body_credential_rewrite = 17; -} - -// Trusted GraphQL operation classification. -message GraphqlOperation { - // Operation type: "query", "mutation", or "subscription". - string operation_type = 1; - // Operation name, if known. - string operation_name = 2; - // Root field names selected by the operation. - repeated string fields = 3; -} - -// An L7 deny rule that blocks specific requests. -// Mirrors L7Allow — same fields, same matching semantics, inverted effect. -// Deny rules are evaluated after allow rules and take precedence. -message L7DenyRule { - // HTTP method (REST): GET, POST, etc. or "*" for any. - string method = 1; - // URL path glob pattern (REST): "/repos/*/pulls/*/reviews", "**" for any. - string path = 2; - // SQL command (SQL): SELECT, INSERT, etc. or "*" for any. - string command = 3; - // Query parameter matcher map (REST). - // Same semantics as L7Allow.query. - map query = 4; - // GraphQL operation type: "query", "mutation", "subscription", or "*" for any. - string operation_type = 5; - // GraphQL operation name glob. "*" matches any operation name. - string operation_name = 6; - // GraphQL root field globs. Deny rules match when any selected root field - // matches any configured glob. - repeated string fields = 7; -} - -// An L7 policy rule (allow-only). -message L7Rule { - L7Allow allow = 1; -} - -// Allowed action definition for L7 rules. -message L7Allow { - // HTTP method (REST): GET, POST, etc. or "*" for any. - string method = 1; - // URL path glob pattern (REST): "/repos/**", "**" for any. - string path = 2; - // SQL command (SQL): SELECT, INSERT, etc. or "*" for any. - string command = 3; - // Query parameter matcher map (REST). - // Key is the decoded query parameter name (case-sensitive). - // Value supports either a single glob (`glob`) or a list (`any`). - map query = 4; - // GraphQL operation type: "query", "mutation", "subscription", or "*" for any. - string operation_type = 5; - // GraphQL operation name glob. "*" matches any operation name. - string operation_name = 6; - // GraphQL root field globs. Allow rules match only when every selected root - // field matches one of the configured globs. Omit to match all fields. - repeated string fields = 7; -} - -// Query value matcher for one query parameter key. -message L7QueryMatcher { - // Single glob pattern. - string glob = 1; - // Any-of glob patterns. - repeated string any = 2; -} - -// A binary identity for network policy matching. -message NetworkBinary { - string path = 1; - // Deprecated: the harness concept has been removed. This field is ignored. - bool harness = 2 [deprecated = true]; -} - -// Request to get sandbox settings by sandbox ID. -message GetSandboxConfigRequest { - // The sandbox ID. - string sandbox_id = 1; -} - -// Request to get gateway-global settings. -message GetGatewayConfigRequest {} - -// Response containing gateway-global settings. -message GetGatewayConfigResponse { - // Gateway-global settings map excluding the reserved policy key. - // Registered keys without a configured value are returned with an empty SettingValue. - map settings = 1; - // Monotonically increasing revision for gateway-global settings. - uint64 settings_revision = 2; -} - -// Scope that currently controls a setting. -enum SettingScope { - SETTING_SCOPE_UNSPECIFIED = 0; - SETTING_SCOPE_SANDBOX = 1; - SETTING_SCOPE_GLOBAL = 2; -} - -// Type-aware setting value for sandbox/gateway settings. -message SettingValue { - oneof value { - string string_value = 1; - bool bool_value = 2; - int64 int_value = 3; - bytes bytes_value = 4; - } -} - -// Effective setting value and the scope it was resolved from. -message EffectiveSetting { - SettingValue value = 1; - SettingScope scope = 2; -} - -// Source used for the policy payload in GetSandboxConfigResponse. -enum PolicySource { - POLICY_SOURCE_UNSPECIFIED = 0; - POLICY_SOURCE_SANDBOX = 1; - POLICY_SOURCE_GLOBAL = 2; -} - -// Response containing effective sandbox settings and policy. -message GetSandboxConfigResponse { - // The sandbox policy configuration. - SandboxPolicy policy = 1; - // Current policy version (monotonically increasing per sandbox). - uint32 version = 2; - // SHA-256 hash of the serialized policy payload. - string policy_hash = 3; - // Effective settings resolved for this sandbox, excluding the reserved policy key. - // Registered keys without a configured value are returned with an empty EffectiveSetting.value. - map settings = 4; - // Fingerprint for effective config (policy + settings). Changes when any effective input changes. - uint64 config_revision = 5; - // Source of the policy payload for this response. - PolicySource policy_source = 6; - // When policy_source is GLOBAL, the version of the global policy revision. - // Zero when no global policy is active or when policy_source is SANDBOX. - uint32 global_policy_version = 7; - // Fingerprint for provider credential inputs attached to this sandbox. - // Changes when attached provider names or attached provider records change. - uint64 provider_env_revision = 8; -} diff --git a/go/api/openshell/proto/test.proto b/go/api/openshell/proto/test.proto deleted file mode 100644 index 17d695a433..0000000000 --- a/go/api/openshell/proto/test.proto +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -syntax = "proto3"; - -package openshell.test.v1; - -// Simple object for persistence tests. -message ObjectForTest { - string id = 1; - string name = 2; - uint32 count = 3; -} diff --git a/go/api/v1alpha2/agent_runtime_test.go b/go/api/v1alpha2/agent_runtime_test.go index 9ff0c14815..f12d8f5c31 100644 --- a/go/api/v1alpha2/agent_runtime_test.go +++ b/go/api/v1alpha2/agent_runtime_test.go @@ -14,21 +14,16 @@ func TestEffectiveDeclarativeRuntimeForAgent(t *testing.T) { }, } - t.Run("regular Agent keeps configured runtime on substrate platform", func(t *testing.T) { + t.Run("regular Agent keeps configured runtime", func(t *testing.T) { agent := &Agent{Spec: substrateSpec} require.Equal(t, DeclarativeRuntime_Python, EffectiveDeclarativeRuntimeForAgent(agent)) }) - t.Run("SandboxAgent on substrate uses Go", func(t *testing.T) { - sa := &SandboxAgent{Spec: SandboxAgentSpec{AgentSpec: substrateSpec, Platform: SandboxPlatformSubstrate}} + t.Run("SandboxAgent uses Go", func(t *testing.T) { + sa := &SandboxAgent{Spec: SandboxAgentSpec{AgentSpec: substrateSpec}} require.Equal(t, DeclarativeRuntime_Go, EffectiveDeclarativeRuntimeForAgent(sa)) }) - t.Run("SandboxAgent on agent-sandbox keeps configured runtime", func(t *testing.T) { - sa := &SandboxAgent{Spec: SandboxAgentSpec{AgentSpec: substrateSpec, Platform: SandboxPlatformAgentSandbox}} - require.Equal(t, DeclarativeRuntime_Python, EffectiveDeclarativeRuntimeForAgent(sa)) - }) - t.Run("regular Agent honors Go runtime", func(t *testing.T) { agent := &Agent{Spec: AgentSpec{ Type: AgentType_Declarative, diff --git a/go/api/v1alpha2/agent_spec_validation.go b/go/api/v1alpha2/agent_spec_validation.go index baa5c62c4d..73e7e6e2a4 100644 --- a/go/api/v1alpha2/agent_spec_validation.go +++ b/go/api/v1alpha2/agent_spec_validation.go @@ -3,9 +3,9 @@ package v1alpha2 import "fmt" const ( - substrateSandboxSkillsUnsupportedMsg = "spec.skills is not supported when spec.platform is substrate" - substrateSandboxPythonRuntimeUnsupportedMsg = "spec.declarative.runtime must be \"go\" when spec.platform is substrate" - substrateSandboxBYOUnsupportedMsg = "BYO agents are not supported when spec.platform is substrate" + substrateSandboxSkillsUnsupportedMsg = "spec.skills is not supported for sandbox agents" + substrateSandboxPythonRuntimeUnsupportedMsg = "spec.declarative.runtime must be \"go\" for sandbox agents" + substrateSandboxBYOUnsupportedMsg = "BYO agents are not supported for sandbox agents" ) // AgentSpecHasSkills reports whether the spec configures any skill sources. @@ -17,10 +17,10 @@ func AgentSpecHasSkills(spec *AgentSpec) bool { return len(s.Refs) > 0 || len(s.GitRefs) > 0 } -// ValidateSubstrateSandboxAgentSpec rejects substrate sandbox configurations that kagent -// does not support yet (for example declarative skills on Agent Substrate). +// ValidateSubstrateSandboxAgentSpec rejects sandbox agent configurations that kagent +// does not support on Agent Substrate (for example declarative skills or BYO agents). func ValidateSubstrateSandboxAgentSpec(agent *SandboxAgent) error { - if agent == nil || AgentSandboxPlatform(agent) != SandboxPlatformSubstrate { + if agent == nil { return nil } spec := agent.GetAgentSpec() diff --git a/go/api/v1alpha2/agent_spec_validation_test.go b/go/api/v1alpha2/agent_spec_validation_test.go index ca43ccb576..cfe6f6a3b8 100644 --- a/go/api/v1alpha2/agent_spec_validation_test.go +++ b/go/api/v1alpha2/agent_spec_validation_test.go @@ -7,27 +7,16 @@ import ( ) func TestValidateSubstrateSandboxAgentSpec(t *testing.T) { - t.Run("allows substrate without skills", func(t *testing.T) { + t.Run("allows sandbox agent without skills", func(t *testing.T) { agent := &SandboxAgent{ - Spec: SandboxAgentSpec{Platform: SandboxPlatformSubstrate}, + Spec: SandboxAgentSpec{}, } require.NoError(t, ValidateSubstrateSandboxAgentSpec(agent)) }) - t.Run("allows skills on agent-sandbox platform", func(t *testing.T) { + t.Run("rejects skills", func(t *testing.T) { agent := &SandboxAgent{ Spec: SandboxAgentSpec{ - Platform: SandboxPlatformAgentSandbox, - AgentSpec: AgentSpec{Skills: &SkillForAgent{Refs: []string{"ghcr.io/org/skill:latest"}}}, - }, - } - require.NoError(t, ValidateSubstrateSandboxAgentSpec(agent)) - }) - - t.Run("rejects skills on substrate platform", func(t *testing.T) { - agent := &SandboxAgent{ - Spec: SandboxAgentSpec{ - Platform: SandboxPlatformSubstrate, AgentSpec: AgentSpec{Skills: &SkillForAgent{Refs: []string{"ghcr.io/org/skill:latest"}}}, }, } @@ -36,10 +25,9 @@ func TestValidateSubstrateSandboxAgentSpec(t *testing.T) { require.Contains(t, err.Error(), substrateSandboxSkillsUnsupportedMsg) }) - t.Run("rejects python runtime on substrate platform", func(t *testing.T) { + t.Run("rejects python runtime", func(t *testing.T) { agent := &SandboxAgent{ Spec: SandboxAgentSpec{ - Platform: SandboxPlatformSubstrate, AgentSpec: AgentSpec{ Type: AgentType_Declarative, Declarative: &DeclarativeAgentSpec{ @@ -53,10 +41,9 @@ func TestValidateSubstrateSandboxAgentSpec(t *testing.T) { require.Contains(t, err.Error(), substrateSandboxPythonRuntimeUnsupportedMsg) }) - t.Run("rejects BYO agents on substrate platform", func(t *testing.T) { + t.Run("rejects BYO agents", func(t *testing.T) { agent := &SandboxAgent{ Spec: SandboxAgentSpec{ - Platform: SandboxPlatformSubstrate, AgentSpec: AgentSpec{ Type: AgentType_BYO, BYO: &BYOAgentSpec{}, @@ -68,23 +55,9 @@ func TestValidateSubstrateSandboxAgentSpec(t *testing.T) { require.Contains(t, err.Error(), substrateSandboxBYOUnsupportedMsg) }) - t.Run("allows BYO agents on agent-sandbox platform", func(t *testing.T) { - agent := &SandboxAgent{ - Spec: SandboxAgentSpec{ - Platform: SandboxPlatformAgentSandbox, - AgentSpec: AgentSpec{ - Type: AgentType_BYO, - BYO: &BYOAgentSpec{}, - }, - }, - } - require.NoError(t, ValidateSubstrateSandboxAgentSpec(agent)) - }) - - t.Run("allows go runtime on substrate platform", func(t *testing.T) { + t.Run("allows go runtime", func(t *testing.T) { agent := &SandboxAgent{ Spec: SandboxAgentSpec{ - Platform: SandboxPlatformSubstrate, AgentSpec: AgentSpec{ Type: AgentType_Declarative, Declarative: &DeclarativeAgentSpec{ diff --git a/go/api/v1alpha2/agent_types.go b/go/api/v1alpha2/agent_types.go index ebfdcd7325..298a49a420 100644 --- a/go/api/v1alpha2/agent_types.go +++ b/go/api/v1alpha2/agent_types.go @@ -230,15 +230,6 @@ type DeclarativeAgentSpec struct { Context *ContextConfig `json:"context,omitempty"` } -// SandboxPlatform selects the control plane for sandboxed agents. -// +kubebuilder:validation:Enum=agent-sandbox;substrate -type SandboxPlatform string - -const ( - SandboxPlatformAgentSandbox SandboxPlatform = "agent-sandbox" - SandboxPlatformSubstrate SandboxPlatform = "substrate" -) - // SandboxSubstrateSpec configures Agent Substrate for a SandboxAgent. // WorkerPool capacity is referenced from workerPoolRef or the controller default. type SandboxSubstrateSpec struct { @@ -260,15 +251,6 @@ type SandboxConfig struct { Network *NetworkConfig `json:"network,omitempty"` } -// AgentSandboxPlatform returns the effective sandbox platform for an agent. -func AgentSandboxPlatform(agent AgentObject) SandboxPlatform { - sa, ok := agent.(*SandboxAgent) - if !ok || sa == nil || sa.Spec.Platform == "" { - return SandboxPlatformAgentSandbox - } - return sa.Spec.Platform -} - // EffectiveDeclarativeRuntime returns the ADK runtime from spec fields (defaults to Python). func EffectiveDeclarativeRuntime(spec *AgentSpec) DeclarativeRuntime { if spec == nil { @@ -282,11 +264,10 @@ func EffectiveDeclarativeRuntime(spec *AgentSpec) DeclarativeRuntime { } // EffectiveDeclarativeRuntimeForAgent returns the runtime for a reconciled agent object. -// Substrate SandboxAgents always use Go; regular Agents honor spec.declarative.runtime. +// SandboxAgents always use Go; regular Agents honor spec.declarative.runtime. func EffectiveDeclarativeRuntimeForAgent(agent AgentObject) DeclarativeRuntime { spec := agent.GetAgentSpec() if agent.GetWorkloadMode() == WorkloadModeSandbox && - AgentSandboxPlatform(agent) == SandboxPlatformSubstrate && spec != nil && spec.Type == AgentType_Declarative { return DeclarativeRuntime_Go diff --git a/go/api/v1alpha2/agentharness_types.go b/go/api/v1alpha2/agentharness_types.go index ee74118827..34d2023443 100644 --- a/go/api/v1alpha2/agentharness_types.go +++ b/go/api/v1alpha2/agentharness_types.go @@ -18,19 +18,18 @@ import ( // AgentHarnessBackendType selects which sandbox control plane provisions the // environment. Additional backends may be added in the future. -// +kubebuilder:validation:Enum=openclaw;nemoclaw;hermes +// +kubebuilder:validation:Enum=openclaw;hermes type AgentHarnessBackendType string const ( AgentHarnessBackendOpenClaw AgentHarnessBackendType = "openclaw" - AgentHarnessBackendNemoClaw AgentHarnessBackendType = "nemoclaw" AgentHarnessBackendHermes AgentHarnessBackendType = "hermes" ) -// IsKnownAgentHarnessBackend reports backends the OpenShell harness controller and API expose. +// IsKnownAgentHarnessBackend reports backends the API exposes. func IsKnownAgentHarnessBackend(b AgentHarnessBackendType) bool { switch b { - case AgentHarnessBackendOpenClaw, AgentHarnessBackendNemoClaw, AgentHarnessBackendHermes: + case AgentHarnessBackendOpenClaw, AgentHarnessBackendHermes: return true default: return false @@ -38,11 +37,10 @@ func IsKnownAgentHarnessBackend(b AgentHarnessBackendType) bool { } // AgentHarnessRuntime selects which control plane provisions the harness VM. -// +kubebuilder:validation:Enum=openshell;substrate +// +kubebuilder:validation:Enum=substrate type AgentHarnessRuntime string const ( - AgentHarnessRuntimeOpenshell AgentHarnessRuntime = "openshell" AgentHarnessRuntimeSubstrate AgentHarnessRuntime = "substrate" ) @@ -61,7 +59,7 @@ type AgentHarnessSubstrateSnapshotsConfig struct { // kagent generates a per-harness ActorTemplate and creates an Actor from it. WorkerPool // capacity is referenced from workerPoolRef or the controller default; it is not // created or deleted by the AgentHarness controller. -// +kubebuilder:validation:XValidation:rule="(has(self.gatewayToken) && !has(self.gatewayTokenSecretRef)) || (!has(self.gatewayToken) && has(self.gatewayTokenSecretRef))",message="Exactly one of gatewayToken or gatewayTokenSecretRef must be specified" +// +kubebuilder:validation:XValidation:rule="!(has(self.gatewayToken) && has(self.gatewayTokenSecretRef))",message="Specify at most one of gatewayToken or gatewayTokenSecretRef" type AgentHarnessSubstrateSpec struct { // WorkerPoolRef references an existing ate.dev WorkerPool in the harness namespace. // When unset, the controller uses its configured default WorkerPool. @@ -73,11 +71,14 @@ type AgentHarnessSubstrateSpec struct { // +optional SnapshotsConfig *AgentHarnessSubstrateSnapshotsConfig `json:"snapshotsConfig,omitempty"` - // WorkloadImage overrides the default nemoclaw/openclaw sandbox image in the ActorTemplate. + // WorkloadImage overrides the default openclaw sandbox image in the ActorTemplate. // +optional WorkloadImage string `json:"workloadImage,omitempty"` // GatewayToken is the OpenClaw gateway Bearer token for this harness. + // Optional: when neither gatewayToken nor gatewayTokenSecretRef is set, the + // controller generates a random token and stores it in a Secret named + // "-gateway-token" (key "token"), which can be retrieved later. // Prefer gatewayTokenSecretRef for production secrets. // +optional // +kubebuilder:validation:MinLength=1 @@ -132,7 +133,7 @@ type AgentHarnessTelegramChannelSpec struct { AllowedUserIDsFrom *ValueSource `json:"allowedUserIDsFrom,omitempty"` } -// AgentHarnessOpenClawSlackOptions configures OpenClaw/NemoClaw-specific Slack routing. +// AgentHarnessOpenClawSlackOptions configures OpenClaw-specific Slack routing. // // +kubebuilder:validation:XValidation:rule="!has(self.channelAccess) || self.channelAccess != 'allowlist' || (has(self.allowlistChannels) && size(self.allowlistChannels) > 0)",message="allowlistChannels is required when channelAccess is allowlist" type AgentHarnessOpenClawSlackOptions struct { @@ -173,7 +174,7 @@ type AgentHarnessSlackChannelSpec struct { BotToken AgentHarnessChannelCredential `json:"botToken"` // +required AppToken AgentHarnessChannelCredential `json:"appToken"` - // OpenClaw configures OpenClaw/NemoClaw-specific Slack routing. + // OpenClaw configures OpenClaw-specific Slack routing. // +optional OpenClaw *AgentHarnessOpenClawSlackOptions `json:"openclaw,omitempty"` // Hermes configures Hermes-specific Slack settings. @@ -204,7 +205,7 @@ type AgentHarnessChannel struct { // An AgentHarness is distinct from a SandboxAgent: it has no agent runtime baked // in. The backend is responsible for provisioning an environment that stays // ready to accept incoming commands. -// +kubebuilder:validation:XValidation:rule="!has(self.channels) || self.channels.all(c, c.type != 'slack' || (has(c.slack) && ((self.backend == 'hermes' && has(c.slack.hermes) && !has(c.slack.openclaw)) || ((self.backend == 'openclaw' || self.backend == 'nemoclaw') && has(c.slack.openclaw) && !has(c.slack.hermes)))))",message="slack backend-specific settings must match spec.backend" +// +kubebuilder:validation:XValidation:rule="!has(self.channels) || self.channels.all(c, c.type != 'slack' || (has(c.slack) && ((self.backend == 'hermes' && has(c.slack.hermes) && !has(c.slack.openclaw)) || (self.backend == 'openclaw' && has(c.slack.openclaw) && !has(c.slack.hermes)))))",message="slack backend-specific settings must match spec.backend" // +kubebuilder:validation:XValidation:rule="!has(self.substrate) || self.runtime == 'substrate'",message="spec.substrate may only be set when runtime is substrate" // +kubebuilder:validation:XValidation:rule="self.runtime != 'substrate' || has(self.substrate)",message="spec.substrate is required when runtime is substrate" type AgentHarnessSpec struct { @@ -212,9 +213,9 @@ type AgentHarnessSpec struct { // +required Backend AgentHarnessBackendType `json:"backend"` - // Runtime selects the harness provisioning stack. Defaults to openshell when unset. + // Runtime selects the harness provisioning stack. Defaults to substrate when unset. // +optional - // +kubebuilder:default=openshell + // +kubebuilder:default=substrate Runtime AgentHarnessRuntime `json:"runtime,omitempty"` // Substrate is required when runtime is substrate. @@ -226,8 +227,8 @@ type AgentHarnessSpec struct { Description string `json:"description,omitempty"` // Image is the container image to run in the harness VM, if the backend - // supports per-resource images. Backends openclaw and nemoclaw pin the image - // to the NemoClaw sandbox base when this field is empty; backend hermes pins + // supports per-resource images. Backend openclaw pins the image + // to the OpenClaw sandbox base when this field is empty; backend hermes pins // to the Hermes sandbox base image when empty. // +optional Image string `json:"image,omitempty"` @@ -313,8 +314,8 @@ const ( // +kubebuilder:printcolumn:name="ID",type="string",JSONPath=".status.backendRef.id" // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" -// AgentHarness is a generic remote execution environment provisioned by a backend -// (e.g. OpenShell) and addressable by exec/SSH. +// AgentHarness is a generic remote execution environment provisioned by a +// backend (OpenClaw or Hermes) running on Agent Substrate. type AgentHarness struct { metav1.TypeMeta `json:",inline"` // +optional diff --git a/go/api/v1alpha2/sandboxagent_types.go b/go/api/v1alpha2/sandboxagent_types.go index fcfa3db9cf..4cf28fc02a 100644 --- a/go/api/v1alpha2/sandboxagent_types.go +++ b/go/api/v1alpha2/sandboxagent_types.go @@ -25,7 +25,7 @@ import ( // +kubebuilder:subresource:status // +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status",description="Whether the sandbox workload is ready." // +kubebuilder:printcolumn:name="Accepted",type="string",JSONPath=".status.conditions[?(@.type=='Accepted')].status",description="Whether configuration was accepted." -// SandboxAgent declares an agent that runs in an isolated sandbox (agent-sandbox or Agent Substrate). +// SandboxAgent declares an agent that runs in an isolated sandbox on Agent Substrate. type SandboxAgent struct { metav1.TypeMeta `json:",inline"` // +optional @@ -37,18 +37,12 @@ type SandboxAgent struct { Status AgentStatus `json:"status,omitempty"` } -// +kubebuilder:validation:XValidation:rule="!has(self.skills) || self.platform != 'substrate'",message="spec.skills is not supported when spec.platform is substrate" -// +kubebuilder:validation:XValidation:rule="!has(self.substrate) || self.platform == 'substrate'",message="spec.substrate may only be set when spec.platform is substrate" -// +kubebuilder:validation:XValidation:rule="!has(self.type) || self.type != 'BYO' || self.platform != 'substrate'",message="BYO agents are not supported when spec.platform is substrate" +// +kubebuilder:validation:XValidation:rule="!has(self.skills)",message="spec.skills is not supported for sandbox agents" +// +kubebuilder:validation:XValidation:rule="!has(self.type) || self.type != 'BYO'",message="BYO agents are not supported for sandbox agents" type SandboxAgentSpec struct { AgentSpec `json:",inline"` - // Platform selects the sandbox control plane. Defaults to agent-sandbox. - // +optional - // +kubebuilder:default=agent-sandbox - Platform SandboxPlatform `json:"platform,omitempty"` - - // Substrate is optional substrate-specific settings when platform is substrate. + // Substrate is optional Agent Substrate-specific settings. // +optional Substrate *SandboxSubstrateSpec `json:"substrate,omitempty"` } diff --git a/go/core/cmd/acp-shim/main.go b/go/core/cmd/acp-shim/main.go new file mode 100644 index 0000000000..c4ebe1b05d --- /dev/null +++ b/go/core/cmd/acp-shim/main.go @@ -0,0 +1,87 @@ +// Command acp-shim exposes a stdio ACP agent over a WebSocket endpoint. +// +// It is the in-sandbox half of kagent's ACP integration: Substrate's only +// ingress is the network, so this shim listens for a single WebSocket client +// (the kagent A2A↔ACP bridge), spawns the configured agent subprocess, and +// pumps frames — one WebSocket text frame per newline-delimited JSON-RPC +// line — without ever parsing the protocol. The child command after "--" is +// the only per-backend configuration. +// +// Usage: +// +// acp-shim --listen :9000 --token-file /var/run/acp/token -- hermes acp +// acp-shim --child-policy per-connection -- gemini --experimental-acp +package main + +import ( + "context" + "flag" + "fmt" + "log" + "os" + "os/signal" + "syscall" + "time" + + "github.com/kagent-dev/kagent/go/core/pkg/acpshim" +) + +func main() { + log.SetFlags(log.LstdFlags | log.Lmicroseconds) + + cfg := &acpshim.Config{} + var policy string + flag.StringVar(&cfg.ListenAddr, "listen", ":9000", "address to serve the WebSocket endpoint on") + flag.StringVar(&cfg.TokenFile, "token-file", "", "path to bearer token file used to authenticate clients (empty disables auth)") + flag.StringVar(&cfg.ChildDir, "workdir", "", "working directory for the agent process") + flag.StringVar(&policy, "child-policy", string(acpshim.ChildPolicyLongLived), "child lifecycle policy: long-lived or per-connection") + flag.DurationVar(&cfg.GracePeriod, "grace", 5*time.Second, "SIGTERM-to-SIGKILL grace period when stopping the agent") + flag.DurationVar(&cfg.ReconnectGrace, "reconnect-grace", 0, "how long a long-lived agent survives after the client disconnects (0 = forever)") + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "Usage: %s [flags] -- [args...]\n\nFlags:\n", os.Args[0]) + flag.PrintDefaults() + } + flag.Parse() + + cfg.Policy = acpshim.ChildPolicy(policy) + cfg.ChildArgv = flag.Args() + // Env-var fallbacks so the agent command can be baked into an image + // without overriding the entrypoint. + if len(cfg.ChildArgv) == 0 { + if v := os.Getenv("ACP_SHIM_CHILD"); v != "" { + cfg.ChildArgv = []string{"/bin/sh", "-c", v} + } + } + if cfg.TokenFile == "" { + cfg.TokenFile = os.Getenv("ACP_SHIM_TOKEN_FILE") + } + // Substrate ActorTemplate containers support env (incl. secretKeyRef) + // but not volume mounts, so allow passing the token directly. A literal + // token wins over the token file (the base image bakes in a default + // ACP_SHIM_TOKEN_FILE that only exists when a Secret is mounted). + if cfg.Token == "" { + cfg.Token = os.Getenv("ACP_SHIM_TOKEN") + } + + if err := cfg.Validate(); err != nil { + log.Fatalf("acp-shim: %v", err) + } + + srv := acpshim.NewServer(cfg) + + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) + go func() { + sig := <-sigCh + log.Printf("acp-shim: received %s, shutting down", sig) + ctx, cancel := context.WithTimeout(context.Background(), cfg.GracePeriod+5*time.Second) + defer cancel() + if err := srv.Shutdown(ctx); err != nil { + log.Printf("acp-shim: shutdown error: %v", err) + } + }() + + if err := srv.ListenAndServe(); err != nil { + log.Fatalf("acp-shim: %v", err) + } +} diff --git a/go/core/cmd/controller/main.go b/go/core/cmd/controller/main.go index b2d741ab2f..c966a460b3 100644 --- a/go/core/cmd/controller/main.go +++ b/go/core/cmd/controller/main.go @@ -22,7 +22,6 @@ import ( "github.com/kagent-dev/kagent/go/core/internal/httpserver/auth" "github.com/kagent-dev/kagent/go/core/pkg/app" pkgauth "github.com/kagent-dev/kagent/go/core/pkg/auth" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/agentsxk8s" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. @@ -38,10 +37,9 @@ func main() { return nil, err } return &app.ExtensionConfig{ - Authenticator: authenticator, - Authorizer: authorizer, - AgentPlugins: nil, - SandboxBackend: agentsxk8s.New(), + Authenticator: authenticator, + Authorizer: authorizer, + AgentPlugins: nil, }, nil }, nil) } diff --git a/go/core/internal/a2a/a2a_registrar.go b/go/core/internal/a2a/a2a_registrar.go index 5c1281504b..db2aaeff5a 100644 --- a/go/core/internal/a2a/a2a_registrar.go +++ b/go/core/internal/a2a/a2a_registrar.go @@ -218,7 +218,6 @@ func (a *A2ARegistrar) upsertAgentHandler(ctx context.Context, agent v1alpha2.Ag httpClient := debugHTTPClient() if sa, ok := agent.(*v1alpha2.SandboxAgent); ok && - v1alpha2.AgentSandboxPlatform(sa) == v1alpha2.SandboxPlatformSubstrate && a.substrateSandboxActorBackend != nil { routerURL := a.ateneRouterURL if routerURL == "" { diff --git a/go/core/internal/controller/agentharness_controller_test.go b/go/core/internal/controller/agentharness_controller_test.go index 7d09b37dee..9958c6728a 100644 --- a/go/core/internal/controller/agentharness_controller_test.go +++ b/go/core/internal/controller/agentharness_controller_test.go @@ -39,6 +39,17 @@ func (f *fakeSubstrateLifecycle) CleanupGeneratedTemplate(_ context.Context, _ * return f.cleanupDone, f.cleanupErr } +type fakeSessionActorCleaner struct { + done bool + err error + calls int +} + +func (f *fakeSessionActorCleaner) DeleteAllAgentHarnessActors(_ context.Context, _ *v1alpha2.AgentHarness) (bool, error) { + f.calls++ + return f.done, f.err +} + type fakeAgentHarnessBackend struct { ensureCalls int deleteCalls int @@ -100,7 +111,7 @@ func TestAgentHarnessController_SubstrateWaitsForGeneratedTemplate(t *testing.T) lifecycle := &fakeSubstrateLifecycle{state: substrate.LifecycleState{ActorTemplateReady: false}} backend := &fakeAgentHarnessBackend{} controller.SubstrateLifecycle = lifecycle - controller.OpenClawBackend = backend + setSubstrateTestBackend(controller, backend) result, err := controller.Reconcile(ctx, ctrl.Request{NamespacedName: client.ObjectKeyFromObject(ah)}) require.NoError(t, err) @@ -122,7 +133,7 @@ func TestAgentHarnessController_SubstrateLifecycleErrorSetsStatus(t *testing.T) lifecycle := &fakeSubstrateLifecycle{ensureErr: errors.New("workerpool missing")} backend := &fakeAgentHarnessBackend{} controller.SubstrateLifecycle = lifecycle - controller.OpenClawBackend = backend + setSubstrateTestBackend(controller, backend) _, err := controller.Reconcile(ctx, ctrl.Request{NamespacedName: client.ObjectKeyFromObject(ah)}) require.ErrorContains(t, err, "workerpool missing") @@ -134,36 +145,33 @@ func TestAgentHarnessController_SubstrateLifecycleErrorSetsStatus(t *testing.T) requireCondition(t, latest, v1alpha2.AgentHarnessConditionTypeReady, metav1.ConditionFalse, "SubstrateLifecycleFailed") } -func TestAgentHarnessController_SubstrateReadyCreatesActorAndRunsBootstrap(t *testing.T) { +func TestAgentHarnessController_SubstrateReadyMarksTemplateReady(t *testing.T) { ctx := context.Background() ah := newSubstrateHarness("kagent", "claw") controller := newAgentHarnessTestController(t, ah) lifecycle := &fakeSubstrateLifecycle{state: substrate.LifecycleState{ActorTemplateReady: true}} - backend := &fakeAgentHarnessBackend{ensureHandle: "actor-1", endpoint: "kagent gateway: /api/agentharnesses/kagent/claw/gateway/"} + backend := &fakeAgentHarnessBackend{} controller.SubstrateLifecycle = lifecycle - controller.OpenClawBackend = backend + setSubstrateTestBackend(controller, backend) result, err := controller.Reconcile(ctx, ctrl.Request{NamespacedName: client.ObjectKeyFromObject(ah)}) require.NoError(t, err) require.Equal(t, ctrl.Result{}, result) require.Equal(t, 1, lifecycle.ensureCalls) - require.Equal(t, 1, backend.ensureCalls) - require.Equal(t, 1, backend.readyCalls) + require.Zero(t, backend.ensureCalls, "harness is a template: no persistent actor is created") + require.Zero(t, backend.readyCalls, "harness is a template: no per-harness bootstrap runs") latest := getAgentHarness(t, controller.Client, ah) - require.NotNil(t, latest.Status.BackendRef) - require.Equal(t, "actor-1", latest.Status.BackendRef.ID) - require.NotNil(t, latest.Status.Connection) + require.Nil(t, latest.Status.BackendRef, "template harness has no persistent actor backend ref") requireCondition(t, latest, v1alpha2.AgentHarnessConditionTypeAccepted, metav1.ConditionTrue, "AgentHarnessAccepted") requireCondition(t, latest, v1alpha2.AgentHarnessConditionTypeActorTemplateReady, metav1.ConditionTrue, "Ready") - requireCondition(t, latest, v1alpha2.AgentHarnessConditionTypeActorReady, metav1.ConditionTrue, "Running") - requireCondition(t, latest, v1alpha2.AgentHarnessConditionTypeBootstrapReady, metav1.ConditionTrue, "BootstrapComplete") - requireCondition(t, latest, v1alpha2.AgentHarnessConditionTypeReady, metav1.ConditionTrue, "Running") + requireCondition(t, latest, v1alpha2.AgentHarnessConditionTypeActorReady, metav1.ConditionTrue, "TemplateReady") + requireCondition(t, latest, v1alpha2.AgentHarnessConditionTypeReady, metav1.ConditionTrue, "TemplateReady") result, err = controller.Reconcile(ctx, ctrl.Request{NamespacedName: client.ObjectKeyFromObject(ah)}) require.NoError(t, err) require.Equal(t, ctrl.Result{}, result) - require.Equal(t, 1, backend.readyCalls, "bootstrap should not rerun for an already bootstrapped generation") + require.Zero(t, backend.ensureCalls, "no actor should ever be created for the template harness") } func TestAgentHarnessController_SubstrateDeleteWaitsForActorBeforeTemplateCleanup(t *testing.T) { @@ -172,15 +180,15 @@ func TestAgentHarnessController_SubstrateDeleteWaitsForActorBeforeTemplateCleanu ah.Status.BackendRef = &v1alpha2.AgentHarnessStatusRef{Backend: v1alpha2.AgentHarnessBackendOpenClaw, ID: "actor-1"} controller := newAgentHarnessTestController(t, ah) lifecycle := &fakeSubstrateLifecycle{cleanupDone: true} - backend := &fakeAgentHarnessBackend{deleteDone: false} + cleaner := &fakeSessionActorCleaner{done: false} controller.SubstrateLifecycle = lifecycle - controller.OpenClawBackend = backend + controller.SessionActorBackend = cleaner result, err := controller.Reconcile(ctx, ctrl.Request{NamespacedName: client.ObjectKeyFromObject(ah)}) require.NoError(t, err) require.Equal(t, agentHarnessNotReadyRequeue, result.RequeueAfter) - require.Equal(t, 1, backend.deleteCalls) - require.Zero(t, lifecycle.cleanupCalls, "template cleanup must wait for harness actor deletion") + require.Equal(t, 1, cleaner.calls) + require.Zero(t, lifecycle.cleanupCalls, "template cleanup must wait for session actor deletion") latest := getAgentHarness(t, controller.Client, ah) require.NotNil(t, latest.Status.BackendRef) @@ -194,14 +202,14 @@ func TestAgentHarnessController_SubstrateDeleteWaitsForGeneratedTemplateCleanup( ah.Status.BackendRef = &v1alpha2.AgentHarnessStatusRef{Backend: v1alpha2.AgentHarnessBackendOpenClaw, ID: "actor-1"} controller := newAgentHarnessTestController(t, ah) lifecycle := &fakeSubstrateLifecycle{cleanupDone: false} - backend := &fakeAgentHarnessBackend{deleteDone: true} + cleaner := &fakeSessionActorCleaner{done: true} controller.SubstrateLifecycle = lifecycle - controller.OpenClawBackend = backend + controller.SessionActorBackend = cleaner result, err := controller.Reconcile(ctx, ctrl.Request{NamespacedName: client.ObjectKeyFromObject(ah)}) require.NoError(t, err) require.Equal(t, agentHarnessNotReadyRequeue, result.RequeueAfter) - require.Equal(t, 1, backend.deleteCalls) + require.Equal(t, 1, cleaner.calls) require.Equal(t, 1, lifecycle.cleanupCalls) latest := getAgentHarness(t, controller.Client, ah) @@ -216,14 +224,14 @@ func TestAgentHarnessController_SubstrateDeleteRemovesFinalizerAfterCleanup(t *t ah.Status.BackendRef = &v1alpha2.AgentHarnessStatusRef{Backend: v1alpha2.AgentHarnessBackendOpenClaw, ID: "actor-1"} controller := newAgentHarnessTestController(t, ah) lifecycle := &fakeSubstrateLifecycle{cleanupDone: true} - backend := &fakeAgentHarnessBackend{deleteDone: true} + cleaner := &fakeSessionActorCleaner{done: true} controller.SubstrateLifecycle = lifecycle - controller.OpenClawBackend = backend + controller.SessionActorBackend = cleaner result, err := controller.Reconcile(ctx, ctrl.Request{NamespacedName: client.ObjectKeyFromObject(ah)}) require.NoError(t, err) require.Equal(t, ctrl.Result{}, result) - require.Equal(t, 1, backend.deleteCalls) + require.Equal(t, 1, cleaner.calls) require.Equal(t, 1, lifecycle.cleanupCalls) var latest v1alpha2.AgentHarness @@ -243,6 +251,12 @@ func newAgentHarnessTestController(t *testing.T, objects ...client.Object) *Subs return &SubstrateAgentHarnessController{Client: kube} } +func setSubstrateTestBackend(c *SubstrateAgentHarnessController, b sandboxbackend.AsyncBackend) { + c.Backends = map[v1alpha2.AgentHarnessBackendType]sandboxbackend.AsyncBackend{ + v1alpha2.AgentHarnessBackendOpenClaw: b, + } +} + func newSubstrateHarness(namespace, name string) *v1alpha2.AgentHarness { return &v1alpha2.AgentHarness{ TypeMeta: metav1.TypeMeta{APIVersion: v1alpha2.GroupVersion.String(), Kind: "AgentHarness"}, diff --git a/go/core/internal/controller/agentharness_openshell_controller.go b/go/core/internal/controller/agentharness_openshell_controller.go deleted file mode 100644 index be25f2f7d0..0000000000 --- a/go/core/internal/controller/agentharness_openshell_controller.go +++ /dev/null @@ -1,205 +0,0 @@ -/* -Copyright 2026. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 -*/ - -package controller - -import ( - "context" - "fmt" - - "github.com/go-logr/logr" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/events" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/builder" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend" -) - -// OpenShellAgentHarnessController reconciles AgentHarness resources that use the -// OpenShell runtime. -type OpenShellAgentHarnessController struct { - Client client.Client - Recorder events.EventRecorder - OpenClawBackend sandboxbackend.AsyncBackend - HermesBackend sandboxbackend.AsyncBackend -} - -func (r *OpenShellAgentHarnessController) backendFor(ah *v1alpha2.AgentHarness) sandboxbackend.AsyncBackend { - switch ah.Spec.Backend { - case v1alpha2.AgentHarnessBackendOpenClaw, v1alpha2.AgentHarnessBackendNemoClaw: - return r.OpenClawBackend - case v1alpha2.AgentHarnessBackendHermes: - return r.HermesBackend - default: - return nil - } -} - -func (r *OpenShellAgentHarnessController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := ctrl.LoggerFrom(ctx).WithValues("agentHarness", req.NamespacedName) - - var ah v1alpha2.AgentHarness - if err := r.Client.Get(ctx, req.NamespacedName, &ah); err != nil { - if apierrors.IsNotFound(err) { - return ctrl.Result{}, nil - } - return ctrl.Result{}, fmt.Errorf("get AgentHarness: %w", err) - } - if effectiveAgentHarnessRuntime(&ah) != v1alpha2.AgentHarnessRuntimeOpenshell { - return ctrl.Result{}, nil - } - - if !ah.DeletionTimestamp.IsZero() { - return r.reconcileDelete(ctx, &ah) - } - - if controllerutil.AddFinalizer(&ah, agentHarnessFinalizer) { - if err := r.Client.Update(ctx, &ah); err != nil { - return ctrl.Result{}, fmt.Errorf("add finalizer: %w", err) - } - return ctrl.Result{Requeue: true}, nil - } - - backend := r.backendFor(&ah) - if backend == nil { - return reconcileBackendUnavailable(ctx, r.Client, &ah, v1alpha2.AgentHarnessRuntimeOpenshell) - } - - return r.reconcileBackend(ctx, req, &ah, backend, log) -} - -func (r *OpenShellAgentHarnessController) reconcileBackend(ctx context.Context, req ctrl.Request, ah *v1alpha2.AgentHarness, backend sandboxbackend.AsyncBackend, log logr.Logger) (ctrl.Result, error) { - res, err := backend.EnsureAgentHarness(ctx, ah) - if err != nil { - log.Error(err, "EnsureAgentHarness failed") - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeAccepted, metav1.ConditionFalse, - "EnsureFailed", err.Error()) - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeReady, metav1.ConditionFalse, - "EnsureFailed", err.Error()) - if perr := patchAgentHarnessStatus(ctx, r.Client, ah); perr != nil { - return ctrl.Result{}, perr - } - return ctrl.Result{}, err - } - - ah.Status.BackendRef = &v1alpha2.AgentHarnessStatusRef{ - Backend: ah.Spec.Backend, - ID: res.Handle.ID, - } - if res.Endpoint != "" { - ah.Status.Connection = &v1alpha2.AgentHarnessConnection{Endpoint: res.Endpoint} - } - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeAccepted, metav1.ConditionTrue, - "AgentHarnessAccepted", "backend accepted sandbox request") - - st, reason, msg := backend.GetStatus(ctx, res.Handle) - pending := postReadyBootstrapPending(ah) - if st == metav1.ConditionTrue && pending { - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeActorReady, st, reason, msg) - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeBootstrapReady, metav1.ConditionFalse, - "BootstrapPending", - "waiting for post-ready bootstrap (OnAgentHarnessReady) to finish") - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeReady, metav1.ConditionFalse, - "BootstrapPending", - "gateway sandbox is ready; waiting for post-ready bootstrap (OnAgentHarnessReady) to finish") - } else { - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeActorReady, st, reason, msg) - if pending { - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeBootstrapReady, metav1.ConditionFalse, - "ActorNotReady", "waiting for actor before post-ready bootstrap") - } - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeReady, st, reason, msg) - } - ah.Status.ObservedGeneration = ah.Generation - - if err := patchAgentHarnessStatus(ctx, r.Client, ah); err != nil { - return ctrl.Result{}, err - } - - if st != metav1.ConditionTrue { - return ctrl.Result{RequeueAfter: agentHarnessNotReadyRequeue}, nil - } - if pending { - if err := maybePostReadyBootstrap(ctx, client.ObjectKeyFromObject(ah), ah, res.Handle, backend); err != nil { - log.Error(err, "post-ready sandbox bootstrap failed") - return ctrl.Result{}, err - } - var latest v1alpha2.AgentHarness - if err := r.Client.Get(ctx, req.NamespacedName, &latest); err != nil { - return ctrl.Result{}, fmt.Errorf("get AgentHarness after bootstrap: %w", err) - } - st2, reason2, msg2 := backend.GetStatus(ctx, res.Handle) - setAgentHarnessCondition(&latest, v1alpha2.AgentHarnessConditionTypeActorReady, st2, reason2, msg2) - setAgentHarnessCondition(&latest, v1alpha2.AgentHarnessConditionTypeBootstrapReady, metav1.ConditionTrue, - "BootstrapComplete", "post-ready bootstrap completed") - setAgentHarnessCondition(&latest, v1alpha2.AgentHarnessConditionTypeReady, st2, reason2, msg2) - latest.Status.ObservedGeneration = latest.Generation - if err := r.Client.Status().Update(ctx, &latest); err != nil { - return ctrl.Result{}, fmt.Errorf("update AgentHarness status after bootstrap: %w", err) - } - } - return ctrl.Result{}, nil -} - -func (r *OpenShellAgentHarnessController) reconcileDelete(ctx context.Context, ah *v1alpha2.AgentHarness) (ctrl.Result, error) { - if !controllerutil.ContainsFinalizer(ah, agentHarnessFinalizer) { - return ctrl.Result{}, nil - } - - if ah.Status.BackendRef != nil { - actorID := ah.Status.BackendRef.ID - if actorID != "" { - backend := r.backendFor(ah) - actorDone := true - var err error - if backend != nil { - actorDone, err = backend.DeleteAgentHarness(ctx, sandboxbackend.Handle{ID: actorID}) - } - if err != nil { - if r.Recorder != nil { - r.Recorder.Eventf(ah, nil, "Warning", "AgentHarnessDeleteFailed", "DeleteAgentHarness", "%s", err.Error()) - } - return ctrl.Result{RequeueAfter: agentHarnessNotReadyRequeue}, err - } - if !actorDone { - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeActorReady, - metav1.ConditionFalse, "ActorDeleting", fmt.Sprintf("waiting for backend actor %q deletion", actorID)) - if err := patchAgentHarnessStatus(ctx, r.Client, ah); err != nil { - return ctrl.Result{}, err - } - return ctrl.Result{RequeueAfter: agentHarnessNotReadyRequeue}, nil - } - } - ah.Status.BackendRef = nil - if err := patchAgentHarnessStatus(ctx, r.Client, ah); err != nil { - return ctrl.Result{}, err - } - } - - controllerutil.RemoveFinalizer(ah, agentHarnessFinalizer) - if err := r.Client.Update(ctx, ah); err != nil { - return ctrl.Result{}, fmt.Errorf("remove finalizer: %w", err) - } - return ctrl.Result{}, nil -} - -// SetupWithManager registers the OpenShell AgentHarness controller with the manager. -func (r *OpenShellAgentHarnessController) SetupWithManager(mgr ctrl.Manager) error { - b := ctrl.NewControllerManagedBy(mgr). - WithOptions(controller.Options{NeedLeaderElection: new(true)}). - For(&v1alpha2.AgentHarness{}, builder.WithPredicates(agentHarnessRuntimePredicate(v1alpha2.AgentHarnessRuntimeOpenshell))) - return b.Named("agentharness-openshell").Complete(r) -} diff --git a/go/core/internal/controller/agentharness_shared.go b/go/core/internal/controller/agentharness_shared.go index d0fd40cb22..6b34e78e6b 100644 --- a/go/core/internal/controller/agentharness_shared.go +++ b/go/core/internal/controller/agentharness_shared.go @@ -24,7 +24,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend" ) const ( @@ -53,23 +52,6 @@ func reconcileBackendUnavailable(ctx context.Context, kube client.Client, ah *v1 return ctrl.Result{}, nil } -func postReadyBootstrapPending(ah *v1alpha2.AgentHarness) bool { - cond := meta.FindStatusCondition(ah.Status.Conditions, v1alpha2.AgentHarnessConditionTypeBootstrapReady) - return cond == nil || cond.ObservedGeneration != ah.Generation || cond.Status != metav1.ConditionTrue -} - -func maybePostReadyBootstrap(ctx context.Context, key client.ObjectKey, ah *v1alpha2.AgentHarness, h sandboxbackend.Handle, async sandboxbackend.AsyncBackend) error { - if !postReadyBootstrapPending(ah) { - return nil - } - if err := async.OnAgentHarnessReady(ctx, ah, h); err != nil { - return err - } - ctrl.LoggerFrom(ctx).WithValues("agentHarness", key.String()).Info( - "recorded post-ready bootstrap for AgentHarness generation", "generation", ah.Generation) - return nil -} - func patchAgentHarnessStatus(ctx context.Context, kube client.Client, ah *v1alpha2.AgentHarness) error { var current v1alpha2.AgentHarness if err := kube.Get(ctx, client.ObjectKeyFromObject(ah), ¤t); err != nil { @@ -89,7 +71,7 @@ func patchAgentHarnessStatus(ctx context.Context, kube client.Client, ah *v1alph func effectiveAgentHarnessRuntime(ah *v1alpha2.AgentHarness) v1alpha2.AgentHarnessRuntime { if ah.Spec.Runtime == "" { - return v1alpha2.AgentHarnessRuntimeOpenshell + return v1alpha2.AgentHarnessRuntimeSubstrate } return ah.Spec.Runtime } diff --git a/go/core/internal/controller/agentharness_substrate_controller.go b/go/core/internal/controller/agentharness_substrate_controller.go index 3c56e2073c..a36e1a8ce3 100644 --- a/go/core/internal/controller/agentharness_substrate_controller.go +++ b/go/core/internal/controller/agentharness_substrate_controller.go @@ -15,7 +15,6 @@ import ( "fmt" "time" - "github.com/go-logr/logr" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/events" @@ -39,25 +38,30 @@ const ( // +kubebuilder:rbac:groups=ate.dev,resources=actortemplates,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=ate.dev,resources=actortemplates/status,verbs=get +// AgentHarnessSessionActorCleaner deletes the substrate actors spun from an +// AgentHarness's generated ActorTemplate. Implemented by +// *substrate.AgentHarnessSessionActorBackend. +type AgentHarnessSessionActorCleaner interface { + DeleteAllAgentHarnessActors(ctx context.Context, ah *v1alpha2.AgentHarness) (bool, error) +} + // SubstrateAgentHarnessController reconciles AgentHarness resources that use the // Substrate runtime. type SubstrateAgentHarnessController struct { - Client client.Client - Recorder events.EventRecorder - OpenClawBackend sandboxbackend.AsyncBackend - NemoClawBackend sandboxbackend.AsyncBackend + Client client.Client + Recorder events.EventRecorder + // Backends maps the harness backend type to its substrate AsyncBackend. + Backends map[v1alpha2.AgentHarnessBackendType]sandboxbackend.AsyncBackend SubstrateLifecycle substrate.AgentHarnessLifecycle + // SessionActorBackend manages the per-session actors spun from the + // harness's generated ActorTemplate. The controller uses it only to clean + // up session actors on delete; session actors are created on demand by the + // HTTP gateway when a chat connects. + SessionActorBackend AgentHarnessSessionActorCleaner } func (r *SubstrateAgentHarnessController) backendFor(ah *v1alpha2.AgentHarness) sandboxbackend.AsyncBackend { - switch ah.Spec.Backend { - case v1alpha2.AgentHarnessBackendOpenClaw: - return r.OpenClawBackend - case v1alpha2.AgentHarnessBackendNemoClaw: - return r.NemoClawBackend - default: - return nil - } + return r.Backends[ah.Spec.Backend] } func (r *SubstrateAgentHarnessController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { @@ -124,83 +128,21 @@ func (r *SubstrateAgentHarnessController) Reconcile(ctx context.Context, req ctr } return ctrl.Result{RequeueAfter: agentHarnessNotReadyRequeue}, nil } - if err := r.Client.Get(ctx, req.NamespacedName, &ah); err != nil { - return ctrl.Result{}, fmt.Errorf("reload AgentHarness after substrate lifecycle reconciliation: %w", err) - } - - return r.reconcileBackend(ctx, req, &ah, backend, log) -} - -func (r *SubstrateAgentHarnessController) reconcileBackend(ctx context.Context, req ctrl.Request, ah *v1alpha2.AgentHarness, backend sandboxbackend.AsyncBackend, log logr.Logger) (ctrl.Result, error) { - res, err := backend.EnsureAgentHarness(ctx, ah) - if err != nil { - log.Error(err, "EnsureAgentHarness failed") - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeAccepted, metav1.ConditionFalse, - "EnsureFailed", err.Error()) - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeReady, metav1.ConditionFalse, - "EnsureFailed", err.Error()) - if perr := patchAgentHarnessStatus(ctx, r.Client, ah); perr != nil { - return ctrl.Result{}, perr - } - return ctrl.Result{}, err - } - ah.Status.BackendRef = &v1alpha2.AgentHarnessStatusRef{ - Backend: ah.Spec.Backend, - ID: res.Handle.ID, - } - if res.Endpoint != "" { - ah.Status.Connection = &v1alpha2.AgentHarnessConnection{Endpoint: res.Endpoint} - } - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeAccepted, metav1.ConditionTrue, - "AgentHarnessAccepted", "backend accepted sandbox request") - - st, reason, msg := backend.GetStatus(ctx, res.Handle) - pending := postReadyBootstrapPending(ah) - if st == metav1.ConditionTrue && pending { - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeActorReady, st, reason, msg) - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeBootstrapReady, metav1.ConditionFalse, - "BootstrapPending", - "waiting for post-ready bootstrap (OnAgentHarnessReady) to finish") - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeReady, metav1.ConditionFalse, - "BootstrapPending", - "gateway sandbox is ready; waiting for post-ready bootstrap (OnAgentHarnessReady) to finish") - } else { - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeActorReady, st, reason, msg) - if pending { - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeBootstrapReady, metav1.ConditionFalse, - "ActorNotReady", "waiting for actor before post-ready bootstrap") - } - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeReady, st, reason, msg) - } + // The AgentHarness is a template: once its generated ActorTemplate golden + // snapshot is Ready, the harness is Ready. We do NOT create a persistent + // actor here. Each chat session gets its own actor, created on demand by the + // HTTP gateway (AgentHarnessSessionActorBackend) when a chat connects. + setAgentHarnessCondition(&ah, v1alpha2.AgentHarnessConditionTypeAccepted, metav1.ConditionTrue, + "AgentHarnessAccepted", "ActorTemplate golden snapshot is ready") + setAgentHarnessCondition(&ah, v1alpha2.AgentHarnessConditionTypeActorReady, metav1.ConditionTrue, + "TemplateReady", "session actors are created on demand per chat session") + setAgentHarnessCondition(&ah, v1alpha2.AgentHarnessConditionTypeReady, metav1.ConditionTrue, + "TemplateReady", "AgentHarness template is ready; chat sessions spawn their own actors") ah.Status.ObservedGeneration = ah.Generation - - if err := patchAgentHarnessStatus(ctx, r.Client, ah); err != nil { + if err := patchAgentHarnessStatus(ctx, r.Client, &ah); err != nil { return ctrl.Result{}, err } - - if st != metav1.ConditionTrue { - return ctrl.Result{RequeueAfter: agentHarnessNotReadyRequeue}, nil - } - if pending { - if err := maybePostReadyBootstrap(ctx, client.ObjectKeyFromObject(ah), ah, res.Handle, backend); err != nil { - log.Error(err, "post-ready sandbox bootstrap failed") - return ctrl.Result{}, err - } - var latest v1alpha2.AgentHarness - if err := r.Client.Get(ctx, req.NamespacedName, &latest); err != nil { - return ctrl.Result{}, fmt.Errorf("get AgentHarness after bootstrap: %w", err) - } - st2, reason2, msg2 := backend.GetStatus(ctx, res.Handle) - setAgentHarnessCondition(&latest, v1alpha2.AgentHarnessConditionTypeActorReady, st2, reason2, msg2) - setAgentHarnessCondition(&latest, v1alpha2.AgentHarnessConditionTypeBootstrapReady, metav1.ConditionTrue, - "BootstrapComplete", "post-ready bootstrap completed") - setAgentHarnessCondition(&latest, v1alpha2.AgentHarnessConditionTypeReady, st2, reason2, msg2) - latest.Status.ObservedGeneration = latest.Generation - if err := r.Client.Status().Update(ctx, &latest); err != nil { - return ctrl.Result{}, fmt.Errorf("update AgentHarness status after bootstrap: %w", err) - } - } return ctrl.Result{}, nil } @@ -218,30 +160,27 @@ func (r *SubstrateAgentHarnessController) reconcileDelete(ctx context.Context, a return ctrl.Result{}, fmt.Errorf("substrate cleanup timed out for AgentHarness %s", ah.Name) } - if ah.Status.BackendRef != nil { - actorID := ah.Status.BackendRef.ID - if actorID != "" { - backend := r.backendFor(ah) - actorDone := true - var err error - if backend != nil { - actorDone, err = backend.DeleteAgentHarness(ctx, sandboxbackend.Handle{ID: actorID}) + // Delete every actor belonging to this harness: the legacy single actor (if + // any harness still has one recorded) plus all per-session actors spun from + // the generated ActorTemplate. + if r.SessionActorBackend != nil { + actorsDone, err := r.SessionActorBackend.DeleteAllAgentHarnessActors(ctx, ah) + if err != nil { + if r.Recorder != nil { + r.Recorder.Eventf(ah, nil, "Warning", "AgentHarnessDeleteFailed", "DeleteAgentHarnessActors", "%s", err.Error()) } - if err != nil { - if r.Recorder != nil { - r.Recorder.Eventf(ah, nil, "Warning", "AgentHarnessDeleteFailed", "DeleteAgentHarness", "%s", err.Error()) - } - return ctrl.Result{RequeueAfter: agentHarnessNotReadyRequeue}, err - } - if !actorDone { - setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeActorReady, - metav1.ConditionFalse, "ActorDeleting", fmt.Sprintf("waiting for substrate actor %q deletion", actorID)) - if err := patchAgentHarnessStatus(ctx, r.Client, ah); err != nil { - return ctrl.Result{}, err - } - return ctrl.Result{RequeueAfter: agentHarnessNotReadyRequeue}, nil + return ctrl.Result{RequeueAfter: agentHarnessNotReadyRequeue}, err + } + if !actorsDone { + setAgentHarnessCondition(ah, v1alpha2.AgentHarnessConditionTypeActorReady, + metav1.ConditionFalse, "ActorDeleting", "waiting for substrate session actors deletion") + if err := patchAgentHarnessStatus(ctx, r.Client, ah); err != nil { + return ctrl.Result{}, err } + return ctrl.Result{RequeueAfter: agentHarnessNotReadyRequeue}, nil } + } + if ah.Status.BackendRef != nil { ah.Status.BackendRef = nil if err := patchAgentHarnessStatus(ctx, r.Client, ah); err != nil { return ctrl.Result{}, err diff --git a/go/core/internal/controller/reconciler/reconciler.go b/go/core/internal/controller/reconciler/reconciler.go index bcee94e4c3..fef95adb35 100644 --- a/go/core/internal/controller/reconciler/reconciler.go +++ b/go/core/internal/controller/reconciler/reconciler.go @@ -230,19 +230,8 @@ func (a *kagentReconciler) reconcileSandboxAgent(ctx context.Context, sa *v1alph return err } - platform := v1alpha2.AgentSandboxPlatform(sa) - switch platform { - case v1alpha2.SandboxPlatformSubstrate: - if err := sandboxbackend.ValidateSandboxPlatform(a.sandboxBackend, sa); err != nil { - return err - } - default: - if a.sandboxBackend == nil { - return fmt.Errorf("sandbox backend is not configured") - } - if err := sandboxbackend.EnsureAgentSandboxAPIsRegistered(ctx, a.kube); err != nil { - return err - } + if a.sandboxBackend == nil { + return fmt.Errorf("sandbox backend is not configured") } return a.reconcileTranslatedAgent(ctx, sa, "sandboxagent", func(manifest []client.Object) error { diff --git a/go/core/internal/controller/sandboxagent_controller.go b/go/core/internal/controller/sandboxagent_controller.go index 1a7c6c39e0..ecc0a53239 100644 --- a/go/core/internal/controller/sandboxagent_controller.go +++ b/go/core/internal/controller/sandboxagent_controller.go @@ -43,9 +43,8 @@ var ( sandboxAgentControllerLog = ctrl.Log.WithName("sandboxagent-controller") ) -// SandboxAgentController reconciles SandboxAgent objects for both agent-sandbox and -// Agent Substrate platforms. Platform-specific workload objects are selected by the -// sandbox routing backend; substrate delete cleanup is handled in this controller. +// SandboxAgentController reconciles SandboxAgent objects, which always run on the +// Agent Substrate platform. Substrate delete cleanup is handled in this controller. type SandboxAgentController struct { Client client.Client Scheme *runtime.Scheme @@ -80,7 +79,7 @@ func (r *SandboxAgentController) Reconcile(ctx context.Context, req ctrl.Request return ctrl.Result{}, fmt.Errorf("get SandboxAgent: %w", err) } - if sandboxAgentUsesSubstrate(&sa) && r.SubstrateLifecycle != nil { + if r.SubstrateLifecycle != nil { if res, err := r.reconcileSubstrateSandboxAgent(ctx, &sa); err != nil || !res.IsZero() { return res, err } diff --git a/go/core/internal/controller/sandboxagent_substrate.go b/go/core/internal/controller/sandboxagent_substrate.go index 8ed117071a..8003bf1378 100644 --- a/go/core/internal/controller/sandboxagent_substrate.go +++ b/go/core/internal/controller/sandboxagent_substrate.go @@ -16,10 +16,6 @@ import ( const sandboxAgentSubstrateFinalizer = "kagent.dev/sandbox-agent-substrate-cleanup" -func sandboxAgentUsesSubstrate(sa *v1alpha2.SandboxAgent) bool { - return sa != nil && v1alpha2.AgentSandboxPlatform(sa) == v1alpha2.SandboxPlatformSubstrate -} - func (r *SandboxAgentController) reconcileSubstrateSandboxAgent(ctx context.Context, sa *v1alpha2.SandboxAgent) (ctrl.Result, error) { if r.SubstrateLifecycle == nil { return ctrl.Result{}, fmt.Errorf("substrate sandbox backend is not configured") diff --git a/go/core/internal/controller/translator/agent/adk_api_translator_test.go b/go/core/internal/controller/translator/agent/adk_api_translator_test.go index 506ebb1cbf..8c4202f663 100644 --- a/go/core/internal/controller/translator/agent/adk_api_translator_test.go +++ b/go/core/internal/controller/translator/agent/adk_api_translator_test.go @@ -11,7 +11,6 @@ import ( "github.com/kagent-dev/kagent/go/api/adk" "github.com/kagent-dev/kagent/go/api/v1alpha2" translator "github.com/kagent-dev/kagent/go/core/internal/controller/translator/agent" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/agentsxk8s" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -19,8 +18,6 @@ import ( schemev1 "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" - - agentsandboxv1 "sigs.k8s.io/agent-sandbox/api/v1alpha1" ) // Test_AdkApiTranslator_CrossNamespaceAgentTool tests that the translator can @@ -1348,115 +1345,3 @@ func Test_AdkApiTranslator_ContextConfig(t *testing.T) { }) } } - -func Test_AdkApiTranslator_SandboxAgent_defaultEmitsSandbox(t *testing.T) { - ctx := context.Background() - scheme := schemev1.Scheme - require.NoError(t, v1alpha2.AddToScheme(scheme)) - require.NoError(t, agentsandboxv1.AddToScheme(scheme)) - - ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "sandbox-ns"}} - modelConfig := &v1alpha2.ModelConfig{ - ObjectMeta: metav1.ObjectMeta{Name: "m1", Namespace: "sandbox-ns"}, - Spec: v1alpha2.ModelConfigSpec{ - Model: "gpt-4", - Provider: v1alpha2.ModelProviderOpenAI, - }, - } - sa := &v1alpha2.SandboxAgent{ - ObjectMeta: metav1.ObjectMeta{Name: "ag1", Namespace: "sandbox-ns"}, - Spec: v1alpha2.SandboxAgentSpec{ - AgentSpec: v1alpha2.AgentSpec{ - Type: v1alpha2.AgentType_Declarative, - Declarative: &v1alpha2.DeclarativeAgentSpec{ - SystemMessage: "You are a sandboxed agent", - ModelConfig: "m1", - }, - }, - }, - } - kubeClient := fake.NewClientBuilder(). - WithScheme(scheme). - WithObjects(ns, modelConfig). - Build() - - trans := translator.NewAdkApiTranslator( - kubeClient, - types.NamespacedName{Namespace: "sandbox-ns", Name: "m1"}, - nil, - "", - agentsxk8s.New(), - ) - outputs, err := translator.TranslateAgent(ctx, trans, sa) - require.NoError(t, err) - require.NotNil(t, outputs) - - var sawSandbox, sawDeploy, sawService bool - for _, o := range outputs.Manifest { - switch o.(type) { - case *agentsandboxv1.Sandbox: - sawSandbox = true - case *appsv1.Deployment: - sawDeploy = true - case *corev1.Service: - sawService = true - } - } - require.True(t, sawSandbox, "sandbox runtime should emit a Sandbox CR") - require.False(t, sawDeploy, "manifest should not include Deployment when runInSandbox is true") - require.False(t, sawService, "sandbox runtime must not include Service; agent-sandbox owns it") -} - -func Test_AdkApiTranslator_SandboxAgent_BYOEmitsSandbox(t *testing.T) { - ctx := context.Background() - scheme := schemev1.Scheme - require.NoError(t, v1alpha2.AddToScheme(scheme)) - require.NoError(t, agentsandboxv1.AddToScheme(scheme)) - - ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "sandbox-ns"}} - cmd := "/app/run" - sa := &v1alpha2.SandboxAgent{ - ObjectMeta: metav1.ObjectMeta{Name: "byo-sb", Namespace: "sandbox-ns"}, - Spec: v1alpha2.SandboxAgentSpec{ - AgentSpec: v1alpha2.AgentSpec{ - Type: v1alpha2.AgentType_BYO, - BYO: &v1alpha2.BYOAgentSpec{ - Deployment: &v1alpha2.ByoDeploymentSpec{ - Image: "example.com/agent:1", - Cmd: &cmd, - }, - }, - }, - }, - } - kubeClient := fake.NewClientBuilder(). - WithScheme(scheme). - WithObjects(ns). - Build() - - trans := translator.NewAdkApiTranslator( - kubeClient, - types.NamespacedName{Namespace: "sandbox-ns", Name: "default"}, - nil, - "", - agentsxk8s.New(), - ) - outputs, err := translator.TranslateAgent(ctx, trans, sa) - require.NoError(t, err) - require.NotNil(t, outputs) - - var sawSandbox, sawDeploy, sawService bool - for _, o := range outputs.Manifest { - switch o.(type) { - case *agentsandboxv1.Sandbox: - sawSandbox = true - case *appsv1.Deployment: - sawDeploy = true - case *corev1.Service: - sawService = true - } - } - require.True(t, sawSandbox) - require.False(t, sawDeploy) - require.False(t, sawService, "sandbox runtime must not include Service; agent-sandbox owns it") -} diff --git a/go/core/internal/controller/translator/agent/compiler.go b/go/core/internal/controller/translator/agent/compiler.go index 2e51e426b4..0d4a1b25bd 100644 --- a/go/core/internal/controller/translator/agent/compiler.go +++ b/go/core/internal/controller/translator/agent/compiler.go @@ -150,8 +150,8 @@ func (a *adkApiTranslator) CompileAgent( if runInSandbox && a.sandboxBackend == nil { return nil, fmt.Errorf("sandbox backend is not configured") } - if runInSandbox && v1alpha2.AgentSandboxPlatform(agent) == v1alpha2.SandboxPlatformSubstrate { - if err := v1alpha2.ValidateSubstrateSandboxAgentSpec(agent.(*v1alpha2.SandboxAgent)); err != nil { + if sa, ok := agent.(*v1alpha2.SandboxAgent); ok { + if err := v1alpha2.ValidateSubstrateSandboxAgentSpec(sa); err != nil { return nil, NewValidationError("%s", err.Error()) } } diff --git a/go/core/internal/controller/translator/mutate.go b/go/core/internal/controller/translator/mutate.go index 485975919d..252bf0cc6d 100644 --- a/go/core/internal/controller/translator/mutate.go +++ b/go/core/internal/controller/translator/mutate.go @@ -7,7 +7,6 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" - agentsandboxv1 "sigs.k8s.io/agent-sandbox/api/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) @@ -60,11 +59,6 @@ func MutateFuncFor(existing, desired client.Object) controllerutil.MutateFn { wantDpl := desired.(*appsv1.Deployment) return mutateDeployment(dpl, wantDpl) - case *agentsandboxv1.Sandbox: - sb := existing.(*agentsandboxv1.Sandbox) - want := desired.(*agentsandboxv1.Sandbox) - mutateSandbox(sb, want) - default: return mergeWithOverride(existing, desired) } @@ -126,10 +120,3 @@ func mutatePodTemplate(existing, desired *corev1.PodTemplateSpec) error { return nil } - -// mutateSandbox replaces the spec wholesale. The default mergo path does not reliably replace -// slice fields (containers, volumes, env), so SandboxAgent updates would not roll pods until we -// assign spec explicitly (same idea as mutatePodTemplate / Deployment). -func mutateSandbox(existing, desired *agentsandboxv1.Sandbox) { - existing.Spec = desired.Spec -} diff --git a/go/core/internal/controller/translator/mutate_sandbox_test.go b/go/core/internal/controller/translator/mutate_sandbox_test.go deleted file mode 100644 index 275ec084d7..0000000000 --- a/go/core/internal/controller/translator/mutate_sandbox_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package translator - -import ( - "testing" - - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - agentsandboxv1 "sigs.k8s.io/agent-sandbox/api/v1alpha1" -) - -func TestMutateFuncFor_Sandbox_replacesPodTemplateSlices(t *testing.T) { - oldImg := "registry.example/old:v1" - newImg := "registry.example/new:v2" - - existing := &agentsandboxv1.Sandbox{ - ObjectMeta: metav1.ObjectMeta{Name: "a", Namespace: "ns"}, - Spec: agentsandboxv1.SandboxSpec{ - PodTemplate: agentsandboxv1.PodTemplate{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{{Name: "agent", Image: oldImg}}, - }, - }, - }, - } - desired := existing.DeepCopy() - desired.Spec.PodTemplate.Spec.Containers[0].Image = newImg - - f := MutateFuncFor(existing, desired) - require.NoError(t, f()) - require.Equal(t, newImg, existing.Spec.PodTemplate.Spec.Containers[0].Image) -} diff --git a/go/core/internal/httpserver/handlers/agentharness_gateway.go b/go/core/internal/httpserver/handlers/agentharness_gateway.go index 2762605c37..015a5c543d 100644 --- a/go/core/internal/httpserver/handlers/agentharness_gateway.go +++ b/go/core/internal/httpserver/handlers/agentharness_gateway.go @@ -17,20 +17,17 @@ import ( ctrllog "sigs.k8s.io/controller-runtime/pkg/log" ) -const ( - // OpenClaw 2026.3.28+ returns 403 without operator scopes on HTTP/WS when only Bearer token is sent. - openclawDefaultOperatorScopes = "operator.admin" - // Origin OpenClaw accepts by default for bind=lan port=80 (localhost/127.0.0.1 on gateway port). - openclawLoopbackOrigin = "http://127.0.0.1:80" -) - -// AgentHarnessGatewayConfig configures Substrate harness HTTP/WebSocket proxy. -// Traffic is proxied through atenet-router (Envoy) using actor Host-based routing. +// AgentHarnessGatewayConfig configures the Substrate harness /acp WebSocket +// proxy. Traffic is proxied through atenet-router (Envoy) using actor +// Host-based routing. type AgentHarnessGatewayConfig struct { AtenetRouterURL string } -// HandleAgentHarnessGateway proxies browser traffic to the actor OpenClaw gateway via atenet-router. +// HandleAgentHarnessGateway proxies the browser /acp WebSocket to the actor's +// acp-shim via atenet-router. It is the only externally reachable surface for +// a Substrate harness actor: every backend (including OpenClaw) is reached +// purely over ACP, never the in-sandbox gateway directly. func (h *Handlers) HandleAgentHarnessGateway(w ErrorResponseWriter, r *http.Request) { log := ctrllog.FromContext(r.Context()).WithName("agentharness-gateway") if h.AgentHarnessGateway == nil { @@ -59,14 +56,28 @@ func (h *Handlers) HandleAgentHarnessGateway(w ErrorResponseWriter, r *http.Requ runtime := ah.Spec.Runtime if runtime == "" { - runtime = v1alpha2.AgentHarnessRuntimeOpenshell + runtime = v1alpha2.AgentHarnessRuntimeSubstrate } if runtime != v1alpha2.AgentHarnessRuntimeSubstrate { http.Error(w, "gateway proxy is only available for runtime=substrate", http.StatusBadRequest) return } - if ah.Status.BackendRef == nil || ah.Status.BackendRef.ID == "" { - http.Error(w, "harness has no substrate actor yet", http.StatusServiceUnavailable) + if h.AgentHarnessSessionActor == nil { + http.Error(w, "substrate session actor backend is not configured", http.StatusServiceUnavailable) + return + } + + // The only externally reachable path is the per-session /acp WebSocket; the + // actor's in-sandbox gateway is never exposed. Each chat session maps to its + // own substrate actor, selected by the {sessionId} path segment. + acpPrefix := agentHarnessHarnessBase(namespace, name) + "/acp/" + if !strings.HasPrefix(r.URL.Path, acpPrefix) { + http.NotFound(w, r) + return + } + sessionID := strings.TrimPrefix(r.URL.Path, acpPrefix) + if sessionID == "" || strings.Contains(sessionID, "/") { + http.Error(w, "a single session id path segment is required", http.StatusBadRequest) return } @@ -77,41 +88,39 @@ func (h *Handlers) HandleAgentHarnessGateway(w ErrorResponseWriter, r *http.Requ return } - target, upstreamHost, err := h.resolveSubstrateGatewayTarget(r.Context(), &ah) + // Provision (create + resume) the per-session actor on demand, then route to + // it. If the chat was already provisioned via the ensure endpoint this is a + // fast no-op that just resumes a suspended actor. + ensureRes, err := h.AgentHarnessSessionActor.EnsureSessionActor(r.Context(), &ah, sessionID) if err != nil { - log.Info("resolve substrate gateway target failed", "error", err) + log.Info("ensure session actor failed", "session", sessionID, "error", err) http.Error(w, err.Error(), http.StatusServiceUnavailable) return } - publicPrefix := agentHarnessGatewayPublicPrefix(namespace, name) - - _, redirectTo, ok := resolveGatewayUpstreamPath(r.URL.Path, namespace, name, isWebSocketUpgrade(r)) - if !ok { - http.NotFound(w, r) - return - } - // Browsers do not complete WebSocket handshakes through 30x redirects. - if redirectTo != "" && !isWebSocketUpgrade(r) { - dest := redirectTo - if r.URL.RawQuery != "" { - dest += "?" + r.URL.RawQuery - } - http.Redirect(w, r, dest, http.StatusPermanentRedirect) + target, upstreamHost, err := h.resolveSubstrateGatewayTarget(r.Context(), ensureRes.Handle.ID) + if err != nil { + log.Info("resolve substrate gateway target failed", "error", err) + http.Error(w, err.Error(), http.StatusServiceUnavailable) return } - proxy := newAgentHarnessGatewayProxy(target, upstreamHost, token, publicPrefix, namespace, name, log) + proxy := newAgentHarnessACPProxy(target, upstreamHost, token, log) proxy.ServeHTTP(w, r) + + // The session actor is intentionally left running after the WebSocket + // disconnects so multiple chats can stay live concurrently (e.g. when the + // user switches between chats). Actors are only suspended when the user + // explicitly suspends a session via the suspend endpoint. } -func (h *Handlers) resolveSubstrateGatewayTarget(ctx context.Context, ah *v1alpha2.AgentHarness) (*url.URL, string, error) { +func (h *Handlers) resolveSubstrateGatewayTarget(ctx context.Context, actorID string) (*url.URL, string, error) { cfg := h.AgentHarnessGateway if cfg == nil { return nil, "", fmt.Errorf("substrate gateway is not configured") } - actorID := strings.TrimSpace(ah.Status.BackendRef.ID) + actorID = strings.TrimSpace(actorID) target, host, err := substrate.GatewayRouterTarget(cfg.AtenetRouterURL, actorID) if err != nil { return nil, "", fmt.Errorf("substrate actor %q: %w", actorID, err) @@ -129,61 +138,7 @@ func agentHarnessHarnessBase(namespace, name string) string { return "/api/agentharnesses/" + namespace + "/" + name } -func agentHarnessGatewayPublicPrefix(namespace, name string) string { - return agentHarnessHarnessBase(namespace, name) + "/gateway/" -} - -// resolveGatewayUpstreamPath maps the public URL to the upstream path on the actor. -// redirectTo is set when the browser should use a trailing slash under /gateway/. -// OpenClaw is configured with the same controlUi.basePath, so the proxy preserves -// the public gateway base path when forwarding to the actor. -func resolveGatewayUpstreamPath(requestPath, namespace, name string, wsUpgrade bool) (upstreamPath, redirectTo string, ok bool) { - base := agentHarnessHarnessBase(namespace, name) - if !strings.HasPrefix(requestPath, base) { - return "", "", false - } - rel := strings.TrimPrefix(requestPath, base) - if rel == "" { - return "", agentHarnessGatewayPublicPrefix(namespace, name), true - } - - switch { - case rel == "/gateway": - upstream := agentHarnessGatewayPublicPrefix(namespace, name) - if wsUpgrade { - return upstream, "", true - } - return upstream, upstream, true - case strings.HasPrefix(rel, "/gateway/"): - return requestPath, "", true - default: - return "", "", false - } -} - -// normalizeOpenClawBrowserOrigin rewrites Origin/Referer so OpenClaw accepts WS/API from kagent-ui -// (e.g. http://localhost:8001) while the gateway listens on the actor pod :80. -func normalizeOpenClawBrowserOrigin(req *http.Request) { - if req == nil { - return - } - if req.Header.Get("Origin") != "" { - req.Header.Set("Origin", openclawLoopbackOrigin) - } - if req.Header.Get("Referer") != "" { - req.Header.Set("Referer", openclawLoopbackOrigin+"/") - } -} - -func isWebSocketUpgrade(r *http.Request) bool { - if r == nil { - return false - } - return strings.EqualFold(r.Header.Get("Upgrade"), "websocket") && - strings.Contains(strings.ToLower(r.Header.Get("Connection")), "upgrade") -} - -func newAgentHarnessGatewayProxy(target *url.URL, upstreamHost, token, publicPrefix, namespace, name string, log interface { +func newAgentHarnessACPProxy(target *url.URL, upstreamHost, token string, log interface { Error(error, string, ...any) }) *httputil.ReverseProxy { proxy := &httputil.ReverseProxy{ @@ -196,40 +151,19 @@ func newAgentHarnessGatewayProxy(target *url.URL, upstreamHost, token, publicPre Rewrite: func(pr *httputil.ProxyRequest) { pr.SetURL(target) pr.Out.Host = upstreamHost + // The acp-shim authenticates with a bearer token (the harness + // gateway token); the browser WebSocket cannot set headers, so the + // proxy injects it server-side. if token != "" { pr.Out.Header.Set("Authorization", "Bearer "+token) } - pr.Out.Header.Set("x-openclaw-scopes", openclawDefaultOperatorScopes) - normalizeOpenClawBrowserOrigin(pr.Out) - subPath, _, pathOK := resolveGatewayUpstreamPath(pr.In.URL.Path, namespace, name, isWebSocketUpgrade(pr.In)) - if !pathOK { - subPath = "/" - } - if subPath == "" { - subPath = "/" - } else if !strings.HasPrefix(subPath, "/") { - subPath = "/" + subPath - } - pr.Out.URL.Path = subPath - pr.Out.URL.RawPath = subPath + pr.Out.URL.Path = "/acp" + pr.Out.URL.RawPath = "/acp" }, } - proxy.ModifyResponse = func(resp *http.Response) error { - if resp.StatusCode == http.StatusSwitchingProtocols { - return nil - } - - if loc := resp.Header.Get("Location"); loc != "" { - publicBase := strings.TrimSuffix(publicPrefix, "/") - if strings.HasPrefix(loc, "/") && !strings.HasPrefix(loc, publicBase) { - resp.Header.Set("Location", publicBase+loc) - } - } - return nil - } proxy.ErrorHandler = func(rw http.ResponseWriter, req *http.Request, proxyErr error) { - log.Error(proxyErr, "gateway proxy error", "host", upstreamHost) - http.Error(rw, "gateway proxy error", http.StatusBadGateway) + log.Error(proxyErr, "acp proxy error", "host", upstreamHost) + http.Error(rw, "acp proxy error", http.StatusBadGateway) } return proxy } diff --git a/go/core/internal/httpserver/handlers/agentharness_gateway_path_test.go b/go/core/internal/httpserver/handlers/agentharness_gateway_path_test.go deleted file mode 100644 index 433bcd5205..0000000000 --- a/go/core/internal/httpserver/handlers/agentharness_gateway_path_test.go +++ /dev/null @@ -1,89 +0,0 @@ -package handlers - -import ( - "net/http" - "testing" -) - -func TestResolveGatewayUpstreamPath(t *testing.T) { - t.Parallel() - ns, name := "kagent", "my-claw" - public := agentHarnessGatewayPublicPrefix(ns, name) - - tests := []struct { - name string - path string - wsUpgrade bool - wantUp string - wantRedir string - wantOK bool - }{ - { - name: "harness root redirects", - path: "/api/agentharnesses/kagent/my-claw", - wantRedir: public, - wantOK: true, - }, - { - name: "gateway without slash redirects", - path: "/api/agentharnesses/kagent/my-claw/gateway", - wantUp: public, - wantRedir: public, - wantOK: true, - }, - { - name: "gateway without slash websocket", - path: "/api/agentharnesses/kagent/my-claw/gateway", - wsUpgrade: true, - wantUp: public, - wantOK: true, - }, - { - name: "gateway index", - path: "/api/agentharnesses/kagent/my-claw/gateway/", - wantUp: public, - wantOK: true, - }, - { - name: "gateway asset", - path: "/api/agentharnesses/kagent/my-claw/gateway/assets/foo.js", - wantUp: "/api/agentharnesses/kagent/my-claw/gateway/assets/foo.js", - wantOK: true, - }, - { - name: "unknown path", - path: "/api/agentharnesses/kagent/my-claw/api/v1/foo", - wantOK: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - up, redir, ok := resolveGatewayUpstreamPath(tt.path, ns, name, tt.wsUpgrade) - if ok != tt.wantOK { - t.Fatalf("ok = %v, want %v", ok, tt.wantOK) - } - if up != tt.wantUp { - t.Fatalf("upstream = %q, want %q", up, tt.wantUp) - } - if redir != tt.wantRedir { - t.Fatalf("redirect = %q, want %q", redir, tt.wantRedir) - } - }) - } -} - -func TestIsWebSocketUpgrade(t *testing.T) { - t.Parallel() - req, _ := http.NewRequest(http.MethodGet, "http://example.com/api/x/gateway", nil) - req.Header.Set("Connection", "Upgrade") - req.Header.Set("Upgrade", "websocket") - if !isWebSocketUpgrade(req) { - t.Fatal("expected websocket upgrade") - } - req2, _ := http.NewRequest(http.MethodGet, "http://example.com/", nil) - if isWebSocketUpgrade(req2) { - t.Fatal("expected not websocket upgrade") - } -} diff --git a/go/core/internal/httpserver/handlers/agentharness_gateway_test.go b/go/core/internal/httpserver/handlers/agentharness_gateway_test.go index 943aff65ce..b2fad1a1b4 100644 --- a/go/core/internal/httpserver/handlers/agentharness_gateway_test.go +++ b/go/core/internal/httpserver/handlers/agentharness_gateway_test.go @@ -12,21 +12,18 @@ import ( "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/substrate" ) -func TestGatewayProxyForwardsToAtenetRouterWithActorHost(t *testing.T) { +func TestACPProxyForwardsToAtenetRouterWithActorHost(t *testing.T) { t.Parallel() const actorHost = "ahr-kagent-my-claw.actors.resources.substrate.ate.dev" const token = "some-token" - ns, name := "kagent", "my-claw" - publicPrefix := agentHarnessGatewayPublicPrefix(ns, name) - var gotHost, gotAuth, gotScopes, gotPath string + var gotHost, gotAuth, gotPath string upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gotHost = r.Host gotAuth = r.Header.Get("Authorization") - gotScopes = r.Header.Get("x-openclaw-scopes") gotPath = r.URL.Path - w.Header().Set("Content-Type", "text/html") - _, _ = w.Write([]byte("ok")) + w.Header().Set("Content-Type", "text/plain") + _, _ = w.Write([]byte("ok")) })) defer upstream.Close() @@ -35,8 +32,8 @@ func TestGatewayProxyForwardsToAtenetRouterWithActorHost(t *testing.T) { t.Fatal(err) } - proxy := newAgentHarnessGatewayProxy(target, actorHost, token, publicPrefix, ns, name, testLog{t}) - req := httptest.NewRequest(http.MethodGet, publicPrefix, nil) + proxy := newAgentHarnessACPProxy(target, actorHost, token, testLog{t}) + req := httptest.NewRequest(http.MethodGet, "/api/agentharnesses/kagent/my-claw/acp", nil) rec := httptest.NewRecorder() proxy.ServeHTTP(rec, req) @@ -49,11 +46,8 @@ func TestGatewayProxyForwardsToAtenetRouterWithActorHost(t *testing.T) { if gotAuth != "Bearer "+token { t.Fatalf("Authorization = %q", gotAuth) } - if gotScopes != openclawDefaultOperatorScopes { - t.Fatalf("x-openclaw-scopes = %q", gotScopes) - } - if gotPath != publicPrefix { - t.Fatalf("upstream path = %q, want %q", gotPath, publicPrefix) + if gotPath != "/acp" { + t.Fatalf("upstream path = %q, want /acp", gotPath) } body, _ := io.ReadAll(rec.Body) if !strings.Contains(string(body), "ok") { @@ -61,11 +55,9 @@ func TestGatewayProxyForwardsToAtenetRouterWithActorHost(t *testing.T) { } } -func TestGatewayProxyRewriteTargetsAtenetRouterHostOnWebSocketPath(t *testing.T) { +func TestACPProxyRewriteTargetsAtenetRouterHost(t *testing.T) { t.Parallel() const actorHost = "ahr-kagent-my-claw.actors.resources.substrate.ate.dev" - ns, name := "kagent", "my-claw" - publicPrefix := agentHarnessGatewayPublicPrefix(ns, name) target, host, err := substrate.GatewayRouterTarget(substrate.DefaultAtenetRouterURL, "ahr-kagent-my-claw") if err != nil { @@ -74,12 +66,10 @@ func TestGatewayProxyRewriteTargetsAtenetRouterHostOnWebSocketPath(t *testing.T) if host != actorHost { t.Fatalf("host = %q, want %q", host, actorHost) } - proxy := newAgentHarnessGatewayProxy(target, host, "tok", publicPrefix, ns, name, testLog{t}) - req := httptest.NewRequest(http.MethodGet, strings.TrimSuffix(publicPrefix, "/"), nil) + proxy := newAgentHarnessACPProxy(target, host, "tok", testLog{t}) + req := httptest.NewRequest(http.MethodGet, "/api/agentharnesses/kagent/my-claw/acp", nil) req.Header.Set("Connection", "Upgrade") req.Header.Set("Upgrade", "websocket") - req.Header.Set("Origin", "http://localhost:8001") - req.Header.Set("Referer", "http://localhost:8001/api/agentharnesses/kagent/my-claw/gateway/") outReq := req.Clone(req.Context()) proxy.Rewrite(&httputil.ProxyRequest{In: req, Out: outReq}) @@ -90,20 +80,14 @@ func TestGatewayProxyRewriteTargetsAtenetRouterHostOnWebSocketPath(t *testing.T) if outReq.URL.Host != target.Host { t.Fatalf("URL.Host = %q, want router %q", outReq.URL.Host, target.Host) } - if outReq.URL.Path != publicPrefix { - t.Fatalf("URL.Path = %q, want %q", outReq.URL.Path, publicPrefix) + if outReq.URL.Path != "/acp" { + t.Fatalf("URL.Path = %q, want /acp", outReq.URL.Path) } if outReq.Header.Get("Authorization") != "Bearer tok" { t.Fatalf("missing Authorization") } - if outReq.Header.Get("x-openclaw-scopes") != openclawDefaultOperatorScopes { - t.Fatalf("missing scopes header") - } - if outReq.Header.Get("Origin") != openclawLoopbackOrigin { - t.Fatalf("Origin = %q, want %q", outReq.Header.Get("Origin"), openclawLoopbackOrigin) - } - if outReq.Header.Get("Referer") != openclawLoopbackOrigin+"/" { - t.Fatalf("Referer = %q", outReq.Header.Get("Referer")) + if outReq.Header.Get("x-openclaw-scopes") != "" { + t.Fatalf("scopes header should not be set: %q", outReq.Header.Get("x-openclaw-scopes")) } } diff --git a/go/core/internal/httpserver/handlers/agentharness_session.go b/go/core/internal/httpserver/handlers/agentharness_session.go new file mode 100644 index 0000000000..00cf59e9c9 --- /dev/null +++ b/go/core/internal/httpserver/handlers/agentharness_session.go @@ -0,0 +1,146 @@ +package handlers + +import ( + "net/http" + "strings" + + "github.com/gorilla/mux" + api "github.com/kagent-dev/kagent/go/api/httpapi" + "github.com/kagent-dev/kagent/go/api/v1alpha2" + "github.com/kagent-dev/kagent/go/core/internal/httpserver/errors" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + ctrllog "sigs.k8s.io/controller-runtime/pkg/log" +) + +// AgentHarnessSessionActorResponse is returned when a per-session actor is +// provisioned or suspended. +type AgentHarnessSessionActorResponse struct { + Namespace string `json:"namespace"` + Name string `json:"name"` + SessionID string `json:"sessionId"` + ActorID string `json:"actorId,omitempty"` + // State is the actor lifecycle state ("running", "suspended", "missing"). + State string `json:"state,omitempty"` +} + +// HandleEnsureAgentHarnessSessionActor provisions (creates + resumes) the +// substrate actor for a single AgentHarness chat session. The UI calls this when +// a new chat is started so the actor is warm before the /acp WebSocket connects; +// provisioning could alternatively be deferred to the first message, in which +// case the gateway lazily creates the actor on connect. +func (h *Handlers) HandleEnsureAgentHarnessSessionActor(w ErrorResponseWriter, r *http.Request) { + log := ctrllog.FromContext(r.Context()).WithName("agentharness-session-actor").WithValues("operation", "ensure") + + ah, sessionID, apiErr := h.loadAgentHarnessSession(r) + if apiErr != nil { + w.RespondWithError(apiErr) + return + } + + res, err := h.AgentHarnessSessionActor.EnsureSessionActor(r.Context(), ah, sessionID) + if err != nil { + log.Error(err, "ensure session actor", "session", sessionID) + w.RespondWithError(errors.NewInternalServerError("Failed to provision session actor", err)) + return + } + + data := api.NewResponse(AgentHarnessSessionActorResponse{ + Namespace: ah.Namespace, + Name: ah.Name, + SessionID: sessionID, + ActorID: res.Handle.ID, + }, "Session actor ready", false) + RespondWithJSON(w, http.StatusOK, data) +} + +// HandleSuspendAgentHarnessSessionActor checkpoints and frees the substrate +// actor for a single AgentHarness chat session. The actor is resumed +// automatically on the next /acp connection. +func (h *Handlers) HandleSuspendAgentHarnessSessionActor(w ErrorResponseWriter, r *http.Request) { + log := ctrllog.FromContext(r.Context()).WithName("agentharness-session-actor").WithValues("operation", "suspend") + + ah, sessionID, apiErr := h.loadAgentHarnessSession(r) + if apiErr != nil { + w.RespondWithError(apiErr) + return + } + + if err := h.AgentHarnessSessionActor.SuspendSessionActor(r.Context(), ah, sessionID); err != nil { + log.Error(err, "suspend session actor", "session", sessionID) + w.RespondWithError(errors.NewInternalServerError("Failed to suspend session actor", err)) + return + } + + data := api.NewResponse(AgentHarnessSessionActorResponse{ + Namespace: ah.Namespace, + Name: ah.Name, + SessionID: sessionID, + }, "Session actor suspended", false) + RespondWithJSON(w, http.StatusOK, data) +} + +// HandleGetAgentHarnessSessionActor reports the lifecycle state of a single +// AgentHarness chat session actor ("running", "suspended", or "missing"). The UI +// uses it to render the per-session status indicator in the sidebar. +func (h *Handlers) HandleGetAgentHarnessSessionActor(w ErrorResponseWriter, r *http.Request) { + log := ctrllog.FromContext(r.Context()).WithName("agentharness-session-actor").WithValues("operation", "status") + + ah, sessionID, apiErr := h.loadAgentHarnessSession(r) + if apiErr != nil { + w.RespondWithError(apiErr) + return + } + + state, err := h.AgentHarnessSessionActor.GetSessionActorState(r.Context(), ah, sessionID) + if err != nil { + log.Error(err, "get session actor state", "session", sessionID) + w.RespondWithError(errors.NewInternalServerError("Failed to read session actor state", err)) + return + } + + data := api.NewResponse(AgentHarnessSessionActorResponse{ + Namespace: ah.Namespace, + Name: ah.Name, + SessionID: sessionID, + State: string(state), + }, "Session actor state", false) + RespondWithJSON(w, http.StatusOK, data) +} + +// loadAgentHarnessSession validates the request, loads the AgentHarness and +// returns it together with the session id. +func (h *Handlers) loadAgentHarnessSession(r *http.Request) (*v1alpha2.AgentHarness, string, *errors.APIError) { + if h.AgentHarnessSessionActor == nil { + return nil, "", errors.NewNotImplementedError("substrate session actor backend is not configured", nil) + } + + vars := mux.Vars(r) + namespace := strings.TrimSpace(vars["namespace"]) + name := strings.TrimSpace(vars["name"]) + sessionID := strings.TrimSpace(vars["session_id"]) + if namespace == "" || name == "" { + return nil, "", errors.NewBadRequestError("namespace and name are required", nil) + } + if sessionID == "" { + return nil, "", errors.NewBadRequestError("session id is required", nil) + } + + var ah v1alpha2.AgentHarness + if err := h.KubeClient.Get(r.Context(), types.NamespacedName{Namespace: namespace, Name: name}, &ah); err != nil { + if apierrors.IsNotFound(err) { + return nil, "", errors.NewNotFoundError("AgentHarness not found", err) + } + return nil, "", errors.NewInternalServerError("Failed to load AgentHarness", err) + } + + runtime := ah.Spec.Runtime + if runtime == "" { + runtime = v1alpha2.AgentHarnessRuntimeSubstrate + } + if runtime != v1alpha2.AgentHarnessRuntimeSubstrate { + return nil, "", errors.NewBadRequestError("session actors are only available for runtime=substrate", nil) + } + + return &ah, sessionID, nil +} diff --git a/go/core/internal/httpserver/handlers/agents.go b/go/core/internal/httpserver/handlers/agents.go index 37ff185597..1dd3caa26d 100644 --- a/go/core/internal/httpserver/handlers/agents.go +++ b/go/core/internal/httpserver/handlers/agents.go @@ -14,7 +14,6 @@ import ( "github.com/kagent-dev/kagent/go/core/internal/httpserver/errors" "github.com/kagent-dev/kagent/go/core/internal/utils" "github.com/kagent-dev/kagent/go/core/pkg/auth" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -130,7 +129,7 @@ func (h *AgentsHandler) listAgentResponses(ctx context.Context, log logr.Logger, if !v1alpha2.IsKnownAgentHarnessBackend(sb.Spec.Backend) { continue } - result = append(result, h.openshellAgentHarnessAgentResponse(ctx, log, sb)) + result = append(result, h.agentHarnessAgentResponse(ctx, log, sb)) } return result, nil @@ -151,7 +150,7 @@ func (h *AgentsHandler) appendAgentResponses( } } -func (h *AgentsHandler) openshellAgentHarnessAgentResponse(ctx context.Context, log logr.Logger, sb *v1alpha2.AgentHarness) api.AgentResponse { +func (h *AgentsHandler) agentHarnessAgentResponse(ctx context.Context, log logr.Logger, sb *v1alpha2.AgentHarness) api.AgentResponse { ref := utils.GetObjectRef(sb) id := utils.ConvertToPythonIdentifier(ref) @@ -168,10 +167,9 @@ func (h *AgentsHandler) openshellAgentHarnessAgentResponse(ctx context.Context, runtime := sb.Spec.Runtime if runtime == "" { - runtime = v1alpha2.AgentHarnessRuntimeOpenshell + runtime = v1alpha2.AgentHarnessRuntimeSubstrate } - gatewayName := fmt.Sprintf("%s-%s", sb.Namespace, sb.Name) desc := strings.TrimSpace(sb.Spec.Description) resp := api.AgentResponse{ @@ -190,36 +188,21 @@ func (h *AgentsHandler) openshellAgentHarnessAgentResponse(ctx context.Context, Accepted: accepted, } - switch runtime { - case v1alpha2.AgentHarnessRuntimeSubstrate: - subEntry := &api.SubstrateAgentHarnessListEntry{ - Backend: sb.Spec.Backend, - Runtime: runtime, - ModelConfigRef: sb.Spec.ModelConfigRef, - GatewayUIPath: fmt.Sprintf("/api/agentharnesses/%s/%s/gateway/", sb.Namespace, sb.Name), - } - if sb.Status.BackendRef != nil { - subEntry.BackendRefID = sb.Status.BackendRef.ID - subEntry.ActorID = sb.Status.BackendRef.ID - } - if sb.Status.Connection != nil { - subEntry.Endpoint = sb.Status.Connection.Endpoint - } - resp.SubstrateAgentHarness = subEntry - default: - entry := &api.OpenshellAgentHarnessListEntry{ - Backend: sb.Spec.Backend, - GatewaySandboxName: gatewayName, - ModelConfigRef: sb.Spec.ModelConfigRef, - } - if sb.Status.BackendRef != nil { - entry.BackendRefID = sb.Status.BackendRef.ID - } - if sb.Status.Connection != nil { - entry.Endpoint = sb.Status.Connection.Endpoint - } - resp.OpenshellAgentHarness = entry + // Only substrate runtime is supported + subEntry := &api.SubstrateAgentHarnessListEntry{ + Backend: sb.Spec.Backend, + Runtime: runtime, + ModelConfigRef: sb.Spec.ModelConfigRef, + AcpPath: fmt.Sprintf("/api/agentharnesses/%s/%s/acp", sb.Namespace, sb.Name), } + if sb.Status.BackendRef != nil { + subEntry.BackendRefID = sb.Status.BackendRef.ID + subEntry.ActorID = sb.Status.BackendRef.ID + } + if sb.Status.Connection != nil { + subEntry.Endpoint = sb.Status.Connection.Endpoint + } + resp.SubstrateAgentHarness = subEntry mcRef := strings.TrimSpace(sb.Spec.ModelConfigRef) if mcRef == "" { @@ -339,13 +322,6 @@ func (h *AgentsHandler) validateAgentObject(ctx context.Context, agent v1alpha2. } } - if agent.GetWorkloadMode() == v1alpha2.WorkloadModeSandbox && h.SandboxBackend != nil && - v1alpha2.AgentSandboxPlatform(agent) != v1alpha2.SandboxPlatformSubstrate { - if err := sandboxbackend.EnsureAgentSandboxAPIsRegistered(ctx, h.KubeClient); err != nil { - return errors.NewBadRequestError(err.Error(), err) - } - } - kubeClientWrapper := utils.NewKubeClientWrapper(h.KubeClient) if err := kubeClientWrapper.AddInMemory(agent); err != nil { return errors.NewInternalServerError("Failed to add Agent to Kubernetes wrapper", err) @@ -676,7 +652,7 @@ func (h *AgentsHandler) HandleGetAgentHarness(w ErrorResponseWriter, r *http.Req w.RespondWithError(errors.NewNotFoundError("AgentHarness not found", nil)) return } - resp := h.openshellAgentHarnessAgentResponse(ctx, log, sb) + resp := h.agentHarnessAgentResponse(ctx, log, sb) log.Info("Successfully retrieved AgentHarness") RespondWithJSON(w, http.StatusOK, api.NewResponse(resp, "Successfully retrieved AgentHarness", false)) } @@ -818,7 +794,7 @@ func normalizeSandboxAgentForAPI(sa *v1alpha2.SandboxAgent) { } } -// HandleCreateAgentHarness handles POST /api/agentharnesses requests (kagent.dev/v1alpha2 AgentHarness — OpenClaw/NemoClaw VM, etc.). +// HandleCreateAgentHarness handles POST /api/agentharnesses requests (kagent.dev/v1alpha2 AgentHarness — OpenClaw VM, etc.). func (h *AgentsHandler) HandleCreateAgentHarness(w ErrorResponseWriter, r *http.Request) { log := ctrllog.FromContext(r.Context()).WithName("agents-handler").WithValues("operation", "create-agentharness") sb := &v1alpha2.AgentHarness{} @@ -852,7 +828,7 @@ func (h *AgentsHandler) HandleCreateAgentHarness(w ErrorResponseWriter, r *http. return } - resp := h.openshellAgentHarnessAgentResponse(r.Context(), log, sb) + resp := h.agentHarnessAgentResponse(r.Context(), log, sb) log.Info("Successfully created AgentHarness", "agentHarnessRef", agentRef) respondWithObjectResponse(w, http.StatusCreated, resp, "Successfully created AgentHarness") } diff --git a/go/core/internal/httpserver/handlers/agents_test.go b/go/core/internal/httpserver/handlers/agents_test.go index ea99c1c7be..4b2b581d3d 100644 --- a/go/core/internal/httpserver/handlers/agents_test.go +++ b/go/core/internal/httpserver/handlers/agents_test.go @@ -349,7 +349,7 @@ func TestHandleGetSandboxAgent(t *testing.T) { } func TestHandleGetAgentHarness(t *testing.T) { - t.Run("gets openshell AgentHarness", func(t *testing.T) { + t.Run("gets AgentHarness", func(t *testing.T) { sb := &v1alpha2.AgentHarness{ ObjectMeta: metav1.ObjectMeta{Name: "gh-get", Namespace: "default"}, Spec: v1alpha2.AgentHarnessSpec{Backend: v1alpha2.AgentHarnessBackendOpenClaw}, @@ -541,11 +541,11 @@ func TestHandleListAgents(t *testing.T) { var found bool for _, row := range response.Data { - if row.OpenshellAgentHarness == nil { + if row.SubstrateAgentHarness == nil { continue } found = true - require.Equal(t, "default-openclaw-1", row.OpenshellAgentHarness.GatewaySandboxName) + require.Equal(t, v1alpha2.AgentHarnessBackendOpenClaw, row.SubstrateAgentHarness.Backend) require.Equal(t, "AgentHarness", row.Agent.Kind) require.Equal(t, "openclaw-1", row.Agent.Metadata.Name) require.Equal(t, "Workload VM for experiments", row.Agent.Spec.Description) @@ -993,7 +993,7 @@ func TestHandleDeleteTeam(t *testing.T) { } func TestHandleDeleteAgentHarness(t *testing.T) { - t.Run("deletes openshell AgentHarness", func(t *testing.T) { + t.Run("deletes AgentHarness", func(t *testing.T) { sb := &v1alpha2.AgentHarness{ ObjectMeta: metav1.ObjectMeta{Name: "sb-only", Namespace: "default"}, Spec: v1alpha2.AgentHarnessSpec{Backend: v1alpha2.AgentHarnessBackendOpenClaw}, @@ -1066,8 +1066,8 @@ func TestHandleCreateAgentHarness(t *testing.T) { require.NoError(t, json.Unmarshal(w.Body.Bytes(), &response)) require.Equal(t, "AgentHarness", response.Data.Agent.Kind) require.Equal(t, "my-openclaw", response.Data.Agent.Metadata.Name) - require.NotNil(t, response.Data.OpenshellAgentHarness) - require.Equal(t, v1alpha2.AgentHarnessBackendOpenClaw, response.Data.OpenshellAgentHarness.Backend) + require.NotNil(t, response.Data.SubstrateAgentHarness) + require.Equal(t, v1alpha2.AgentHarnessBackendOpenClaw, response.Data.SubstrateAgentHarness.Backend) var created v1alpha2.AgentHarness require.NoError(t, handler.KubeClient.Get(context.Background(), types.NamespacedName{Namespace: "default", Name: "my-openclaw"}, &created)) @@ -1105,7 +1105,7 @@ func TestHandleCreateAgentHarness(t *testing.T) { var response api.StandardResponse[api.AgentResponse] require.NoError(t, json.Unmarshal(w.Body.Bytes(), &response)) - require.Equal(t, v1alpha2.AgentHarnessBackendHermes, response.Data.OpenshellAgentHarness.Backend) + require.Equal(t, v1alpha2.AgentHarnessBackendHermes, response.Data.SubstrateAgentHarness.Backend) var created v1alpha2.AgentHarness require.NoError(t, handler.KubeClient.Get(context.Background(), types.NamespacedName{Namespace: "default", Name: "my-hermes"}, &created)) diff --git a/go/core/internal/httpserver/handlers/handlers.go b/go/core/internal/httpserver/handlers/handlers.go index 513cf9c173..d96d9ee26b 100644 --- a/go/core/internal/httpserver/handlers/handlers.go +++ b/go/core/internal/httpserver/handlers/handlers.go @@ -15,6 +15,9 @@ import ( type Handlers struct { KubeClient client.Client AgentHarnessGateway *AgentHarnessGatewayConfig + // AgentHarnessSessionActor creates/suspends the per-session substrate actors + // that back each AgentHarness chat session. + AgentHarnessSessionActor *substrate.AgentHarnessSessionActorBackend Health *HealthHandler ModelConfig *ModelConfigHandler @@ -62,6 +65,7 @@ func NewHandlers( substrateAteClient *substrate.Client, mcpEgressPlaintext bool, substrateSandboxActorBackend *substrate.SandboxAgentActorBackend, + agentHarnessSessionActorBackend *substrate.AgentHarnessSessionActorBackend, ) *Handlers { base := &Base{ KubeClient: kubeClient, @@ -75,25 +79,26 @@ func NewHandlers( } return &Handlers{ - KubeClient: kubeClient, - AgentHarnessGateway: agentHarnessGateway, - Health: NewHealthHandler(), - ModelConfig: NewModelConfigHandler(base), - Model: NewModelHandler(base), - ModelProviderConfig: NewModelProviderConfigHandler(base, rcnclr), - Sessions: NewSessionsHandler(base, substrateSandboxActorBackend), - Agents: NewAgentsHandler(base), - Tools: NewToolsHandler(base), - ToolServers: NewToolServersHandler(base), - ToolServerTypes: NewToolServerTypesHandler(base), - Memory: NewMemoryHandler(base), - Feedback: NewFeedbackHandler(base), - Namespaces: NewNamespacesHandler(base), - PromptTemplates: NewPromptTemplatesHandler(base), - Tasks: NewTasksHandler(base), - Checkpoints: NewCheckpointsHandler(base), - CrewAI: NewCrewAIHandler(base), - CurrentUser: NewCurrentUserHandler(), - Substrate: NewSubstrateHandler(base, substrateAteClient), + KubeClient: kubeClient, + AgentHarnessGateway: agentHarnessGateway, + AgentHarnessSessionActor: agentHarnessSessionActorBackend, + Health: NewHealthHandler(), + ModelConfig: NewModelConfigHandler(base), + Model: NewModelHandler(base), + ModelProviderConfig: NewModelProviderConfigHandler(base, rcnclr), + Sessions: NewSessionsHandler(base, substrateSandboxActorBackend), + Agents: NewAgentsHandler(base), + Tools: NewToolsHandler(base), + ToolServers: NewToolServersHandler(base), + ToolServerTypes: NewToolServerTypesHandler(base), + Memory: NewMemoryHandler(base), + Feedback: NewFeedbackHandler(base), + Namespaces: NewNamespacesHandler(base), + PromptTemplates: NewPromptTemplatesHandler(base), + Tasks: NewTasksHandler(base), + Checkpoints: NewCheckpointsHandler(base), + CrewAI: NewCrewAIHandler(base), + CurrentUser: NewCurrentUserHandler(), + Substrate: NewSubstrateHandler(base, substrateAteClient), } } diff --git a/go/core/internal/httpserver/handlers/sandbox_ssh.go b/go/core/internal/httpserver/handlers/sandbox_ssh.go deleted file mode 100644 index 58aa241efd..0000000000 --- a/go/core/internal/httpserver/handlers/sandbox_ssh.go +++ /dev/null @@ -1,604 +0,0 @@ -package handlers - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "io" - "net" - "net/http" - "os" - "strings" - "sync" - "time" - - "github.com/gorilla/websocket" - openshellv1 "github.com/kagent-dev/kagent/go/api/openshell/gen/openshellv1" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openshell" - "golang.org/x/crypto/ssh" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - ctrllog "sigs.k8s.io/controller-runtime/pkg/log" -) - -const ( - // Default when OPENSHELL_GRPC_ADDR is unset and the WebSocket start frame omits grpc_address. - // Short name "openshell:8080" only resolves if a Service "openshell" exists in the controller's - // namespace; OpenShell is often installed in its own namespace (e.g. openshell). - defaultOpenshellGRPCAddr = "openshell.openshell.svc.cluster.local:8080" - openshellGRPCEnv = "OPENSHELL_GRPC_ADDR" - - sandboxSSHWSReadBufSize = 4096 - sandboxSSHHandshakeTimeout = 90 * time.Second - sandboxSSHWSWriteDeadline = 15 * time.Second - sandboxSSHDefaultCols = 120 - sandboxSSHDefaultRows = 36 - sandboxSSHCopyBufSize = 32 * 1024 - sandboxSSHClientConnTimeout = 60 * time.Second - sandboxSSHUser = "sandbox" - sandboxSSHPTYTerm = "xterm-256color" -) - -type sshStartMsg struct { - SandboxName string `json:"sandbox_name"` - GRPCAddress string `json:"grpc_address,omitempty"` - Cols int `json:"cols,omitempty"` - Rows int `json:"rows,omitempty"` - PlainShell bool `json:"plain_shell,omitempty"` - LaunchCommand string `json:"launch_command,omitempty"` - HarnessBackend string `json:"harness_backend,omitempty"` -} - -type resizeMsg struct { - Type string `json:"type"` - Cols int `json:"cols"` - Rows int `json:"rows"` -} - -type wsCtrlMsg struct { - Type string `json:"type"` - Message string `json:"message,omitempty"` -} - -// HandleSandboxSSHWebSocket upgrades to WebSocket, accepts one JSON start frame, mints an SSH -// session via OpenShell gRPC from inside the cluster, opens a ForwardTcp relay and SSH shell, -// then proxies terminal I/O (same wire protocol as scripts/openshell-ssh-ws.mjs). -func (h *Handlers) HandleSandboxSSHWebSocket(w ErrorResponseWriter, r *http.Request) { - log := ctrllog.FromContext(r.Context()).WithName("sandbox-ssh-ws") - if r.Method != http.MethodGet { - http.Error(w, "method not allowed", http.StatusMethodNotAllowed) - return - } - - up := websocket.Upgrader{ - ReadBufferSize: sandboxSSHWSReadBufSize, - WriteBufferSize: sandboxSSHWSReadBufSize, - CheckOrigin: func(*http.Request) bool { - return true - }, - } - wsConn, err := up.Upgrade(w, r, nil) - if err != nil { - log.Info("websocket upgrade failed", "error", err) - return - } - - start, err := readSandboxSSHStart(wsConn) - if err != nil { - closeWSWithError(wsConn, err.Error()) - return - } - grpcAddr := resolveOpenshellGRPCAddr(start) - - log.Info("openshell gRPC target", "addr", grpcAddr) - - handshakeCtx, handshakeCancel := context.WithTimeout(r.Context(), sandboxSSHHandshakeTimeout) - defer handshakeCancel() - - // ForwardTcp must outlive the handshake; do not tie the relay stream to handshakeCtx. - grpcConn, sshClient, session, stdin, stdout, stderr, err := h.dialOpenshellShellSession( - handshakeCtx, r.Context(), grpcAddr, start.SandboxName, start.Rows, start.Cols, start.PlainShell, start.LaunchCommand, start.HarnessBackend) - if err != nil { - log.Info("openshell ssh session failed", "error", err) - closeWSWithError(wsConn, err.Error()) - return - } - defer grpcConn.Close() - defer func() { - _ = session.Close() - _ = sshClient.Close() - }() - - sendWSCtrl(wsConn, "ready", "") - - var wsWriteMu sync.Mutex - writeWS := func(messageType int, p []byte) error { - wsWriteMu.Lock() - defer wsWriteMu.Unlock() - deadline := time.Now().Add(sandboxSSHWSWriteDeadline) - _ = wsConn.SetWriteDeadline(deadline) - return wsConn.WriteMessage(messageType, p) - } - - copyDone := make(chan struct{}) - go func() { - defer close(copyDone) - runSandboxSSHWSReader(wsConn, session, stdin) - }() - - var streamWG sync.WaitGroup - streamWG.Add(2) - go copySSHStreamToWebSocket(stdout, writeWS, &streamWG) - go copySSHStreamToWebSocket(stderr, writeWS, &streamWG) - go func() { - streamWG.Wait() - log.Info("ssh stdout/stderr copy finished") - }() - - select { - case <-copyDone: - case <-r.Context().Done(): - } - _ = wsConn.Close() - <-copyDone -} - -func readSandboxSSHStart(wsConn *websocket.Conn) (sshStartMsg, error) { - _, raw, err := wsConn.ReadMessage() - if err != nil { - return sshStartMsg{}, fmt.Errorf("failed to read start frame: %w", err) - } - var start sshStartMsg - if err := json.Unmarshal(raw, &start); err != nil { - return sshStartMsg{}, errors.New("first frame must be JSON start payload") - } - start.SandboxName = strings.TrimSpace(start.SandboxName) - if start.SandboxName == "" { - return sshStartMsg{}, errors.New("sandbox_name is required") - } - if start.Cols <= 0 { - start.Cols = sandboxSSHDefaultCols - } - if start.Rows <= 0 { - start.Rows = sandboxSSHDefaultRows - } - return start, nil -} - -func resolveOpenshellGRPCAddr(start sshStartMsg) string { - grpcAddr := strings.TrimSpace(start.GRPCAddress) - if grpcAddr == "" { - grpcAddr = strings.TrimSpace(os.Getenv(openshellGRPCEnv)) - } - if grpcAddr == "" { - grpcAddr = defaultOpenshellGRPCAddr - } - return grpcAddr -} - -func closeWSWithError(ws *websocket.Conn, msg string) { - sendWSCtrl(ws, "error", msg) - _ = ws.Close() -} - -func runSandboxSSHWSReader(wsConn *websocket.Conn, session *ssh.Session, stdin io.Writer) { - for { - mt, payload, rerr := wsConn.ReadMessage() - if rerr != nil { - return - } - handleSandboxSSHWSInbound(mt, payload, session, stdin) - } -} - -func handleSandboxSSHWSInbound(mt int, payload []byte, session *ssh.Session, stdin io.Writer) { - switch mt { - case websocket.TextMessage: - if tryHandleSSHResize(payload, session) { - return - } - _, _ = stdin.Write(payload) - case websocket.BinaryMessage: - _, _ = stdin.Write(payload) - } -} - -// parseSSHResizePayload parses a browser JSON resize control frame into PTY rows/cols. -func parseSSHResizePayload(payload []byte) (rows, cols int, ok bool) { - if len(payload) == 0 || payload[0] != '{' { - return 0, 0, false - } - var rm resizeMsg - if json.Unmarshal(payload, &rm) != nil || rm.Type != "resize" || rm.Cols <= 0 || rm.Rows <= 0 { - return 0, 0, false - } - return rm.Rows, rm.Cols, true -} - -func tryHandleSSHResize(payload []byte, session *ssh.Session) bool { - rows, cols, ok := parseSSHResizePayload(payload) - if !ok { - return false - } - _ = session.WindowChange(rows, cols) - return true -} - -func sendWSCtrl(ws *websocket.Conn, typ, msg string) { - payload, _ := json.Marshal(wsCtrlMsg{Type: typ, Message: msg}) - _ = ws.WriteMessage(websocket.TextMessage, payload) -} - -// copySSHStreamToWebSocket forwards one SSH session stream (stdout or stderr) to the browser WebSocket. -func copySSHStreamToWebSocket(r io.Reader, writeWS func(messageType int, p []byte) error, wg *sync.WaitGroup) { - defer wg.Done() - buf := make([]byte, sandboxSSHCopyBufSize) - for { - n, rerr := r.Read(buf) - if n > 0 { - if werr := writeWS(websocket.BinaryMessage, buf[:n]); werr != nil { - return - } - } - if rerr != nil { - return - } - } -} - -func (h *Handlers) dialOpenshellShellSession( - handshakeCtx, streamCtx context.Context, - grpcAddr, sandboxName string, - rows, cols int, - plainShell bool, - launchCommandFromClient string, - harnessBackend string, -) ( - grpcConn *grpc.ClientConn, - sshClient *ssh.Client, - session *ssh.Session, - stdin io.WriteCloser, - stdout io.Reader, - stderr io.Reader, - err error, -) { - grpcConn, err = grpc.NewClient(grpcAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) - if err != nil { - return nil, nil, nil, nil, nil, nil, fmt.Errorf("grpc dial %q: %w", grpcAddr, err) - } - - cli := openshellv1.NewOpenShellClient(grpcConn) - sandboxID, sshRes, err := openshellCreateSSHSession(handshakeCtx, cli, sandboxName) - if err != nil { - _ = grpcConn.Close() - return nil, nil, nil, nil, nil, nil, err - } - - if sid := strings.TrimSpace(sshRes.GetSandboxId()); sid != "" { - sandboxID = sid - } - tunnelConn, err := openshellDialForwardTcp(streamCtx, cli, sandboxID, sshRes.GetToken()) - if err != nil { - _ = grpcConn.Close() - return nil, nil, nil, nil, nil, nil, err - } - - sshClient, session, stdin, stdout, stderr, err = openSSHSessionOverTunnel( - tunnelConn, "openshell", rows, cols, plainShell, launchCommandFromClient, harnessBackend, - ) - if err != nil { - _ = grpcConn.Close() - return nil, nil, nil, nil, nil, nil, err - } - return grpcConn, sshClient, session, stdin, stdout, stderr, nil -} - -func sandboxIDForSSH(sb *openshellv1.Sandbox) string { - if sb == nil || sb.GetMetadata() == nil { - return "" - } - meta := sb.GetMetadata() - if id := strings.TrimSpace(meta.GetId()); id != "" { - return id - } - return strings.TrimSpace(meta.GetName()) -} - -func openshellCreateSSHSession( - ctx context.Context, - cli openshellv1.OpenShellClient, - sandboxName string, -) (sandboxID string, sshRes *openshellv1.CreateSshSessionResponse, err error) { - sbRes, err := cli.GetSandbox(ctx, &openshellv1.GetSandboxRequest{Name: sandboxName}) - if err != nil { - return "", nil, fmt.Errorf("GetSandbox: %w", err) - } - sandboxID = sandboxIDForSSH(sbRes.GetSandbox()) - if sandboxID == "" { - return "", nil, fmt.Errorf("sandbox %q: response missing metadata id and name", sandboxName) - } - - sshRes, err = cli.CreateSshSession(ctx, &openshellv1.CreateSshSessionRequest{SandboxId: sandboxID}) - if err != nil { - return "", nil, fmt.Errorf("CreateSshSession: %w", err) - } - - token := strings.TrimSpace(sshRes.GetToken()) - if token == "" { - return "", nil, errors.New("CreateSshSession returned empty session token") - } - return sandboxID, sshRes, nil -} - -// openshellDialForwardTcp opens the OpenShell ForwardTcp bidi stream used by the CLI ssh-proxy. -func openshellDialForwardTcp( - ctx context.Context, - cli openshellv1.OpenShellClient, - sandboxID, token string, -) (net.Conn, error) { - sandboxID = strings.TrimSpace(sandboxID) - token = strings.TrimSpace(token) - if sandboxID == "" || token == "" { - return nil, errors.New("sandbox id and session token are required for SSH forward") - } - - stream, err := cli.ForwardTcp(ctx) - if err != nil { - return nil, fmt.Errorf("ForwardTcp: %w", err) - } - - if err := stream.Send(buildForwardTcpSSHInit(sandboxID, token)); err != nil { - _ = stream.CloseSend() - return nil, fmt.Errorf("ForwardTcp init: %w", err) - } - return newTCPForwardConn(stream), nil -} - -func buildForwardTcpSSHInit(sandboxID, token string) *openshellv1.TcpForwardFrame { - return &openshellv1.TcpForwardFrame{ - Payload: &openshellv1.TcpForwardFrame_Init{ - Init: &openshellv1.TcpForwardInit{ - SandboxId: sandboxID, - ServiceId: fmt.Sprintf("ssh-proxy:%s", sandboxID), - AuthorizationToken: token, - Target: &openshellv1.TcpForwardInit_Ssh{ - Ssh: &openshellv1.SshRelayTarget{}, - }, - }, - }, - } -} - -// tcpForwardConn adapts OpenShell ForwardTcp to net.Conn for golang.org/x/crypto/ssh. -// A background reader pumps gateway data into rbuf so ssh handshakes can read and write -// concurrently (same pattern as openshell-cli ssh-proxy's split stdin/stdout tasks). -type tcpForwardConn struct { - stream openshellv1.OpenShell_ForwardTcpClient - - readMu sync.Mutex - readCond *sync.Cond - rbuf []byte - recvErr error - closed bool - - sendMu sync.Mutex -} - -func newTCPForwardConn(stream openshellv1.OpenShell_ForwardTcpClient) *tcpForwardConn { - c := &tcpForwardConn{stream: stream} - c.readCond = sync.NewCond(&c.readMu) - go c.pumpRecv() - return c -} - -func (c *tcpForwardConn) pumpRecv() { - for { - frame, err := c.stream.Recv() - c.readMu.Lock() - if c.closed { - c.readMu.Unlock() - return - } - if err != nil { - if c.recvErr == nil { - c.recvErr = err - } - c.readCond.Broadcast() - c.readMu.Unlock() - return - } - if frame != nil { - if data := frame.GetData(); len(data) > 0 { - c.rbuf = append(c.rbuf, data...) - c.readCond.Broadcast() - } - } - c.readMu.Unlock() - } -} - -func (c *tcpForwardConn) Read(p []byte) (int, error) { - c.readMu.Lock() - defer c.readMu.Unlock() - for len(c.rbuf) == 0 && c.recvErr == nil && !c.closed { - c.readCond.Wait() - } - if c.closed && len(c.rbuf) == 0 { - return 0, io.EOF - } - if len(c.rbuf) == 0 { - if c.recvErr != nil { - return 0, c.recvErr - } - return 0, io.EOF - } - n := copy(p, c.rbuf) - c.rbuf = c.rbuf[n:] - return n, nil -} - -func (c *tcpForwardConn) Write(p []byte) (int, error) { - if len(p) == 0 { - return 0, nil - } - c.sendMu.Lock() - defer c.sendMu.Unlock() - c.readMu.Lock() - closed := c.closed - c.readMu.Unlock() - if closed { - return 0, io.ErrClosedPipe - } - if err := c.stream.Send(&openshellv1.TcpForwardFrame{ - Payload: &openshellv1.TcpForwardFrame_Data{Data: append([]byte(nil), p...)}, - }); err != nil { - return 0, err - } - return len(p), nil -} - -func (c *tcpForwardConn) Close() error { - c.readMu.Lock() - if c.closed { - c.readMu.Unlock() - return nil - } - c.closed = true - if c.recvErr == nil { - c.recvErr = io.EOF - } - c.readCond.Broadcast() - c.readMu.Unlock() - - c.sendMu.Lock() - defer c.sendMu.Unlock() - return c.stream.CloseSend() -} - -func (c *tcpForwardConn) LocalAddr() net.Addr { return &tcpForwardAddr{} } -func (c *tcpForwardConn) RemoteAddr() net.Addr { return &tcpForwardAddr{} } - -func (c *tcpForwardConn) SetDeadline(t time.Time) error { - return nil -} - -func (c *tcpForwardConn) SetReadDeadline(t time.Time) error { - return nil -} - -func (c *tcpForwardConn) SetWriteDeadline(t time.Time) error { - return nil -} - -type tcpForwardAddr struct{} - -func (tcpForwardAddr) Network() string { return "tcp" } -func (tcpForwardAddr) String() string { return "openshell-forward-tcp" } - -func openSSHSessionOverTunnel( - tunnelConn net.Conn, - dialHost string, - rows, cols int, - plainShell bool, - launchCommandFromClient string, - harnessBackend string, -) ( - sshClient *ssh.Client, - session *ssh.Session, - stdin io.WriteCloser, - stdout io.Reader, - stderr io.Reader, - err error, -) { - sshConn, chans, reqs, err := ssh.NewClientConn(tunnelConn, dialHost, &ssh.ClientConfig{ - User: sandboxSSHUser, - Auth: []ssh.AuthMethod{ssh.KeyboardInteractive(func(_ string, _ string, questions []string, _ []bool) ([]string, error) { - return make([]string, len(questions)), nil - })}, - HostKeyCallback: ssh.InsecureIgnoreHostKey(), - Timeout: sandboxSSHClientConnTimeout, - }) - if err != nil { - _ = tunnelConn.Close() - return nil, nil, nil, nil, nil, fmt.Errorf("ssh handshake: %w", err) - } - sshClient = ssh.NewClient(sshConn, chans, reqs) - - session, err = sshClient.NewSession() - if err != nil { - _ = sshClient.Close() - return nil, nil, nil, nil, nil, fmt.Errorf("ssh NewSession: %w", err) - } - - stdin, err = session.StdinPipe() - if err != nil { - _ = session.Close() - _ = sshClient.Close() - return nil, nil, nil, nil, nil, fmt.Errorf("ssh StdinPipe: %w", err) - } - stdout, err = session.StdoutPipe() - if err != nil { - _ = session.Close() - _ = sshClient.Close() - return nil, nil, nil, nil, nil, fmt.Errorf("ssh StdoutPipe: %w", err) - } - stderr, err = session.StderrPipe() - if err != nil { - _ = session.Close() - _ = sshClient.Close() - return nil, nil, nil, nil, nil, fmt.Errorf("ssh StderrPipe: %w", err) - } - - modes := ssh.TerminalModes{ssh.ECHO: 1} - if err := session.RequestPty(sandboxSSHPTYTerm, rows, cols, modes); err != nil { - _ = session.Close() - _ = sshClient.Close() - return nil, nil, nil, nil, nil, fmt.Errorf("ssh RequestPty: %w", err) - } - useShell, remoteCmd := openshell.ResolveSSHRemoteCommand(plainShell, launchCommandFromClient, harnessBackend) - if useShell { - if err := session.Shell(); err != nil { - _ = session.Close() - _ = sshClient.Close() - return nil, nil, nil, nil, nil, fmt.Errorf("ssh Shell: %w", err) - } - } else { - if err := session.Start(remoteCmd); err != nil { - _ = session.Close() - _ = sshClient.Close() - return nil, nil, nil, nil, nil, fmt.Errorf("ssh Start %q: %w", remoteCmd, err) - } - } - - return sshClient, session, stdin, stdout, stderr, nil -} - -func isLoopbackHost(h string) bool { - switch strings.ToLower(strings.TrimSpace(h)) { - case "127.0.0.1", "localhost", "::1", "[::1]": - return true - default: - return false - } -} - -// resolveGatewayDialHost maps CreateSshSession's gateway_host to a TCP dial target from the controller pod. -// When OpenShell returns loopback, we dial the host from the OpenShell gRPC address (same Service, any namespace). -func resolveGatewayDialHost(gatewayHost, grpcTarget string) (string, error) { - if !isLoopbackHost(gatewayHost) { - return gatewayHost, nil - } - host, _, err := net.SplitHostPort(grpcTarget) - if err != nil { - return "", fmt.Errorf( - "CreateSshSession gateway_host=%q is loopback; grpc target %q must be host:port so kagent can dial the OpenShell service: %w", - gatewayHost, grpcTarget, err) - } - if host == "" { - return "", fmt.Errorf( - "CreateSshSession gateway_host=%q is loopback; grpc target %q has an empty host", - gatewayHost, grpcTarget) - } - return host, nil -} diff --git a/go/core/internal/httpserver/handlers/sandbox_ssh_forward_test.go b/go/core/internal/httpserver/handlers/sandbox_ssh_forward_test.go deleted file mode 100644 index 6dc86dbef4..0000000000 --- a/go/core/internal/httpserver/handlers/sandbox_ssh_forward_test.go +++ /dev/null @@ -1,129 +0,0 @@ -package handlers - -import ( - "context" - "io" - "testing" - "time" - - openshellv1 "github.com/kagent-dev/kagent/go/api/openshell/gen/openshellv1" - "github.com/stretchr/testify/require" - "google.golang.org/grpc/metadata" - "google.golang.org/protobuf/proto" -) - -func TestBuildForwardTcpSSHInit(t *testing.T) { - init := buildForwardTcpSSHInit("sb-uuid", "tok-abc") - require.NotNil(t, init.GetInit()) - require.Equal(t, "sb-uuid", init.GetInit().GetSandboxId()) - require.Equal(t, "ssh-proxy:sb-uuid", init.GetInit().GetServiceId()) - require.Equal(t, "tok-abc", init.GetInit().GetAuthorizationToken()) - require.NotNil(t, init.GetInit().GetSsh()) -} - -func TestTCPForwardConnReadWrite(t *testing.T) { - stream := newMockForwardTcpStream() - conn := newTCPForwardConn(stream) - - require.NoError(t, stream.pushRecv(&openshellv1.TcpForwardFrame{ - Payload: &openshellv1.TcpForwardFrame_Data{Data: []byte("SSH-2.0-test\r\n")}, - })) - - buf := make([]byte, 32) - n, err := conn.Read(buf) - require.NoError(t, err) - require.Equal(t, "SSH-2.0-test\r\n", string(buf[:n])) - - _, err = conn.Write([]byte("SSH-2.0-client\r\n")) - require.NoError(t, err) - - select { - case frame := <-stream.sent: - require.Equal(t, "SSH-2.0-client\r\n", string(frame.GetData())) - default: - t.Fatal("expected forwarded client data frame") - } - - require.NoError(t, conn.Close()) -} - -func TestTCPForwardConnCloseUnblocksRead(t *testing.T) { - stream := newMockForwardTcpStream() - conn := newTCPForwardConn(stream) - - readDone := make(chan struct{}) - var readErr error - go func() { - _, readErr = conn.Read(make([]byte, 1)) - close(readDone) - }() - - time.Sleep(50 * time.Millisecond) - - require.NoError(t, conn.Close()) - - select { - case <-readDone: - case <-time.After(2 * time.Second): - t.Fatal("Read did not unblock after Close") - } - require.ErrorIs(t, readErr, io.EOF) -} - -// mockForwardTcpStream is a minimal OpenShell_ForwardTcpClient for unit tests. -type mockForwardTcpStream struct { - recv chan *openshellv1.TcpForwardFrame - sent chan *openshellv1.TcpForwardFrame -} - -func newMockForwardTcpStream() *mockForwardTcpStream { - return &mockForwardTcpStream{ - recv: make(chan *openshellv1.TcpForwardFrame, 8), - sent: make(chan *openshellv1.TcpForwardFrame, 8), - } -} - -func (m *mockForwardTcpStream) pushRecv(frame *openshellv1.TcpForwardFrame) error { - m.recv <- frame - return nil -} - -func (m *mockForwardTcpStream) Send(frame *openshellv1.TcpForwardFrame) error { - m.sent <- frame - return nil -} - -func (m *mockForwardTcpStream) Recv() (*openshellv1.TcpForwardFrame, error) { - frame, ok := <-m.recv - if !ok { - return nil, io.EOF - } - return frame, nil -} - -func (m *mockForwardTcpStream) SendMsg(msg any) error { - frame, ok := msg.(*openshellv1.TcpForwardFrame) - if !ok { - return io.ErrUnexpectedEOF - } - return m.Send(frame) -} - -func (m *mockForwardTcpStream) RecvMsg(msg any) error { - frame, err := m.Recv() - if err != nil { - return err - } - out, ok := msg.(*openshellv1.TcpForwardFrame) - if !ok { - return io.ErrUnexpectedEOF - } - proto.Reset(out) - proto.Merge(out, frame) - return nil -} - -func (m *mockForwardTcpStream) Header() (metadata.MD, error) { return nil, nil } -func (m *mockForwardTcpStream) Trailer() metadata.MD { return nil } -func (m *mockForwardTcpStream) CloseSend() error { close(m.sent); return nil } -func (m *mockForwardTcpStream) Context() context.Context { return context.Background() } diff --git a/go/core/internal/httpserver/handlers/sandbox_ssh_test.go b/go/core/internal/httpserver/handlers/sandbox_ssh_test.go deleted file mode 100644 index f26d5ed2c7..0000000000 --- a/go/core/internal/httpserver/handlers/sandbox_ssh_test.go +++ /dev/null @@ -1,243 +0,0 @@ -package handlers - -import ( - "bytes" - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/gorilla/websocket" -) - -func TestIsLoopbackHost(t *testing.T) { - tests := []struct { - host string - want bool - }{ - {"127.0.0.1", true}, - {"localhost", true}, - {"LOCALHOST", true}, - {"::1", true}, - {"[::1]", true}, - {"openshell.openshell.svc.cluster.local", false}, - {"10.0.0.1", false}, - } - for _, tt := range tests { - if got := isLoopbackHost(tt.host); got != tt.want { - t.Errorf("isLoopbackHost(%q) = %v, want %v", tt.host, got, tt.want) - } - } -} - -func TestResolveGatewayDialHost(t *testing.T) { - got, err := resolveGatewayDialHost("ingress.example.com", "ignored:8080") - if err != nil || got != "ingress.example.com" { - t.Fatalf("non-loopback: got %q err %v", got, err) - } - - got, err = resolveGatewayDialHost("127.0.0.1", "openshell.openshell.svc.cluster.local:8080") - if err != nil || got != "openshell.openshell.svc.cluster.local" { - t.Fatalf("loopback + grpc: got %q err %v", got, err) - } - - got, err = resolveGatewayDialHost("127.0.0.1", "grpc.other.namespace.svc.cluster.local:9090") - if err != nil || got != "grpc.other.namespace.svc.cluster.local" { - t.Fatalf("loopback + other grpc host: got %q err %v", got, err) - } -} - -func TestResolveGatewayDialHost_InvalidGRPCTarget(t *testing.T) { - _, err := resolveGatewayDialHost("127.0.0.1", "not-a-valid-hostport") - if err == nil { - t.Fatal("expected error for invalid grpc host:port") - } - - _, err = resolveGatewayDialHost("127.0.0.1", ":8080") - if err == nil { - t.Fatal("expected error for empty grpc host with loopback gateway") - } - if !strings.Contains(err.Error(), "empty host") { - t.Fatalf("unexpected error: %v", err) - } -} - -func TestResolveOpenshellGRPCAddr(t *testing.T) { - t.Run("start frame wins over env", func(t *testing.T) { - t.Setenv(openshellGRPCEnv, "env-host:9090") - got := resolveOpenshellGRPCAddr(sshStartMsg{GRPCAddress: " frame:1111 "}) - if got != "frame:1111" { - t.Fatalf("got %q", got) - } - }) - t.Run("env when frame empty", func(t *testing.T) { - t.Setenv(openshellGRPCEnv, "env-host:9090") - got := resolveOpenshellGRPCAddr(sshStartMsg{}) - if got != "env-host:9090" { - t.Fatalf("got %q", got) - } - }) - t.Run("default when frame and env empty", func(t *testing.T) { - t.Setenv(openshellGRPCEnv, "") - got := resolveOpenshellGRPCAddr(sshStartMsg{}) - if got != defaultOpenshellGRPCAddr { - t.Fatalf("got %q", got) - } - }) -} - -func TestParseSSHResizePayload(t *testing.T) { - tests := []struct { - name string - payload string - wantOK bool - wantR int - wantC int - }{ - {"empty", "", false, 0, 0}, - {"not json", "hello", false, 0, 0}, - {"json not object prefix", "[1,2]", false, 0, 0}, - {"wrong type", `{"type":"ping","cols":80,"rows":24}`, false, 0, 0}, - {"zero cols", `{"type":"resize","cols":0,"rows":24}`, false, 0, 0}, - {"zero rows", `{"type":"resize","cols":80,"rows":0}`, false, 0, 0}, - {"ok", `{"type":"resize","cols":100,"rows":40}`, true, 40, 100}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r, c, ok := parseSSHResizePayload([]byte(tt.payload)) - if ok != tt.wantOK || r != tt.wantR || c != tt.wantC { - t.Fatalf("parseSSHResizePayload(%q) = (%d,%d,%v), want (%d,%d,%v)", - tt.payload, r, c, ok, tt.wantR, tt.wantC, tt.wantOK) - } - }) - } -} - -func TestHandleSandboxSSHWSInbound_WritesStdin(t *testing.T) { - var buf bytes.Buffer - handleSandboxSSHWSInbound(websocket.TextMessage, []byte("hello"), nil, &buf) - if buf.String() != "hello" { - t.Fatalf("text: got %q", buf.String()) - } - buf.Reset() - handleSandboxSSHWSInbound(websocket.BinaryMessage, []byte{0xaa, 0xbb}, nil, &buf) - want := []byte{0xaa, 0xbb} - if !bytes.Equal(buf.Bytes(), want) { - t.Fatalf("binary: got %x want %x", buf.Bytes(), want) - } -} - -func TestReadSandboxSSHStart(t *testing.T) { - out := make(chan any, 1) - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - up := websocket.Upgrader{CheckOrigin: func(*http.Request) bool { return true }} - c, err := up.Upgrade(w, r, nil) - if err != nil { - out <- err - return - } - defer c.Close() - start, err := readSandboxSSHStart(c) - if err != nil { - out <- err - return - } - out <- start - })) - defer srv.Close() - - dialURL := "ws" + strings.TrimPrefix(srv.URL, "http") - - t.Run("valid", func(t *testing.T) { - client, _, err := websocket.DefaultDialer.Dial(dialURL, nil) - if err != nil { - t.Fatal(err) - } - defer client.Close() - if err := client.WriteMessage(websocket.TextMessage, []byte(`{"sandbox_name":"my-sb","cols":80,"rows":24}`)); err != nil { - t.Fatal(err) - } - switch v := (<-out).(type) { - case error: - t.Fatal(v) - case sshStartMsg: - if v.SandboxName != "my-sb" || v.Cols != 80 || v.Rows != 24 { - t.Fatalf("%+v", v) - } - default: - t.Fatalf("unexpected %T", v) - } - }) - - t.Run("defaults term size", func(t *testing.T) { - client, _, err := websocket.DefaultDialer.Dial(dialURL, nil) - if err != nil { - t.Fatal(err) - } - defer client.Close() - if err := client.WriteMessage(websocket.TextMessage, []byte(`{"sandbox_name":"x"}`)); err != nil { - t.Fatal(err) - } - switch v := (<-out).(type) { - case error: - t.Fatal(v) - case sshStartMsg: - if v.Cols != sandboxSSHDefaultCols || v.Rows != sandboxSSHDefaultRows { - t.Fatalf("cols=%d rows=%d", v.Cols, v.Rows) - } - default: - t.Fatalf("unexpected %T", v) - } - }) - - t.Run("trims sandbox name", func(t *testing.T) { - client, _, err := websocket.DefaultDialer.Dial(dialURL, nil) - if err != nil { - t.Fatal(err) - } - defer client.Close() - if err := client.WriteMessage(websocket.TextMessage, []byte(`{"sandbox_name":" sb "}`)); err != nil { - t.Fatal(err) - } - switch v := (<-out).(type) { - case error: - t.Fatal(v) - case sshStartMsg: - if v.SandboxName != "sb" { - t.Fatalf("got %q", v.SandboxName) - } - default: - t.Fatalf("unexpected %T", v) - } - }) - - t.Run("missing sandbox_name", func(t *testing.T) { - client, _, err := websocket.DefaultDialer.Dial(dialURL, nil) - if err != nil { - t.Fatal(err) - } - defer client.Close() - if err := client.WriteMessage(websocket.TextMessage, []byte(`{"sandbox_name":" "}`)); err != nil { - t.Fatal(err) - } - gotErr, ok := (<-out).(error) - if !ok || gotErr == nil || !strings.Contains(gotErr.Error(), "sandbox_name") { - t.Fatalf("got %v ok=%v", gotErr, ok) - } - }) - - t.Run("invalid json", func(t *testing.T) { - client, _, err := websocket.DefaultDialer.Dial(dialURL, nil) - if err != nil { - t.Fatal(err) - } - defer client.Close() - if err := client.WriteMessage(websocket.TextMessage, []byte(`not-json`)); err != nil { - t.Fatal(err) - } - gotErr, ok := (<-out).(error) - if !ok || gotErr == nil { - t.Fatalf("got %v ok=%v", gotErr, ok) - } - }) -} diff --git a/go/core/internal/httpserver/handlers/sessions.go b/go/core/internal/httpserver/handlers/sessions.go index 70ab8e6902..c9c8252d6f 100644 --- a/go/core/internal/httpserver/handlers/sessions.go +++ b/go/core/internal/httpserver/handlers/sessions.go @@ -17,6 +17,7 @@ import ( "github.com/kagent-dev/kagent/go/core/pkg/a2acompat/trpcv0" "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/substrate" apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" ctrllog "sigs.k8s.io/controller-runtime/pkg/log" ) @@ -34,6 +35,30 @@ func NewSessionsHandler(base *Base, substrateSandboxActorBackend *substrate.Sand } } +// splitAgentRef splits a "namespace/name" agent reference. ok is false when the +// ref is not in that form. +func splitAgentRef(ref string) (namespace, name string, ok bool) { + parts := strings.SplitN(ref, "/", 2) + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return "", "", false + } + return parts[0], parts[1], true +} + +// isAgentHarnessRef reports whether the given namespace/name resolves to an +// AgentHarness CR. AgentHarnesses are not stored as DB agents but reuse the same +// session storage, so session handlers fall back to this lookup. +func (h *SessionsHandler) isAgentHarnessRef(ctx context.Context, namespace, name string) (bool, error) { + var ah v1alpha2.AgentHarness + if err := h.KubeClient.Get(ctx, types.NamespacedName{Namespace: namespace, Name: name}, &ah); err != nil { + if apierrors.IsNotFound(err) { + return false, nil + } + return false, err + } + return true, nil +} + // RunRequest represents a run creation request type RunRequest struct { Task string `json:"task"` @@ -62,15 +87,23 @@ func (h *SessionsHandler) HandleGetSessionsForAgent(w ErrorResponseWriter, r *ht return } - // Get agent ID from agent ref - agent, err := h.DatabaseService.GetAgent(r.Context(), utils.ConvertToPythonIdentifier(namespace+"/"+agentName)) - if err != nil { - w.RespondWithError(errors.NewNotFoundError("Agent not found", err)) - return + // Get agent ID from agent ref. AgentHarnesses are not stored as DB agents + // but reuse the same session storage keyed by the python-identifier ref. + agentID := utils.ConvertToPythonIdentifier(namespace + "/" + agentName) + if _, err := h.DatabaseService.GetAgent(r.Context(), agentID); err != nil { + isHarness, herr := h.isAgentHarnessRef(r.Context(), namespace, agentName) + if herr != nil { + w.RespondWithError(errors.NewInternalServerError("Failed to resolve agent ref", herr)) + return + } + if !isHarness { + w.RespondWithError(errors.NewNotFoundError("Agent not found", err)) + return + } } log.V(1).Info("Getting sessions for agent from database") - sessions, err := h.DatabaseService.ListSessionsForAgent(r.Context(), agent.ID, userID) + sessions, err := h.DatabaseService.ListSessionsForAgent(r.Context(), agentID, userID) if err != nil { w.RespondWithError(errors.NewInternalServerError("Failed to get sessions for agent", err)) return @@ -135,22 +168,32 @@ func (h *SessionsHandler) HandleCreateSession(w ErrorResponseWriter, r *http.Req log.V(1).Info("Getting agent from database", "session_request", sessionRequest) - agent, err := h.DatabaseService.GetAgent(r.Context(), utils.ConvertToPythonIdentifier(*sessionRequest.AgentRef)) + // AgentHarnesses are not stored as DB agents but reuse the same session + // storage; allow a session when the ref resolves to an AgentHarness CR. + agentID := utils.ConvertToPythonIdentifier(*sessionRequest.AgentRef) + agent, err := h.DatabaseService.GetAgent(r.Context(), agentID) if err != nil { - w.RespondWithError(errors.NewBadRequestError(fmt.Sprintf("Agent ref is invalid, please check the agent ref %s", *sessionRequest.AgentRef), err)) - return - } - - isSubstrateSandbox := false - if agent.WorkloadType == v1alpha2.WorkloadModeSandbox { - var lookupErr error - _, isSubstrateSandbox, lookupErr = h.lookupSubstrateSandboxAgent(r.Context(), *sessionRequest.AgentRef) + namespace, name, ok := splitAgentRef(*sessionRequest.AgentRef) + isHarness := false + if ok { + var herr error + if isHarness, herr = h.isAgentHarnessRef(r.Context(), namespace, name); herr != nil { + w.RespondWithError(errors.NewInternalServerError("Failed to resolve agent ref", herr)) + return + } + } + if !isHarness { + w.RespondWithError(errors.NewBadRequestError(fmt.Sprintf("Agent ref is invalid, please check the agent ref %s", *sessionRequest.AgentRef), err)) + return + } + } else if agent.WorkloadType == v1alpha2.WorkloadModeSandbox { + _, isSubstrateSandbox, lookupErr := h.lookupSubstrateSandboxAgent(r.Context(), *sessionRequest.AgentRef) if lookupErr != nil { w.RespondWithError(errors.NewInternalServerError("Failed to inspect sandbox agent", lookupErr)) return } if !isSubstrateSandbox { - existing, lerr := h.DatabaseService.ListSessionsForAgentAllUsers(r.Context(), agent.ID) + existing, lerr := h.DatabaseService.ListSessionsForAgentAllUsers(r.Context(), agentID) if lerr != nil { w.RespondWithError(errors.NewInternalServerError("Failed to list sessions for agent", lerr)) return @@ -166,7 +209,7 @@ func (h *SessionsHandler) HandleCreateSession(w ErrorResponseWriter, r *http.Req ID: id, Name: sessionRequest.Name, UserID: userID, - AgentID: &agent.ID, + AgentID: &agentID, Source: sessionRequest.Source, } @@ -311,6 +354,52 @@ func (h *SessionsHandler) HandleUpdateSession(w ErrorResponseWriter, r *http.Req RespondWithJSON(w, http.StatusOK, data) } +// HandleRenameSession handles PATCH /api/sessions/{session_id} requests, setting +// the session's display name. Unlike HandleUpdateSession it does not require the +// session to resolve to a DB agent, so it also works for AgentHarness sessions. +func (h *SessionsHandler) HandleRenameSession(w ErrorResponseWriter, r *http.Request) { + log := ctrllog.FromContext(r.Context()).WithName("sessions-handler").WithValues("operation", "rename-db") + + userID, err := GetUserID(r) + if err != nil { + w.RespondWithError(errors.NewBadRequestError("Failed to get user ID", err)) + return + } + + sessionID, err := GetPathParam(r, "session_id") + if err != nil { + w.RespondWithError(errors.NewBadRequestError("Failed to get session ID from path", err)) + return + } + log = log.WithValues("userID", userID, "session_id", sessionID) + + var sessionRequest api.SessionRequest + if err := DecodeJSONBody(r, &sessionRequest); err != nil { + w.RespondWithError(errors.NewBadRequestError("Invalid request body", err)) + return + } + if sessionRequest.Name == nil { + w.RespondWithError(errors.NewBadRequestError("session name is required", nil)) + return + } + + session, err := h.DatabaseService.GetSession(r.Context(), sessionID, userID) + if err != nil { + w.RespondWithError(errors.NewNotFoundError("Session not found", err)) + return + } + + session.Name = sessionRequest.Name + if err := h.DatabaseService.StoreSession(r.Context(), session); err != nil { + w.RespondWithError(errors.NewInternalServerError("Failed to rename session", err)) + return + } + + log.Info("Successfully renamed session") + data := api.NewResponse(session, "Successfully renamed session", false) + RespondWithJSON(w, http.StatusOK, data) +} + // HandleDeleteSession handles DELETE /api/sessions/{session_id} requests using database func (h *SessionsHandler) HandleDeleteSession(w ErrorResponseWriter, r *http.Request) { log := ctrllog.FromContext(r.Context()).WithName("sessions-handler").WithValues("operation", "delete-db") @@ -531,8 +620,5 @@ func (h *SessionsHandler) lookupSubstrateSandboxAgent(ctx context.Context, agent } return nil, false, err } - if v1alpha2.AgentSandboxPlatform(sa) != v1alpha2.SandboxPlatformSubstrate { - return nil, false, nil - } return sa, true, nil } diff --git a/go/core/internal/httpserver/handlers/sessions_test.go b/go/core/internal/httpserver/handlers/sessions_test.go index 949bfaa0c9..84f3f85187 100644 --- a/go/core/internal/httpserver/handlers/sessions_test.go +++ b/go/core/internal/httpserver/handlers/sessions_test.go @@ -269,9 +269,7 @@ func TestSessionsHandler(t *testing.T) { Name: "test-substrate-agent", Namespace: "kagent", }, - Spec: v1alpha2.SandboxAgentSpec{ - Platform: v1alpha2.SandboxPlatformSubstrate, - }, + Spec: v1alpha2.SandboxAgentSpec{}, }).Build() handler.KubeClient = kubeClient diff --git a/go/core/internal/httpserver/middleware.go b/go/core/internal/httpserver/middleware.go index 31280097e4..20f4548bee 100644 --- a/go/core/internal/httpserver/middleware.go +++ b/go/core/internal/httpserver/middleware.go @@ -5,7 +5,6 @@ import ( "fmt" "net" "net/http" - "strings" "time" "github.com/kagent-dev/kagent/go/core/internal/httpserver/handlers" @@ -78,18 +77,9 @@ func (w *statusResponseWriter) RespondWithError(err error) { } } -func isAgentHarnessGatewayPath(path string) bool { - if !strings.HasPrefix(path, "/api/agentharnesses/") { - return false - } - return strings.Contains(path, "/gateway") -} - func contentTypeMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if len(r.URL.Path) >= 4 && r.URL.Path[:4] == "/api" && - r.URL.Path != APIPathSandboxSSH && - !isAgentHarnessGatewayPath(r.URL.Path) { + if len(r.URL.Path) >= 4 && r.URL.Path[:4] == "/api" { w.Header().Set("Content-Type", "application/json") } next.ServeHTTP(w, r) diff --git a/go/core/internal/httpserver/server.go b/go/core/internal/httpserver/server.go index d81f592391..b255cdcf11 100644 --- a/go/core/internal/httpserver/server.go +++ b/go/core/internal/httpserver/server.go @@ -50,7 +50,6 @@ const ( APIPathFeedback = "/api/feedback" APIPathLangGraph = "/api/langgraph" APIPathCrewAI = "/api/crewai" - APIPathSandboxSSH = "/api/sandbox/ssh" APIPathAgentHarnessHarness = "/api/agentharnesses/{namespace}/{name}/" APIPathSubstrateStatus = "/api/substrate/status" ) @@ -78,6 +77,7 @@ type ServerConfig struct { SubstrateAteClient *substrate.Client MCPEgressPlaintext bool SubstrateSandboxActorBackend *substrate.SandboxAgentActorBackend + AgentHarnessSessionActor *substrate.AgentHarnessSessionActorBackend } // HTTPServer is the structure that manages the HTTP server @@ -109,6 +109,7 @@ func NewHTTPServer(config ServerConfig) (*HTTPServer, error) { config.SubstrateAteClient, config.MCPEgressPlaintext, config.SubstrateSandboxActorBackend, + config.AgentHarnessSessionActor, ), authenticator: config.Authenticator, }, nil @@ -245,6 +246,7 @@ func (s *HTTPServer) setupRoutes() { s.router.HandleFunc(APIPathSessions+"/{session_id}/tasks", adaptHandler(s.handlers.Sessions.HandleListTasksForSession)).Methods(http.MethodGet) s.router.HandleFunc(APIPathSessions+"/{session_id}", adaptHandler(s.handlers.Sessions.HandleDeleteSession)).Methods(http.MethodDelete) s.router.HandleFunc(APIPathSessions+"/{session_id}", adaptHandler(s.handlers.Sessions.HandleUpdateSession)).Methods(http.MethodPut) + s.router.HandleFunc(APIPathSessions+"/{session_id}", adaptHandler(s.handlers.Sessions.HandleRenameSession)).Methods(http.MethodPatch) s.router.HandleFunc(APIPathSessions+"/{session_id}/events", adaptHandler(s.handlers.Sessions.HandleAddEventToSession)).Methods(http.MethodPost) // Tasks @@ -324,10 +326,14 @@ func (s *HTTPServer) setupRoutes() { s.router.HandleFunc(APIPathCrewAI+"/flows/state", adaptHandler(s.handlers.CrewAI.HandleStoreFlowState)).Methods(http.MethodPost) s.router.HandleFunc(APIPathCrewAI+"/flows/state", adaptHandler(s.handlers.CrewAI.HandleGetFlowState)).Methods(http.MethodGet) - // OpenShell sandbox PTY (browser WebSocket → gateway CONNECT → SSH). Authenticated like other /api routes. - s.router.HandleFunc(APIPathSandboxSSH, adaptHandler(s.handlers.HandleSandboxSSHWebSocket)).Methods(http.MethodGet) + // Substrate harness per-session actor lifecycle (provision on New Chat, + // suspend from the chat UI). Registered before the /acp gateway catch-all so + // these specific paths win over the PathPrefix match. + s.router.HandleFunc(APIPathAgentHarnesses+"/{namespace}/{name}/sessions/{session_id}/ensure", adaptHandler(s.handlers.HandleEnsureAgentHarnessSessionActor)).Methods(http.MethodPost) + s.router.HandleFunc(APIPathAgentHarnesses+"/{namespace}/{name}/sessions/{session_id}/suspend", adaptHandler(s.handlers.HandleSuspendAgentHarnessSessionActor)).Methods(http.MethodPost) + s.router.HandleFunc(APIPathAgentHarnesses+"/{namespace}/{name}/sessions/{session_id}/status", adaptHandler(s.handlers.HandleGetAgentHarnessSessionActor)).Methods(http.MethodGet) - // Substrate OpenClaw gateway proxy (HTTP + WebSocket) via atenet-router. + // Substrate harness /acp WebSocket proxy via atenet-router. s.router.PathPrefix(APIPathAgentHarnessHarness).Handler( adaptHandler(s.handlers.HandleAgentHarnessGateway), ) @@ -359,10 +365,8 @@ func wsAuthQueryMiddleware(next http.Handler) http.Handler { } var token string switch { - case r.URL.Path == APIPathSandboxSSH || strings.HasSuffix(r.URL.Path, "/ssh"): + case strings.HasSuffix(r.URL.Path, "/ssh"): token = r.URL.Query().Get("access_token") - case isAgentHarnessGatewayPath(r.URL.Path): - token = r.URL.Query().Get("token") } if token != "" { r.Header.Set("Authorization", "Bearer "+strings.TrimSpace(token)) diff --git a/go/core/pkg/acpshim/child.go b/go/core/pkg/acpshim/child.go new file mode 100644 index 0000000000..668a3b7cd4 --- /dev/null +++ b/go/core/pkg/acpshim/child.go @@ -0,0 +1,141 @@ +package acpshim + +import ( + "bufio" + "fmt" + "io" + "log" + "os" + "os/exec" + "sync" + "syscall" + "time" +) + +// maxLineSize bounds a single JSON-RPC line read from the child. ACP +// messages can embed file contents, so this is generous. +const maxLineSize = 16 * 1024 * 1024 // 16 MiB + +// child wraps a running stdio ACP agent subprocess. It exposes the child's +// stdout as a channel of newline-delimited lines and serializes writes to +// its stdin. The channel applies natural backpressure: if no client is +// draining it, the OS pipe buffer eventually fills and the child blocks. +type child struct { + cmd *exec.Cmd + stdin io.WriteCloser + + // out carries one element per stdout line (without trailing newline). + out chan []byte + // done is closed when the process has exited and out is drained-closed. + done chan struct{} + + writeMu sync.Mutex + + exitMu sync.Mutex + exitErr error // valid after done is closed +} + +// startChild spawns the configured agent process and begins reading its +// stdout. Child stderr is passed through to the shim's stderr so it lands +// in container logs. +func startChild(cfg *Config) (*child, error) { + cmd := exec.Command(cfg.ChildArgv[0], cfg.ChildArgv[1:]...) + cmd.Dir = cfg.ChildDir + cmd.Env = append(os.Environ(), cfg.ChildEnv...) + cmd.Stderr = os.Stderr + // Put the child in its own process group so terminate() can signal the + // whole tree (agents commonly fork helpers). + cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} + + stdin, err := cmd.StdinPipe() + if err != nil { + return nil, fmt.Errorf("failed to create stdin pipe: %w", err) + } + stdout, err := cmd.StdoutPipe() + if err != nil { + return nil, fmt.Errorf("failed to create stdout pipe: %w", err) + } + if err := cmd.Start(); err != nil { + return nil, fmt.Errorf("failed to start child %q: %w", cfg.ChildArgv[0], err) + } + log.Printf("acp-shim: started child pid=%d argv=%q", cmd.Process.Pid, cfg.ChildArgv) + + c := &child{ + cmd: cmd, + stdin: stdin, + out: make(chan []byte, 64), + done: make(chan struct{}), + } + + go func() { + sc := bufio.NewScanner(stdout) + sc.Buffer(make([]byte, 64*1024), maxLineSize) + for sc.Scan() { + line := make([]byte, len(sc.Bytes())) + copy(line, sc.Bytes()) + c.out <- line + } + if err := sc.Err(); err != nil { + log.Printf("acp-shim: child stdout read error: %v", err) + } + err := cmd.Wait() + c.exitMu.Lock() + c.exitErr = err + c.exitMu.Unlock() + close(c.out) + close(c.done) + log.Printf("acp-shim: child pid=%d exited: %v", cmd.Process.Pid, err) + }() + + return c, nil +} + +// writeLine writes one JSON-RPC line (newline appended) to the child stdin. +func (c *child) writeLine(line []byte) error { + c.writeMu.Lock() + defer c.writeMu.Unlock() + if _, err := c.stdin.Write(line); err != nil { + return fmt.Errorf("failed to write to child stdin: %w", err) + } + if _, err := c.stdin.Write([]byte{'\n'}); err != nil { + return fmt.Errorf("failed to write newline to child stdin: %w", err) + } + return nil +} + +// exited reports whether the child process has exited. +func (c *child) exited() bool { + select { + case <-c.done: + return true + default: + return false + } +} + +// exitError returns the child's exit error (nil for clean exit). Only +// meaningful after done is closed. +func (c *child) exitError() error { + c.exitMu.Lock() + defer c.exitMu.Unlock() + return c.exitErr +} + +// terminate sends SIGTERM to the child's process group, waits up to grace, +// then SIGKILLs. It blocks until the process has exited. +func (c *child) terminate(grace time.Duration) { + if c.exited() { + return + } + pgid := -c.cmd.Process.Pid + _ = c.stdin.Close() + _ = syscall.Kill(pgid, syscall.SIGTERM) + select { + case <-c.done: + return + case <-time.After(grace): + log.Printf("acp-shim: child pid=%d did not exit within %s, sending SIGKILL", c.cmd.Process.Pid, grace) + _ = syscall.Kill(pgid, syscall.SIGKILL) + <-c.done + } +} diff --git a/go/core/pkg/acpshim/config.go b/go/core/pkg/acpshim/config.go new file mode 100644 index 0000000000..71f506c10f --- /dev/null +++ b/go/core/pkg/acpshim/config.go @@ -0,0 +1,88 @@ +// Package acpshim implements the agent-agnostic WebSocket↔stdio shim that +// exposes a stdio ACP agent (Hermes, Codex, Gemini CLI, openclaw acp, ...) +// over a WebSocket endpoint reachable through Substrate's atenet ingress. +// +// The shim deliberately knows nothing about ACP semantics: it pumps frames +// (one WebSocket text frame ⇄ one newline-delimited JSON-RPC line on the +// child's stdin/stdout) and couples the child process lifecycle to the +// connection. All protocol translation lives on the kagent controller side. +package acpshim + +import ( + "fmt" + "os" + "strings" + "time" +) + +// ChildPolicy controls how the child agent process lifecycle relates to +// WebSocket connections. +type ChildPolicy string + +const ( + // ChildPolicyLongLived starts the child once and keeps it alive across + // reconnects (required for agents with in-memory sessions, e.g. Hermes). + ChildPolicyLongLived ChildPolicy = "long-lived" + // ChildPolicyPerConnection starts a fresh child per WebSocket connection + // and terminates it when the connection closes. + ChildPolicyPerConnection ChildPolicy = "per-connection" +) + +// Config holds the shim's runtime configuration. The child command is the +// only per-backend part; everything else is uniform across agents. +type Config struct { + // ListenAddr is the address the WebSocket server binds to, e.g. ":9000". + ListenAddr string + // TokenFile is the path to the bearer token used to authenticate the + // WebSocket handshake. If empty, Token must be set (or auth is disabled + // when both are empty — prototype/testing only). + TokenFile string + // Token is the literal bearer token. Takes precedence over TokenFile. + Token string + // ChildArgv is the argv vector of the stdio ACP agent to run. Never + // interpreted by a shell. + ChildArgv []string + // ChildDir is the working directory for the child process. + ChildDir string + // ChildEnv is extra environment for the child, appended to os.Environ(). + ChildEnv []string + // Policy selects long-lived vs per-connection child lifecycle. + Policy ChildPolicy + // GracePeriod is how long to wait after SIGTERM before SIGKILL. + GracePeriod time.Duration + // ReconnectGrace is how long a long-lived child is kept alive after the + // last client disconnects before the shim terminates it. Zero means + // keep the child alive indefinitely. + ReconnectGrace time.Duration +} + +// Validate checks the config and resolves the token from TokenFile if needed. +func (c *Config) Validate() error { + if c.ListenAddr == "" { + return fmt.Errorf("listen address is required") + } + if len(c.ChildArgv) == 0 { + return fmt.Errorf("child command is required") + } + switch c.Policy { + case ChildPolicyLongLived, ChildPolicyPerConnection: + case "": + c.Policy = ChildPolicyLongLived + default: + return fmt.Errorf("invalid child policy %q (want %q or %q)", c.Policy, ChildPolicyLongLived, ChildPolicyPerConnection) + } + if c.GracePeriod <= 0 { + c.GracePeriod = 5 * time.Second + } + if c.Token == "" && c.TokenFile != "" { + b, err := os.ReadFile(c.TokenFile) + if err != nil { + return fmt.Errorf("failed to read token file %s: %w", c.TokenFile, err) + } + c.Token = strings.TrimSpace(string(b)) + if c.Token == "" { + return fmt.Errorf("token file %s is empty", c.TokenFile) + } + } + return nil +} diff --git a/go/core/pkg/acpshim/server.go b/go/core/pkg/acpshim/server.go new file mode 100644 index 0000000000..2abf9a2c03 --- /dev/null +++ b/go/core/pkg/acpshim/server.go @@ -0,0 +1,274 @@ +package acpshim + +import ( + "context" + "crypto/subtle" + "fmt" + "log" + "net" + "net/http" + "sync" + "time" + + "github.com/gorilla/websocket" +) + +// WebSocket close codes (4000-4999 is the application-defined range) used so +// the bridge can distinguish why the stream ended. +const ( + // CloseChildExited signals the child agent exited cleanly (status 0). + CloseChildExited = 4000 + // CloseChildFailed signals the child agent exited with an error. + CloseChildFailed = 4001 +) + +// Server is the shim's WebSocket server. It accepts at most one client at a +// time (the kagent bridge owns the stream) and pumps frames between the +// client and the child agent's stdio. +type Server struct { + cfg *Config + upgrader websocket.Upgrader + httpSrv *http.Server + + mu sync.Mutex + child *child + connBusy bool + graceTimer *time.Timer +} + +// NewServer creates a Server from a validated Config. +func NewServer(cfg *Config) *Server { + s := &Server{ + cfg: cfg, + upgrader: websocket.Upgrader{ + ReadBufferSize: 64 * 1024, + WriteBufferSize: 64 * 1024, + // Browser clients (e.g. the kagent UI) connect cross-origin. The + // shim authenticates with an explicit bearer token rather than + // ambient cookie credentials, so Origin checks add no protection. + CheckOrigin: func(*http.Request) bool { return true }, + }, + } + mux := http.NewServeMux() + // Alias used by infrastructure probes (e.g. kagent's substrate actor + // reachability check hits /health through atenet-router). + mux.HandleFunc("/health", s.handleHealth) + mux.HandleFunc("/acp", s.handleACP) + s.httpSrv = &http.Server{ + Addr: cfg.ListenAddr, + Handler: mux, + ReadHeaderTimeout: 10 * time.Second, + } + return s +} + +// ListenAndServe runs the server until Shutdown is called. +func (s *Server) ListenAndServe() error { + l, err := net.Listen("tcp", s.cfg.ListenAddr) + if err != nil { + return fmt.Errorf("failed to listen on %s: %w", s.cfg.ListenAddr, err) + } + return s.Serve(l) +} + +// Serve runs the server on the given listener (used by tests to bind an +// ephemeral port). +func (s *Server) Serve(l net.Listener) error { + log.Printf("acp-shim: listening on %s (policy=%s)", l.Addr(), s.cfg.Policy) + err := s.httpSrv.Serve(l) + if err == http.ErrServerClosed { + return nil + } + return err +} + +// Shutdown stops the HTTP server and terminates any running child. +func (s *Server) Shutdown(ctx context.Context) error { + err := s.httpSrv.Shutdown(ctx) + s.mu.Lock() + c := s.child + s.child = nil + if s.graceTimer != nil { + s.graceTimer.Stop() + s.graceTimer = nil + } + s.mu.Unlock() + if c != nil { + c.terminate(s.cfg.GracePeriod) + } + return err +} + +func (s *Server) handleHealth(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("ok")) +} + +// authorized checks the bearer token on the WebSocket handshake. The token +// may arrive in the Authorization header or, for clients that cannot set +// headers, the access_token query parameter. +func (s *Server) authorized(r *http.Request) bool { + if s.cfg.Token == "" { + return true // auth disabled (prototype/testing only) + } + presented := "" + if h := r.Header.Get("Authorization"); len(h) > 7 && h[:7] == "Bearer " { + presented = h[7:] + } else if q := r.URL.Query().Get("access_token"); q != "" { + presented = q + } + return subtle.ConstantTimeCompare([]byte(presented), []byte(s.cfg.Token)) == 1 +} + +func (s *Server) handleACP(w http.ResponseWriter, r *http.Request) { + if !s.authorized(r) { + http.Error(w, "unauthorized", http.StatusUnauthorized) + return + } + + // Exactly-one-client: the kagent bridge owns this stream. Reject + // concurrent connections instead of multiplexing. + s.mu.Lock() + if s.connBusy { + s.mu.Unlock() + http.Error(w, "another client is connected", http.StatusConflict) + return + } + s.connBusy = true + if s.graceTimer != nil { + s.graceTimer.Stop() + s.graceTimer = nil + } + s.mu.Unlock() + + defer func() { + s.mu.Lock() + s.connBusy = false + s.mu.Unlock() + }() + + c, err := s.acquireChild() + if err != nil { + log.Printf("acp-shim: failed to start child: %v", err) + http.Error(w, "failed to start agent", http.StatusBadGateway) + return + } + + conn, err := s.upgrader.Upgrade(w, r, nil) + if err != nil { + log.Printf("acp-shim: websocket upgrade failed: %v", err) + s.releaseChild(c) + return + } + log.Printf("acp-shim: client connected from %s", r.RemoteAddr) + + s.pump(conn, c) + + _ = conn.Close() + s.releaseChild(c) + log.Printf("acp-shim: client %s disconnected", r.RemoteAddr) +} + +// acquireChild returns the child process for a new connection, starting one +// according to the configured policy. +func (s *Server) acquireChild() (*child, error) { + s.mu.Lock() + defer s.mu.Unlock() + + if s.cfg.Policy == ChildPolicyLongLived && s.child != nil && !s.child.exited() { + return s.child, nil + } + c, err := startChild(s.cfg) + if err != nil { + return nil, err + } + s.child = c + return c, nil +} + +// releaseChild handles child lifecycle when a connection ends: terminate for +// per-connection policy, or arm the reconnect grace timer for long-lived. +func (s *Server) releaseChild(c *child) { + if s.cfg.Policy == ChildPolicyPerConnection { + s.mu.Lock() + if s.child == c { + s.child = nil + } + s.mu.Unlock() + c.terminate(s.cfg.GracePeriod) + return + } + // Long-lived: keep the child alive so the next connection can resume + // its sessions, unless a reconnect grace window is configured. + if s.cfg.ReconnectGrace <= 0 || c.exited() { + return + } + s.mu.Lock() + defer s.mu.Unlock() + if s.graceTimer != nil { + s.graceTimer.Stop() + } + s.graceTimer = time.AfterFunc(s.cfg.ReconnectGrace, func() { + s.mu.Lock() + busy := s.connBusy + if !busy && s.child == c { + s.child = nil + } + s.mu.Unlock() + if !busy { + log.Printf("acp-shim: reconnect grace expired, terminating child") + c.terminate(s.cfg.GracePeriod) + } + }) +} + +// pump moves frames between the WebSocket and the child's stdio until either +// side ends. One WebSocket text frame corresponds to one newline-delimited +// JSON-RPC line; the shim never parses the payload. +func (s *Server) pump(conn *websocket.Conn, c *child) { + readerDone := make(chan struct{}) + + // WebSocket → child stdin. + go func() { + defer close(readerDone) + for { + msgType, data, err := conn.ReadMessage() + if err != nil { + return + } + if msgType != websocket.TextMessage && msgType != websocket.BinaryMessage { + continue + } + if err := c.writeLine(data); err != nil { + log.Printf("acp-shim: %v", err) + return + } + } + }() + + // Child stdout → WebSocket. + for { + select { + case line, ok := <-c.out: + if !ok { + // Child exited: tell the client why with a distinguishable + // close code so the bridge can decide whether to restart. + code := CloseChildExited + reason := "agent exited" + if err := c.exitError(); err != nil { + code = CloseChildFailed + reason = fmt.Sprintf("agent exited: %v", err) + } + msg := websocket.FormatCloseMessage(code, reason) + _ = conn.WriteControl(websocket.CloseMessage, msg, time.Now().Add(5*time.Second)) + return + } + if err := conn.WriteMessage(websocket.TextMessage, line); err != nil { + log.Printf("acp-shim: websocket write failed: %v", err) + return + } + case <-readerDone: + return + } + } +} diff --git a/go/core/pkg/acpshim/server_test.go b/go/core/pkg/acpshim/server_test.go new file mode 100644 index 0000000000..db9169c51a --- /dev/null +++ b/go/core/pkg/acpshim/server_test.go @@ -0,0 +1,292 @@ +package acpshim + +import ( + "context" + "net" + "net/http" + "os" + "path/filepath" + "testing" + "time" + + "github.com/gorilla/websocket" +) + +func TestConfigValidate(t *testing.T) { + tokenFile := filepath.Join(t.TempDir(), "token") + if err := os.WriteFile(tokenFile, []byte("s3cret\n"), 0o600); err != nil { + t.Fatal(err) + } + + tests := []struct { + name string + cfg Config + wantErr bool + wantToken string + }{ + { + name: "missing listen addr", + cfg: Config{ChildArgv: []string{"cat"}}, + wantErr: true, + }, + { + name: "missing child command", + cfg: Config{ListenAddr: ":0"}, + wantErr: true, + }, + { + name: "invalid policy", + cfg: Config{ListenAddr: ":0", ChildArgv: []string{"cat"}, Policy: "sometimes"}, + wantErr: true, + }, + { + name: "defaults applied", + cfg: Config{ListenAddr: ":0", ChildArgv: []string{"cat"}}, + }, + { + name: "token loaded and trimmed from file", + cfg: Config{ListenAddr: ":0", ChildArgv: []string{"cat"}, TokenFile: tokenFile}, + wantToken: "s3cret", + }, + { + name: "missing token file", + cfg: Config{ListenAddr: ":0", ChildArgv: []string{"cat"}, TokenFile: "/nonexistent/token"}, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.cfg.Validate() + if (err != nil) != tt.wantErr { + t.Fatalf("Validate() error = %v, wantErr %v", err, tt.wantErr) + } + if err != nil { + return + } + if tt.cfg.Policy != ChildPolicyLongLived && tt.cfg.Policy != ChildPolicyPerConnection { + t.Errorf("Validate() did not default policy, got %q", tt.cfg.Policy) + } + if tt.wantToken != "" && tt.cfg.Token != tt.wantToken { + t.Errorf("Validate() token = %q, want %q", tt.cfg.Token, tt.wantToken) + } + }) + } +} + +// startTestServer runs a shim Server on an ephemeral port and returns the +// ws:// URL of the ACP endpoint. +func startTestServer(t *testing.T, cfg *Config) string { + t.Helper() + cfg.ListenAddr = "127.0.0.1:0" + if err := cfg.Validate(); err != nil { + t.Fatalf("config: %v", err) + } + l, err := net.Listen("tcp", cfg.ListenAddr) + if err != nil { + t.Fatalf("listen: %v", err) + } + srv := NewServer(cfg) + go func() { _ = srv.Serve(l) }() + t.Cleanup(func() { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + _ = srv.Shutdown(ctx) + }) + return "ws://" + l.Addr().String() + "/acp" +} + +func dial(t *testing.T, url, token string) *websocket.Conn { + t.Helper() + h := http.Header{} + if token != "" { + h.Set("Authorization", "Bearer "+token) + } + conn, _, err := websocket.DefaultDialer.Dial(url, h) + if err != nil { + t.Fatalf("dial %s: %v", url, err) + } + return conn +} + +func TestEchoRoundTrip(t *testing.T) { + url := startTestServer(t, &Config{ChildArgv: []string{"cat"}}) + conn := dial(t, url, "") + defer conn.Close() + + msg := `{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":1}}` + if err := conn.WriteMessage(websocket.TextMessage, []byte(msg)); err != nil { + t.Fatalf("write: %v", err) + } + _ = conn.SetReadDeadline(time.Now().Add(5 * time.Second)) + _, got, err := conn.ReadMessage() + if err != nil { + t.Fatalf("read: %v", err) + } + if string(got) != msg { + t.Errorf("round trip = %q, want %q", got, msg) + } +} + +func TestAuth(t *testing.T) { + tests := []struct { + name string + token string + wantErr bool + }{ + {name: "correct token", token: "hunter2", wantErr: false}, + {name: "wrong token", token: "wrong", wantErr: true}, + {name: "missing token", token: "", wantErr: true}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + url := startTestServer(t, &Config{ChildArgv: []string{"cat"}, Token: "hunter2"}) + h := http.Header{} + if tt.token != "" { + h.Set("Authorization", "Bearer "+tt.token) + } + conn, resp, err := websocket.DefaultDialer.Dial(url, h) + if (err != nil) != tt.wantErr { + t.Fatalf("Dial() error = %v, wantErr %v", err, tt.wantErr) + } + if conn != nil { + conn.Close() + } + if tt.wantErr && resp != nil && resp.StatusCode != http.StatusUnauthorized { + t.Errorf("status = %d, want %d", resp.StatusCode, http.StatusUnauthorized) + } + }) + } +} + +func TestSecondClientRejected(t *testing.T) { + url := startTestServer(t, &Config{ChildArgv: []string{"cat"}}) + conn := dial(t, url, "") + defer conn.Close() + + _, resp, err := websocket.DefaultDialer.Dial(url, nil) + if err == nil { + t.Fatal("second Dial() succeeded, want rejection") + } + if resp == nil || resp.StatusCode != http.StatusConflict { + t.Errorf("second dial status = %v, want %d", resp, http.StatusConflict) + } +} + +func TestChildExitCloseCodes(t *testing.T) { + tests := []struct { + name string + argv []string + wantCode int + }{ + { + name: "clean exit", + argv: []string{"sh", "-c", "echo done; exit 0"}, + wantCode: CloseChildExited, + }, + { + name: "failed exit", + argv: []string{"sh", "-c", "echo oops; exit 3"}, + wantCode: CloseChildFailed, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + url := startTestServer(t, &Config{ChildArgv: tt.argv, Policy: ChildPolicyPerConnection}) + conn := dial(t, url, "") + defer conn.Close() + + _ = conn.SetReadDeadline(time.Now().Add(5 * time.Second)) + // First read gets the child's output line. + if _, _, err := conn.ReadMessage(); err != nil { + t.Fatalf("read output line: %v", err) + } + // Next read should observe the close frame with the right code. + _, _, err := conn.ReadMessage() + closeErr, ok := err.(*websocket.CloseError) + if !ok { + t.Fatalf("read after child exit = %v, want *websocket.CloseError", err) + } + if closeErr.Code != tt.wantCode { + t.Errorf("close code = %d, want %d", closeErr.Code, tt.wantCode) + } + }) + } +} + +func TestLongLivedChildSurvivesReconnect(t *testing.T) { + // The child prints "ready" exactly once at startup. If the second + // connection echoes our ping without a second "ready", the same child + // survived the reconnect. + url := startTestServer(t, &Config{ + ChildArgv: []string{"sh", "-c", "echo ready; cat"}, + Policy: ChildPolicyLongLived, + }) + + conn1 := dial(t, url, "") + _ = conn1.SetReadDeadline(time.Now().Add(5 * time.Second)) + _, got, err := conn1.ReadMessage() + if err != nil || string(got) != "ready" { + t.Fatalf("first read = %q, %v; want \"ready\"", got, err) + } + conn1.Close() + + // Give the server a moment to release the connection slot. + deadline := time.Now().Add(5 * time.Second) + var conn2 *websocket.Conn + for { + c, _, err := websocket.DefaultDialer.Dial(url, nil) + if err == nil { + conn2 = c + break + } + if time.Now().After(deadline) { + t.Fatalf("reconnect: %v", err) + } + time.Sleep(10 * time.Millisecond) + } + defer conn2.Close() + + if err := conn2.WriteMessage(websocket.TextMessage, []byte("ping")); err != nil { + t.Fatalf("write: %v", err) + } + _ = conn2.SetReadDeadline(time.Now().Add(5 * time.Second)) + _, got, err = conn2.ReadMessage() + if err != nil { + t.Fatalf("read: %v", err) + } + if string(got) != "ping" { + t.Errorf("after reconnect read %q, want \"ping\" (a second \"ready\" means the child was restarted)", got) + } +} + +func TestPerConnectionChildRestarted(t *testing.T) { + url := startTestServer(t, &Config{ + ChildArgv: []string{"sh", "-c", "echo ready; cat"}, + Policy: ChildPolicyPerConnection, + }) + + for i := 0; i < 2; i++ { + var conn *websocket.Conn + deadline := time.Now().Add(5 * time.Second) + for { + c, _, err := websocket.DefaultDialer.Dial(url, nil) + if err == nil { + conn = c + break + } + if time.Now().After(deadline) { + t.Fatalf("connection %d: %v", i, err) + } + time.Sleep(10 * time.Millisecond) + } + _ = conn.SetReadDeadline(time.Now().Add(5 * time.Second)) + _, got, err := conn.ReadMessage() + if err != nil || string(got) != "ready" { + t.Fatalf("connection %d read = %q, %v; want \"ready\" (fresh child per connection)", i, got, err) + } + conn.Close() + } +} diff --git a/go/core/pkg/app/app.go b/go/core/pkg/app/app.go index 6ee09b0e8c..e27a10e671 100644 --- a/go/core/pkg/app/app.go +++ b/go/core/pkg/app/app.go @@ -59,7 +59,6 @@ import ( "github.com/kagent-dev/kagent/go/core/pkg/migrations" "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend" "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openclaw" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openshell" "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/substrate" "github.com/kagent-dev/kagent/go/core/pkg/translator" "k8s.io/apimachinery/pkg/runtime" @@ -80,7 +79,6 @@ import ( "github.com/kagent-dev/kagent/go/core/internal/controller" "github.com/kagent-dev/kmcp/api/v1alpha1" corev1 "k8s.io/api/core/v1" - agentsandboxv1 "sigs.k8s.io/agent-sandbox/api/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" // +kubebuilder:scaffold:imports ) @@ -101,7 +99,6 @@ func init() { utilruntime.Must(v1alpha1.AddToScheme(scheme)) utilruntime.Must(v1alpha2.AddToScheme(scheme)) - utilruntime.Must(agentsandboxv1.AddToScheme(scheme)) utilruntime.Must(atev1alpha1.AddToScheme(scheme)) // +kubebuilder:scaffold:scheme } @@ -145,15 +142,6 @@ type Config struct { UrlFile string VectorEnabled bool } - Openshell struct { - GatewayURL string - Token string - TokenFile string - CAFile string - Insecure bool - DialTimeout time.Duration - CallTimeout time.Duration - } Substrate struct { AteAPIEndpoint string AteAPITokenFile string @@ -219,14 +207,6 @@ func (cfg *Config) SetFlags(commandLine *flag.FlagSet) { commandLine.StringVar(&agent_translator.DefaultSkillsInitImageConfig.PullPolicy, "skills-init-image-pull-policy", agent_translator.DefaultSkillsInitImageConfig.PullPolicy, "The pull policy to use for the skills init image.") commandLine.StringVar(&agent_translator.DefaultSkillsInitImageConfig.Repository, "skills-init-image-repository", agent_translator.DefaultSkillsInitImageConfig.Repository, "The repository to use for the skills init image.") - commandLine.StringVar(&cfg.Openshell.GatewayURL, "openshell-gateway-url", "", "gRPC target for the OpenShell sandbox gateway (e.g. dns:///openshell.openshell.svc:443). When empty, the Sandbox controller is disabled.") - commandLine.StringVar(&cfg.Openshell.Token, "openshell-token", "", "Static bearer token for the OpenShell gateway. Prefer --openshell-token-file for secrets.") - commandLine.StringVar(&cfg.Openshell.TokenFile, "openshell-token-file", "", "Path to a file containing the OpenShell gateway bearer token. Takes precedence over --openshell-token.") - commandLine.StringVar(&cfg.Openshell.CAFile, "openshell-tls-ca-file", "", "Path to a PEM file containing CA bundle for verifying the OpenShell gateway TLS certificate. Optional.") - commandLine.BoolVar(&cfg.Openshell.Insecure, "openshell-insecure", false, "Dial the OpenShell gateway without TLS. Use only for local development.") - commandLine.DurationVar(&cfg.Openshell.DialTimeout, "openshell-dial-timeout", 10*time.Second, "Timeout for the initial dial to the OpenShell gateway.") - commandLine.DurationVar(&cfg.Openshell.CallTimeout, "openshell-call-timeout", 30*time.Second, "Per-RPC timeout for OpenShell gateway calls.") - commandLine.StringVar(&cfg.Substrate.AteAPIEndpoint, "substrate-ate-api-endpoint", "", "gRPC target for Agent Substrate ate-api (e.g. dns:///api.ate-system.svc:443). Enables substrate AgentHarness runtime when set.") commandLine.StringVar(&cfg.Substrate.AteAPITokenFile, "substrate-ate-api-token-file", "", "Path to a Kubernetes projected service account token used as an ate-api bearer token.") commandLine.StringVar(&cfg.Substrate.AtenetRouterURL, "substrate-atenet-router-url", "", "HTTP URL for Substrate atenet-router (Envoy). Defaults to http://atenet-router.ate-system.svc:80 when unset.") @@ -546,6 +526,7 @@ func Start(getExtensionConfig GetExtensionConfig, migrationRunner MigrationRunne var substrateAteClient *substrate.Client var substrateLifecycle *substrate.Lifecycle var substrateSandboxActorBackend *substrate.SandboxAgentActorBackend + var agentHarnessSessionActorBackend *substrate.AgentHarnessSessionActorBackend if cfg.Substrate.AteAPIEndpoint != "" { var dialErr error substrateAteClient, dialErr = substrate.Dial(ctx, substrateAppConfig(&cfg)) @@ -559,8 +540,9 @@ func Start(getExtensionConfig GetExtensionConfig, migrationRunner MigrationRunne atenetRouterURL = substrate.DefaultAtenetRouterURL } substrateSandboxActorBackend = substrate.NewSandboxAgentActorBackend(substrateAteClient, atenetRouterURL) + agentHarnessSessionActorBackend = substrate.NewAgentHarnessSessionActorBackend(substrateAteClient, atenetRouterURL) agentsSubstrate := substrate.NewAgentsBackend(substrateLifecycle, substrateAteClient) - extensionCfg.SandboxBackend = sandboxbackend.NewRoutingBackend(extensionCfg.SandboxBackend, agentsSubstrate) + extensionCfg.SandboxBackend = agentsSubstrate } apiTranslator := agent_translator.NewAdkApiTranslatorWithWatchedNamespaces( @@ -609,21 +591,10 @@ func Start(getExtensionConfig GetExtensionConfig, migrationRunner MigrationRunne } kubeClient := mgr.GetClient() - var openshellOpenClawBackend sandboxbackend.AsyncBackend - var openshellHermesBackend sandboxbackend.AsyncBackend - if cfg.Openshell.GatewayURL != "" { - var err error - openshellOpenClawBackend, openshellHermesBackend, err = buildOpenshellSandboxBackends(ctx, &cfg, kubeClient) - if err != nil { - setupLog.Error(err, "unable to build openshell sandbox backends") - os.Exit(1) - } - } - var substrateOpenClawBackend sandboxbackend.AsyncBackend - var substrateNemoClawBackend sandboxbackend.AsyncBackend + var substrateHarnessBackends map[v1alpha2.AgentHarnessBackendType]sandboxbackend.AsyncBackend if cfg.Substrate.AteAPIEndpoint != "" { var err error - substrateOpenClawBackend, substrateNemoClawBackend, err = buildSubstrateHarnessBackends(ctx, &cfg, substrateAteClient) + substrateHarnessBackends, err = buildSubstrateHarnessBackends(ctx, &cfg, substrateAteClient) if err != nil { setupLog.Error(err, "unable to build substrate harness backends") os.Exit(1) @@ -641,31 +612,20 @@ func Start(getExtensionConfig GetExtensionConfig, migrationRunner MigrationRunne setupLog.Error(err, "unable to create controller", "controller", "SandboxAgent") os.Exit(1) } - if openshellOpenClawBackend != nil || openshellHermesBackend != nil { - if err := (&controller.OpenShellAgentHarnessController{ - Client: kubeClient, - Recorder: mgr.GetEventRecorder("agentharness-openshell-controller"), - OpenClawBackend: openshellOpenClawBackend, - HermesBackend: openshellHermesBackend, - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "OpenShellAgentHarness") - os.Exit(1) - } - } - if substrateOpenClawBackend != nil || substrateNemoClawBackend != nil { + if len(substrateHarnessBackends) > 0 { if err := (&controller.SubstrateAgentHarnessController{ - Client: kubeClient, - Recorder: mgr.GetEventRecorder("agentharness-substrate-controller"), - OpenClawBackend: substrateOpenClawBackend, - NemoClawBackend: substrateNemoClawBackend, - SubstrateLifecycle: substrateLifecycle, + Client: kubeClient, + Recorder: mgr.GetEventRecorder("agentharness-substrate-controller"), + Backends: substrateHarnessBackends, + SubstrateLifecycle: substrateLifecycle, + SessionActorBackend: agentHarnessSessionActorBackend, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "SubstrateAgentHarness") os.Exit(1) } } - if openshellOpenClawBackend == nil && openshellHermesBackend == nil && substrateOpenClawBackend == nil && substrateNemoClawBackend == nil { - setupLog.Info("AgentHarness controller disabled: set --openshell-gateway-url and/or --substrate-ate-api-endpoint") + if len(substrateHarnessBackends) == 0 { + setupLog.Info("AgentHarness controller disabled: set --substrate-ate-api-endpoint") } if err = (&controller.ModelConfigController{ @@ -792,6 +752,7 @@ func Start(getExtensionConfig GetExtensionConfig, migrationRunner MigrationRunne SubstrateAteClient: substrateAteClient, MCPEgressPlaintext: cfg.MCPEgressPlaintext, SubstrateSandboxActorBackend: substrateSandboxActorBackend, + AgentHarnessSessionActor: agentHarnessSessionActorBackend, }) if err != nil { setupLog.Error(err, "unable to create HTTP server") @@ -815,51 +776,20 @@ func Start(getExtensionConfig GetExtensionConfig, migrationRunner MigrationRunne } } -// buildOpenshellSandboxBackends constructs AsyncBackend values for openclaw and -// nemoclaw from flag config. It dials the gateway once; OpenShell and Inference RPCs -// share that connection (see openshell.OpenShellClients). The connection is not explicitly -// closed today — same lifetime as the process. -func buildOpenshellSandboxBackends(ctx context.Context, cfg *Config, kubeClient client.Client) (sandboxbackend.AsyncBackend, sandboxbackend.AsyncBackend, error) { - oc := openshell.Config{ - GatewayURL: cfg.Openshell.GatewayURL, - Token: cfg.Openshell.Token, - Insecure: cfg.Openshell.Insecure, - DialTimeout: cfg.Openshell.DialTimeout, - CallTimeout: cfg.Openshell.CallTimeout, - } - if cfg.Openshell.TokenFile != "" { - data, err := os.ReadFile(cfg.Openshell.TokenFile) - if err != nil { - return nil, nil, fmt.Errorf("read openshell token file: %w", err) - } - oc.Token = strings.TrimSpace(string(data)) - } - if cfg.Openshell.CAFile != "" { - data, err := os.ReadFile(cfg.Openshell.CAFile) - if err != nil { - return nil, nil, fmt.Errorf("read openshell CA file: %w", err) - } - oc.TLSCAPEM = data - } - clients, err := openshell.Dial(ctx, oc) - if err != nil { - return nil, nil, err - } - - ocl := openshell.NewOpenClawBackend(kubeClient, clients, oc, nil) - hermesBackend := openshell.NewHermesBackend(kubeClient, clients, oc, nil) - return ocl, hermesBackend, nil -} - -func buildSubstrateHarnessBackends(ctx context.Context, cfg *Config, client *substrate.Client) (sandboxbackend.AsyncBackend, sandboxbackend.AsyncBackend, error) { +func buildSubstrateHarnessBackends(ctx context.Context, cfg *Config, client *substrate.Client) (map[v1alpha2.AgentHarnessBackendType]sandboxbackend.AsyncBackend, error) { if client == nil { - return nil, nil, fmt.Errorf("substrate ate-api client is required") + return nil, fmt.Errorf("substrate ate-api client is required") } _ = ctx _ = cfg - ocl := substrate.NewOpenClawBackend(client, v1alpha2.AgentHarnessBackendOpenClaw, nil) - ncl := substrate.NewOpenClawBackend(client, v1alpha2.AgentHarnessBackendNemoClaw, nil) - return ocl, ncl, nil + backends := make(map[v1alpha2.AgentHarnessBackendType]sandboxbackend.AsyncBackend) + for _, b := range []v1alpha2.AgentHarnessBackendType{ + v1alpha2.AgentHarnessBackendOpenClaw, + v1alpha2.AgentHarnessBackendHermes, + } { + backends[b] = substrate.NewOpenClawBackend(client, b, nil) + } + return backends, nil } func substrateAppConfig(cfg *Config) substrate.Config { @@ -880,7 +810,7 @@ func substrateLifecycleFromConfig(kubeClient client.Client, cfg *Config, ate *su RunscAMD64SHA256: cfg.Substrate.RunscAMD64SHA256, RunscARM64URL: cfg.Substrate.RunscARM64URL, RunscARM64SHA256: cfg.Substrate.RunscARM64SHA256, - DefaultWorkloadImage: openclaw.NemoclawSandboxBaseImage, + DefaultWorkloadImage: openclaw.AcpSandboxOpenClawImage, DefaultWorkerPool: types.NamespacedName{ Namespace: cfg.Substrate.DefaultWorkerPoolNamespace, Name: cfg.Substrate.DefaultWorkerPoolName, diff --git a/go/core/pkg/sandboxbackend/agentsxk8s/agentsxk8s.go b/go/core/pkg/sandboxbackend/agentsxk8s/agentsxk8s.go deleted file mode 100644 index d42b2804fd..0000000000 --- a/go/core/pkg/sandboxbackend/agentsxk8s/agentsxk8s.go +++ /dev/null @@ -1,111 +0,0 @@ -package agentsxk8s - -import ( - "context" - "fmt" - "maps" - - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - - agentsandboxv1 "sigs.k8s.io/agent-sandbox/api/v1alpha1" -) - -// Backend builds kubernetes-sigs/agent-sandbox Sandbox CRs directly (no SandboxTemplate/SandboxClaim). -type Backend struct{} - -var _ sandboxbackend.Backend = (*Backend)(nil) - -// New returns the agent-sandbox backend. -func New() *Backend { - return &Backend{} -} - -func (b *Backend) GetOwnedResourceTypes() []client.Object { - return []client.Object{ - &agentsandboxv1.Sandbox{}, - } -} - -func (b *Backend) OwnedResourceTypesFor(_ v1alpha2.AgentObject) ([]client.Object, error) { - return b.GetOwnedResourceTypes(), nil -} - -func (b *Backend) BuildSandbox(_ context.Context, in sandboxbackend.BuildInput) ([]client.Object, error) { - if in.Agent == nil { - return nil, fmt.Errorf("agent is required") - } - name := in.Agent.GetName() - if in.WorkloadName != "" { - name = in.WorkloadName - } - podLabels := in.PodTemplate.Labels - if len(in.ExtraLabels) > 0 { - podLabels = mapsUnion(podLabels, in.ExtraLabels) - } - - pt := agentsandboxv1.PodTemplate{ - Spec: in.PodTemplate.Spec, - ObjectMeta: agentsandboxv1.PodMetadata{ - Labels: podLabels, - Annotations: in.PodTemplate.Annotations, - }, - } - - labelUnion := mapsUnion(podLabels, in.Agent.GetLabels()) - - replicas := int32(1) - sb := &agentsandboxv1.Sandbox{ - TypeMeta: metav1.TypeMeta{ - APIVersion: agentsandboxv1.GroupVersion.String(), - Kind: "Sandbox", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: in.Agent.GetNamespace(), - Annotations: in.Agent.GetAnnotations(), - Labels: labelUnion, - }, - Spec: agentsandboxv1.SandboxSpec{ - PodTemplate: pt, - Replicas: &replicas, - }, - } - - return []client.Object{sb}, nil -} - -func mapsUnion(podLabels map[string]string, agentLabels map[string]string) map[string]string { - if len(podLabels) == 0 && len(agentLabels) == 0 { - return nil - } - out := make(map[string]string, len(podLabels)+len(agentLabels)) - maps.Copy(out, podLabels) - for k, v := range agentLabels { - if _, ok := out[k]; !ok { - out[k] = v - } - } - return out -} - -func (b *Backend) ComputeReady(ctx context.Context, cl client.Client, nn types.NamespacedName) (metav1.ConditionStatus, string, string) { - sb := &agentsandboxv1.Sandbox{} - if err := cl.Get(ctx, nn, sb); err != nil { - if apierrors.IsNotFound(err) { - return metav1.ConditionUnknown, "SandboxNotFound", err.Error() - } - return metav1.ConditionUnknown, "SandboxGetFailed", err.Error() - } - for i := range sb.Status.Conditions { - c := sb.Status.Conditions[i] - if c.Type == string(agentsandboxv1.SandboxConditionReady) { - return c.Status, c.Reason, c.Message - } - } - return metav1.ConditionUnknown, "SandboxReadyPending", "Sandbox Ready condition not yet reported" -} diff --git a/go/core/pkg/sandboxbackend/agentsxk8s/agentsxk8s_test.go b/go/core/pkg/sandboxbackend/agentsxk8s/agentsxk8s_test.go deleted file mode 100644 index 965bbe883d..0000000000 --- a/go/core/pkg/sandboxbackend/agentsxk8s/agentsxk8s_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package agentsxk8s - -import ( - "context" - "testing" - - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - agentsandboxv1 "sigs.k8s.io/agent-sandbox/api/v1alpha1" -) - -func TestBackend_BuildSandbox_emitsSandbox(t *testing.T) { - b := New() - agent := &v1alpha2.Agent{ - ObjectMeta: metav1.ObjectMeta{Name: "a1", Namespace: "ns1", Labels: map[string]string{"app": "x"}}, - } - pt := corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"kagent": "a1"}}, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{{Name: "kagent", Image: "img:v1"}}, - }, - } - objs, err := b.BuildSandbox(context.Background(), sandboxbackend.BuildInput{ - Agent: agent, - PodTemplate: pt, - }) - require.NoError(t, err) - require.Len(t, objs, 1) - - sb, ok := objs[0].(*agentsandboxv1.Sandbox) - require.True(t, ok) - require.Equal(t, "a1", sb.Name) - require.Equal(t, "ns1", sb.Namespace) - require.Equal(t, "img:v1", sb.Spec.PodTemplate.Spec.Containers[0].Image) - require.NotNil(t, sb.Spec.Replicas) - require.Equal(t, int32(1), *sb.Spec.Replicas) -} diff --git a/go/core/pkg/sandboxbackend/apis_available.go b/go/core/pkg/sandboxbackend/apis_available.go deleted file mode 100644 index 3e485d4c80..0000000000 --- a/go/core/pkg/sandboxbackend/apis_available.go +++ /dev/null @@ -1,35 +0,0 @@ -package sandboxbackend - -import ( - "context" - "fmt" - - "k8s.io/apimachinery/pkg/api/meta" - "sigs.k8s.io/controller-runtime/pkg/client" - - agentsandboxv1 "sigs.k8s.io/agent-sandbox/api/v1alpha1" -) - -// EnsureAgentSandboxAPIsRegistered checks that the apiserver exposes the agent-sandbox -// resources kagent needs (SandboxTemplate, SandboxClaim, Sandbox). Call this before -// creating or reconciling SandboxAgent when a sandbox backend is configured. -// -// When CRDs are missing, the apiserver returns a *meta.NoKindMatchError (or similar); -// that surfaces as a clear prerequisite error instead of a late reconcile failure. -func EnsureAgentSandboxAPIsRegistered(ctx context.Context, c client.Client) error { - checks := []struct { - list client.ObjectList - kind string - }{ - {&agentsandboxv1.SandboxList{}, "Sandbox (agents.x-k8s.io/v1alpha1)"}, - } - for _, ch := range checks { - if err := c.List(ctx, ch.list, client.Limit(1)); err != nil { - if meta.IsNoMatchError(err) { - return fmt.Errorf("agent-sandbox API %s is not available on this cluster; install the agent-sandbox CRDs and controller before using SandboxAgent: %w", ch.kind, err) - } - return fmt.Errorf("could not reach agent-sandbox API %s (check RBAC and apiserver connectivity): %w", ch.kind, err) - } - } - return nil -} diff --git a/go/core/pkg/sandboxbackend/async.go b/go/core/pkg/sandboxbackend/async.go index 52c85d5849..a8cc3bf172 100644 --- a/go/core/pkg/sandboxbackend/async.go +++ b/go/core/pkg/sandboxbackend/async.go @@ -14,8 +14,7 @@ type Handle struct { } // EnsureResult is returned by EnsureAgentHarness. Endpoint (if set) is surfaced -// to users via AgentHarness.Status.Connection (OpenShell: gateway URL#sandbox id; -// Substrate: kagent gateway proxy path). +// to users via AgentHarness.Status.Connection (Substrate: kagent gateway proxy path). type EnsureResult struct { Handle Handle Endpoint string diff --git a/go/core/pkg/sandboxbackend/filter_translator_owned.go b/go/core/pkg/sandboxbackend/filter_translator_owned.go index 63d6375a5a..86ad325c96 100644 --- a/go/core/pkg/sandboxbackend/filter_translator_owned.go +++ b/go/core/pkg/sandboxbackend/filter_translator_owned.go @@ -11,8 +11,8 @@ import ( // FilterTranslatorOwnedTypesForList returns the owned-resource types the reconciler should pass to // FindOwnedObjects. It drops sandbox-backend-only types when the workload is not sandbox, so -// reconcile does not List agent-sandbox APIs on clusters where those CRDs are not installed. -// For sandbox workloads it keeps only the owned types for the agent's sandbox platform. +// reconcile does not List substrate sandbox APIs on clusters where those CRDs are not installed. +// For sandbox workloads it keeps only the owned types for the Agent Substrate backend. // // translatorOwnedTypes is typically AdkApiTranslator.GetOwnedResourceTypes() (full set used for watches). func FilterTranslatorOwnedTypesForList(cl client.Client, agent v1alpha2.AgentObject, translatorOwnedTypes []client.Object, backend Backend) ([]client.Object, error) { diff --git a/go/core/pkg/sandboxbackend/filter_translator_owned_test.go b/go/core/pkg/sandboxbackend/filter_translator_owned_test.go index 6537fdac29..35f92d7daa 100644 --- a/go/core/pkg/sandboxbackend/filter_translator_owned_test.go +++ b/go/core/pkg/sandboxbackend/filter_translator_owned_test.go @@ -8,7 +8,6 @@ import ( atev1alpha1 "github.com/agent-substrate/substrate/pkg/api/v1alpha1" "github.com/kagent-dev/kagent/go/api/v1alpha2" "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/agentsxk8s" "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/substrate" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -16,24 +15,22 @@ import ( "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" - - agentsandboxv1 "sigs.k8s.io/agent-sandbox/api/v1alpha1" ) func TestFilterTranslatorOwnedTypesForList(t *testing.T) { scheme := runtime.NewScheme() require.NoError(t, appsv1.AddToScheme(scheme)) require.NoError(t, corev1.AddToScheme(scheme)) - require.NoError(t, agentsandboxv1.AddToScheme(scheme)) + require.NoError(t, atev1alpha1.AddToScheme(scheme)) require.NoError(t, v1alpha2.AddToScheme(scheme)) cl := fake.NewClientBuilder().WithScheme(scheme).Build() - backend := agentsxk8s.New() + backend := substrate.NewAgentsBackend(nil, nil) allTypes := []client.Object{ &appsv1.Deployment{}, &corev1.ConfigMap{}, - &agentsandboxv1.Sandbox{}, + &atev1alpha1.ActorTemplate{}, } t.Run("plain Agent drops sandbox GVKs", func(t *testing.T) { @@ -41,6 +38,10 @@ func TestFilterTranslatorOwnedTypesForList(t *testing.T) { out, err := sandboxbackend.FilterTranslatorOwnedTypesForList(cl, agent, allTypes, backend) require.NoError(t, err) require.Len(t, out, 2) + for _, o := range out { + _, ok := o.(*atev1alpha1.ActorTemplate) + require.False(t, ok, "plain agents must not list substrate ActorTemplate resources") + } }) t.Run("SandboxAgent keeps sandbox GVKs", func(t *testing.T) { @@ -48,64 +49,20 @@ func TestFilterTranslatorOwnedTypesForList(t *testing.T) { out, err := sandboxbackend.FilterTranslatorOwnedTypesForList(cl, sa, allTypes, backend) require.NoError(t, err) require.Len(t, out, len(allTypes)) - }) - - t.Run("nil backend is passthrough", func(t *testing.T) { - agent := &v1alpha2.Agent{ObjectMeta: metav1.ObjectMeta{Name: "a", Namespace: "ns"}} - out, err := sandboxbackend.FilterTranslatorOwnedTypesForList(cl, agent, allTypes, nil) - require.NoError(t, err) - require.Len(t, out, len(allTypes)) - }) - - t.Run("substrate SandboxAgent drops agent-sandbox GVKs", func(t *testing.T) { - require.NoError(t, atev1alpha1.AddToScheme(scheme)) - routing := sandboxbackend.NewRoutingBackend(agentsxk8s.New(), substrate.NewAgentsBackend(nil, nil)) - allWithSubstrate := append(allTypes, &atev1alpha1.ActorTemplate{}) - sa := &v1alpha2.SandboxAgent{ - ObjectMeta: metav1.ObjectMeta{Name: "s", Namespace: "ns"}, - Spec: v1alpha2.SandboxAgentSpec{ - Platform: v1alpha2.SandboxPlatformSubstrate, - }, - } - out, err := sandboxbackend.FilterTranslatorOwnedTypesForList(cl, sa, allWithSubstrate, routing) - require.NoError(t, err) - require.Len(t, out, 3) - var sawSandbox, sawActorTemplate bool + var sawActorTemplate bool for _, o := range out { - if _, ok := o.(*agentsandboxv1.Sandbox); ok { - sawSandbox = true - } if _, ok := o.(*atev1alpha1.ActorTemplate); ok { sawActorTemplate = true } } - require.False(t, sawSandbox, "substrate agents must not list agent-sandbox Sandbox resources") require.True(t, sawActorTemplate) }) - t.Run("agent-sandbox SandboxAgent keeps Sandbox GVK only", func(t *testing.T) { - require.NoError(t, atev1alpha1.AddToScheme(scheme)) - routing := sandboxbackend.NewRoutingBackend(agentsxk8s.New(), substrate.NewAgentsBackend(nil, nil)) - allWithSubstrate := append(allTypes, &atev1alpha1.ActorTemplate{}) - sa := &v1alpha2.SandboxAgent{ - ObjectMeta: metav1.ObjectMeta{Name: "s", Namespace: "ns"}, - Spec: v1alpha2.SandboxAgentSpec{ - Platform: v1alpha2.SandboxPlatformAgentSandbox, - }, - } - out, err := sandboxbackend.FilterTranslatorOwnedTypesForList(cl, sa, allWithSubstrate, routing) + t.Run("nil backend is passthrough", func(t *testing.T) { + agent := &v1alpha2.Agent{ObjectMeta: metav1.ObjectMeta{Name: "a", Namespace: "ns"}} + out, err := sandboxbackend.FilterTranslatorOwnedTypesForList(cl, agent, allTypes, nil) require.NoError(t, err) - require.Len(t, out, 3) - var sawSandbox, sawActorTemplate bool - for _, o := range out { - if _, ok := o.(*agentsandboxv1.Sandbox); ok { - sawSandbox = true - } - if _, ok := o.(*atev1alpha1.ActorTemplate); ok { - sawActorTemplate = true - } - } - require.True(t, sawSandbox) - require.False(t, sawActorTemplate, "agent-sandbox agents must not list substrate ActorTemplate resources") + require.Len(t, out, len(allTypes)) }) } + diff --git a/go/core/pkg/sandboxbackend/openclaw/bootstrap_openshell.go b/go/core/pkg/sandboxbackend/openclaw/bootstrap_openshell.go deleted file mode 100644 index ede55d15b8..0000000000 --- a/go/core/pkg/sandboxbackend/openclaw/bootstrap_openshell.go +++ /dev/null @@ -1,83 +0,0 @@ -package openclaw - -import ( - "context" - "encoding/json" - "fmt" - "slices" - - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// BuildBootstrapJSON builds ~/.openclaw/openclaw.json contents plus environment variables that must be present when -// OpenClaw resolves openshell:resolve:env: (API key + channel tokens). -// -// defaultBaseURLWhenUnset is used when ModelConfig has no explicit provider base URL. -// OpenShell callers should pass DefaultInferenceBaseURL. -func BuildBootstrapJSON(ctx context.Context, kube client.Client, namespace string, sbx *v1alpha2.AgentHarness, mc *v1alpha2.ModelConfig, gw GatewayBootstrapConfig, defaultBaseURLWhenUnset string) ([]byte, map[string]string, error) { - if mc == nil { - return nil, nil, fmt.Errorf("ModelConfig is required") - } - apiKey, err := ResolveModelConfigAPIKey(ctx, kube, mc) - if err != nil { - return nil, nil, fmt.Errorf("resolve model API key: %w", err) - } - apiAdapter, err := providerAPI(mc) - if err != nil { - return nil, nil, err - } - - apiKeyEnv := DefaultAPIKeyEnvVar(mc.Spec.Provider) - env := map[string]string{ - apiKeyEnv: apiKey, - } - - modelID, err := requiredModelID(mc) - if err != nil { - return nil, nil, err - } - - providerRecord := GatewayProviderRecordName(mc.Spec.Provider) - doc := buildCoreBootstrapDocument(mc, gw, credentialValue{literal: openshellResolveEnv(apiKeyEnv)}, providerRecord, modelID, apiAdapter, defaultBaseURLWhenUnset) - - chState, err := accumulateHarnessChannels(ctx, kube, namespace, sbx.Spec.Channels, env) - if err != nil { - return nil, nil, err - } - doc.Channels = chState.channelsJSON() - - applyOpenshellSecretsAllowlist(&doc, env) - - raw, err := json.Marshal(doc) - if err != nil { - return nil, nil, fmt.Errorf("marshal openclaw json: %w", err) - } - return raw, env, nil -} - -func applyOpenshellSecretsAllowlist(doc *bootstrapDocument, env map[string]string, extraEnvNames ...string) { - seen := make(map[string]struct{}, len(env)+len(extraEnvNames)) - secretAllow := make([]string, 0, len(env)+len(extraEnvNames)) - for k := range env { - if _, ok := seen[k]; !ok { - seen[k] = struct{}{} - secretAllow = append(secretAllow, k) - } - } - for _, k := range extraEnvNames { - if _, ok := seen[k]; !ok { - seen[k] = struct{}{} - secretAllow = append(secretAllow, k) - } - } - slices.Sort(secretAllow) - doc.Secrets = secretsSection{ - Providers: map[string]secretProvider{ - openshellSecretProviderID: { - Source: "env", - Allowlist: secretAllow, - }, - }, - } -} diff --git a/go/core/pkg/sandboxbackend/openclaw/bootstrap_openshell_test.go b/go/core/pkg/sandboxbackend/openclaw/bootstrap_openshell_test.go deleted file mode 100644 index 18f7b1ce42..0000000000 --- a/go/core/pkg/sandboxbackend/openclaw/bootstrap_openshell_test.go +++ /dev/null @@ -1,164 +0,0 @@ -package openclaw_test - -import ( - "context" - "encoding/json" - "testing" - - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openclaw" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client/fake" -) - -func TestBuildBootstrapJSON_OpenAIDefaultBaseURLInferenceLocal(t *testing.T) { - scheme := runtime.NewScheme() - utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(v1alpha2.AddToScheme(scheme)) - - ns := "default" - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "openai-key", Namespace: ns}, - Data: map[string][]byte{"OPENAI_API_KEY": []byte("sk-test")}, - } - mc := &v1alpha2.ModelConfig{ - ObjectMeta: metav1.ObjectMeta{Name: "mc1", Namespace: ns}, - Spec: v1alpha2.ModelConfigSpec{ - Model: "gpt-4o", - Provider: v1alpha2.ModelProviderOpenAI, - APIKeySecret: "openai-key", - APIKeySecretKey: "OPENAI_API_KEY", - OpenAI: &v1alpha2.OpenAIConfig{}, - }, - } - sbx := &v1alpha2.AgentHarness{ObjectMeta: metav1.ObjectMeta{Name: "s1", Namespace: ns}} - - kube := fake.NewClientBuilder().WithScheme(scheme).WithObjects(secret, mc).Build() - raw, _, err := openclaw.BuildBootstrapJSON(context.Background(), kube, ns, sbx, mc, openclaw.OpenshellGatewayBootstrap(18800), openclaw.DefaultInferenceBaseURL) - require.NoError(t, err) - - var root map[string]any - require.NoError(t, json.Unmarshal(raw, &root)) - models := root["models"].(map[string]any) - provs := models["providers"].(map[string]any) - openai := provs["openai"].(map[string]any) - require.Equal(t, openclaw.DefaultInferenceBaseURL, openai["baseUrl"]) - require.Equal(t, "openshell:resolve:env:OPENAI_API_KEY", openai["apiKey"]) - secRoot := root["secrets"].(map[string]any) - secProvs := secRoot["providers"].(map[string]any) - kagent := secProvs["kagent"].(map[string]any) - require.Equal(t, "env", kagent["source"]) - require.Contains(t, kagent["allowlist"], "OPENAI_API_KEY") -} - -func TestBuildBootstrapJSON_SubstrateOmitsModelsWhenNoExplicitBaseURL(t *testing.T) { - scheme := runtime.NewScheme() - utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(v1alpha2.AddToScheme(scheme)) - - ns := "default" - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "openai-key", Namespace: ns}, - Data: map[string][]byte{"OPENAI_API_KEY": []byte("sk-test")}, - } - mc := &v1alpha2.ModelConfig{ - ObjectMeta: metav1.ObjectMeta{Name: "mc1", Namespace: ns}, - Spec: v1alpha2.ModelConfigSpec{ - Model: "gpt-4o", - Provider: v1alpha2.ModelProviderOpenAI, - APIKeySecret: "openai-key", - APIKeySecretKey: "OPENAI_API_KEY", - OpenAI: &v1alpha2.OpenAIConfig{}, - }, - } - sbx := &v1alpha2.AgentHarness{ObjectMeta: metav1.ObjectMeta{Name: "s1", Namespace: ns}} - - kube := fake.NewClientBuilder().WithScheme(scheme).WithObjects(secret, mc).Build() - raw, _, err := openclaw.BuildBootstrapJSON(context.Background(), kube, ns, sbx, mc, openclaw.SubstrateGatewayBootstrap("tok", 80, "/api/agentharnesses/default/s1/gateway"), openclaw.SubstrateBootstrapDefaultBaseURL) - require.NoError(t, err) - - var root map[string]any - require.NoError(t, json.Unmarshal(raw, &root)) - _, hasModels := root["models"] - require.False(t, hasModels) - agents := root["agents"].(map[string]any) - defaults := agents["defaults"].(map[string]any) - model := defaults["model"].(map[string]any) - require.Equal(t, "openai/gpt-4o", model["primary"]) -} - -func TestBuildBootstrapJSON_OpenAIAndTelegram(t *testing.T) { - scheme := runtime.NewScheme() - utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(v1alpha2.AddToScheme(scheme)) - - ns := "default" - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "openai-key", Namespace: ns}, - Data: map[string][]byte{"OPENAI_API_KEY": []byte("sk-test")}, - } - mc := &v1alpha2.ModelConfig{ - ObjectMeta: metav1.ObjectMeta{Name: "mc1", Namespace: ns}, - Spec: v1alpha2.ModelConfigSpec{ - Model: "gpt-4o", - Provider: v1alpha2.ModelProviderOpenAI, - APIKeySecret: "openai-key", - APIKeySecretKey: "OPENAI_API_KEY", - OpenAI: &v1alpha2.OpenAIConfig{BaseURL: "https://api.example/v1"}, - }, - } - sbx := &v1alpha2.AgentHarness{ - ObjectMeta: metav1.ObjectMeta{Name: "s1", Namespace: ns}, - Spec: v1alpha2.AgentHarnessSpec{ - Channels: []v1alpha2.AgentHarnessChannel{ - { - Name: "tg1", - Type: v1alpha2.AgentHarnessChannelTypeTelegram, - Telegram: &v1alpha2.AgentHarnessTelegramChannelSpec{ - BotToken: v1alpha2.AgentHarnessChannelCredential{Value: "telegram-bot-token"}, - }, - }, - }, - }, - } - - kube := fake.NewClientBuilder().WithScheme(scheme).WithObjects(secret, mc).Build() - raw, env, err := openclaw.BuildBootstrapJSON(context.Background(), kube, ns, sbx, mc, openclaw.OpenshellGatewayBootstrap(18800), openclaw.DefaultInferenceBaseURL) - require.NoError(t, err) - require.Equal(t, "sk-test", env["OPENAI_API_KEY"]) - require.Equal(t, "telegram-bot-token", env["TELEGRAM_BOT_TOKEN_TG1"]) - - var root map[string]any - require.NoError(t, json.Unmarshal(raw, &root)) - require.Contains(t, root, "gateway") - gw := root["gateway"].(map[string]any) - require.Equal(t, "loopback", gw["bind"]) - require.Contains(t, root, "models") - require.Contains(t, root, "agents") - models := root["models"].(map[string]any) - provs := models["providers"].(map[string]any) - openai := provs["openai"].(map[string]any) - modelList := openai["models"].([]any) - require.Len(t, modelList, 1) - entry := modelList[0].(map[string]any) - require.Equal(t, "gpt-4o", entry["id"]) - require.Equal(t, "gpt-4o", entry["name"]) - require.Equal(t, "openshell:resolve:env:OPENAI_API_KEY", openai["apiKey"]) - secRoot := root["secrets"].(map[string]any) - secProvs := secRoot["providers"].(map[string]any) - kagent := secProvs["kagent"].(map[string]any) - require.Equal(t, "env", kagent["source"]) - al := kagent["allowlist"].([]any) - require.ElementsMatch(t, []any{"TELEGRAM_BOT_TOKEN_TG1", "OPENAI_API_KEY"}, al) - ch := root["channels"].(map[string]any) - require.Contains(t, ch, "telegram") - tg := ch["telegram"].(map[string]any) - tgAcc := tg["accounts"].(map[string]any) - tg1 := tgAcc["tg1"].(map[string]any) - require.Equal(t, "openshell:resolve:env:TELEGRAM_BOT_TOKEN_TG1", tg1["botToken"]) -} diff --git a/go/core/pkg/sandboxbackend/openclaw/bootstrap_shared.go b/go/core/pkg/sandboxbackend/openclaw/bootstrap_shared.go index b547c83f37..06689b049a 100644 --- a/go/core/pkg/sandboxbackend/openclaw/bootstrap_shared.go +++ b/go/core/pkg/sandboxbackend/openclaw/bootstrap_shared.go @@ -10,51 +10,25 @@ import ( // GatewayBootstrapConfig describes the gateway section of openclaw.json for a harness runtime. type GatewayBootstrapConfig struct { - Port int - Bind string // loopback | lan - AuthMode string // none | token - Token string // required when AuthMode is token - ControlUI *ControlUIBootstrapConfig + Port int + Bind string // loopback | lan + AuthMode string // none | token + Token string // required when AuthMode is token } -// ControlUIBootstrapConfig maps to gateway.controlUi in openclaw.json. -type ControlUIBootstrapConfig struct { - BasePath string - AllowedOrigins []string - DangerouslyDisableDeviceAuth bool -} - -// OpenshellGatewayBootstrap is the default gateway profile for OpenShell sandboxes. -func OpenshellGatewayBootstrap(port int) GatewayBootstrapConfig { - return GatewayBootstrapConfig{Port: port, Bind: "loopback", AuthMode: "none"} -} - -// SubstrateGatewayBootstrap is the gateway profile for Agent Substrate actors (port 80, token auth, proxied Control UI). -func SubstrateGatewayBootstrap(token string, port int, controlUIBasePath string) GatewayBootstrapConfig { +// SubstrateGatewayBootstrap is the gateway profile for Agent Substrate actors +// (token auth, loopback-only). The gateway has no Control UI: kagent reaches +// the actor solely through the acp-shim's /acp WebSocket, so the gateway is a +// private in-sandbox detail the `openclaw acp` child connects to. +func SubstrateGatewayBootstrap(token string, port int) GatewayBootstrapConfig { return GatewayBootstrapConfig{ Port: port, Bind: "lan", AuthMode: "token", Token: strings.TrimSpace(token), - ControlUI: &ControlUIBootstrapConfig{ - BasePath: normalizeControlUIBasePath(controlUIBasePath), - AllowedOrigins: []string{"*"}, - DangerouslyDisableDeviceAuth: true, - }, } } -func normalizeControlUIBasePath(path string) string { - path = strings.TrimSpace(path) - if path == "" || path == "/" { - return "" - } - if !strings.HasPrefix(path, "/") { - path = "/" + path - } - return strings.TrimRight(path, "/") -} - // BuildGatewayOnlyBootstrapJSON returns a minimal openclaw.json with gateway settings only (no models/channels). func BuildGatewayOnlyBootstrapJSON(gw GatewayBootstrapConfig) ([]byte, error) { doc := bootstrapDocument{Gateway: buildGatewaySection(gw)} @@ -138,12 +112,14 @@ func buildGatewaySection(gw GatewayBootstrapConfig) gatewaySection { } if authMode == "token" { section.Auth.Token = gw.Token - } - if gw.ControlUI != nil { - section.ControlUi = &controlUiSection{ - BasePath: normalizeControlUIBasePath(gw.ControlUI.BasePath), - AllowedOrigins: gw.ControlUI.AllowedOrigins, - DangerouslyDisableDeviceAuth: gw.ControlUI.DangerouslyDisableDeviceAuth, + // openclaw acp (the in-sandbox ACP bridge) authenticates to a + // token-auth gateway via gateway.remote.{url,token}. The URL must be + // in the config too: when --url is passed on the command line the CLI + // ignores remote.token (verified against OpenClaw 2026.5.27), so the + // in-sandbox client is launched without --url and resolves both here. + section.Remote = &gatewayRemote{ + URL: fmt.Sprintf("ws://127.0.0.1:%d", port), + Token: gw.Token, } } return section diff --git a/go/core/pkg/sandboxbackend/openclaw/bootstrap_substrate.go b/go/core/pkg/sandboxbackend/openclaw/bootstrap_substrate.go index f20e1294b6..619de92c8a 100644 --- a/go/core/pkg/sandboxbackend/openclaw/bootstrap_substrate.go +++ b/go/core/pkg/sandboxbackend/openclaw/bootstrap_substrate.go @@ -56,7 +56,7 @@ func substrateContainerEnv(apiKey corev1.EnvVar, extra []corev1.EnvVar) []corev1 out := make([]corev1.EnvVar, 0, len(extra)+2) out = append(out, apiKey) out = append(out, extra...) - out = append(out, corev1.EnvVar{Name: "HOME", Value: "/root"}) + out = append(out, corev1.EnvVar{Name: "HOME", Value: SubstrateActorHome}) return out } diff --git a/go/core/pkg/sandboxbackend/openclaw/bootstrap_substrate_test.go b/go/core/pkg/sandboxbackend/openclaw/bootstrap_substrate_test.go index a5136bc81f..799ee4be6f 100644 --- a/go/core/pkg/sandboxbackend/openclaw/bootstrap_substrate_test.go +++ b/go/core/pkg/sandboxbackend/openclaw/bootstrap_substrate_test.go @@ -18,15 +18,14 @@ import ( func TestSubstrateGatewayBootstrap(t *testing.T) { t.Parallel() - raw, err := openclaw.BuildGatewayOnlyBootstrapJSON(openclaw.SubstrateGatewayBootstrap("tok", 80, "/api/agentharnesses/kagent/claw/gateway/")) + raw, err := openclaw.BuildGatewayOnlyBootstrapJSON(openclaw.SubstrateGatewayBootstrap("tok", 80)) require.NoError(t, err) var root map[string]any require.NoError(t, json.Unmarshal(raw, &root)) gw := root["gateway"].(map[string]any) require.Equal(t, "lan", gw["bind"]) - cui := gw["controlUi"].(map[string]any) - require.Equal(t, "/api/agentharnesses/kagent/claw/gateway", cui["basePath"]) - require.Equal(t, true, cui["dangerouslyDisableDeviceAuth"]) + _, hasControlUI := gw["controlUi"] + require.False(t, hasControlUI, "controlUi should not be emitted") } func TestBuildSubstrateBootstrapJSON_ModelConfigAPIKeyUsesSecretRef(t *testing.T) { @@ -48,7 +47,7 @@ func TestBuildSubstrateBootstrapJSON_ModelConfigAPIKeyUsesSecretRef(t *testing.T sbx := &v1alpha2.AgentHarness{ObjectMeta: metav1.ObjectMeta{Name: "s1", Namespace: ns}} kube := fake.NewClientBuilder().WithScheme(scheme).WithObjects(mc).Build() - raw, env, err := openclaw.BuildSubstrateBootstrapJSON(context.Background(), kube, ns, sbx, mc, openclaw.SubstrateGatewayBootstrap("tok", 80, "/gw/")) + raw, env, err := openclaw.BuildSubstrateBootstrapJSON(context.Background(), kube, ns, sbx, mc, openclaw.SubstrateGatewayBootstrap("tok", 80)) require.NoError(t, err) var root map[string]any @@ -117,7 +116,7 @@ func TestBuildSubstrateBootstrapJSON_TelegramUsesEnvSecretRef(t *testing.T) { } kube := fake.NewClientBuilder().WithScheme(scheme).WithObjects(mc, secret).Build() - raw, env, err := openclaw.BuildSubstrateBootstrapJSON(context.Background(), kube, ns, sbx, mc, openclaw.SubstrateGatewayBootstrap("tok", 80, "/gw/")) + raw, env, err := openclaw.BuildSubstrateBootstrapJSON(context.Background(), kube, ns, sbx, mc, openclaw.SubstrateGatewayBootstrap("tok", 80)) require.NoError(t, err) var root map[string]any diff --git a/go/core/pkg/sandboxbackend/openclaw/channels_openshell.go b/go/core/pkg/sandboxbackend/openclaw/channels_openshell.go deleted file mode 100644 index a4000aa0b7..0000000000 --- a/go/core/pkg/sandboxbackend/openclaw/channels_openshell.go +++ /dev/null @@ -1,123 +0,0 @@ -package openclaw - -import ( - "context" - "fmt" - "strings" - - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openshell/channels" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func accumulateHarnessChannels(ctx context.Context, kube client.Client, namespace string, specChannels []v1alpha2.AgentHarnessChannel, env map[string]string) (*harnessChannels, error) { - a := newHarnessChannels() - for _, ch := range specChannels { - switch ch.Type { - case v1alpha2.AgentHarnessChannelTypeTelegram: - if err := a.addTelegram(ctx, kube, namespace, ch, env); err != nil { - return nil, err - } - case v1alpha2.AgentHarnessChannelTypeSlack: - if err := a.addSlack(ctx, kube, namespace, ch, env); err != nil { - return nil, err - } - default: - return nil, unsupportedChannelType(ch.Name, ch.Type) - } - } - return a, nil -} - -func (a *harnessChannels) addTelegram(ctx context.Context, kube client.Client, namespace string, ch v1alpha2.AgentHarnessChannel, env map[string]string) error { - spec := ch.Telegram - if spec == nil { - return fmt.Errorf("channel %q: telegram spec is required", ch.Name) - } - botEnv := channels.TelegramBotTokenEnvKey(ch.Name) - if err := putChannelCredential(ctx, kube, namespace, spec.BotToken, botEnv, env); err != nil { - return fmt.Errorf("channel %q telegram bot token: %w", ch.Name, err) - } - allowFrom, err := telegramAllowFrom(ctx, kube, namespace, spec) - if err != nil { - return fmt.Errorf("channel %q telegram allowlist: %w", ch.Name, err) - } - acc := telegramAccount{ - Name: ch.Name, - Enabled: true, - BotToken: credentialValue{literal: openshellResolveEnv(botEnv)}, - } - if len(allowFrom) > 0 { - acc.DMPolicy = "allowlist" - acc.AllowFrom = allowFrom - } else { - acc.DMPolicy = "pairing" - } - a.telegram[ch.Name] = acc - if a.tgDef == "" { - a.tgDef = ch.Name - } - return nil -} - -func (a *harnessChannels) addSlack(ctx context.Context, kube client.Client, namespace string, ch v1alpha2.AgentHarnessChannel, env map[string]string) error { - spec := ch.Slack - if spec == nil { - return fmt.Errorf("channel %q: slack spec is required", ch.Name) - } - botEnv := channels.SlackBotTokenEnvKey(ch.Name) - appEnv := channels.SlackAppTokenEnvKey(ch.Name) - if err := putChannelCredential(ctx, kube, namespace, spec.BotToken, botEnv, env); err != nil { - return fmt.Errorf("channel %q slack bot token: %w", ch.Name, err) - } - if err := putChannelCredential(ctx, kube, namespace, spec.AppToken, appEnv, env); err != nil { - return fmt.Errorf("channel %q slack app token: %w", ch.Name, err) - } - opts := openClawSlackOptions(spec) - access := openClawSlackChannelAccess(opts) - acc := slackAccount{ - Name: ch.Name, - Enabled: true, - Mode: "socket", - BotToken: credentialValue{literal: channels.SlackBotTokenPlaceholder(botEnv)}, - AppToken: credentialValue{literal: channels.SlackAppTokenPlaceholder(appEnv)}, - UserTokenReadOnly: true, - GroupPolicy: string(access), - Capabilities: slackCaps{ - InteractiveReplies: slackInteractiveReplies(opts), - }, - } - if chans := trimNonEmptyStrings(opts.AllowlistChannels); len(chans) > 0 { - acc.DM = &groupDM{GroupEnabled: true, GroupChannels: chans} - } - a.slack[ch.Name] = acc - if a.slDef == "" { - a.slDef = ch.Name - } - if !a.slackSeen { - a.slackRootPolicy = access - a.slackSeen = true - } - return nil -} - -func telegramAllowFrom(ctx context.Context, kube client.Client, namespace string, spec *v1alpha2.AgentHarnessTelegramChannelSpec) ([]string, error) { - if len(spec.AllowedUserIDs) > 0 { - out := make([]string, 0, len(spec.AllowedUserIDs)) - for _, id := range spec.AllowedUserIDs { - s := strings.TrimSpace(id) - if s != "" { - out = append(out, s) - } - } - return out, nil - } - if spec.AllowedUserIDsFrom != nil { - raw, err := spec.AllowedUserIDsFrom.Resolve(ctx, kube, namespace) - if err != nil { - return nil, fmt.Errorf("resolve allowedUserIDsFrom: %w", err) - } - return splitAllowedList(raw), nil - } - return nil, nil -} diff --git a/go/core/pkg/sandboxbackend/openclaw/channels_shared.go b/go/core/pkg/sandboxbackend/openclaw/channels_shared.go index 75aa66872c..46731e9089 100644 --- a/go/core/pkg/sandboxbackend/openclaw/channels_shared.go +++ b/go/core/pkg/sandboxbackend/openclaw/channels_shared.go @@ -1,10 +1,12 @@ package openclaw import ( + "context" "fmt" "strings" "github.com/kagent-dev/kagent/go/api/v1alpha2" + "sigs.k8s.io/controller-runtime/pkg/client" ) type harnessChannels struct { @@ -103,3 +105,24 @@ func trimNonEmptyStrings(ss []string) []string { func unsupportedChannelType(name string, typ v1alpha2.AgentHarnessChannelType) error { return fmt.Errorf("channel %q: unsupported type %q", name, typ) } + +func telegramAllowFrom(ctx context.Context, kube client.Client, namespace string, spec *v1alpha2.AgentHarnessTelegramChannelSpec) ([]string, error) { + if len(spec.AllowedUserIDs) > 0 { + out := make([]string, 0, len(spec.AllowedUserIDs)) + for _, id := range spec.AllowedUserIDs { + s := strings.TrimSpace(id) + if s != "" { + out = append(out, s) + } + } + return out, nil + } + if spec.AllowedUserIDsFrom != nil { + raw, err := spec.AllowedUserIDsFrom.Resolve(ctx, kube, namespace) + if err != nil { + return nil, fmt.Errorf("resolve allowedUserIDsFrom: %w", err) + } + return splitAllowedList(raw), nil + } + return nil, nil +} diff --git a/go/core/pkg/sandboxbackend/openclaw/constants.go b/go/core/pkg/sandboxbackend/openclaw/constants.go index 1f6f88b687..6bb8798164 100644 --- a/go/core/pkg/sandboxbackend/openclaw/constants.go +++ b/go/core/pkg/sandboxbackend/openclaw/constants.go @@ -1,18 +1,26 @@ package openclaw const ( - // NemoclawSandboxBaseImage is the default OpenShell VM image for OpenClaw/NemoClaw harnesses. - // Substrate requires workload images to use @sha256:... refs (see pinImageRef). (OpenShell doesn't care) + // NemoclawSandboxBaseImage is the default VM base image for OpenClaw/NemoClaw harnesses. + // Substrate requires workload images to use @sha256:... refs (see pinImageRef). // Tag: 2026.5.4 NemoclawSandboxBaseImage = "ghcr.io/kagent-dev/nemoclaw/sandbox-base@sha256:d52bee415dc4c0dba7164f9eabe727574c056d4f211781f20af249707883a3b4" - // openshellSecretProviderID is the secrets.providers key written into openclaw.json for OpenShell sandboxes. - openshellSecretProviderID = "kagent" + // AcpSandboxOpenClawImage is the default Substrate workload image for + // OpenClaw harnesses: the kagent acp-sandbox openclaw target + // (docker/acp-sandbox/Dockerfile), which layers the acp-shim and the + // restore-proof gateway-ensure scripts onto an OpenClaw install. + // Substrate admission requires a digest-pinned ref. + AcpSandboxOpenClawImage = "ttl.sh/kagent-acp-openclaw@sha256:f8c7b73253dd00098d3f2cb2c3a3d7585fa549daadeefdacd563362e4d40c7e6" + + // SubstrateActorHome is the home directory of the unprivileged user in + // AcpSandboxOpenClawImage (USER agent); openclaw.json is written under it. + SubstrateActorHome = "/home/agent" // substrateSecretProviderID is the env SecretRef provider id for native OpenClaw on Substrate. substrateSecretProviderID = "default" - // DefaultInferenceBaseURL is the Model provider baseUrl when ModelConfig does not set an explicit upstream (OpenShell). + // DefaultInferenceBaseURL is the Model provider baseUrl when ModelConfig does not set an explicit upstream. DefaultInferenceBaseURL = "https://inference.local/v1" // SubstrateBootstrapDefaultBaseURL is passed when building openclaw.json for Substrate harnesses. diff --git a/go/core/pkg/sandboxbackend/openclaw/defaults.go b/go/core/pkg/sandboxbackend/openclaw/defaults.go index 15384730ad..97db505826 100644 --- a/go/core/pkg/sandboxbackend/openclaw/defaults.go +++ b/go/core/pkg/sandboxbackend/openclaw/defaults.go @@ -5,7 +5,6 @@ import ( "strings" "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openshell/channels" ) // DefaultAPIKeyEnvVar is the environment variable name used for the model provider API key in the sandbox. @@ -14,12 +13,7 @@ func DefaultAPIKeyEnvVar(provider v1alpha2.ModelProvider) string { } // DefaultSSHLaunchCommand is the interactive CLI started when connecting to an -// OpenClaw or NemoClaw harness sandbox via the UI terminal (unless plain shell is requested). +// OpenClaw harness sandbox via the UI terminal (unless plain shell is requested). func DefaultSSHLaunchCommand() string { return "openclaw tui" } - -// openshellResolveEnv matches OpenClaw onboard placeholders for OpenShell L7 credential rewrite. -func openshellResolveEnv(envVar string) string { - return channels.ResolveEnvPlaceholder(envVar) -} diff --git a/go/core/pkg/sandboxbackend/openclaw/modelconfig.go b/go/core/pkg/sandboxbackend/openclaw/modelconfig.go index a83e5871b6..a45cd6cc8a 100644 --- a/go/core/pkg/sandboxbackend/openclaw/modelconfig.go +++ b/go/core/pkg/sandboxbackend/openclaw/modelconfig.go @@ -11,7 +11,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -// GatewayProviderRecordName returns the OpenShell / OpenClaw provider record id for a ModelConfig provider. +// GatewayProviderRecordName returns the OpenClaw provider record id for a ModelConfig provider. func GatewayProviderRecordName(provider v1alpha2.ModelProvider) string { return strings.ToLower(string(provider)) } @@ -44,7 +44,7 @@ func ModelConfigAPIKeyEnvVar(mc *v1alpha2.ModelConfig) (corev1.EnvVar, error) { // ResolveModelConfigAPIKey reads the API key from the Secret referenced by ModelConfig. func ResolveModelConfigAPIKey(ctx context.Context, kube client.Client, mc *v1alpha2.ModelConfig) (string, error) { if mc.Spec.APIKeyPassthrough { - return "", fmt.Errorf("APIKeyPassthrough is not supported when registering an OpenShell gateway provider from ModelConfig") + return "", fmt.Errorf("APIKeyPassthrough is not supported when registering an OpenClaw gateway provider from ModelConfig") } if mc.Spec.APIKeySecret == "" || mc.Spec.APIKeySecretKey == "" { return "", fmt.Errorf("modelConfig %s/%s requires apiKeySecret and apiKeySecretKey", mc.Namespace, mc.Name) diff --git a/go/core/pkg/sandboxbackend/openclaw/policy.go b/go/core/pkg/sandboxbackend/openclaw/policy.go deleted file mode 100644 index b3d6632b04..0000000000 --- a/go/core/pkg/sandboxbackend/openclaw/policy.go +++ /dev/null @@ -1,322 +0,0 @@ -package openclaw - -import ( - "maps" - "strings" - - sandboxv1 "github.com/kagent-dev/kagent/go/api/openshell/gen/sandboxv1" - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend" -) - -// Network policy map keys for OpenClaw / NemoClaw sandbox egress (OpenShell SandboxPolicy.network_policies). -const ( - NetworkPolicyKeyClawhub = "clawhub" - NetworkPolicyKeyAPI = "openclaw_api" - NetworkPolicyKeyDocs = "openclaw_docs" - NetworkPolicyKeyTelegramBot = "telegram_bot" - NetworkPolicyKeySlack = "slack" - NetworkPolicyKeyNPMYarn = "npm_yarn" - - RegistryHostClawhub = "clawhub.ai" - RegistryHostAPI = "openclaw.ai" - RegistryHostDocs = "docs.openclaw.ai" -) - -// L7 REST settings for fixed claw endpoints; see -// https://docs.nvidia.com/openshell/reference/policy-schema (network_policies, endpoints). -const ( - endpointProtocolREST = "rest" - endpointEnforcement = "enforce" - endpointAccessFull = "full" - endpointTLSSkip = "skip" -) - -var ( - openClawCLIAndNodeBinaries = []*sandboxv1.NetworkBinary{ - {Path: "/usr/local/bin/openclaw"}, - {Path: "/usr/local/bin/node"}, - } - openClawCLIBinariesOnly = []*sandboxv1.NetworkBinary{ - {Path: "/usr/local/bin/openclaw"}, - } -) - -// Immutable L7 rule slices reused across policy rules (safe to share; not mutated). -var ( - l7WildcardGETPOST = []*sandboxv1.L7Rule{ - {Allow: &sandboxv1.L7Allow{Method: "GET", Path: "**"}}, - {Allow: &sandboxv1.L7Allow{Method: "POST", Path: "**"}}, - } - l7WildcardGETOnly = []*sandboxv1.L7Rule{ - {Allow: &sandboxv1.L7Allow{Method: "GET", Path: "**"}}, - } - telegramBotHTTPRules = []*sandboxv1.L7Rule{ - {Allow: &sandboxv1.L7Allow{Method: "GET", Path: "/bot*/**"}}, - {Allow: &sandboxv1.L7Allow{Method: "POST", Path: "/bot*/**"}}, - {Allow: &sandboxv1.L7Allow{Method: "GET", Path: "/file/bot*/**"}}, - } - slackRESTRules = []*sandboxv1.L7Rule{ - {Allow: &sandboxv1.L7Allow{Method: "GET", Path: "/**"}}, - {Allow: &sandboxv1.L7Allow{Method: "POST", Path: "/**"}}, - } - slackWssRules = []*sandboxv1.L7Rule{ - {Allow: &sandboxv1.L7Allow{Method: "GET", Path: "/**"}}, - {Allow: &sandboxv1.L7Allow{Method: "WEBSOCKET_TEXT", Path: "/**"}}, - } -) - -// restNetworkEndpoint is HTTPS:443 with protocol rest + enforce and explicit L7 rules (OpenShell policy schema). -func restNetworkEndpoint(host string, rules []*sandboxv1.L7Rule) *sandboxv1.NetworkEndpoint { - return &sandboxv1.NetworkEndpoint{ - Host: host, - Ports: []uint32{443}, - Protocol: endpointProtocolREST, - Enforcement: endpointEnforcement, - Rules: rules, - } -} - -func restSlackNetworkEndpoint(host string) *sandboxv1.NetworkEndpoint { - ep := restNetworkEndpoint(host, slackRESTRules) - ep.RequestBodyCredentialRewrite = true - return ep -} - -func slackWssNetworkEndpoint(host string) *sandboxv1.NetworkEndpoint { - return &sandboxv1.NetworkEndpoint{ - Host: host, - Ports: []uint32{443}, - Protocol: "websocket", - Enforcement: endpointEnforcement, - WebsocketCredentialRewrite: true, - Rules: slackWssRules, - } -} - -// messengerChannelNodeBinaries for Telegram / Slack OpenClaw channel egress (Node runtime). -var messengerChannelNodeBinaries = []*sandboxv1.NetworkBinary{ - {Path: "/usr/local/bin/node"}, - {Path: "/usr/bin/node"}, -} - -// telegramBotPolicyBinaries adds curl so probes/scripts hitting api.telegram.org match telegram_bot -// (OpenShell denies unless the executable is listed; otherwise OPA may attribute traffic to clawhub). -var telegramBotPolicyBinaries = append(messengerChannelNodeBinaries, - &sandboxv1.NetworkBinary{Path: "/usr/bin/curl"}, -) - -// wssTunnelEndpoint is L4 TLS passthrough for WebSocket gateways (OpenShell tls: skip + access: full). -func wssTunnelEndpoint(host string) *sandboxv1.NetworkEndpoint { - return &sandboxv1.NetworkEndpoint{ - Host: host, - Ports: []uint32{443}, - Access: endpointAccessFull, - Tls: endpointTLSSkip, - } -} - -// npmYarnRegistryHosts are covered by npm_yarn (L4 CONNECT / undici); omit from kagent_allowed_domains for claw sandboxes. -var npmYarnRegistryHosts = map[string]struct{}{ - "registry.npmjs.org": {}, - "registry.yarnpkg.com": {}, -} - -var npmYarnBinaries = []*sandboxv1.NetworkBinary{ - {Path: "/usr/local/bin/npm*"}, - {Path: "/usr/local/bin/npx*"}, - {Path: "/usr/local/bin/node*"}, - {Path: "/usr/local/bin/yarn*"}, - {Path: "/usr/bin/npm*"}, - {Path: "/usr/bin/node*"}, -} - -func npmYarnNetworkPolicyRule() *sandboxv1.NetworkPolicyRule { - return &sandboxv1.NetworkPolicyRule{ - Name: "npm_yarn", - Endpoints: []*sandboxv1.NetworkEndpoint{ - wssTunnelEndpoint("registry.npmjs.org"), - wssTunnelEndpoint("registry.yarnpkg.com"), - }, - Binaries: npmYarnBinaries, - } -} - -// OmitNPMPresetRegistryHosts drops registry hosts handled by npm_yarn when merging user allowedDomains (claw only). -func OmitNPMPresetRegistryHosts(domains []string) []string { - if len(domains) == 0 { - return domains - } - out := make([]string, 0, len(domains)) - for _, raw := range domains { - host, ok := sandboxbackend.NormalizeAllowedDomainHost(raw) - if !ok { - continue - } - if _, skip := npmYarnRegistryHosts[strings.ToLower(host)]; skip { - continue - } - out = append(out, raw) - } - return out -} - -func telegramBotNetworkPolicyRule() *sandboxv1.NetworkPolicyRule { - return &sandboxv1.NetworkPolicyRule{ - Name: "telegram_bot", - Endpoints: []*sandboxv1.NetworkEndpoint{restNetworkEndpoint("api.telegram.org", telegramBotHTTPRules)}, - Binaries: telegramBotPolicyBinaries, - } -} - -func slackNetworkPolicyRule() *sandboxv1.NetworkPolicyRule { - return &sandboxv1.NetworkPolicyRule{ - Name: "slack", - Endpoints: []*sandboxv1.NetworkEndpoint{ - restSlackNetworkEndpoint("slack.com"), - restSlackNetworkEndpoint("api.slack.com"), - restSlackNetworkEndpoint("hooks.slack.com"), - slackWssNetworkEndpoint("wss-primary.slack.com"), - slackWssNetworkEndpoint("wss-backup.slack.com"), - }, - Binaries: messengerChannelNodeBinaries, - } -} - -func channelSpecPresent(ch v1alpha2.AgentHarnessChannel) bool { - switch ch.Type { - case v1alpha2.AgentHarnessChannelTypeTelegram: - return ch.Telegram != nil - case v1alpha2.AgentHarnessChannelTypeSlack: - return ch.Slack != nil - default: - return false - } -} - -func sandboxHasChannelType(sbx *v1alpha2.AgentHarness, typ v1alpha2.AgentHarnessChannelType) bool { - if sbx == nil { - return false - } - for _, ch := range sbx.Spec.Channels { - if ch.Type == typ && channelSpecPresent(ch) { - return true - } - } - return false -} - -func defaultOpenClawNetworkPolicies() map[string]*sandboxv1.NetworkPolicyRule { - return map[string]*sandboxv1.NetworkPolicyRule{ - NetworkPolicyKeyClawhub: { - Name: "clawhub", - Endpoints: []*sandboxv1.NetworkEndpoint{ - restNetworkEndpoint(RegistryHostClawhub, l7WildcardGETPOST), - }, - Binaries: openClawCLIAndNodeBinaries, - }, - NetworkPolicyKeyAPI: { - Name: "openclaw_api", - Endpoints: []*sandboxv1.NetworkEndpoint{ - restNetworkEndpoint(RegistryHostAPI, l7WildcardGETPOST), - }, - Binaries: openClawCLIAndNodeBinaries, - }, - NetworkPolicyKeyDocs: { - Name: "openclaw_docs", - Endpoints: []*sandboxv1.NetworkEndpoint{ - restNetworkEndpoint(RegistryHostDocs, l7WildcardGETOnly), - }, - Binaries: openClawCLIBinariesOnly, - }, - } -} - -// IsClawSandboxBackend reports backends that use the OpenClaw-style sandbox baseline (OpenClaw / NemoClaw). -func IsClawSandboxBackend(b v1alpha2.AgentHarnessBackendType) bool { - return b == v1alpha2.AgentHarnessBackendOpenClaw || b == v1alpha2.AgentHarnessBackendNemoClaw -} - -// defaultClawFilesystemPolicy mirrors openclaw-sandbox.yaml (OpenShell rejects live changes to -// include_workdir and read_only removals). Workdir is included read-write in addition to paths below. -func defaultClawFilesystemPolicy() *sandboxv1.FilesystemPolicy { - return &sandboxv1.FilesystemPolicy{ - IncludeWorkdir: true, - ReadWrite: []string{ - "/tmp", - "/dev/null", - "/sandbox/.openclaw", - "/sandbox/.nemoclaw", - }, - ReadOnly: []string{ - "/usr", - "/lib", - "/proc", - "/dev/urandom", - "/app", - "/etc", - "/var/log", - }, - } -} - -func defaultClawLandlockPolicy() *sandboxv1.LandlockPolicy { - return &sandboxv1.LandlockPolicy{ - Compatibility: "best_effort", - } -} - -func defaultClawProcessPolicy() *sandboxv1.ProcessPolicy { - return &sandboxv1.ProcessPolicy{ - RunAsUser: "sandbox", - RunAsGroup: "sandbox", - } -} - -// ApplyClawBaselinePolicies adds OpenClaw/NemoClaw fixed network rules plus filesystem / landlock / process policies. -func ApplyClawBaselinePolicies( - net map[string]*sandboxv1.NetworkPolicyRule, -) (fs *sandboxv1.FilesystemPolicy, landlock *sandboxv1.LandlockPolicy, process *sandboxv1.ProcessPolicy) { - maps.Copy(net, defaultOpenClawNetworkPolicies()) - net[NetworkPolicyKeyNPMYarn] = npmYarnNetworkPolicyRule() - return defaultClawFilesystemPolicy(), defaultClawLandlockPolicy(), defaultClawProcessPolicy() -} - -// ApplyChannelNetworkPolicies adds Telegram / Slack egress when channels are configured. -func ApplyChannelNetworkPolicies(sbx *v1alpha2.AgentHarness, net map[string]*sandboxv1.NetworkPolicyRule) { - if sandboxHasChannelType(sbx, v1alpha2.AgentHarnessChannelTypeTelegram) { - net[NetworkPolicyKeyTelegramBot] = telegramBotNetworkPolicyRule() - } - if sandboxHasChannelType(sbx, v1alpha2.AgentHarnessChannelTypeSlack) { - net[NetworkPolicyKeySlack] = slackNetworkPolicyRule() - } -} - -// SandboxPolicyVersion is OpenShell SandboxPolicy.version for fragments produced here and merged by openshell. -const SandboxPolicyVersion = 1 - -// BaselineSandboxPolicy returns the fixed OpenClaw/NemoClaw baseline (clawhub, API, docs, npm/yarn, filesystem, landlock, process). -func BaselineSandboxPolicy() *sandboxv1.SandboxPolicy { - net := map[string]*sandboxv1.NetworkPolicyRule{} - fs, landlock, process := ApplyClawBaselinePolicies(net) - return &sandboxv1.SandboxPolicy{ - Version: SandboxPolicyVersion, - NetworkPolicies: net, - Filesystem: fs, - Landlock: landlock, - Process: process, - } -} - -// ChannelNetworkPolicyFragment returns Telegram/Slack egress as a network-only policy fragment when channels are configured, or nil. -func ChannelNetworkPolicyFragment(ah *v1alpha2.AgentHarness) *sandboxv1.SandboxPolicy { - if ah == nil { - return nil - } - net := map[string]*sandboxv1.NetworkPolicyRule{} - ApplyChannelNetworkPolicies(ah, net) - if len(net) == 0 { - return nil - } - return &sandboxv1.SandboxPolicy{Version: SandboxPolicyVersion, NetworkPolicies: net} -} diff --git a/go/core/pkg/sandboxbackend/openclaw/secrets.go b/go/core/pkg/sandboxbackend/openclaw/secrets.go index 82bf3b8e97..7e97432af1 100644 --- a/go/core/pkg/sandboxbackend/openclaw/secrets.go +++ b/go/core/pkg/sandboxbackend/openclaw/secrets.go @@ -19,7 +19,7 @@ func openclawEnvSecretRef(envVar string) envSecretRef { } } -// credentialValue marshals as either a plaintext string (OpenShell) or an OpenClaw env SecretRef (Substrate). +// credentialValue marshals as either a plaintext string or an OpenClaw env SecretRef (Substrate). type credentialValue struct { literal string envSecret *envSecretRef diff --git a/go/core/pkg/sandboxbackend/openclaw/types.go b/go/core/pkg/sandboxbackend/openclaw/types.go index 5d993dd824..4e8a0cb829 100644 --- a/go/core/pkg/sandboxbackend/openclaw/types.go +++ b/go/core/pkg/sandboxbackend/openclaw/types.go @@ -16,6 +16,7 @@ type gatewaySection struct { Bind string `json:"bind"` Auth gatewayAuth `json:"auth"` Port int `json:"port"` + Remote *gatewayRemote `json:"remote,omitempty"` ControlUi *controlUiSection `json:"controlUi,omitempty"` } @@ -24,6 +25,16 @@ type gatewayAuth struct { Token string `json:"token,omitempty"` } +// gatewayRemote carries the URL and token in-sandbox clients (openclaw acp) +// use when connecting to a token-auth gateway. Both must be set together: +// `openclaw acp` only applies remote.token when it also resolves the +// gateway URL from remote.url (an explicit --url flag bypasses the config +// token entirely). +type gatewayRemote struct { + URL string `json:"url,omitempty"` + Token string `json:"token,omitempty"` +} + type controlUiSection struct { BasePath string `json:"basePath,omitempty"` AllowedOrigins []string `json:"allowedOrigins,omitempty"` diff --git a/go/core/pkg/sandboxbackend/openshell/agent_harness_ensure.go b/go/core/pkg/sandboxbackend/openshell/agent_harness_ensure.go deleted file mode 100644 index e79a4c9cfb..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/agent_harness_ensure.go +++ /dev/null @@ -1,38 +0,0 @@ -package openshell - -import ( - "context" - - openshellv1 "github.com/kagent-dev/kagent/go/api/openshell/gen/openshellv1" - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend" -) - -type createSandboxRequestBuilder func(*v1alpha2.AgentHarness, []string) (*openshellv1.CreateSandboxRequest, []string) - -// ensureAgentHarnessSandbox translates model config, upserts messaging providers, and creates the sandbox. -func (b *agentHarnessOpenShellBackend) ensureAgentHarnessSandbox( - ctx context.Context, - ah *v1alpha2.AgentHarness, - build createSandboxRequestBuilder, -) (sandboxbackend.EnsureResult, error) { - if err := translateModelConfig(ctx, ah, b.kubeClient, b.clients); err != nil { - return sandboxbackend.EnsureResult{}, err - } - providerNames, err := UpsertMessagingProviders(ctx, b.clients, b.kubeClient, ah) - if err != nil { - return sandboxbackend.EnsureResult{}, err - } - req, unsupported := build(ah, providerNames) - return b.CreateAgentHarnessSandbox(ctx, ah, req, unsupported) -} - -func attachMessagingProviders(req *openshellv1.CreateSandboxRequest, names []string) { - if req == nil || len(names) == 0 { - return - } - if req.Spec == nil { - req.Spec = &openshellv1.SandboxSpec{} - } - req.Spec.Providers = append(req.Spec.Providers, names...) -} diff --git a/go/core/pkg/sandboxbackend/openshell/agentharness_openshell_client.go b/go/core/pkg/sandboxbackend/openshell/agentharness_openshell_client.go deleted file mode 100644 index f35fec7acf..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/agentharness_openshell_client.go +++ /dev/null @@ -1,244 +0,0 @@ -package openshell - -import ( - "context" - "errors" - "fmt" - "io" - "strings" - - openshellv1 "github.com/kagent-dev/kagent/go/api/openshell/gen/openshellv1" - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/record" - ctrllog "sigs.k8s.io/controller-runtime/pkg/log" -) - -// AgentHarnessOpenShellClient performs OpenShell gRPC operations for AgentHarness -// lifecycle (sandbox create/get/delete/exec). It wraps *OpenShellClients from Dial -// (client.go) together with Config and optional events: per-RPC timeouts, bearer -// auth on the context, and helpers that map responses to sandboxbackend types. -// It does not run backend-specific pre-create work (e.g. translateModelConfig); -// concrete backends compose findExistingSandbox + their own translation step + -// createSandbox in their own EnsureAgentHarness implementation. -// -// Named with an AgentHarness prefix to avoid confusion with OpenShellClients (dial bundle) and -// openshellv1.OpenShellClient (generated gRPC interface). -type AgentHarnessOpenShellClient struct { - clients *OpenShellClients - cfg Config - recorder record.EventRecorder -} - -func newAgentHarnessOpenShellClient(clients *OpenShellClients, cfg Config, recorder record.EventRecorder) *AgentHarnessOpenShellClient { - return &AgentHarnessOpenShellClient{ - clients: clients, - cfg: cfg, - recorder: recorder, - } -} - -func (c *AgentHarnessOpenShellClient) openShell() openshellv1.OpenShellClient { - if c.clients == nil { - return nil - } - return c.clients.OpenShell -} - -// CallCtx applies CallTimeout from Config when positive. -func (c *AgentHarnessOpenShellClient) CallCtx(ctx context.Context) (context.Context, context.CancelFunc) { - if c.cfg.CallTimeout <= 0 { - return ctx, func() {} - } - return context.WithTimeout(ctx, c.cfg.CallTimeout) -} - -func (c *AgentHarnessOpenShellClient) warnUnsupportedAgentHarnessFields(ctx context.Context, ah *v1alpha2.AgentHarness, fields []string) { - if len(fields) == 0 { - return - } - msg := fmt.Sprintf("OpenShell backend ignored unsupported AgentHarness fields: %v", fields) - if c.recorder != nil && ah != nil { - c.recorder.Event(ah, "Warning", "OpenshellUnsupportedField", msg) - return - } - ctrllog.FromContext(ctx).Info(msg, "agentHarness", ah.Namespace+"/"+ah.Name) -} - -// CreateAgentHarnessSandbox runs CreateSandbox for an AgentHarness after idempotency has been checked upstream. -func (c *AgentHarnessOpenShellClient) CreateAgentHarnessSandbox( - ctx context.Context, - ah *v1alpha2.AgentHarness, - req *openshellv1.CreateSandboxRequest, - unsupported []string, -) (sandboxbackend.EnsureResult, error) { - if ah == nil { - return sandboxbackend.EnsureResult{}, fmt.Errorf("AgentHarness is required") - } - ctx, cancel := c.CallCtx(ctx) - defer cancel() - ctx = withAuth(ctx, c.cfg.Token) - - osCli := c.openShell() - if osCli == nil { - return sandboxbackend.EnsureResult{}, fmt.Errorf("openshell: OpenShell client is required (use Dial or non-nil OpenShellClients.OpenShell)") - } - - name := agentHarnessGatewayName(ah) - c.warnUnsupportedAgentHarnessFields(ctx, ah, unsupported) - - createResp, err := osCli.CreateSandbox(ctx, req) - if err != nil { - return sandboxbackend.EnsureResult{}, fmt.Errorf("openshell CreateSandbox %s: %w", name, err) - } - if createResp.GetSandbox() == nil { - return sandboxbackend.EnsureResult{}, fmt.Errorf("openshell CreateSandbox %s: %w", name, ErrEmptyResponse) - } - handleID := sandboxBackendHandleID(createResp.GetSandbox()) - return sandboxbackend.EnsureResult{ - Handle: sandboxbackend.Handle{ID: handleID}, - Endpoint: endpointFor(c.cfg.GatewayURL, handleID), - }, nil -} - -// GetSandboxStatus maps OpenShell sandbox phase to Ready condition pieces for AgentHarness status. -func (c *AgentHarnessOpenShellClient) GetSandboxStatus(ctx context.Context, h sandboxbackend.Handle) (metav1.ConditionStatus, string, string) { - if h.ID == "" { - return metav1.ConditionUnknown, "SandboxHandleMissing", "no openshell sandbox handle recorded yet" - } - ctx, cancel := c.CallCtx(ctx) - defer cancel() - ctx = withAuth(ctx, c.cfg.Token) - - osCli := c.openShell() - if osCli == nil { - return metav1.ConditionUnknown, "OpenShellClientMissing", "openshell OpenShell gRPC client is not configured" - } - - resp, err := osCli.GetSandbox(ctx, &openshellv1.GetSandboxRequest{Name: h.ID}) - if err != nil { - if status.Code(err) == codes.NotFound { - return metav1.ConditionUnknown, "SandboxNotFound", fmt.Sprintf("openshell sandbox %q not found", h.ID) - } - return metav1.ConditionUnknown, "SandboxGetFailed", err.Error() - } - return phaseToCondition(resp.GetSandbox()) -} - -// DeleteAgentHarnessSandbox deletes the OpenShell sandbox; NotFound is success. -func (c *AgentHarnessOpenShellClient) DeleteAgentHarnessSandbox(ctx context.Context, h sandboxbackend.Handle) (bool, error) { - if h.ID == "" { - return true, nil - } - ctx, cancel := c.CallCtx(ctx) - defer cancel() - ctx = withAuth(ctx, c.cfg.Token) - - osCli := c.openShell() - if osCli == nil { - return false, fmt.Errorf("openshell: OpenShell client is required") - } - - _, err := osCli.DeleteSandbox(ctx, &openshellv1.DeleteSandboxRequest{Name: h.ID}) - if err == nil { - return true, nil - } - if status.Code(err) == codes.NotFound { - return true, nil - } - return false, fmt.Errorf("openshell DeleteSandbox %s: %w", h.ID, err) -} - -// ExecSandboxID resolves metadata.id for ExecSandbox RPCs. -func (c *AgentHarnessOpenShellClient) ExecSandboxID(ctx context.Context, sandboxHandleName string) (string, error) { - name := strings.TrimSpace(sandboxHandleName) - if name == "" { - return "", fmt.Errorf("sandbox handle name is empty") - } - osCli := c.openShell() - if osCli == nil { - return "", fmt.Errorf("openshell client is nil") - } - resp, err := osCli.GetSandbox(ctx, &openshellv1.GetSandboxRequest{Name: name}) - if err != nil { - return "", fmt.Errorf("GetSandbox %q for exec sandbox_id: %w", name, err) - } - sb := resp.GetSandbox() - if sb == nil || sb.GetMetadata() == nil { - return "", fmt.Errorf("GetSandbox %q: empty sandbox", name) - } - id := strings.TrimSpace(sb.GetMetadata().GetId()) - if id != "" { - return id, nil - } - return name, nil -} - -type ExecSandboxResult struct { - ExitCode int32 - Stdout string - Stderr string -} - -// ExecSandbox runs a command inside the sandbox via OpenShell ExecSandbox streaming RPC. -func (c *AgentHarnessOpenShellClient) ExecSandbox(ctx context.Context, sandboxID string, command []string, stdin []byte, env map[string]string, timeoutSec uint32) (int32, string, error) { - res, err := c.ExecSandboxOutput(ctx, sandboxID, command, stdin, env, timeoutSec) - return res.ExitCode, res.Stderr, err -} - -// ExecSandboxOutput runs a command inside the sandbox and captures stdout, stderr, and the exit code. -func (c *AgentHarnessOpenShellClient) ExecSandboxOutput(ctx context.Context, sandboxID string, command []string, stdin []byte, env map[string]string, timeoutSec uint32) (ExecSandboxResult, error) { - osCli := c.openShell() - if osCli == nil { - return ExecSandboxResult{ExitCode: -1}, fmt.Errorf("openshell client is nil") - } - req := &openshellv1.ExecSandboxRequest{ - SandboxId: sandboxID, - Command: command, - Stdin: stdin, - TimeoutSeconds: timeoutSec, - } - if len(env) > 0 { - req.Environment = env - } - stream, err := osCli.ExecSandbox(ctx, req) - if err != nil { - return ExecSandboxResult{ExitCode: -1}, err - } - var stdout strings.Builder - var stderr strings.Builder - var exitCode int32 = -1 - for { - ev, err := stream.Recv() - if err != nil { - if errors.Is(err, io.EOF) { - break - } - return ExecSandboxResult{ExitCode: exitCode, Stdout: stdout.String(), Stderr: stderr.String()}, err - } - switch p := ev.GetPayload().(type) { - case *openshellv1.ExecSandboxEvent_Stdout: - if p.Stdout != nil { - stdout.Write(p.Stdout.GetData()) - } - case *openshellv1.ExecSandboxEvent_Stderr: - if p.Stderr != nil { - stderr.Write(p.Stderr.GetData()) - } - case *openshellv1.ExecSandboxEvent_Exit: - if p.Exit != nil { - exitCode = p.Exit.GetExitCode() - } - } - } - if exitCode == -1 { - return ExecSandboxResult{ExitCode: exitCode, Stdout: stdout.String(), Stderr: stderr.String()}, fmt.Errorf("ExecSandbox finished without exit status") - } - return ExecSandboxResult{ExitCode: exitCode, Stdout: stdout.String(), Stderr: stderr.String()}, nil -} - -// ErrEmptyResponse is returned when OpenShell returns success with an empty Sandbox payload. -var ErrEmptyResponse = errors.New("openshell: empty sandbox in response") diff --git a/go/core/pkg/sandboxbackend/openshell/channels/credentials.go b/go/core/pkg/sandboxbackend/openshell/channels/credentials.go deleted file mode 100644 index 315a2e6c84..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/channels/credentials.go +++ /dev/null @@ -1,105 +0,0 @@ -package channels - -import ( - "context" - "fmt" - "strings" - - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// PutChannelCredential resolves a channel credential into env[envKey]. -func PutChannelCredential(ctx context.Context, kube client.Client, namespace string, cred v1alpha2.AgentHarnessChannelCredential, envKey string, env map[string]string) error { - var v string - if strings.TrimSpace(cred.Value) != "" { - v = strings.TrimSpace(cred.Value) - } else if cred.ValueFrom == nil { - return fmt.Errorf("channel credential requires value or valueFrom") - } else { - var err error - v, err = cred.ValueFrom.Resolve(ctx, kube, namespace) - if err != nil { - return fmt.Errorf("resolve credential %s: %w", envKey, err) - } - } - if prev, ok := env[envKey]; ok && prev != v { - return fmt.Errorf("env %s already set to a different value (duplicate channel binding?)", envKey) - } - env[envKey] = v - return nil -} - -// TelegramAllowFrom returns allowed Telegram user IDs from the channel spec. -func TelegramAllowFrom(ctx context.Context, kube client.Client, namespace string, spec *v1alpha2.AgentHarnessTelegramChannelSpec) ([]string, error) { - if len(spec.AllowedUserIDs) > 0 { - out := make([]string, 0, len(spec.AllowedUserIDs)) - for _, id := range spec.AllowedUserIDs { - s := strings.TrimSpace(id) - if s != "" { - out = append(out, s) - } - } - return out, nil - } - if spec.AllowedUserIDsFrom != nil { - raw, err := spec.AllowedUserIDsFrom.Resolve(ctx, kube, namespace) - if err != nil { - return nil, fmt.Errorf("resolve allowedUserIDsFrom: %w", err) - } - return SplitAllowedList(raw), nil - } - return nil, nil -} - -// HermesSlackAllowedUsers returns allowed Slack member IDs from the Hermes channel spec (SLACK_ALLOWED_USERS). -func HermesSlackAllowedUsers(ctx context.Context, kube client.Client, namespace string, opts *v1alpha2.AgentHarnessHermesSlackOptions) ([]string, error) { - if len(opts.AllowedUserIDs) > 0 { - out := make([]string, 0, len(opts.AllowedUserIDs)) - for _, id := range opts.AllowedUserIDs { - s := strings.TrimSpace(id) - if s != "" { - out = append(out, s) - } - } - return out, nil - } - if opts.AllowedUserIDsFrom != nil { - raw, err := opts.AllowedUserIDsFrom.Resolve(ctx, kube, namespace) - if err != nil { - return nil, fmt.Errorf("resolve allowedUserIDsFrom: %w", err) - } - return SplitAllowedList(raw), nil - } - return nil, nil -} - -// SplitAllowedList parses comma/newline/semicolon-separated ID lists. -func SplitAllowedList(raw string) []string { - raw = strings.TrimSpace(raw) - if raw == "" { - return nil - } - var out []string - for _, part := range strings.FieldsFunc(raw, func(r rune) bool { - return r == ',' || r == '\n' || r == ';' - }) { - s := strings.TrimSpace(part) - if s != "" { - out = append(out, s) - } - } - return out -} - -// TrimNonEmptyStrings returns trimmed non-empty strings from ss. -func TrimNonEmptyStrings(ss []string) []string { - out := make([]string, 0, len(ss)) - for _, s := range ss { - s = strings.TrimSpace(s) - if s != "" { - out = append(out, s) - } - } - return out -} diff --git a/go/core/pkg/sandboxbackend/openshell/channels/envkeys.go b/go/core/pkg/sandboxbackend/openshell/channels/envkeys.go deleted file mode 100644 index a4cff6e756..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/channels/envkeys.go +++ /dev/null @@ -1,60 +0,0 @@ -package channels - -import ( - "strings" - "unicode" -) - -// EnvKeySuffix sanitizes a channel binding name for use in environment variable names. -func EnvKeySuffix(channelName string) string { - var b strings.Builder - for _, r := range strings.TrimSpace(channelName) { - switch { - case unicode.IsLetter(r): - b.WriteRune(unicode.ToUpper(r)) - case unicode.IsDigit(r): - b.WriteRune(r) - case r == '-' || r == '_': - b.WriteRune('_') - } - } - if b.Len() == 0 { - return "CHANNEL" - } - return b.String() -} - -// TelegramBotTokenEnvKey is the per-channel Telegram bot token env var. -func TelegramBotTokenEnvKey(channelName string) string { - return "TELEGRAM_BOT_TOKEN_" + EnvKeySuffix(channelName) -} - -// TelegramAllowedUsersEnvKey is the per-channel Telegram allowlist env var (Hermes). -func TelegramAllowedUsersEnvKey(channelName string) string { - return "TELEGRAM_ALLOWED_USERS_" + EnvKeySuffix(channelName) -} - -// SlackBotTokenEnvKey is the per-channel Slack bot token env var. -func SlackBotTokenEnvKey(channelName string) string { - return "SLACK_BOT_TOKEN_" + EnvKeySuffix(channelName) -} - -// SlackAppTokenEnvKey is the per-channel Slack app token env var. -func SlackAppTokenEnvKey(channelName string) string { - return "SLACK_APP_TOKEN_" + EnvKeySuffix(channelName) -} - -// SlackAllowedUsersEnvKey is the per-channel Slack allowlist env var (Hermes). -func SlackAllowedUsersEnvKey(channelName string) string { - return "SLACK_ALLOWED_USERS_" + EnvKeySuffix(channelName) -} - -// SlackHomeChannelEnvKey is the per-channel Slack home channel env var (Hermes). -func SlackHomeChannelEnvKey(channelName string) string { - return "SLACK_HOME_CHANNEL_" + EnvKeySuffix(channelName) -} - -// SlackHomeChannelNameEnvKey is the per-channel Slack home channel display name env var (Hermes). -func SlackHomeChannelNameEnvKey(channelName string) string { - return "SLACK_HOME_CHANNEL_NAME_" + EnvKeySuffix(channelName) -} diff --git a/go/core/pkg/sandboxbackend/openshell/channels/envkeys_test.go b/go/core/pkg/sandboxbackend/openshell/channels/envkeys_test.go deleted file mode 100644 index 95a844309b..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/channels/envkeys_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package channels - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestEnvKeySuffix(t *testing.T) { - require.Equal(t, "MY_BOT", EnvKeySuffix("my-bot")) - require.Equal(t, "CHANNEL", EnvKeySuffix(" ")) - require.Equal(t, "TELEGRAM_BOT_TOKEN_MY_BOT", TelegramBotTokenEnvKey("my-bot")) -} diff --git a/go/core/pkg/sandboxbackend/openshell/channels/placeholders.go b/go/core/pkg/sandboxbackend/openshell/channels/placeholders.go deleted file mode 100644 index 9fe130c442..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/channels/placeholders.go +++ /dev/null @@ -1,16 +0,0 @@ -package channels - -// ResolveEnvPlaceholder is the in-sandbox placeholder OpenShell rewrites at L7 egress. -func ResolveEnvPlaceholder(envKey string) string { - return "openshell:resolve:env:" + envKey -} - -// SlackBotTokenPlaceholder matches NemoClaw Hermes/OpenClaw Slack bot token shape validation. -func SlackBotTokenPlaceholder(envKey string) string { - return "xoxb-OPENSHELL-RESOLVE-ENV-" + envKey -} - -// SlackAppTokenPlaceholder matches NemoClaw Hermes/OpenClaw Slack app token shape validation. -func SlackAppTokenPlaceholder(envKey string) string { - return "xapp-OPENSHELL-RESOLVE-ENV-" + envKey -} diff --git a/go/core/pkg/sandboxbackend/openshell/channels/providers.go b/go/core/pkg/sandboxbackend/openshell/channels/providers.go deleted file mode 100644 index c6334105ce..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/channels/providers.go +++ /dev/null @@ -1,54 +0,0 @@ -package channels - -// MessagingProviderDef is an OpenShell gateway provider for one messaging credential. -type MessagingProviderDef struct { - Name string - Credentials map[string]string -} - -// TelegramBridgeName is the OpenShell provider name for a Telegram channel binding. -func TelegramBridgeName(sandboxName, channelName string) string { - return sandboxName + "-telegram-" + EnvKeySuffix(channelName) -} - -// SlackBridgeName is the OpenShell provider name for a Slack bot token binding. -func SlackBridgeName(sandboxName, channelName string) string { - return sandboxName + "-slack-" + EnvKeySuffix(channelName) -} - -// SlackAppBridgeName is the OpenShell provider name for a Slack app token binding. -func SlackAppBridgeName(sandboxName, channelName string) string { - return sandboxName + "-slack-app-" + EnvKeySuffix(channelName) -} - -// MessagingProviderDefs builds per-channel provider upsert records from resolved secrets. -func MessagingProviderDefs(sandboxName string, secrets map[string]string, resolved *Resolved) []MessagingProviderDef { - if sandboxName == "" || resolved == nil { - return nil - } - var defs []MessagingProviderDef - for _, tg := range resolved.Telegram { - envKey := TelegramBotTokenEnvKey(tg.Name) - if tok := secrets[envKey]; tok != "" { - defs = append(defs, MessagingProviderDef{ - Name: TelegramBridgeName(sandboxName, tg.Name), - Credentials: map[string]string{envKey: tok}, - }) - } - } - for _, sl := range resolved.Slack { - if tok := secrets[SlackBotTokenEnvKey(sl.Name)]; tok != "" { - defs = append(defs, MessagingProviderDef{ - Name: SlackBridgeName(sandboxName, sl.Name), - Credentials: map[string]string{SlackBotTokenEnvKey(sl.Name): tok}, - }) - } - if tok := secrets[SlackAppTokenEnvKey(sl.Name)]; tok != "" { - defs = append(defs, MessagingProviderDef{ - Name: SlackAppBridgeName(sandboxName, sl.Name), - Credentials: map[string]string{SlackAppTokenEnvKey(sl.Name): tok}, - }) - } - } - return defs -} diff --git a/go/core/pkg/sandboxbackend/openshell/channels/providers_test.go b/go/core/pkg/sandboxbackend/openshell/channels/providers_test.go deleted file mode 100644 index 72265058fa..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/channels/providers_test.go +++ /dev/null @@ -1,24 +0,0 @@ -package channels - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestMessagingProviderDefs(t *testing.T) { - resolved := &Resolved{ - Telegram: []TelegramAccount{{Name: "tg"}}, - Slack: []SlackAccount{{Name: "sl"}}, - Secrets: map[string]string{ - TelegramBotTokenEnvKey("tg"): "tg-secret", - SlackBotTokenEnvKey("sl"): "xoxb", - SlackAppTokenEnvKey("sl"): "xapp", - }, - } - defs := MessagingProviderDefs("ns-h", resolved.Secrets, resolved) - require.Len(t, defs, 3) - require.Equal(t, "ns-h-telegram-TG", defs[0].Name) - require.Equal(t, "ns-h-slack-SL", defs[1].Name) - require.Equal(t, "ns-h-slack-app-SL", defs[2].Name) -} diff --git a/go/core/pkg/sandboxbackend/openshell/channels/resolve.go b/go/core/pkg/sandboxbackend/openshell/channels/resolve.go deleted file mode 100644 index 8f424d3d62..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/channels/resolve.go +++ /dev/null @@ -1,194 +0,0 @@ -package channels - -import ( - "context" - "fmt" - "strings" - - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// TelegramAccount is one Telegram channel account in the harness. -type TelegramAccount struct { - Name string - AllowFrom []string -} - -// SlackAccount is one Slack channel account in the harness. -type SlackAccount struct { - Name string - ChannelAccess v1alpha2.AgentHarnessChannelAccess - AllowlistChannels []string - AllowedUserIDs []string - HomeChannel string - HomeChannelName string - InteractiveReplies bool -} - -// Resolved holds channel credentials and per-backend configuration derived from AgentHarness.spec.channels. -type Resolved struct { - Secrets map[string]string - - HasTelegram bool - HasSlack bool - - TelegramAllow []string - SlackAllow []string - - // Hermes: first Slack channel with homeChannel / homeChannelName wins. - SlackHomeChannel string - SlackHomeChannelName string - - Telegram []TelegramAccount - Slack []SlackAccount - - slackRootPolicy v1alpha2.AgentHarnessChannelAccess - slackSeen bool -} - -// Resolve reads AgentHarness channels, populates per-channel credential env keys in Secrets, -// and returns structured account metadata for Hermes/OpenClaw bootstrap. -func Resolve(ctx context.Context, kube client.Client, namespace string, backend v1alpha2.AgentHarnessBackendType, channels []v1alpha2.AgentHarnessChannel) (*Resolved, error) { - r := &Resolved{Secrets: map[string]string{}} - seenNames := make(map[string]struct{}, len(channels)) - for _, ch := range channels { - name := strings.TrimSpace(ch.Name) - if name == "" { - continue - } - if _, dup := seenNames[name]; dup { - return nil, fmt.Errorf("channel %q: duplicate binding name", name) - } - seenNames[name] = struct{}{} - switch ch.Type { - case v1alpha2.AgentHarnessChannelTypeTelegram: - if err := r.addTelegram(ctx, kube, namespace, ch); err != nil { - return nil, err - } - case v1alpha2.AgentHarnessChannelTypeSlack: - if err := r.addSlackChannel(ctx, kube, namespace, backend, ch); err != nil { - return nil, err - } - default: - return nil, fmt.Errorf("channel %q: unsupported type %q", ch.Name, ch.Type) - } - } - return r, nil -} - -func (r *Resolved) addTelegram(ctx context.Context, kube client.Client, namespace string, ch v1alpha2.AgentHarnessChannel) error { - spec := ch.Telegram - if spec == nil { - return fmt.Errorf("channel %q: telegram spec is required", ch.Name) - } - if err := PutChannelCredential(ctx, kube, namespace, spec.BotToken, TelegramBotTokenEnvKey(ch.Name), r.Secrets); err != nil { - return fmt.Errorf("channel %q telegram bot token: %w", ch.Name, err) - } - allow, err := TelegramAllowFrom(ctx, kube, namespace, spec) - if err != nil { - return fmt.Errorf("channel %q telegram allowlist: %w", ch.Name, err) - } - r.HasTelegram = true - if len(allow) > 0 { - r.TelegramAllow = allow - } - r.Telegram = append(r.Telegram, TelegramAccount{Name: ch.Name, AllowFrom: allow}) - return nil -} - -func (r *Resolved) addSlackChannel(ctx context.Context, kube client.Client, namespace string, backend v1alpha2.AgentHarnessBackendType, ch v1alpha2.AgentHarnessChannel) error { - spec := ch.Slack - if spec == nil { - return fmt.Errorf("channel %q: slack spec is required", ch.Name) - } - switch backend { - case v1alpha2.AgentHarnessBackendHermes: - opts := spec.Hermes - if opts == nil { - opts = &v1alpha2.AgentHarnessHermesSlackOptions{} - } - return r.addHermesSlack(ctx, kube, namespace, ch.Name, spec.BotToken, spec.AppToken, opts) - case v1alpha2.AgentHarnessBackendOpenClaw, v1alpha2.AgentHarnessBackendNemoClaw: - opts := spec.OpenClaw - if opts == nil { - opts = &v1alpha2.AgentHarnessOpenClawSlackOptions{} - } - return r.addOpenClawSlack(ctx, kube, namespace, ch.Name, spec.BotToken, spec.AppToken, opts) - default: - return fmt.Errorf("channel %q: slack channels are not supported for backend %q", ch.Name, backend) - } -} - -func (r *Resolved) putSlackCredentials(ctx context.Context, kube client.Client, namespace, channelName string, botToken, appToken v1alpha2.AgentHarnessChannelCredential) error { - if err := PutChannelCredential(ctx, kube, namespace, botToken, SlackBotTokenEnvKey(channelName), r.Secrets); err != nil { - return fmt.Errorf("channel %q slack bot token: %w", channelName, err) - } - if err := PutChannelCredential(ctx, kube, namespace, appToken, SlackAppTokenEnvKey(channelName), r.Secrets); err != nil { - return fmt.Errorf("channel %q slack app token: %w", channelName, err) - } - return nil -} - -func (r *Resolved) addOpenClawSlack(ctx context.Context, kube client.Client, namespace, channelName string, botToken, appToken v1alpha2.AgentHarnessChannelCredential, opts *v1alpha2.AgentHarnessOpenClawSlackOptions) error { - if err := r.putSlackCredentials(ctx, kube, namespace, channelName, botToken, appToken); err != nil { - return err - } - interactive := true - if opts.InteractiveReplies != nil { - interactive = *opts.InteractiveReplies - } - access := opts.ChannelAccess - if access == "" { - access = v1alpha2.AgentHarnessChannelAccessOpen - } - r.HasSlack = true - r.Slack = append(r.Slack, SlackAccount{ - Name: channelName, - ChannelAccess: access, - AllowlistChannels: TrimNonEmptyStrings(opts.AllowlistChannels), - InteractiveReplies: interactive, - }) - if !r.slackSeen { - r.slackRootPolicy = access - r.slackSeen = true - } - return nil -} - -func (r *Resolved) addHermesSlack(ctx context.Context, kube client.Client, namespace, channelName string, botToken, appToken v1alpha2.AgentHarnessChannelCredential, opts *v1alpha2.AgentHarnessHermesSlackOptions) error { - if err := r.putSlackCredentials(ctx, kube, namespace, channelName, botToken, appToken); err != nil { - return err - } - allow, err := HermesSlackAllowedUsers(ctx, kube, namespace, opts) - if err != nil { - return fmt.Errorf("channel %q slack allowed users: %w", channelName, err) - } - homeChannel := strings.TrimSpace(opts.HomeChannel) - homeChannelName := strings.TrimSpace(opts.HomeChannelName) - r.HasSlack = true - if len(allow) > 0 { - r.SlackAllow = append(r.SlackAllow, allow...) - } - r.Slack = append(r.Slack, SlackAccount{ - Name: channelName, - ChannelAccess: v1alpha2.AgentHarnessChannelAccessOpen, - AllowedUserIDs: allow, - HomeChannel: homeChannel, - HomeChannelName: homeChannelName, - InteractiveReplies: true, - }) - if r.SlackHomeChannel == "" && homeChannel != "" { - r.SlackHomeChannel = homeChannel - r.SlackHomeChannelName = homeChannelName - } - return nil -} - -// SlackRootGroupPolicy returns the group policy for the first Slack channel (OpenClaw bundle). -func (r *Resolved) SlackRootGroupPolicy() v1alpha2.AgentHarnessChannelAccess { - if r.slackSeen { - return r.slackRootPolicy - } - return v1alpha2.AgentHarnessChannelAccessOpen -} diff --git a/go/core/pkg/sandboxbackend/openshell/channels/resolve_test.go b/go/core/pkg/sandboxbackend/openshell/channels/resolve_test.go deleted file mode 100644 index 330b71ed3f..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/channels/resolve_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package channels - -import ( - "context" - "testing" - - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/stretchr/testify/require" - "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client/fake" -) - -func TestResolve_perChannelTelegramSecrets(t *testing.T) { - ns := "default" - kube := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build() - channels := []v1alpha2.AgentHarnessChannel{ - { - Name: "bot-a", - Type: v1alpha2.AgentHarnessChannelTypeTelegram, - Telegram: &v1alpha2.AgentHarnessTelegramChannelSpec{ - BotToken: v1alpha2.AgentHarnessChannelCredential{Value: "token-a"}, - }, - }, - { - Name: "bot-b", - Type: v1alpha2.AgentHarnessChannelTypeTelegram, - Telegram: &v1alpha2.AgentHarnessTelegramChannelSpec{ - BotToken: v1alpha2.AgentHarnessChannelCredential{Value: "token-b"}, - }, - }, - } - resolved, err := Resolve(context.Background(), kube, ns, v1alpha2.AgentHarnessBackendHermes, channels) - require.NoError(t, err) - require.Equal(t, "token-a", resolved.Secrets[TelegramBotTokenEnvKey("bot-a")]) - require.Equal(t, "token-b", resolved.Secrets[TelegramBotTokenEnvKey("bot-b")]) -} - -func TestResolve_duplicateChannelName(t *testing.T) { - kube := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build() - _, err := Resolve(context.Background(), kube, "default", v1alpha2.AgentHarnessBackendHermes, []v1alpha2.AgentHarnessChannel{ - {Name: "dup", Type: v1alpha2.AgentHarnessChannelTypeTelegram, Telegram: &v1alpha2.AgentHarnessTelegramChannelSpec{ - BotToken: v1alpha2.AgentHarnessChannelCredential{Value: "a"}, - }}, - {Name: "dup", Type: v1alpha2.AgentHarnessChannelTypeTelegram, Telegram: &v1alpha2.AgentHarnessTelegramChannelSpec{ - BotToken: v1alpha2.AgentHarnessChannelCredential{Value: "b"}, - }}, - }) - require.Error(t, err) - require.Contains(t, err.Error(), "duplicate binding name") -} - -func TestMessagingProviderDefs_perChannel(t *testing.T) { - resolved := &Resolved{ - Telegram: []TelegramAccount{{Name: "tg1"}, {Name: "tg2"}}, - Slack: []SlackAccount{{Name: "sl1"}}, - Secrets: map[string]string{ - TelegramBotTokenEnvKey("tg1"): "tok1", - TelegramBotTokenEnvKey("tg2"): "tok2", - SlackBotTokenEnvKey("sl1"): "xoxb", - SlackAppTokenEnvKey("sl1"): "xapp", - }, - } - defs := MessagingProviderDefs("ns-h", resolved.Secrets, resolved) - require.Len(t, defs, 4) - require.Equal(t, "ns-h-telegram-TG1", defs[0].Name) - require.Equal(t, "tok1", defs[0].Credentials[TelegramBotTokenEnvKey("tg1")]) - require.Equal(t, "ns-h-telegram-TG2", defs[1].Name) - require.Equal(t, "tok2", defs[1].Credentials[TelegramBotTokenEnvKey("tg2")]) -} diff --git a/go/core/pkg/sandboxbackend/openshell/client.go b/go/core/pkg/sandboxbackend/openshell/client.go deleted file mode 100644 index fc591efdd8..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/client.go +++ /dev/null @@ -1,124 +0,0 @@ -package openshell - -import ( - "context" - "crypto/tls" - "crypto/x509" - "fmt" - - inferencev1 "github.com/kagent-dev/kagent/go/api/openshell/gen/inferencev1" - openshellv1 "github.com/kagent-dev/kagent/go/api/openshell/gen/openshellv1" - "google.golang.org/grpc" - "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/metadata" -) - -// OpenShellClients is the result of Dial: one gRPC connection plus the generated -// openshell.v1.OpenShell and inference.v1.Inference stubs. It does not interpret -// AgentHarness or apply per-call policy; use AgentHarnessOpenShellClient for that -// (see agentharness_openshell_client.go in this package). -type OpenShellClients struct { - OpenShell openshellv1.OpenShellClient - Inference inferencev1.InferenceClient - Conn *grpc.ClientConn -} - -// Close closes the underlying connection. -func (c *OpenShellClients) Close() error { - if c == nil || c.Conn == nil { - return nil - } - return c.Conn.Close() -} - -// Dial opens a single connection to cfg.GatewayURL and constructs clients for -// openshell.v1.OpenShell and openshell.inference.v1.Inference. Close OpenShellClients -// when the connection is no longer needed. -func Dial(ctx context.Context, cfg Config) (*OpenShellClients, error) { - if cfg.GatewayURL == "" { - return nil, fmt.Errorf("openshell: gateway URL is required") - } - - var transportCreds credentials.TransportCredentials - switch { - case cfg.Insecure: - transportCreds = insecure.NewCredentials() - case len(cfg.TLSCAPEM) > 0: - pool := x509.NewCertPool() - if !pool.AppendCertsFromPEM(cfg.TLSCAPEM) { - return nil, fmt.Errorf("openshell: no PEM certificates found in TLS CA bundle") - } - transportCreds = credentials.NewTLS(&tls.Config{RootCAs: pool, MinVersion: tls.VersionTLS12}) - default: - transportCreds = credentials.NewTLS(&tls.Config{MinVersion: tls.VersionTLS12}) - } - - dialCtx := ctx - if cfg.DialTimeout > 0 { - var cancel context.CancelFunc - dialCtx, cancel = context.WithTimeout(ctx, cfg.DialTimeout) - defer cancel() - } - - opts := []grpc.DialOption{grpc.WithTransportCredentials(transportCreds)} - if cfg.Token != "" { - opts = append(opts, grpc.WithPerRPCCredentials(bearerToken{token: cfg.Token, requireTLS: !cfg.Insecure})) - } - - conn, err := grpc.NewClient(cfg.GatewayURL, opts...) - if err != nil { - return nil, fmt.Errorf("openshell: dial %s: %w", cfg.GatewayURL, err) - } - // NewClient stays idle until Connect() or an RPC; otherwise waitConnReady times out. - conn.Connect() - if err := waitConnReady(dialCtx, conn); err != nil { - _ = conn.Close() - return nil, fmt.Errorf("openshell: dial %s: %w", cfg.GatewayURL, err) - } - return &OpenShellClients{ - OpenShell: openshellv1.NewOpenShellClient(conn), - Inference: inferencev1.NewInferenceClient(conn), - Conn: conn, - }, nil -} - -func waitConnReady(ctx context.Context, conn *grpc.ClientConn) error { - for { - switch s := conn.GetState(); s { - case connectivity.Ready: - return nil - case connectivity.Shutdown: - return fmt.Errorf("connection shut down") - default: - if !conn.WaitForStateChange(ctx, s) { - if err := ctx.Err(); err != nil { - return err - } - return fmt.Errorf("connection closed before ready") - } - } - } -} - -type bearerToken struct { - token string - requireTLS bool -} - -func (b bearerToken) GetRequestMetadata(_ context.Context, _ ...string) (map[string]string, error) { - return map[string]string{"authorization": "Bearer " + b.token}, nil -} - -func (b bearerToken) RequireTransportSecurity() bool { return b.requireTLS } - -// withAuth attaches the bearer token to the outgoing context metadata. The -// per-RPC creds already do this on TLS connections; withAuth covers the -// insecure case where RequireTransportSecurity() == false is still respected. -func withAuth(ctx context.Context, token string) context.Context { - if token == "" { - return ctx - } - return metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+token) -} diff --git a/go/core/pkg/sandboxbackend/openshell/client_test.go b/go/core/pkg/sandboxbackend/openshell/client_test.go deleted file mode 100644 index 1a2ea378e6..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/client_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package openshell - -import ( - "context" - "net" - "testing" - "time" - - "github.com/stretchr/testify/require" - "google.golang.org/grpc" -) - -func TestDial_tcpReachesReady(t *testing.T) { - lis, err := net.Listen("tcp", "127.0.0.1:0") - require.NoError(t, err) - srv := grpc.NewServer() - go func() { _ = srv.Serve(lis) }() - t.Cleanup(func() { - srv.Stop() - _ = lis.Close() - }) - - cfg := Config{ - GatewayURL: lis.Addr().String(), - Insecure: true, - DialTimeout: 2 * time.Second, - } - c, err := Dial(context.Background(), cfg) - require.NoError(t, err) - require.NoError(t, c.Close()) -} diff --git a/go/core/pkg/sandboxbackend/openshell/config.go b/go/core/pkg/sandboxbackend/openshell/config.go deleted file mode 100644 index 65e3c561a6..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/config.go +++ /dev/null @@ -1,30 +0,0 @@ -package openshell - -import "time" - -// Config configures the OpenShell gateway gRPC client. -type Config struct { - // GatewayURL is a gRPC target (e.g. "dns:///gateway.openshell.svc:443" - // or "localhost:7443"). Required. - GatewayURL string - - // Token is a static bearer token sent as grpc metadata "authorization: - // Bearer ". Optional. - Token string - - // TLSCAPEM is a PEM-encoded CA bundle used to verify the gateway - // certificate. If empty, system roots are used. If both TLSCAPEM is - // empty and GatewayURL has no TLS scheme, the client dials insecurely - // (intended for local/in-cluster plaintext only). - TLSCAPEM []byte - - // Insecure, when true, dials without TLS regardless of other settings. - // Use only for tests or explicit local development. - Insecure bool - - // DialTimeout bounds the initial dial. Zero means no timeout. - DialTimeout time.Duration - - // CallTimeout bounds each RPC. Zero means no per-call timeout. - CallTimeout time.Duration -} diff --git a/go/core/pkg/sandboxbackend/openshell/hermes.go b/go/core/pkg/sandboxbackend/openshell/hermes.go deleted file mode 100644 index 5f517fd252..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/hermes.go +++ /dev/null @@ -1,209 +0,0 @@ -package openshell - -import ( - "context" - "fmt" - "strings" - "time" - - openshellv1 "github.com/kagent-dev/kagent/go/api/openshell/gen/openshellv1" - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/internal/utils" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openshell/hermes" - "k8s.io/client-go/tools/record" - "sigs.k8s.io/controller-runtime/pkg/client" - ctrllog "sigs.k8s.io/controller-runtime/pkg/log" -) - -// HermesBackend implements AsyncBackend and PostReadyBackend for Hermes AgentHarness resources. -type HermesBackend struct { - *agentHarnessOpenShellBackend -} - -var _ sandboxbackend.AsyncBackend = (*HermesBackend)(nil) - -// NewHermesBackend returns the Hermes harness backend. -func NewHermesBackend(kubeClient client.Client, clients *OpenShellClients, cfg Config, recorder record.EventRecorder) *HermesBackend { - return &HermesBackend{ - agentHarnessOpenShellBackend: newAgentHarnessOpenShellBackend( - kubeClient, clients, cfg, recorder, - v1alpha2.AgentHarnessBackendHermes, - ), - } -} - -// EnsureAgentHarness syncs ModelConfig then creates the Hermes sandbox. -func (b *HermesBackend) EnsureAgentHarness(ctx context.Context, ah *v1alpha2.AgentHarness) (sandboxbackend.EnsureResult, error) { - if ah == nil { - return sandboxbackend.EnsureResult{}, fmt.Errorf("AgentHarness is required") - } - ctx, cancel := b.CallCtx(ctx) - defer cancel() - ctx = withAuth(ctx, b.cfg.Token) - - if res, found, err := b.findExistingSandbox(ctx, ah); err != nil || found { - return res, err - } - return b.ensureAgentHarnessSandbox(ctx, ah, buildHermesCreateRequest) -} - -// OnAgentHarnessReady writes ~/.hermes/config.yaml and .env, updates the config hash, and starts the gateway. -func (b *HermesBackend) OnAgentHarnessReady(ctx context.Context, ah *v1alpha2.AgentHarness, h sandboxbackend.Handle) error { - ref := strings.TrimSpace(ah.Spec.ModelConfigRef) - if ref == "" { - return nil - } - if h.ID == "" { - return fmt.Errorf("sandbox backend handle id is empty") - } - if b.kubeClient == nil { - return fmt.Errorf("kubernetes client is required for hermes bootstrap") - } - - modelConfigRef, err := utils.ParseRefString(ref, ah.Namespace) - if err != nil { - return fmt.Errorf("parse modelConfigRef: %w", err) - } - mc := &v1alpha2.ModelConfig{} - if err := b.kubeClient.Get(ctx, modelConfigRef, mc); err != nil { - return fmt.Errorf("get ModelConfig: %w", err) - } - - if _, err := UpsertMessagingProviders(ctx, b.clients, b.kubeClient, ah); err != nil { - return fmt.Errorf("upsert messaging providers: %w", err) - } - - configYAML, envFile, execEnv, err := hermes.BuildBootstrapArtifacts(ctx, b.kubeClient, ah.Namespace, ah, mc) - if err != nil { - return fmt.Errorf("build hermes config: %w", err) - } - - token := b.cfg.Token - idCtx, cancelID := b.CallCtx(ctx) - defer cancelID() - execID, err := b.ExecSandboxID(withAuth(idCtx, token), h.ID) - if err != nil { - return fmt.Errorf("resolve sandbox exec id: %w", err) - } - - mkdirScript := fmt.Sprintf(`mkdir -p %s`, hermes.HermesConfigDir) - installCtx, cancelInstall := context.WithTimeout(ctx, 120*time.Second+15*time.Second) - defer cancelInstall() - code, stderr, err := b.ExecSandbox(withAuth(installCtx, token), execID, []string{"sh", "-c", mkdirScript}, nil, execEnv, 30) - if err != nil { - return fmt.Errorf("mkdir hermes config dir: %w", err) - } - if code != 0 { - return fmt.Errorf("mkdir hermes config dir: exit %d: %s", code, strings.TrimSpace(stderr)) - } - - configInstall := []string{"sh", "-c", fmt.Sprintf(`cat > %s/config.yaml`, hermes.HermesConfigDir)} - code, stderr, err = b.ExecSandbox(withAuth(installCtx, token), execID, configInstall, configYAML, execEnv, 60) - if err != nil { - return fmt.Errorf("install hermes config.yaml: %w", err) - } - if code != 0 { - return fmt.Errorf("install hermes config.yaml: exit %d: %s", code, strings.TrimSpace(stderr)) - } - - envInstall := []string{"sh", "-c", fmt.Sprintf(`cat > %s/.env`, hermes.HermesConfigDir)} - code, stderr, err = b.ExecSandbox(withAuth(installCtx, token), execID, envInstall, envFile, execEnv, 60) - if err != nil { - return fmt.Errorf("install hermes .env: %w", err) - } - if code != 0 { - return fmt.Errorf("install hermes .env: exit %d: %s", code, strings.TrimSpace(stderr)) - } - - hashScript := fmt.Sprintf( - `mkdir -p /etc/nemoclaw && sha256sum %s/config.yaml %s/.env > %s && chmod 444 %s 2>/dev/null || true`, - hermes.HermesConfigDir, hermes.HermesConfigDir, hermes.HermesConfigHashFile, hermes.HermesConfigHashFile, - ) - hashCtx, cancelHash := context.WithTimeout(ctx, 30*time.Second) - defer cancelHash() - code, stderr, err = b.ExecSandbox(withAuth(hashCtx, token), execID, []string{"sh", "-c", hashScript}, nil, execEnv, 30) - if err != nil { - return fmt.Errorf("write hermes config hash: %w", err) - } - if code != 0 { - ctrllog.FromContext(ctx).Info("hermes config hash write skipped (non-fatal)", "stderr", strings.TrimSpace(stderr)) - } - - gwCtx, cancelGW := context.WithTimeout(ctx, 90*time.Second+15*time.Second) - defer cancelGW() - gatewayStart := fmt.Sprintf( - `HERMES_HOME=%s nohup hermes gateway run >>/tmp/gateway.log 2>&1 &`, - hermes.HermesConfigDir, - ) - code, stderr, err = b.ExecSandbox(withAuth(gwCtx, token), execID, []string{"sh", "-c", gatewayStart}, nil, execEnv, 30) - if err != nil { - return fmt.Errorf("start hermes gateway: %w", err) - } - if code != 0 { - return fmt.Errorf("start hermes gateway: exit %d: %s", code, strings.TrimSpace(stderr)) - } - - if err := b.waitHermesGatewayListen(withAuth(gwCtx, token), execID, hermes.HermesInternalGatewayPort, execEnv); err != nil { - return fmt.Errorf("wait for hermes gateway listen: %w", err) - } - - socatStart := fmt.Sprintf( - `command -v socat >/dev/null 2>&1 && nohup socat TCP-LISTEN:%d,bind=0.0.0.0,fork,reuseaddr TCP:127.0.0.1:%d >>/tmp/socat.log 2>&1 &`, - hermes.HermesPublicGatewayPort, - hermes.HermesInternalGatewayPort, - ) - code, stderr, err = b.ExecSandbox(withAuth(gwCtx, token), execID, []string{"sh", "-c", socatStart}, nil, execEnv, 30) - if err != nil { - return fmt.Errorf("start hermes socat forwarder: %w", err) - } - if code != 0 { - return fmt.Errorf("start hermes socat forwarder: exit %d: %s", code, strings.TrimSpace(stderr)) - } - - ctrllog.FromContext(ctx).Info("hermes bootstrap completed", "agentHarness", ah.Namespace+"/"+ah.Name) - return nil -} - -func (b *HermesBackend) waitHermesGatewayListen(ctx context.Context, execID string, port int, execEnv map[string]string) error { - listenAddr := fmt.Sprintf("127.0.0.1:%d", port) - var lastResult ExecSandboxResult - for range 30 { - result, err := b.ExecSandboxOutput(ctx, execID, []string{"ss", "-tln"}, nil, execEnv, 5) - if err != nil { - return err - } - lastResult = result - if result.ExitCode != 0 { - return fmt.Errorf("ss -tln exit %d: %s", result.ExitCode, strings.TrimSpace(result.Stderr)) - } - if strings.Contains(result.Stdout, listenAddr) { - return nil - } - timer := time.NewTimer(time.Second) - select { - case <-ctx.Done(): - timer.Stop() - return ctx.Err() - case <-timer.C: - } - } - return fmt.Errorf( - "timed out after 30s waiting for %s; last ss output: %s; stderr: %s", - listenAddr, - strings.TrimSpace(lastResult.Stdout), - strings.TrimSpace(lastResult.Stderr), - ) -} - -func buildHermesCreateRequest(ah *v1alpha2.AgentHarness, messagingProviders []string) (*openshellv1.CreateSandboxRequest, []string) { - req, unsupported := buildAgentHarnessOpenshellCreateRequest(ah) - if req.GetSpec().GetTemplate() == nil { - req.Spec.Template = &openshellv1.SandboxTemplate{} - } - if ah.Spec.Image == "" { - req.Spec.Template.Image = hermes.HermesSandboxBaseImage - } - attachMessagingProviders(req, messagingProviders) - return req, unsupported -} diff --git a/go/core/pkg/sandboxbackend/openshell/hermes/bootstrap.go b/go/core/pkg/sandboxbackend/openshell/hermes/bootstrap.go deleted file mode 100644 index 778e468ec2..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/hermes/bootstrap.go +++ /dev/null @@ -1,178 +0,0 @@ -package hermes - -import ( - "context" - "fmt" - "maps" - "strings" - - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openshell/channels" - "gopkg.in/yaml.v3" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// hermesConfig is the YAML shape written to ~/.hermes/config.yaml. -type hermesConfig struct { - ConfigVersion int `yaml:"_config_version"` - Model hermesModel `yaml:"model"` - Terminal hermesTerminal `yaml:"terminal"` - Agent hermesAgent `yaml:"agent"` - Memory hermesMemory `yaml:"memory"` - Skills hermesSkills `yaml:"skills"` - Display hermesDisplay `yaml:"display"` - Platforms hermesPlatforms `yaml:"platforms"` - Telegram *hermesTelegram `yaml:"telegram,omitempty"` -} - -type hermesModel struct { - Default string `yaml:"default"` - Provider string `yaml:"provider"` - BaseURL string `yaml:"base_url"` -} - -type hermesTerminal struct { - Backend string `yaml:"backend"` - Timeout int `yaml:"timeout"` -} - -type hermesAgent struct { - MaxTurns int `yaml:"max_turns"` - ReasoningEffort string `yaml:"reasoning_effort"` -} - -type hermesMemory struct { - MemoryEnabled bool `yaml:"memory_enabled"` - UserProfileEnabled bool `yaml:"user_profile_enabled"` -} - -type hermesSkills struct { - CreationNudgeInterval int `yaml:"creation_nudge_interval"` -} - -type hermesDisplay struct { - Compact bool `yaml:"compact"` - ToolProgress string `yaml:"tool_progress"` -} - -type hermesPlatforms struct { - APIServer hermesAPIServer `yaml:"api_server"` -} - -type hermesAPIServer struct { - Enabled bool `yaml:"enabled"` - Extra hermesAPIServerEx `yaml:"extra"` -} - -type hermesAPIServerEx struct { - Port int `yaml:"port"` - Host string `yaml:"host"` -} - -type hermesTelegram struct { - RequireMention bool `yaml:"require_mention"` -} - -// BuildHermesConfigYAML returns config.yaml bytes for the given ModelConfig. -func BuildHermesConfigYAML(mc *v1alpha2.ModelConfig, msg *messagingState) ([]byte, error) { - if mc == nil { - return nil, fmt.Errorf("ModelConfig is required") - } - modelID := strings.TrimSpace(mc.Spec.Model) - if modelID == "" { - return nil, fmt.Errorf("ModelConfig.spec.model is required for Hermes bootstrap") - } - - cfg := hermesConfig{ - ConfigVersion: 12, - Model: hermesModel{ - Default: modelID, - Provider: "custom", - BaseURL: DefaultInferenceBaseURL, - }, - Terminal: hermesTerminal{Backend: "local", Timeout: 180}, - Agent: hermesAgent{MaxTurns: 60, ReasoningEffort: "medium"}, - Memory: hermesMemory{ - MemoryEnabled: true, - UserProfileEnabled: true, - }, - Skills: hermesSkills{CreationNudgeInterval: 15}, - Display: hermesDisplay{Compact: false, ToolProgress: "all"}, - Platforms: hermesPlatforms{ - APIServer: hermesAPIServer{ - Enabled: true, - Extra: hermesAPIServerEx{ - Port: HermesInternalGatewayPort, - Host: "127.0.0.1", - }, - }, - }, - } - if msg != nil && msg.hasTelegram() { - cfg.Telegram = &hermesTelegram{RequireMention: true} - } - - raw, err := yaml.Marshal(&cfg) - if err != nil { - return nil, fmt.Errorf("marshal hermes config yaml: %w", err) - } - return raw, nil -} - -// BuildHermesEnvFile returns .env file bytes for Hermes bootstrap (gateway port, channel placeholders, allowlists). -// Resolved channel secrets belong in execEnv; callers populate that separately (see BuildBootstrapArtifacts). -func BuildHermesEnvFile(msg *messagingState) []byte { - lines := []string{ - fmt.Sprintf("API_SERVER_PORT=%d", HermesInternalGatewayPort), - "API_SERVER_HOST=127.0.0.1", - } - if msg != nil && msg.resolved != nil { - for _, tg := range msg.resolved.Telegram { - botKey := channels.TelegramBotTokenEnvKey(tg.Name) - lines = append(lines, botKey+"="+channels.ResolveEnvPlaceholder(botKey)) - if len(tg.AllowFrom) > 0 { - lines = append(lines, channels.TelegramAllowedUsersEnvKey(tg.Name)+"="+strings.Join(tg.AllowFrom, ",")) - } - } - for _, sl := range msg.resolved.Slack { - botKey := channels.SlackBotTokenEnvKey(sl.Name) - appKey := channels.SlackAppTokenEnvKey(sl.Name) - lines = append(lines, - botKey+"="+channels.SlackBotTokenPlaceholder(botKey), - appKey+"="+channels.SlackAppTokenPlaceholder(appKey), - ) - if len(sl.AllowedUserIDs) > 0 { - lines = append(lines, channels.SlackAllowedUsersEnvKey(sl.Name)+"="+strings.Join(sl.AllowedUserIDs, ",")) - } - if home := strings.TrimSpace(sl.HomeChannel); home != "" { - lines = append(lines, channels.SlackHomeChannelEnvKey(sl.Name)+"="+home) - if name := strings.TrimSpace(sl.HomeChannelName); name != "" { - lines = append(lines, channels.SlackHomeChannelNameEnvKey(sl.Name)+"="+name) - } - } - } - } - if len(lines) == 0 { - return nil - } - return []byte(strings.Join(lines, "\n") + "\n") -} - -// BuildBootstrapArtifacts builds config.yaml, .env, and exec environment for Hermes bootstrap. -func BuildBootstrapArtifacts(ctx context.Context, kube client.Client, namespace string, ah *v1alpha2.AgentHarness, mc *v1alpha2.ModelConfig) (configYAML, envFile []byte, execEnv map[string]string, err error) { - execEnv = map[string]string{} - var msg *messagingState - if ah != nil && len(ah.Spec.Channels) > 0 { - msg, err = AccumulateMessagingChannels(ctx, kube, namespace, ah.Spec.Backend, ah.Spec.Channels, nil) - if err != nil { - return nil, nil, nil, err - } - maps.Copy(execEnv, msg.secrets()) - } - configYAML, err = BuildHermesConfigYAML(mc, msg) - if err != nil { - return nil, nil, nil, err - } - envFile = BuildHermesEnvFile(msg) - return configYAML, envFile, execEnv, nil -} diff --git a/go/core/pkg/sandboxbackend/openshell/hermes/bootstrap_test.go b/go/core/pkg/sandboxbackend/openshell/hermes/bootstrap_test.go deleted file mode 100644 index 57a3b870b2..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/hermes/bootstrap_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package hermes_test - -import ( - "context" - "testing" - - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openshell/hermes" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client/fake" -) - -func TestBuildHermesConfigYAML_ModelOnly(t *testing.T) { - mc := &v1alpha2.ModelConfig{ - Spec: v1alpha2.ModelConfigSpec{ - Model: "test-model", - Provider: v1alpha2.ModelProviderOpenAI, - }, - } - raw, err := hermes.BuildHermesConfigYAML(mc, nil) - require.NoError(t, err) - s := string(raw) - require.Contains(t, s, "default: test-model") - require.Contains(t, s, "provider: custom") - require.Contains(t, s, "base_url: https://inference.local/v1") - require.Contains(t, s, "port: 18642") -} - -func TestBuildBootstrapArtifacts_TelegramSlack(t *testing.T) { - scheme := runtime.NewScheme() - utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(v1alpha2.AddToScheme(scheme)) - - ns := "default" - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "tg", Namespace: ns}, - Data: map[string][]byte{"token": []byte("tg-token")}, - } - mc := &v1alpha2.ModelConfig{ - ObjectMeta: metav1.ObjectMeta{Name: "mc1", Namespace: ns}, - Spec: v1alpha2.ModelConfigSpec{ - Model: "gpt-4o", - Provider: v1alpha2.ModelProviderOpenAI, - }, - } - ah := &v1alpha2.AgentHarness{ - ObjectMeta: metav1.ObjectMeta{Name: "h1", Namespace: ns}, - Spec: v1alpha2.AgentHarnessSpec{ - Backend: v1alpha2.AgentHarnessBackendHermes, - Channels: []v1alpha2.AgentHarnessChannel{ - { - Name: "tg", - Type: v1alpha2.AgentHarnessChannelTypeTelegram, - Telegram: &v1alpha2.AgentHarnessTelegramChannelSpec{ - BotToken: v1alpha2.AgentHarnessChannelCredential{ - ValueFrom: &v1alpha2.ValueSource{ - Type: v1alpha2.SecretValueSource, - Name: "tg", - Key: "token", - }, - }, - AllowedUserIDs: []string{"123456789"}, - }, - }, - { - Name: "sl", - Type: v1alpha2.AgentHarnessChannelTypeSlack, - Slack: &v1alpha2.AgentHarnessSlackChannelSpec{ - BotToken: v1alpha2.AgentHarnessChannelCredential{Value: "xoxb-bot"}, - AppToken: v1alpha2.AgentHarnessChannelCredential{Value: "xapp-app"}, - Hermes: &v1alpha2.AgentHarnessHermesSlackOptions{ - AllowedUserIDs: []string{"U01234567", "U89ABCDEF"}, - HomeChannel: "C01234567890", - HomeChannelName: "general", - }, - }, - }, - }, - }, - } - - kube := fake.NewClientBuilder().WithScheme(scheme).WithObjects(secret, mc).Build() - configYAML, envFile, execEnv, err := hermes.BuildBootstrapArtifacts(context.Background(), kube, ns, ah, mc) - require.NoError(t, err) - require.Contains(t, string(configYAML), "require_mention: true") - env := string(envFile) - require.Contains(t, env, "TELEGRAM_BOT_TOKEN_TG=openshell:resolve:env:TELEGRAM_BOT_TOKEN_TG") - require.Contains(t, env, "TELEGRAM_ALLOWED_USERS_TG=123456789") - require.Contains(t, env, "SLACK_BOT_TOKEN_SL=xoxb-OPENSHELL-RESOLVE-ENV-SLACK_BOT_TOKEN_SL") - require.Contains(t, env, "SLACK_APP_TOKEN_SL=xapp-OPENSHELL-RESOLVE-ENV-SLACK_APP_TOKEN_SL") - require.Contains(t, env, "SLACK_ALLOWED_USERS_SL=U01234567,U89ABCDEF") - require.Contains(t, env, "SLACK_HOME_CHANNEL_SL=C01234567890") - require.Contains(t, env, "SLACK_HOME_CHANNEL_NAME_SL=general") - require.Equal(t, "tg-token", execEnv["TELEGRAM_BOT_TOKEN_TG"]) - require.Equal(t, "xoxb-bot", execEnv["SLACK_BOT_TOKEN_SL"]) -} diff --git a/go/core/pkg/sandboxbackend/openshell/hermes/channels.go b/go/core/pkg/sandboxbackend/openshell/hermes/channels.go deleted file mode 100644 index 877f16651f..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/hermes/channels.go +++ /dev/null @@ -1,33 +0,0 @@ -package hermes - -import ( - "context" - - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openshell/channels" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -type messagingState struct { - resolved *channels.Resolved -} - -// AccumulateMessagingChannels resolves channel credentials and returns messaging state for Hermes bootstrap. -func AccumulateMessagingChannels(ctx context.Context, kube client.Client, namespace string, backend v1alpha2.AgentHarnessBackendType, specChannels []v1alpha2.AgentHarnessChannel, _ map[string]string) (*messagingState, error) { - resolved, err := channels.Resolve(ctx, kube, namespace, backend, specChannels) - if err != nil { - return nil, err - } - return &messagingState{resolved: resolved}, nil -} - -func (st *messagingState) hasTelegram() bool { - return st != nil && st.resolved != nil && st.resolved.HasTelegram -} - -func (st *messagingState) secrets() map[string]string { - if st == nil || st.resolved == nil { - return nil - } - return st.resolved.Secrets -} diff --git a/go/core/pkg/sandboxbackend/openshell/hermes/constants.go b/go/core/pkg/sandboxbackend/openshell/hermes/constants.go deleted file mode 100644 index 0cca8e1244..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/hermes/constants.go +++ /dev/null @@ -1,21 +0,0 @@ -package hermes - -const ( - // HermesSandboxBaseImage is the default OpenShell VM image for Hermes harnesses. - HermesSandboxBaseImage = "ghcr.io/nvidia/nemoclaw/hermes-sandbox-base:3e56f808" - - // HermesConfigDir is the in-sandbox Hermes config root (HERMES_HOME). - HermesConfigDir = "/sandbox/.hermes" - - // HermesConfigHashFile is the root-owned integrity anchor written at bootstrap. - HermesConfigHashFile = "/etc/nemoclaw/hermes.config-hash" - - // HermesInternalGatewayPort is where Hermes binds (127.0.0.1 only). - HermesInternalGatewayPort = 18642 - - // HermesPublicGatewayPort is exposed via socat for OpenShell port forwarding. - HermesPublicGatewayPort = 8642 - - // DefaultInferenceBaseURL is the model base_url when routing through OpenShell inference.local. - DefaultInferenceBaseURL = "https://inference.local/v1" -) diff --git a/go/core/pkg/sandboxbackend/openshell/hermes/messaging_providers_test.go b/go/core/pkg/sandboxbackend/openshell/hermes/messaging_providers_test.go deleted file mode 100644 index b090a8df90..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/hermes/messaging_providers_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package hermes - -import ( - "context" - "testing" - - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openshell/channels" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes/scheme" - "sigs.k8s.io/controller-runtime/pkg/client/fake" -) - -func TestMessagingProviderDefsFromChannels(t *testing.T) { - ns := "default" - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "tg", Namespace: ns}, - Data: map[string][]byte{"token": []byte("123:ABC")}, - } - kube := fake.NewClientBuilder().WithScheme(scheme.Scheme).WithObjects(secret).Build() - ah := &v1alpha2.AgentHarness{ - ObjectMeta: metav1.ObjectMeta{Name: "mybot", Namespace: ns}, - Spec: v1alpha2.AgentHarnessSpec{ - Backend: v1alpha2.AgentHarnessBackendHermes, - Channels: []v1alpha2.AgentHarnessChannel{ - { - Name: "tg", - Type: v1alpha2.AgentHarnessChannelTypeTelegram, - Telegram: &v1alpha2.AgentHarnessTelegramChannelSpec{ - BotToken: v1alpha2.AgentHarnessChannelCredential{ - ValueFrom: &v1alpha2.ValueSource{ - Type: v1alpha2.SecretValueSource, - Name: "tg", - Key: "token", - }, - }, - }, - }, - }, - }, - } - resolved, err := channels.Resolve(context.Background(), kube, ns, ah.Spec.Backend, ah.Spec.Channels) - require.NoError(t, err) - defs := channels.MessagingProviderDefs("default-mybot", resolved.Secrets, resolved) - require.Len(t, defs, 1) - require.Equal(t, "default-mybot-telegram-TG", defs[0].Name) - require.Equal(t, "123:ABC", defs[0].Credentials[channels.TelegramBotTokenEnvKey("tg")]) -} diff --git a/go/core/pkg/sandboxbackend/openshell/hermes/policy.go b/go/core/pkg/sandboxbackend/openshell/hermes/policy.go deleted file mode 100644 index de153d666c..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/hermes/policy.go +++ /dev/null @@ -1,306 +0,0 @@ -package hermes - -import ( - "maps" - - sandboxv1 "github.com/kagent-dev/kagent/go/api/openshell/gen/sandboxv1" - "github.com/kagent-dev/kagent/go/api/v1alpha2" -) - -// Network policy map keys for Hermes sandbox egress. Source of truth: -// NemoClaw agents/hermes/policy-additions.yaml -const ( - NetworkPolicyKeyNVIDIA = "nvidia" - NetworkPolicyKeyGitHub = "github" - NetworkPolicyKeyNousResearch = "nous_research" - NetworkPolicyKeyPyPI = "pypi" - NetworkPolicyKeyTelegram = "telegram" - NetworkPolicyKeySlack = "slack" -) - -const ( - endpointProtocolREST = "rest" - endpointEnforcement = "enforce" - endpointAccessFull = "full" - endpointTLSSkip = "skip" -) - -var hermesCoreBinaries = []*sandboxv1.NetworkBinary{ - {Path: "/usr/local/bin/hermes"}, - {Path: "/usr/bin/python3*"}, - {Path: "/opt/hermes/.venv/bin/python"}, -} - -var hermesMessagingBinaries = []*sandboxv1.NetworkBinary{ - {Path: "/usr/local/bin/node"}, - {Path: "/usr/bin/python3*"}, - {Path: "/opt/hermes/.venv/bin/python"}, -} - -var nvidiaInferenceRules = []*sandboxv1.L7Rule{ - {Allow: &sandboxv1.L7Allow{Method: "POST", Path: "/v1/chat/completions"}}, - {Allow: &sandboxv1.L7Allow{Method: "POST", Path: "/v1/completions"}}, - {Allow: &sandboxv1.L7Allow{Method: "POST", Path: "/v1/embeddings"}}, - {Allow: &sandboxv1.L7Allow{Method: "GET", Path: "/v1/models"}}, - {Allow: &sandboxv1.L7Allow{Method: "GET", Path: "/v1/models/**"}}, -} - -var nousResearchWildcardRules = []*sandboxv1.L7Rule{ - {Allow: &sandboxv1.L7Allow{Method: "GET", Path: "/**"}}, - {Allow: &sandboxv1.L7Allow{Method: "POST", Path: "/**"}}, -} - -var pypiGETRules = []*sandboxv1.L7Rule{ - {Allow: &sandboxv1.L7Allow{Method: "GET", Path: "/**"}}, -} - -var telegramHermesRules = []*sandboxv1.L7Rule{ - {Allow: &sandboxv1.L7Allow{Method: "GET", Path: "/bot*/**"}}, - {Allow: &sandboxv1.L7Allow{Method: "POST", Path: "/bot*/**"}}, - {Allow: &sandboxv1.L7Allow{Method: "GET", Path: "/file/bot*/**"}}, -} - -var slackRESTRules = []*sandboxv1.L7Rule{ - {Allow: &sandboxv1.L7Allow{Method: "GET", Path: "/**"}}, - {Allow: &sandboxv1.L7Allow{Method: "POST", Path: "/**"}}, -} - -var slackWssRules = []*sandboxv1.L7Rule{ - {Allow: &sandboxv1.L7Allow{Method: "GET", Path: "/**"}}, - {Allow: &sandboxv1.L7Allow{Method: "WEBSOCKET_TEXT", Path: "/**"}}, -} - -func restEndpoint(host string, rules []*sandboxv1.L7Rule) *sandboxv1.NetworkEndpoint { - return &sandboxv1.NetworkEndpoint{ - Host: host, - Ports: []uint32{443}, - Protocol: endpointProtocolREST, - Enforcement: endpointEnforcement, - Rules: rules, - } -} - -func restEndpointFullAccess(host string) *sandboxv1.NetworkEndpoint { - return &sandboxv1.NetworkEndpoint{ - Host: host, - Ports: []uint32{443}, - Protocol: endpointProtocolREST, - Enforcement: endpointEnforcement, - Access: endpointAccessFull, - } -} - -func restEndpointSlack(host string) *sandboxv1.NetworkEndpoint { - ep := restEndpoint(host, slackRESTRules) - ep.RequestBodyCredentialRewrite = true - return ep -} - -func slackWssEndpoint(host string) *sandboxv1.NetworkEndpoint { - return &sandboxv1.NetworkEndpoint{ - Host: host, - Ports: []uint32{443}, - Protocol: "websocket", - Enforcement: endpointEnforcement, - WebsocketCredentialRewrite: true, - Rules: slackWssRules, - } -} - -// IsHermesSandboxBackend reports backends that use the Hermes sandbox baseline. -func IsHermesSandboxBackend(b v1alpha2.AgentHarnessBackendType) bool { - return b == v1alpha2.AgentHarnessBackendHermes -} - -func defaultHermesFilesystemPolicy() *sandboxv1.FilesystemPolicy { - return &sandboxv1.FilesystemPolicy{ - IncludeWorkdir: true, - ReadOnly: []string{ - "/usr", - "/lib", - "/opt/hermes", - "/proc", - "/dev/urandom", - "/app", - "/etc", - "/var/log", - }, - ReadWrite: []string{ - "/sandbox", - "/tmp", - "/dev/null", - HermesConfigDir, - }, - } -} - -func defaultHermesLandlockPolicy() *sandboxv1.LandlockPolicy { - return &sandboxv1.LandlockPolicy{Compatibility: "best_effort"} -} - -func defaultHermesProcessPolicy() *sandboxv1.ProcessPolicy { - return &sandboxv1.ProcessPolicy{ - RunAsUser: "sandbox", - RunAsGroup: "sandbox", - } -} - -func defaultHermesNetworkPolicies() map[string]*sandboxv1.NetworkPolicyRule { - nousHosts := []string{ - "nousresearch.com", - "hermes-agent.nousresearch.com", - "inference-api.nousresearch.com", - "portal.nousresearch.com", - "browser-use-gateway.nousresearch.com", - "modal-gateway.nousresearch.com", - "openai-audio-gateway.nousresearch.com", - "fal-queue-gateway.nousresearch.com", - "firecrawl-gateway.nousresearch.com", - "tool-gateway.nousresearch.com", - } - nousEndpoints := make([]*sandboxv1.NetworkEndpoint, 0, len(nousHosts)) - for _, h := range nousHosts { - nousEndpoints = append(nousEndpoints, restEndpoint(h, nousResearchWildcardRules)) - } - - return map[string]*sandboxv1.NetworkPolicyRule{ - NetworkPolicyKeyNVIDIA: { - Name: "nvidia", - Endpoints: []*sandboxv1.NetworkEndpoint{ - restEndpoint("integrate.api.nvidia.com", nvidiaInferenceRules), - restEndpoint("inference-api.nvidia.com", nvidiaInferenceRules), - }, - Binaries: hermesCoreBinaries, - }, - NetworkPolicyKeyGitHub: { - Name: "github", - Endpoints: []*sandboxv1.NetworkEndpoint{ - restEndpointFullAccess("github.com"), - restEndpointFullAccess("api.github.com"), - }, - Binaries: []*sandboxv1.NetworkBinary{ - {Path: "/usr/bin/git"}, - {Path: "/opt/hermes/.venv/bin/python"}, - }, - }, - NetworkPolicyKeyNousResearch: { - Name: "nous_research", - Endpoints: nousEndpoints, - Binaries: hermesCoreBinaries, - }, - NetworkPolicyKeyPyPI: { - Name: "pypi", - Endpoints: []*sandboxv1.NetworkEndpoint{ - restEndpoint("pypi.org", pypiGETRules), - restEndpoint("files.pythonhosted.org", pypiGETRules), - }, - Binaries: []*sandboxv1.NetworkBinary{ - {Path: "/usr/local/bin/pip3"}, - {Path: "/usr/bin/python3*"}, - {Path: "/opt/hermes/.venv/bin/python"}, - }, - }, - } -} - -func hermesTelegramNetworkPolicyRule() *sandboxv1.NetworkPolicyRule { - return &sandboxv1.NetworkPolicyRule{ - Name: "telegram", - Endpoints: []*sandboxv1.NetworkEndpoint{restEndpoint("api.telegram.org", telegramHermesRules)}, - Binaries: hermesMessagingBinaries, - } -} - -func hermesSlackNetworkPolicyRule() *sandboxv1.NetworkPolicyRule { - return &sandboxv1.NetworkPolicyRule{ - Name: "slack", - Endpoints: []*sandboxv1.NetworkEndpoint{ - restEndpointSlack("slack.com"), - restEndpointSlack("api.slack.com"), - restEndpointSlack("hooks.slack.com"), - slackWssEndpoint("wss-primary.slack.com"), - slackWssEndpoint("wss-backup.slack.com"), - }, - Binaries: hermesCoreBinaries, - } -} - -func channelSpecPresent(ch v1alpha2.AgentHarnessChannel) bool { - switch ch.Type { - case v1alpha2.AgentHarnessChannelTypeTelegram: - return ch.Telegram != nil - case v1alpha2.AgentHarnessChannelTypeSlack: - return ch.Slack != nil - default: - return false - } -} - -func sandboxHasChannelType(ah *v1alpha2.AgentHarness, typ v1alpha2.AgentHarnessChannelType) bool { - if ah == nil { - return false - } - for _, ch := range ah.Spec.Channels { - if ch.Type == typ && channelSpecPresent(ch) { - return true - } - } - return false -} - -// ApplyHermesBaselinePolicies adds Hermes fixed network rules plus filesystem / landlock / process policies. -func ApplyHermesBaselinePolicies(net map[string]*sandboxv1.NetworkPolicyRule) (fs *sandboxv1.FilesystemPolicy, landlock *sandboxv1.LandlockPolicy, process *sandboxv1.ProcessPolicy) { - maps.Copy(net, defaultHermesNetworkPolicies()) - return defaultHermesFilesystemPolicy(), defaultHermesLandlockPolicy(), defaultHermesProcessPolicy() -} - -// ApplyChannelNetworkPolicies adds Telegram / Slack egress when channels are configured. -func ApplyChannelNetworkPolicies(ah *v1alpha2.AgentHarness, net map[string]*sandboxv1.NetworkPolicyRule) { - if sandboxHasChannelType(ah, v1alpha2.AgentHarnessChannelTypeTelegram) { - net[NetworkPolicyKeyTelegram] = hermesTelegramNetworkPolicyRule() - } - if sandboxHasChannelType(ah, v1alpha2.AgentHarnessChannelTypeSlack) { - net[NetworkPolicyKeySlack] = hermesSlackNetworkPolicyRule() - } -} - -// SandboxPolicyVersion is OpenShell SandboxPolicy.version for Hermes fragments. -const SandboxPolicyVersion = 1 - -// BaselineHermesSandboxPolicy returns the fixed Hermes baseline (nvidia, github, nous_research, pypi, filesystem, landlock, process). -func BaselineHermesSandboxPolicy() *sandboxv1.SandboxPolicy { - net := map[string]*sandboxv1.NetworkPolicyRule{} - fs, landlock, process := ApplyHermesBaselinePolicies(net) - return &sandboxv1.SandboxPolicy{ - Version: SandboxPolicyVersion, - NetworkPolicies: net, - Filesystem: fs, - Landlock: landlock, - Process: process, - } -} - -// ChannelNetworkPolicyFragment returns Telegram/Slack egress as a network-only policy fragment when channels are configured, or nil. -func ChannelNetworkPolicyFragment(ah *v1alpha2.AgentHarness) *sandboxv1.SandboxPolicy { - if ah == nil { - return nil - } - net := map[string]*sandboxv1.NetworkPolicyRule{} - ApplyChannelNetworkPolicies(ah, net) - if len(net) == 0 { - return nil - } - return &sandboxv1.SandboxPolicy{Version: SandboxPolicyVersion, NetworkPolicies: net} -} - -// AllowedDomainsBinaries returns executables allowed to use kagent_allowed_domains for Hermes harnesses. -func AllowedDomainsBinaries() []*sandboxv1.NetworkBinary { - return []*sandboxv1.NetworkBinary{ - {Path: "/usr/local/bin/hermes"}, - {Path: "/usr/bin/python3*"}, - {Path: "/opt/hermes/.venv/bin/python"}, - {Path: "/usr/bin/curl"}, - {Path: "/usr/bin/git"}, - {Path: "/sandbox/**"}, - } -} diff --git a/go/core/pkg/sandboxbackend/openshell/hermes/policy_test.go b/go/core/pkg/sandboxbackend/openshell/hermes/policy_test.go deleted file mode 100644 index 69311b3802..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/hermes/policy_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package hermes_test - -import ( - "testing" - - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openshell/hermes" - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestBaselineHermesSandboxPolicy(t *testing.T) { - pol := hermes.BaselineHermesSandboxPolicy() - require.NotNil(t, pol) - net := pol.GetNetworkPolicies() - require.Contains(t, net, hermes.NetworkPolicyKeyNVIDIA) - require.Contains(t, net, hermes.NetworkPolicyKeyNousResearch) - require.Contains(t, net, hermes.NetworkPolicyKeyPyPI) - require.NotContains(t, net, "clawhub") - - fs := pol.GetFilesystem() - require.True(t, fs.GetIncludeWorkdir()) - require.Contains(t, fs.GetReadWrite(), hermes.HermesConfigDir) - require.Contains(t, fs.GetReadOnly(), "/opt/hermes") -} - -func TestChannelNetworkPolicyFragment_Slack(t *testing.T) { - ah := &v1alpha2.AgentHarness{ - Spec: v1alpha2.AgentHarnessSpec{ - Channels: []v1alpha2.AgentHarnessChannel{ - { - Name: "sl", - Type: v1alpha2.AgentHarnessChannelTypeSlack, - Slack: &v1alpha2.AgentHarnessSlackChannelSpec{ - BotToken: v1alpha2.AgentHarnessChannelCredential{Value: "xoxb-bot"}, - AppToken: v1alpha2.AgentHarnessChannelCredential{Value: "xapp-app"}, - }, - }, - }, - }, - } - frag := hermes.ChannelNetworkPolicyFragment(ah) - require.NotNil(t, frag) - slack := frag.GetNetworkPolicies()[hermes.NetworkPolicyKeySlack] - require.NotNil(t, slack) - var restHosts, wssHosts int - for _, ep := range slack.GetEndpoints() { - switch ep.GetHost() { - case "slack.com", "api.slack.com", "hooks.slack.com": - require.True(t, ep.GetRequestBodyCredentialRewrite()) - restHosts++ - case "wss-primary.slack.com", "wss-backup.slack.com": - require.Equal(t, "websocket", ep.GetProtocol()) - require.True(t, ep.GetWebsocketCredentialRewrite()) - wssHosts++ - } - } - require.Equal(t, 3, restHosts) - require.Equal(t, 2, wssHosts) -} - -func TestChannelNetworkPolicyFragment_Telegram(t *testing.T) { - ah := &v1alpha2.AgentHarness{ - Spec: v1alpha2.AgentHarnessSpec{ - Channels: []v1alpha2.AgentHarnessChannel{ - { - Name: "tg", - Type: v1alpha2.AgentHarnessChannelTypeTelegram, - Telegram: &v1alpha2.AgentHarnessTelegramChannelSpec{ - BotToken: v1alpha2.AgentHarnessChannelCredential{Value: "tok"}, - }, - }, - }, - }, - } - frag := hermes.ChannelNetworkPolicyFragment(ah) - require.NotNil(t, frag) - require.Contains(t, frag.GetNetworkPolicies(), hermes.NetworkPolicyKeyTelegram) -} - -func TestIsHermesSandboxBackend(t *testing.T) { - require.True(t, hermes.IsHermesSandboxBackend(v1alpha2.AgentHarnessBackendHermes)) - require.False(t, hermes.IsHermesSandboxBackend(v1alpha2.AgentHarnessBackendOpenClaw)) - _ = metav1.ObjectMeta{} -} diff --git a/go/core/pkg/sandboxbackend/openshell/hermes/ssh.go b/go/core/pkg/sandboxbackend/openshell/hermes/ssh.go deleted file mode 100644 index e031fe9b43..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/hermes/ssh.go +++ /dev/null @@ -1,8 +0,0 @@ -package hermes - -// DefaultSSHLaunchCommand is the interactive CLI started when connecting to a -// Hermes harness sandbox via the UI terminal (unless plain shell is requested). -func DefaultSSHLaunchCommand() string { - // Hermes reads config from HERMES_HOME; bootstrap writes to /sandbox/.hermes. - return "cd " + HermesConfigDir + " && exec hermes" -} diff --git a/go/core/pkg/sandboxbackend/openshell/hermes/ssh_test.go b/go/core/pkg/sandboxbackend/openshell/hermes/ssh_test.go deleted file mode 100644 index 80b05f1234..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/hermes/ssh_test.go +++ /dev/null @@ -1,12 +0,0 @@ -package hermes_test - -import ( - "testing" - - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openshell/hermes" - "github.com/stretchr/testify/require" -) - -func TestDefaultSSHLaunchCommand(t *testing.T) { - require.Equal(t, "cd /sandbox/.hermes && exec hermes", hermes.DefaultSSHLaunchCommand()) -} diff --git a/go/core/pkg/sandboxbackend/openshell/messaging_providers.go b/go/core/pkg/sandboxbackend/openshell/messaging_providers.go deleted file mode 100644 index 147e379bff..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/messaging_providers.go +++ /dev/null @@ -1,54 +0,0 @@ -package openshell - -import ( - "context" - "fmt" - - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openshell/channels" - "sigs.k8s.io/controller-runtime/pkg/client" - ctrllog "sigs.k8s.io/controller-runtime/pkg/log" -) - -// UpsertMessagingProviders registers OpenShell gateway providers for harness channel credentials. -// Returns provider names to attach on CreateSandbox.spec.providers. -func UpsertMessagingProviders( - ctx context.Context, - oc *OpenShellClients, - kube client.Client, - ah *v1alpha2.AgentHarness, -) ([]string, error) { - if ah == nil || len(ah.Spec.Channels) == 0 { - return nil, nil - } - if oc == nil || oc.OpenShell == nil { - return nil, fmt.Errorf("openshell: OpenShell client is required for messaging providers") - } - if kube == nil { - return nil, fmt.Errorf("openshell: Kubernetes client is required for messaging providers") - } - - resolved, err := channels.Resolve(ctx, kube, ah.Namespace, ah.Spec.Backend, ah.Spec.Channels) - if err != nil { - return nil, err - } - sandboxName := agentHarnessGatewayName(ah) - msgDefs := channels.MessagingProviderDefs(sandboxName, resolved.Secrets, resolved) - if len(msgDefs) == 0 { - return nil, nil - } - gwDefs := messagingDefsToGateway(msgDefs) - - names := make([]string, 0, len(gwDefs)) - if err := UpsertGatewayProviders(ctx, oc.OpenShell, gwDefs); err != nil { - return nil, err - } - for _, d := range gwDefs { - names = append(names, d.Name) - } - ctrllog.FromContext(ctx).Info("upserted messaging providers", - "agentHarness", ah.Namespace+"/"+ah.Name, - "providers", names, - ) - return names, nil -} diff --git a/go/core/pkg/sandboxbackend/openshell/openclaw.go b/go/core/pkg/sandboxbackend/openshell/openclaw.go deleted file mode 100644 index 44a3508e10..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/openclaw.go +++ /dev/null @@ -1,144 +0,0 @@ -package openshell - -import ( - "context" - "fmt" - "strings" - "time" - - openshellv1 "github.com/kagent-dev/kagent/go/api/openshell/gen/openshellv1" - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/internal/utils" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openclaw" - "k8s.io/client-go/tools/record" - "sigs.k8s.io/controller-runtime/pkg/client" - ctrllog "sigs.k8s.io/controller-runtime/pkg/log" -) - -// ClawBackend implements AsyncBackend and PostReadyBackend for OpenClaw- and -// NemoClaw-typed AgentHarness resources: sync ModelConfig to the OpenShell control plane before create, -// fixed sandbox image, and post-ready OpenClaw bootstrap when modelConfigRef is set. -type ClawBackend struct { - *agentHarnessOpenShellBackend -} - -var _ sandboxbackend.AsyncBackend = (*ClawBackend)(nil) - -// NewOpenClawBackend returns the shared OpenClaw/NemoClaw harness backend. Register the same -// instance under AgentHarnessBackendOpenClaw and AgentHarnessBackendNemoClaw; the controller -// records status.backendRef.backend from spec.backend so both types stay distinguishable. -func NewOpenClawBackend(kubeClient client.Client, clients *OpenShellClients, cfg Config, recorder record.EventRecorder) *ClawBackend { - return &ClawBackend{ - agentHarnessOpenShellBackend: newAgentHarnessOpenShellBackend( - kubeClient, clients, cfg, recorder, - v1alpha2.AgentHarnessBackendOpenClaw, - ), - } -} - -// EnsureAgentHarness is the OpenClaw/NemoClaw EnsureAgentHarness flow: idempotent gateway lookup, -// then translateModelConfig (apply ModelConfigRef onto the gateway) before CreateSandbox. -func (b *ClawBackend) EnsureAgentHarness(ctx context.Context, ah *v1alpha2.AgentHarness) (sandboxbackend.EnsureResult, error) { - if ah == nil { - return sandboxbackend.EnsureResult{}, fmt.Errorf("AgentHarness is required") - } - ctx, cancel := b.CallCtx(ctx) - defer cancel() - ctx = withAuth(ctx, b.cfg.Token) - - if res, found, err := b.findExistingSandbox(ctx, ah); err != nil || found { - return res, err - } - return b.ensureAgentHarnessSandbox(ctx, ah, buildClawCreateRequest) -} - -const defaultOpenclawGatewayPort = 18800 - -// OnAgentHarnessReady writes ~/.openclaw/openclaw.json from ModelConfig and spec.channels, -// then runs `openclaw gateway start` in the background with injected env (API key + channel secrets). -// No-ops when modelConfigRef is empty. -func (b *ClawBackend) OnAgentHarnessReady(ctx context.Context, ah *v1alpha2.AgentHarness, h sandboxbackend.Handle) error { - ref := strings.TrimSpace(ah.Spec.ModelConfigRef) - if ref == "" { - return nil - } - if h.ID == "" { - return fmt.Errorf("sandbox backend handle id is empty") - } - if b.kubeClient == nil { - return fmt.Errorf("kubernetes client is required for openclaw bootstrap") - } - - modelConfigRef, err := utils.ParseRefString(ref, ah.Namespace) - if err != nil { - return fmt.Errorf("parse modelConfigRef: %w", err) - } - mc := &v1alpha2.ModelConfig{} - if err := b.kubeClient.Get(ctx, modelConfigRef, mc); err != nil { - return fmt.Errorf("get ModelConfig: %w", err) - } - - if _, err := UpsertMessagingProviders(ctx, b.clients, b.kubeClient, ah); err != nil { - return fmt.Errorf("upsert messaging providers: %w", err) - } - - providerRecord := openclaw.GatewayProviderRecordName(mc.Spec.Provider) - gwPort := defaultOpenclawGatewayPort - token := b.cfg.Token - - jsonBytes, env, err := openclaw.BuildBootstrapJSON(ctx, b.kubeClient, ah.Namespace, ah, mc, openclaw.OpenshellGatewayBootstrap(gwPort), openclaw.DefaultInferenceBaseURL) - if err != nil { - return fmt.Errorf("build openclaw config: %w", err) - } - - idCtx, cancelID := b.CallCtx(ctx) - defer cancelID() - execID, err := b.ExecSandboxID(withAuth(idCtx, token), h.ID) - if err != nil { - return fmt.Errorf("resolve sandbox exec id: %w", err) - } - - installCmd := []string{"sh", "-c", `mkdir -p "$HOME/.openclaw" && cat > "$HOME/.openclaw/openclaw.json"`} - installCtx, cancelInstall := context.WithTimeout(ctx, 120*time.Second+15*time.Second) - defer cancelInstall() - code, stderr, err := b.ExecSandbox(withAuth(installCtx, token), execID, installCmd, jsonBytes, env, 120) - if err != nil { - return fmt.Errorf("install openclaw.json: %w", err) - } - if code != 0 { - return fmt.Errorf("install openclaw.json: exit %d: %s", code, strings.TrimSpace(stderr)) - } - - gatewayScript := fmt.Sprintf( - `openclaw gateway run --port %d >>/tmp/openclaw-gateway.log 2>&1 &`, - gwPort, - ) - gatewayCmd := []string{"sh", "-c", gatewayScript} - gwCtx, cancelGW := context.WithTimeout(ctx, 90*time.Second+15*time.Second) - defer cancelGW() - code, stderr, err = b.ExecSandbox(withAuth(gwCtx, token), execID, gatewayCmd, nil, env, 90) - if err != nil { - return fmt.Errorf("exec openclaw gateway run: %w", err) - } - if code != 0 { - return fmt.Errorf("openclaw gateway run: exit %d: %s", code, strings.TrimSpace(stderr)) - } - - ctrllog.FromContext(ctx).Info("openclaw bootstrap completed", - "agentHarness", ah.Namespace+"/"+ah.Name, "providerRecord", providerRecord) - return nil -} - -func buildClawCreateRequest(ah *v1alpha2.AgentHarness, messagingProviders []string) (*openshellv1.CreateSandboxRequest, []string) { - req, unsupported := buildAgentHarnessOpenshellCreateRequest(ah) - if req.GetSpec().GetTemplate() == nil { - req.Spec.Template = &openshellv1.SandboxTemplate{} - } - - if ah.Spec.Image == "" { - req.Spec.Template.Image = openclaw.NemoclawSandboxBaseImage - } - attachMessagingProviders(req, messagingProviders) - return req, unsupported -} diff --git a/go/core/pkg/sandboxbackend/openshell/openshell.go b/go/core/pkg/sandboxbackend/openshell/openshell.go deleted file mode 100644 index 299617245f..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/openshell.go +++ /dev/null @@ -1,87 +0,0 @@ -// Package openshell implements sandboxbackend.AsyncBackend against an external -// OpenShell gateway over gRPC. -// -// Use Dial to obtain OpenShellClients (shared connection for openshell.v1.OpenShell -// and openshell.inference.v1.Inference). -// -// • NewOpenClawBackend — pin the sandbox image to openclaw.NemoclawSandboxBaseImage, translateModelConfig -// when modelConfigRef is set, run OpenClaw bootstrap after Ready. The same instance -// is registered for spec.backend=openclaw and nemoclaw (see app wiring). -// -// Unlike agentsxk8s, these backends do not emit Kubernetes workload objects — -// sandbox lifecycle goes through the gateway over gRPC. -package openshell - -import ( - "context" - "fmt" - - openshellv1 "github.com/kagent-dev/kagent/go/api/openshell/gen/openshellv1" - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/tools/record" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -type agentHarnessOpenShellBackend struct { - *AgentHarnessOpenShellClient - kubeClient client.Client - backendName v1alpha2.AgentHarnessBackendType -} - -func newAgentHarnessOpenShellBackend( - kubeClient client.Client, - clients *OpenShellClients, - cfg Config, - recorder record.EventRecorder, - name v1alpha2.AgentHarnessBackendType, -) *agentHarnessOpenShellBackend { - return &agentHarnessOpenShellBackend{ - AgentHarnessOpenShellClient: newAgentHarnessOpenShellClient(clients, cfg, recorder), - kubeClient: kubeClient, - backendName: name, - } -} - -// Name implements AsyncBackend. -func (b *agentHarnessOpenShellBackend) Name() v1alpha2.AgentHarnessBackendType { - return b.backendName -} - -// findExistingSandbox is the GetSandbox/NotFound idempotency probe shared across backends. It -// returns (res, true, nil) when the gateway already has a sandbox under this AgentHarness's -// deterministic name, (zero, false, nil) when the sandbox does not yet exist, or -// (zero, false, err) on any other gateway error. Callers must already have applied CallCtx and -// withAuth to ctx. -func (b *agentHarnessOpenShellBackend) findExistingSandbox(ctx context.Context, ah *v1alpha2.AgentHarness) (sandboxbackend.EnsureResult, bool, error) { - osCli := b.openShell() - if osCli == nil { - return sandboxbackend.EnsureResult{}, false, fmt.Errorf("openshell: OpenShell client is required (use Dial or non-nil OpenShellClients.OpenShell)") - } - name := agentHarnessGatewayName(ah) - getResp, err := osCli.GetSandbox(ctx, &openshellv1.GetSandboxRequest{Name: name}) - if err == nil && getResp != nil && getResp.GetSandbox() != nil { - handleID := sandboxBackendHandleID(getResp.GetSandbox()) - return sandboxbackend.EnsureResult{ - Handle: sandboxbackend.Handle{ID: handleID}, - Endpoint: endpointFor(b.cfg.GatewayURL, handleID), - }, true, nil - } - if err != nil && status.Code(err) != codes.NotFound { - return sandboxbackend.EnsureResult{}, false, fmt.Errorf("openshell GetSandbox %s: %w", name, err) - } - return sandboxbackend.EnsureResult{}, false, nil -} - -// GetStatus implements AsyncBackend. -func (b *agentHarnessOpenShellBackend) GetStatus(ctx context.Context, h sandboxbackend.Handle) (metav1.ConditionStatus, string, string) { - return b.GetSandboxStatus(ctx, h) -} - -// DeleteAgentHarness implements AsyncBackend. -func (b *agentHarnessOpenShellBackend) DeleteAgentHarness(ctx context.Context, h sandboxbackend.Handle) (bool, error) { - return b.DeleteAgentHarnessSandbox(ctx, h) -} diff --git a/go/core/pkg/sandboxbackend/openshell/openshell_test.go b/go/core/pkg/sandboxbackend/openshell/openshell_test.go deleted file mode 100644 index bf154f7bcd..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/openshell_test.go +++ /dev/null @@ -1,511 +0,0 @@ -package openshell - -import ( - "context" - "net" - "strings" - "sync" - "testing" - "time" - - datamodelv1 "github.com/kagent-dev/kagent/go/api/openshell/gen/datamodelv1" - inferencev1 "github.com/kagent-dev/kagent/go/api/openshell/gen/inferencev1" - openshellv1 "github.com/kagent-dev/kagent/go/api/openshell/gen/openshellv1" - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openclaw" - "github.com/stretchr/testify/require" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/status" - "google.golang.org/grpc/test/bufconn" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client/fake" -) - -type fakeGateway struct { - openshellv1.UnimplementedOpenShellServer - - mu sync.Mutex - sandboxes map[string]*openshellv1.Sandbox - providers map[string]*datamodelv1.Provider - createErr error - getErr error - deleteErr error - - createCalls int - deleteCalls int - - execCalls [][]string - execStdins [][]byte - execSandboxIDs []string -} - -func (f *fakeGateway) CreateSandbox(_ context.Context, req *openshellv1.CreateSandboxRequest) (*openshellv1.SandboxResponse, error) { - f.mu.Lock() - defer f.mu.Unlock() - f.createCalls++ - if f.createErr != nil { - return nil, f.createErr - } - if f.sandboxes == nil { - f.sandboxes = map[string]*openshellv1.Sandbox{} - } - sb := &openshellv1.Sandbox{ - Metadata: &datamodelv1.ObjectMeta{ - Id: "id-" + req.GetName(), - Name: req.GetName(), - }, - Spec: req.GetSpec(), - Phase: openshellv1.SandboxPhase_SANDBOX_PHASE_PROVISIONING, - } - f.sandboxes[req.GetName()] = sb - return &openshellv1.SandboxResponse{Sandbox: sb}, nil -} - -func (f *fakeGateway) GetSandbox(_ context.Context, req *openshellv1.GetSandboxRequest) (*openshellv1.SandboxResponse, error) { - f.mu.Lock() - defer f.mu.Unlock() - if f.getErr != nil { - return nil, f.getErr - } - sb, ok := f.sandboxes[req.GetName()] - if !ok { - return nil, status.Error(codes.NotFound, "sandbox not found") - } - return &openshellv1.SandboxResponse{Sandbox: sb}, nil -} - -func (f *fakeGateway) DeleteSandbox(_ context.Context, req *openshellv1.DeleteSandboxRequest) (*openshellv1.DeleteSandboxResponse, error) { - f.mu.Lock() - defer f.mu.Unlock() - f.deleteCalls++ - if f.deleteErr != nil { - return nil, f.deleteErr - } - if _, ok := f.sandboxes[req.GetName()]; !ok { - return nil, status.Error(codes.NotFound, "sandbox not found") - } - delete(f.sandboxes, req.GetName()) - return &openshellv1.DeleteSandboxResponse{Deleted: true}, nil -} - -func (f *fakeGateway) CreateProvider(_ context.Context, req *openshellv1.CreateProviderRequest) (*openshellv1.ProviderResponse, error) { - f.mu.Lock() - defer f.mu.Unlock() - if f.providers == nil { - f.providers = map[string]*datamodelv1.Provider{} - } - p := req.GetProvider() - name := "" - if p.GetMetadata() != nil { - name = p.GetMetadata().GetName() - } - stored := &datamodelv1.Provider{ - Metadata: &datamodelv1.ObjectMeta{Id: "fake-provider-id", Name: name}, - Type: p.GetType(), - Credentials: p.GetCredentials(), - } - f.providers[name] = stored - return &openshellv1.ProviderResponse{Provider: stored}, nil -} - -func (f *fakeGateway) GetProvider(_ context.Context, req *openshellv1.GetProviderRequest) (*openshellv1.ProviderResponse, error) { - f.mu.Lock() - defer f.mu.Unlock() - p, ok := f.providers[req.GetName()] - if !ok { - return nil, status.Error(codes.NotFound, "provider not found") - } - return &openshellv1.ProviderResponse{Provider: p}, nil -} - -func (f *fakeGateway) UpdateProvider(_ context.Context, req *openshellv1.UpdateProviderRequest) (*openshellv1.ProviderResponse, error) { - f.mu.Lock() - defer f.mu.Unlock() - if f.providers == nil { - f.providers = map[string]*datamodelv1.Provider{} - } - p := req.GetProvider() - name := "" - if p.GetMetadata() != nil { - name = p.GetMetadata().GetName() - } - f.providers[name] = p - return &openshellv1.ProviderResponse{Provider: p}, nil -} - -func (f *fakeGateway) ExecSandbox(req *openshellv1.ExecSandboxRequest, stream grpc.ServerStreamingServer[openshellv1.ExecSandboxEvent]) error { - f.mu.Lock() - f.execCalls = append(f.execCalls, append([]string(nil), req.Command...)) - f.execStdins = append(f.execStdins, append([]byte(nil), req.GetStdin()...)) - f.execSandboxIDs = append(f.execSandboxIDs, req.GetSandboxId()) - f.mu.Unlock() - return stream.Send(&openshellv1.ExecSandboxEvent{ - Payload: &openshellv1.ExecSandboxEvent_Exit{ - Exit: &openshellv1.ExecSandboxExit{ExitCode: 0}, - }, - }) -} - -type fakeInference struct { - inferencev1.UnimplementedInferenceServer - - mu sync.Mutex - lastProvider string - lastModel string -} - -func (f *fakeInference) SetClusterInference(_ context.Context, req *inferencev1.SetClusterInferenceRequest) (*inferencev1.SetClusterInferenceResponse, error) { - f.mu.Lock() - f.lastProvider = req.GetProviderName() - f.lastModel = req.GetModelId() - f.mu.Unlock() - return &inferencev1.SetClusterInferenceResponse{ - ProviderName: req.GetProviderName(), - ModelId: req.GetModelId(), - }, nil -} - -func (f *fakeGateway) setPhase(name string, p openshellv1.SandboxPhase) { - f.mu.Lock() - defer f.mu.Unlock() - if sb, ok := f.sandboxes[name]; ok { - sb.Phase = p - } -} - -func startFake(t *testing.T) (openshellv1.OpenShellClient, inferencev1.InferenceClient, *fakeGateway, *fakeInference, func()) { - t.Helper() - lis := bufconn.Listen(1 << 20) - srv := grpc.NewServer() - fg := &fakeGateway{} - fi := &fakeInference{} - openshellv1.RegisterOpenShellServer(srv, fg) - inferencev1.RegisterInferenceServer(srv, fi) - go func() { _ = srv.Serve(lis) }() - - dialer := func(context.Context, string) (net.Conn, error) { return lis.Dial() } - conn, err := grpc.NewClient("passthrough:///bufconn", - grpc.WithContextDialer(dialer), - grpc.WithTransportCredentials(insecure.NewCredentials())) - require.NoError(t, err) - - cleanup := func() { - _ = conn.Close() - srv.Stop() - _ = lis.Close() - } - return openshellv1.NewOpenShellClient(conn), inferencev1.NewInferenceClient(conn), fg, fi, cleanup -} - -func testScheme(t *testing.T) *runtime.Scheme { - t.Helper() - s := runtime.NewScheme() - require.NoError(t, corev1.AddToScheme(s)) - require.NoError(t, v1alpha2.AddToScheme(s)) - return s -} - -func sampleClawSandbox() *v1alpha2.AgentHarness { - return &v1alpha2.AgentHarness{ - ObjectMeta: metav1.ObjectMeta{Name: "a1", Namespace: "ns1"}, - Spec: v1alpha2.AgentHarnessSpec{ - Backend: v1alpha2.AgentHarnessBackendOpenClaw, - Image: "img:v1", - Env: []corev1.EnvVar{{Name: "FOO", Value: "bar"}}, - }, - } -} - -func TestEnsureSandbox_CreatesThenIdempotent(t *testing.T) { - c, ic, fg, _, cleanup := startFake(t) - defer cleanup() - b := NewOpenClawBackend(nil, &OpenShellClients{OpenShell: c, Inference: ic}, Config{GatewayURL: "grpc://gw"}, nil) - - r, err := b.EnsureAgentHarness(context.Background(), sampleClawSandbox()) - require.NoError(t, err) - require.Equal(t, "ns1-a1", r.Handle.ID) - require.Equal(t, "grpc://gw#ns1-a1", r.Endpoint) - require.Equal(t, 1, fg.createCalls) - - r2, err := b.EnsureAgentHarness(context.Background(), sampleClawSandbox()) - require.NoError(t, err) - require.Equal(t, r.Handle.ID, r2.Handle.ID) - require.Equal(t, 1, fg.createCalls, "second EnsureSandbox must not re-create") -} - -func TestEnsureSandbox_CreateFails(t *testing.T) { - c, ic, fg, _, cleanup := startFake(t) - defer cleanup() - fg.createErr = status.Error(codes.ResourceExhausted, "quota") - - b := NewOpenClawBackend(nil, &OpenShellClients{OpenShell: c, Inference: ic}, Config{}, nil) - _, err := b.EnsureAgentHarness(context.Background(), sampleClawSandbox()) - require.Error(t, err) - require.Contains(t, err.Error(), "CreateSandbox") -} - -func TestGetStatus_PhaseMapping(t *testing.T) { - c, ic, fg, _, cleanup := startFake(t) - defer cleanup() - b := NewOpenClawBackend(nil, &OpenShellClients{OpenShell: c, Inference: ic}, Config{}, nil) - - r, err := b.EnsureAgentHarness(context.Background(), sampleClawSandbox()) - require.NoError(t, err) - - cases := []struct { - phase openshellv1.SandboxPhase - wantStatus metav1.ConditionStatus - wantReason string - }{ - {openshellv1.SandboxPhase_SANDBOX_PHASE_READY, metav1.ConditionTrue, "SandboxReady"}, - {openshellv1.SandboxPhase_SANDBOX_PHASE_PROVISIONING, metav1.ConditionFalse, "SandboxProvisioning"}, - {openshellv1.SandboxPhase_SANDBOX_PHASE_ERROR, metav1.ConditionFalse, "SandboxError"}, - {openshellv1.SandboxPhase_SANDBOX_PHASE_DELETING, metav1.ConditionFalse, "SandboxDeleting"}, - {openshellv1.SandboxPhase_SANDBOX_PHASE_UNKNOWN, metav1.ConditionUnknown, "SandboxPhaseUnknown"}, - } - for _, tc := range cases { - fg.setPhase(r.Handle.ID, tc.phase) - st, reason, _ := b.GetStatus(context.Background(), r.Handle) - require.Equal(t, tc.wantStatus, st, tc.phase.String()) - require.Equal(t, tc.wantReason, reason, tc.phase.String()) - } -} - -func TestGetStatus_EmptyHandle(t *testing.T) { - c, ic, _, _, cleanup := startFake(t) - defer cleanup() - b := NewOpenClawBackend(nil, &OpenShellClients{OpenShell: c, Inference: ic}, Config{}, nil) - - st, reason, _ := b.GetStatus(context.Background(), sandboxbackend.Handle{}) - require.Equal(t, metav1.ConditionUnknown, st) - require.Equal(t, "SandboxHandleMissing", reason) -} - -func TestGetStatus_NotFound(t *testing.T) { - c, ic, _, _, cleanup := startFake(t) - defer cleanup() - b := NewOpenClawBackend(nil, &OpenShellClients{OpenShell: c, Inference: ic}, Config{}, nil) - - st, reason, _ := b.GetStatus(context.Background(), sandboxbackend.Handle{ID: "missing"}) - require.Equal(t, metav1.ConditionUnknown, st) - require.Equal(t, "SandboxNotFound", reason) -} - -func TestDeleteSandbox(t *testing.T) { - c, ic, fg, _, cleanup := startFake(t) - defer cleanup() - b := NewOpenClawBackend(nil, &OpenShellClients{OpenShell: c, Inference: ic}, Config{}, nil) - - r, err := b.EnsureAgentHarness(context.Background(), sampleClawSandbox()) - require.NoError(t, err) - - done, err := b.DeleteAgentHarness(context.Background(), r.Handle) - require.NoError(t, err) - require.True(t, done) - require.Equal(t, 1, fg.deleteCalls) - - done, err = b.DeleteAgentHarness(context.Background(), r.Handle) - require.NoError(t, err) - require.True(t, done) - require.Equal(t, 2, fg.deleteCalls) - - before := fg.deleteCalls - done, err = b.DeleteAgentHarness(context.Background(), sandboxbackend.Handle{}) - require.NoError(t, err) - require.True(t, done) - require.Equal(t, before, fg.deleteCalls) -} - -func TestCallTimeout(t *testing.T) { - c, ic, fg, _, cleanup := startFake(t) - defer cleanup() - fg.getErr = status.Error(codes.Unavailable, "backend down") - - b := NewOpenClawBackend(nil, &OpenShellClients{OpenShell: c, Inference: ic}, Config{CallTimeout: 50 * time.Millisecond}, nil) - _, err := b.EnsureAgentHarness(context.Background(), sampleClawSandbox()) - require.Error(t, err) -} - -func TestEnsureSandbox_WithModelConfigRef_RegistersProvider(t *testing.T) { - s := testScheme(t) - const ns = "ns1" - mc := &v1alpha2.ModelConfig{ - ObjectMeta: metav1.ObjectMeta{Name: "m1", Namespace: ns}, - Spec: v1alpha2.ModelConfigSpec{ - Model: "gpt-4o", - Provider: v1alpha2.ModelProviderOpenAI, - APIKeySecret: "keysec", - APIKeySecretKey: "OPENAI_API_KEY", - }, - } - sec := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "keysec", Namespace: ns}, - Data: map[string][]byte{"OPENAI_API_KEY": []byte("sk-secret")}, - } - kube := fake.NewClientBuilder().WithScheme(s).WithObjects(mc, sec).Build() - - c, ic, _, fi, cleanup := startFake(t) - defer cleanup() - b := NewOpenClawBackend(kube, &OpenShellClients{OpenShell: c, Inference: ic}, Config{GatewayURL: "grpc://gw"}, nil) - - sbx := sampleClawSandbox() - sbx.Spec.ModelConfigRef = "m1" - - _, err := b.EnsureAgentHarness(context.Background(), sbx) - require.NoError(t, err) - - fi.mu.Lock() - defer fi.mu.Unlock() - require.Equal(t, "openai", fi.lastProvider) - require.Equal(t, "gpt-4o", fi.lastModel) -} - -func TestExecSandboxID_UsesGatewayMetadataId(t *testing.T) { - c, ic, _, _, cleanup := startFake(t) - defer cleanup() - b := NewOpenClawBackend(nil, &OpenShellClients{OpenShell: c, Inference: ic}, Config{}, nil) - - r, err := b.EnsureAgentHarness(context.Background(), sampleClawSandbox()) - require.NoError(t, err) - - ctx := withAuth(context.Background(), "") - id, err := b.ExecSandboxID(ctx, r.Handle.ID) - require.NoError(t, err) - require.Equal(t, "id-ns1-a1", id) -} - -func TestOnSandboxReady_ModelConfigRef(t *testing.T) { - s := testScheme(t) - const ns = "ns1" - mc := &v1alpha2.ModelConfig{ - ObjectMeta: metav1.ObjectMeta{Name: "m1", Namespace: ns}, - Spec: v1alpha2.ModelConfigSpec{ - Model: "gpt-4o", - Provider: v1alpha2.ModelProviderOpenAI, - APIKeySecret: "keysec", - APIKeySecretKey: "OPENAI_API_KEY", - }, - } - sec := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "keysec", Namespace: ns}, - Data: map[string][]byte{"OPENAI_API_KEY": []byte("sk-secret")}, - } - kube := fake.NewClientBuilder().WithScheme(s).WithObjects(mc, sec).Build() - - c, ic, fg, _, cleanup := startFake(t) - defer cleanup() - b := NewOpenClawBackend(kube, &OpenShellClients{OpenShell: c, Inference: ic}, Config{Token: "tok"}, nil) - - sbx := &v1alpha2.AgentHarness{ - ObjectMeta: metav1.ObjectMeta{Name: "a1", Namespace: ns}, - Spec: v1alpha2.AgentHarnessSpec{ - Backend: v1alpha2.AgentHarnessBackendOpenClaw, - ModelConfigRef: "m1", - }, - } - - r, err := b.EnsureAgentHarness(context.Background(), sbx) - require.NoError(t, err) - - ctx := withAuth(context.Background(), "tok") - require.NoError(t, b.OnAgentHarnessReady(ctx, sbx, r.Handle)) - require.Len(t, fg.execCalls, 2) - fg.mu.Lock() - require.Equal(t, "id-ns1-a1", fg.execSandboxIDs[0]) - require.Equal(t, "id-ns1-a1", fg.execSandboxIDs[1]) - fg.mu.Unlock() - - installJoined := strings.Join(fg.execCalls[0], " ") - require.Contains(t, installJoined, `mkdir -p "$HOME/.openclaw"`) - require.Contains(t, installJoined, `openclaw.json`) - require.NotEmpty(t, fg.execStdins[0]) - require.Contains(t, string(fg.execStdins[0]), `"openai/gpt-4o"`) - require.Contains(t, string(fg.execStdins[0]), `"OPENAI_API_KEY"`) - require.Contains(t, string(fg.execStdins[0]), `openshell:resolve:env:OPENAI_API_KEY`) - require.Contains(t, string(fg.execStdins[0]), `"secrets"`) - require.Contains(t, string(fg.execStdins[0]), `"kagent"`) - require.Contains(t, string(fg.execStdins[0]), `"allowlist"`) - - gatewayJoined := strings.Join(fg.execCalls[1], " ") - require.Contains(t, gatewayJoined, "openclaw gateway run") - require.Contains(t, gatewayJoined, "--port 18800") - require.NotContains(t, gatewayJoined, "--allow-unconfigured") - require.NotContains(t, gatewayJoined, "--auth none") - require.Empty(t, fg.execStdins[1]) -} - -func TestEnsureSandbox_Claw_PinsNemoclawBaseImage(t *testing.T) { - c, ic, fg, _, cleanup := startFake(t) - defer cleanup() - b := NewOpenClawBackend(nil, &OpenShellClients{OpenShell: c, Inference: ic}, Config{GatewayURL: "grpc://gw"}, nil) - - sbx := sampleClawSandbox() - sbx.Spec.Image = "" - _, err := b.EnsureAgentHarness(context.Background(), sbx) - require.NoError(t, err) - - fg.mu.Lock() - defer fg.mu.Unlock() - sb := fg.sandboxes["ns1-a1"] - require.NotNil(t, sb) - require.Equal(t, openclaw.NemoclawSandboxBaseImage, sb.GetSpec().GetTemplate().GetImage()) -} - -func TestUpsertMessagingProviders(t *testing.T) { - ns := "default" - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "tg", Namespace: ns}, - Data: map[string][]byte{"token": []byte("tg-secret")}, - } - kube := fake.NewClientBuilder().WithScheme(testScheme(t)).WithObjects(secret).Build() - - ah := &v1alpha2.AgentHarness{ - ObjectMeta: metav1.ObjectMeta{Name: "hermes1", Namespace: ns}, - Spec: v1alpha2.AgentHarnessSpec{ - Channels: []v1alpha2.AgentHarnessChannel{ - { - Name: "tg", - Type: v1alpha2.AgentHarnessChannelTypeTelegram, - Telegram: &v1alpha2.AgentHarnessTelegramChannelSpec{ - BotToken: v1alpha2.AgentHarnessChannelCredential{ - ValueFrom: &v1alpha2.ValueSource{ - Type: v1alpha2.SecretValueSource, - Name: "tg", - Key: "token", - }, - }, - }, - }, - }, - }, - } - - c, _, fg, _, cleanup := startFake(t) - defer cleanup() - clients := &OpenShellClients{OpenShell: c} - - names, err := UpsertMessagingProviders(context.Background(), clients, kube, ah) - require.NoError(t, err) - require.Equal(t, []string{"default-hermes1-telegram-TG"}, names) - - fg.mu.Lock() - defer fg.mu.Unlock() - p := fg.providers["default-hermes1-telegram-TG"] - require.NotNil(t, p) - require.Equal(t, "tg-secret", p.GetCredentials()["TELEGRAM_BOT_TOKEN_TG"]) - require.Equal(t, "generic", p.GetType()) -} - -func TestBuildHermesCreateRequest_AttachesMessagingProviders(t *testing.T) { - ah := &v1alpha2.AgentHarness{ - ObjectMeta: metav1.ObjectMeta{Name: "a1", Namespace: "ns1"}, - Spec: v1alpha2.AgentHarnessSpec{Backend: v1alpha2.AgentHarnessBackendHermes}, - } - req, _ := buildHermesCreateRequest(ah, []string{"ns1-a1-telegram-bridge"}) - require.Contains(t, req.GetSpec().GetProviders(), "ns1-a1-telegram-bridge") -} diff --git a/go/core/pkg/sandboxbackend/openshell/policy.go b/go/core/pkg/sandboxbackend/openshell/policy.go deleted file mode 100644 index 5f82cac698..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/policy.go +++ /dev/null @@ -1,180 +0,0 @@ -package openshell - -import ( - "strings" - - sandboxv1 "github.com/kagent-dev/kagent/go/api/openshell/gen/sandboxv1" - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openclaw" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openshell/hermes" - "google.golang.org/protobuf/proto" -) - -// Key used in SandboxPolicy.network_policies for spec.network.allowedDomains → SSRF/network rules. -const kagentAllowedDomainsNetworkPolicyKey = "kagent_allowed_domains" - -// L7 REST settings for allowedDomains entries; see -// https://docs.nvidia.com/openshell/reference/policy-schema (network_policies, endpoints). -const ( - allowedDomainsEndpointProtocol = "rest" - allowedDomainsEndpointEnforcement = "enforce" - allowedDomainsEndpointAccess = "full" // all HTTP methods and paths -) - -// Processes allowed to use the allowedDomains endpoints (NetworkPolicyRule.binaries is required). -// Paths support * / ** globs per policy schema. -// -// OpenShell denies egress unless the executable matches (e.g. curl must be listed explicitly; -// npm/node alone does not cover manual curl tests). -var defaultAllowedDomainsBinaries = []*sandboxv1.NetworkBinary{ - {Path: "/usr/bin/node"}, - {Path: "/usr/local/bin/node"}, - {Path: "/usr/bin/npm"}, - {Path: "/usr/bin/npx"}, - {Path: "/usr/bin/curl"}, - {Path: "/usr/bin/wget"}, - {Path: "/usr/bin/git"}, - {Path: "/sandbox/**"}, -} - -// allowedDomainsNetworkPolicyRule builds one NetworkPolicyRule from CR allowedDomains. -func allowedDomainsNetworkPolicyRule(domains []string) *sandboxv1.NetworkPolicyRule { - endpoints := make([]*sandboxv1.NetworkEndpoint, 0, len(domains)) - seen := make(map[string]struct{}, len(domains)) - for _, raw := range domains { - host, ok := sandboxbackend.NormalizeAllowedDomainHost(raw) - if !ok { - continue - } - key := strings.ToLower(host) - if _, dup := seen[key]; dup { - continue - } - seen[key] = struct{}{} - endpoints = append(endpoints, &sandboxv1.NetworkEndpoint{ - Host: host, - // HTTPS APIs and occasional HTTP redirects. - Ports: []uint32{443, 80}, - // L7 REST policy: method/path space defaults to full allow via `access` - // (mutually exclusive with explicit rules in the schema). - Protocol: allowedDomainsEndpointProtocol, - Enforcement: allowedDomainsEndpointEnforcement, - Access: allowedDomainsEndpointAccess, - }) - } - if len(endpoints) == 0 { - return nil - } - return &sandboxv1.NetworkPolicyRule{ - Name: kagentAllowedDomainsNetworkPolicyKey, - Endpoints: endpoints, - Binaries: defaultAllowedDomainsBinaries, - } -} - -func extractAllowedDomains(sbx *v1alpha2.AgentHarness) []string { - if sbx == nil || sbx.Spec.Network == nil { - return nil - } - return sbx.Spec.Network.AllowedDomains -} - -// applyAllowedDomainsPolicy merges spec.network.allowedDomains into kagent_allowed_domains (deduped hosts). -// For Claw backends, npm/yarn registry hosts are omitted because npm_yarn covers them. -func applyAllowedDomainsPolicy(sbx *v1alpha2.AgentHarness, net map[string]*sandboxv1.NetworkPolicyRule) { - domainList := extractAllowedDomains(sbx) - if sbx != nil && openclaw.IsClawSandboxBackend(sbx.Spec.Backend) { - domainList = openclaw.OmitNPMPresetRegistryHosts(domainList) - } - if rule := allowedDomainsNetworkPolicyRuleForHarness(sbx, domainList); rule != nil { - net[kagentAllowedDomainsNetworkPolicyKey] = rule - } -} - -func allowedDomainsNetworkPolicyRuleForHarness(ah *v1alpha2.AgentHarness, domains []string) *sandboxv1.NetworkPolicyRule { - rule := allowedDomainsNetworkPolicyRule(domains) - if rule == nil { - return nil - } - if ah != nil && hermes.IsHermesSandboxBackend(ah.Spec.Backend) { - rule.Binaries = hermes.AllowedDomainsBinaries() - } - return rule -} - -// mergeOpenShellSandboxPolicies merges two OpenShell SandboxPolicy fragments for AgentHarness provisioning. -// Network policy rules: overlay keys replace base keys. Filesystem, landlock, and process: overlay replaces -// base only when the overlay sets a non-nil value (for future user-defined CRD policy). -func mergeOpenShellSandboxPolicies(base, overlay *sandboxv1.SandboxPolicy) *sandboxv1.SandboxPolicy { - if base == nil { - return cloneSandboxPolicy(overlay) - } - if overlay == nil { - return cloneSandboxPolicy(base) - } - out := proto.Clone(base).(*sandboxv1.SandboxPolicy) - if out.NetworkPolicies == nil { - out.NetworkPolicies = map[string]*sandboxv1.NetworkPolicyRule{} - } - for k, v := range overlay.GetNetworkPolicies() { - out.NetworkPolicies[k] = proto.Clone(v).(*sandboxv1.NetworkPolicyRule) - } - if overlay.GetFilesystem() != nil { - out.Filesystem = proto.Clone(overlay.Filesystem).(*sandboxv1.FilesystemPolicy) - } - if overlay.GetLandlock() != nil { - out.Landlock = proto.Clone(overlay.Landlock).(*sandboxv1.LandlockPolicy) - } - if overlay.GetProcess() != nil { - out.Process = proto.Clone(overlay.Process).(*sandboxv1.ProcessPolicy) - } - return out -} - -func cloneSandboxPolicy(p *sandboxv1.SandboxPolicy) *sandboxv1.SandboxPolicy { - if p == nil { - return nil - } - return proto.Clone(p).(*sandboxv1.SandboxPolicy) -} - -// sandboxPolicyFragmentFromNetwork applies fill to a fresh network map and returns a versioned policy, or nil if no rules were added. -func sandboxPolicyFragmentFromNetwork(ah *v1alpha2.AgentHarness, fill func(*v1alpha2.AgentHarness, map[string]*sandboxv1.NetworkPolicyRule)) *sandboxv1.SandboxPolicy { - if ah == nil { - return nil - } - net := map[string]*sandboxv1.NetworkPolicyRule{} - fill(ah, net) - if len(net) == 0 { - return nil - } - return &sandboxv1.SandboxPolicy{Version: openclaw.SandboxPolicyVersion, NetworkPolicies: net} -} - -// openShellSandboxPolicyForAgentHarness builds the effective OpenShell SandboxPolicy from AgentHarness spec: -// claw/nemoclaw get static baseline + npm/registry nets; any backend may merge Telegram/Slack egress when -// channels are set (CRD validation restricts channels to claw types, but translation stays liberal). -// User allowedDomains are merged last. OpenClaw-specific fragments live in package openclaw. -func openShellSandboxPolicyForAgentHarness(ah *v1alpha2.AgentHarness) *sandboxv1.SandboxPolicy { - if ah == nil { - return nil - } - var pol *sandboxv1.SandboxPolicy - switch { - case openclaw.IsClawSandboxBackend(ah.Spec.Backend): - pol = mergeOpenShellSandboxPolicies(openclaw.BaselineSandboxPolicy(), openclaw.ChannelNetworkPolicyFragment(ah)) - case hermes.IsHermesSandboxBackend(ah.Spec.Backend): - pol = mergeOpenShellSandboxPolicies(hermes.BaselineHermesSandboxPolicy(), hermes.ChannelNetworkPolicyFragment(ah)) - default: - pol = openclaw.ChannelNetworkPolicyFragment(ah) - } - pol = mergeOpenShellSandboxPolicies(pol, sandboxPolicyFragmentFromNetwork(ah, applyAllowedDomainsPolicy)) - if pol == nil { - return nil - } - if len(pol.GetNetworkPolicies()) == 0 && pol.GetFilesystem() == nil && pol.GetLandlock() == nil && pol.GetProcess() == nil { - return nil - } - return pol -} diff --git a/go/core/pkg/sandboxbackend/openshell/providers.go b/go/core/pkg/sandboxbackend/openshell/providers.go deleted file mode 100644 index 6969e5dc52..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/providers.go +++ /dev/null @@ -1,88 +0,0 @@ -package openshell - -import ( - "context" - "fmt" - "strings" - - "github.com/kagent-dev/kagent/go/api/openshell/gen/datamodelv1" - openshellv1 "github.com/kagent-dev/kagent/go/api/openshell/gen/openshellv1" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openshell/channels" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -const genericProviderType = "generic" - -// GatewayProviderDef describes an OpenShell gateway provider to create or update. -type GatewayProviderDef struct { - Name string - Type string - Credentials map[string]string -} - -// UpsertGatewayProvider creates or updates a single OpenShell gateway provider. -func UpsertGatewayProvider(ctx context.Context, osCli openshellv1.OpenShellClient, def GatewayProviderDef) error { - if osCli == nil { - return fmt.Errorf("openshell client is nil") - } - name := strings.TrimSpace(def.Name) - if name == "" { - return fmt.Errorf("provider name is empty") - } - providerType := strings.TrimSpace(def.Type) - if providerType == "" { - providerType = genericProviderType - } - - getResp, err := osCli.GetProvider(ctx, &openshellv1.GetProviderRequest{Name: name}) - exists := false - if err != nil { - if status.Code(err) != codes.NotFound { - return fmt.Errorf("GetProvider %s: %w", name, err) - } - } else if getResp.GetProvider() != nil { - exists = true - } - - providerProto := &datamodelv1.Provider{ - Metadata: &datamodelv1.ObjectMeta{Name: name}, - Type: providerType, - Credentials: def.Credentials, - } - - if exists { - _, err = osCli.UpdateProvider(ctx, &openshellv1.UpdateProviderRequest{Provider: providerProto}) - if err != nil { - return fmt.Errorf("UpdateProvider %s: %w", name, err) - } - return nil - } - _, err = osCli.CreateProvider(ctx, &openshellv1.CreateProviderRequest{Provider: providerProto}) - if err != nil { - return fmt.Errorf("CreateProvider %s: %w", name, err) - } - return nil -} - -// UpsertGatewayProviders upserts each provider definition. -func UpsertGatewayProviders(ctx context.Context, osCli openshellv1.OpenShellClient, defs []GatewayProviderDef) error { - for _, def := range defs { - if err := UpsertGatewayProvider(ctx, osCli, def); err != nil { - return err - } - } - return nil -} - -func messagingDefsToGateway(defs []channels.MessagingProviderDef) []GatewayProviderDef { - out := make([]GatewayProviderDef, 0, len(defs)) - for _, d := range defs { - out = append(out, GatewayProviderDef{ - Name: d.Name, - Type: genericProviderType, - Credentials: d.Credentials, - }) - } - return out -} diff --git a/go/core/pkg/sandboxbackend/openshell/ssh_terminal.go b/go/core/pkg/sandboxbackend/openshell/ssh_terminal.go deleted file mode 100644 index 4437ba35b4..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/ssh_terminal.go +++ /dev/null @@ -1,33 +0,0 @@ -package openshell - -import ( - "strings" - - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openclaw" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openshell/hermes" -) - -// ResolveSSHRemoteCommand decides whether to run an interactive shell or a harness CLI. -// plainShell: client requested bash only. -// launchOverride: non-empty client launch_command wins. -// harnessBackend: wire string from the WebSocket start frame (e.g. "hermes"). -func ResolveSSHRemoteCommand(plainShell bool, launchOverride, harnessBackend string) (useShell bool, execCmd string) { - if plainShell { - return true, "" - } - cmd := strings.TrimSpace(launchOverride) - if cmd != "" { - return false, cmd - } - backend := v1alpha2.AgentHarnessBackendType(strings.TrimSpace(harnessBackend)) - if v1alpha2.IsKnownAgentHarnessBackend(backend) { - switch { - case hermes.IsHermesSandboxBackend(backend): - return false, hermes.DefaultSSHLaunchCommand() - case openclaw.IsClawSandboxBackend(backend): - return false, openclaw.DefaultSSHLaunchCommand() - } - } - return false, openclaw.DefaultSSHLaunchCommand() -} diff --git a/go/core/pkg/sandboxbackend/openshell/ssh_terminal_test.go b/go/core/pkg/sandboxbackend/openshell/ssh_terminal_test.go deleted file mode 100644 index 252b111a7b..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/ssh_terminal_test.go +++ /dev/null @@ -1,63 +0,0 @@ -package openshell_test - -import ( - "testing" - - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openclaw" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openshell" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openshell/hermes" -) - -func TestResolveSSHRemoteCommand(t *testing.T) { - tests := []struct { - name string - plainShell bool - launchOverride string - harnessBackend string - wantPlain bool - wantCmd string - }{ - { - name: "plain shell", - plainShell: true, - wantPlain: true, - }, - { - name: "client override", - launchOverride: " custom ", - wantPlain: false, - wantCmd: "custom", - }, - { - name: "unknown backend defaults to openclaw tui", - wantPlain: false, - wantCmd: openclaw.DefaultSSHLaunchCommand(), - }, - { - name: "hermes backend", - harnessBackend: "hermes", - wantPlain: false, - wantCmd: hermes.DefaultSSHLaunchCommand(), - }, - { - name: "openclaw backend", - harnessBackend: "openclaw", - wantPlain: false, - wantCmd: openclaw.DefaultSSHLaunchCommand(), - }, - { - name: "nemoclaw backend", - harnessBackend: "nemoclaw", - wantPlain: false, - wantCmd: openclaw.DefaultSSHLaunchCommand(), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - plain, cmd := openshell.ResolveSSHRemoteCommand(tt.plainShell, tt.launchOverride, tt.harnessBackend) - if plain != tt.wantPlain || cmd != tt.wantCmd { - t.Fatalf("ResolveSSHRemoteCommand() = (%v, %q), want (%v, %q)", plain, cmd, tt.wantPlain, tt.wantCmd) - } - }) - } -} diff --git a/go/core/pkg/sandboxbackend/openshell/translate.go b/go/core/pkg/sandboxbackend/openshell/translate.go deleted file mode 100644 index d29c1fd8b1..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/translate.go +++ /dev/null @@ -1,178 +0,0 @@ -package openshell - -import ( - "context" - "fmt" - "strings" - - inferencev1 "github.com/kagent-dev/kagent/go/api/openshell/gen/inferencev1" - openshellv1 "github.com/kagent-dev/kagent/go/api/openshell/gen/openshellv1" - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/internal/utils" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openclaw" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - ctrllog "sigs.k8s.io/controller-runtime/pkg/log" -) - -// agentHarnessGatewayName is the deterministic OpenShell sandbox name for an AgentHarness. Format: -// "-". Collisions across clusters sharing one gateway are a known limitation. -func agentHarnessGatewayName(ah *v1alpha2.AgentHarness) string { - return fmt.Sprintf("%s-%s", ah.Namespace, ah.Name) -} - -// sandboxBackendHandleID is ObjectMeta.name — the canonical lookup key for -// GetSandbox / DeleteSandbox (same string as CreateSandboxRequest.Name). -func sandboxBackendHandleID(sb *openshellv1.Sandbox) string { - if sb == nil || sb.GetMetadata() == nil { - return "" - } - return strings.TrimSpace(sb.GetMetadata().GetName()) -} - -// buildAgentHarnessOpenshellCreateRequest maps an AgentHarness into an OpenShell CreateSandboxRequest. -// unsupported collects fields the gateway cannot currently express so callers can surface them as events. -func buildAgentHarnessOpenshellCreateRequest(ah *v1alpha2.AgentHarness) (*openshellv1.CreateSandboxRequest, []string) { - unsupported := []string{} - tpl := &openshellv1.SandboxTemplate{} - env := map[string]string{} - - if ah.Spec.Image != "" { - tpl.Image = ah.Spec.Image - } - for _, e := range ah.Spec.Env { - if e.ValueFrom != nil { - unsupported = append(unsupported, "env."+e.Name+".valueFrom") - continue - } - env[e.Name] = e.Value - } - spec := &openshellv1.SandboxSpec{ - Environment: env, - Template: tpl, - } - if pol := openShellSandboxPolicyForAgentHarness(ah); pol != nil { - spec.Policy = pol - } - - return &openshellv1.CreateSandboxRequest{ - Name: agentHarnessGatewayName(ah), - Spec: spec, - }, unsupported -} - -// phaseToCondition maps OpenShell SandboxPhase + status message into a -// (Ready status, reason, message) triple for AgentHarness.Status. -func phaseToCondition(sb *openshellv1.Sandbox) (metav1.ConditionStatus, string, string) { - if sb == nil { - return metav1.ConditionUnknown, "SandboxNotFound", "no sandbox returned by gateway" - } - msg := summarizeConditions(sb.GetStatus()) - switch sb.GetPhase() { - case openshellv1.SandboxPhase_SANDBOX_PHASE_READY: - return metav1.ConditionTrue, "SandboxReady", msg - case openshellv1.SandboxPhase_SANDBOX_PHASE_PROVISIONING: - return metav1.ConditionFalse, "SandboxProvisioning", msg - case openshellv1.SandboxPhase_SANDBOX_PHASE_ERROR: - return metav1.ConditionFalse, "SandboxError", msg - case openshellv1.SandboxPhase_SANDBOX_PHASE_DELETING: - return metav1.ConditionFalse, "SandboxDeleting", msg - case openshellv1.SandboxPhase_SANDBOX_PHASE_UNKNOWN, openshellv1.SandboxPhase_SANDBOX_PHASE_UNSPECIFIED: - return metav1.ConditionUnknown, "SandboxPhaseUnknown", msg - default: - return metav1.ConditionUnknown, "SandboxPhaseUnrecognized", fmt.Sprintf("unrecognized phase %s", sb.GetPhase()) - } -} - -func summarizeConditions(s *openshellv1.SandboxStatus) string { - if s == nil { - return "" - } - parts := make([]string, 0, len(s.GetConditions())) - for _, c := range s.GetConditions() { - if c.GetMessage() != "" { - parts = append(parts, fmt.Sprintf("%s=%s: %s", c.GetType(), c.GetStatus(), c.GetMessage())) - } - } - return strings.Join(parts, "; ") -} - -// endpointFor returns a connection hint surfaced on AgentHarness.Status.Connection. -// For OpenShell the gateway URL itself is the addressable endpoint — clients -// use it together with the sandbox name to Exec/SSH in. -func endpointFor(gatewayURL, sandboxID string) string { - if gatewayURL == "" { - return "" - } - return fmt.Sprintf("%s#%s", gatewayURL, sandboxID) -} - -// translateModelConfig syncs a ModelConfig CR onto the OpenShell control plane: it registers the -// gateway provider (CreateProvider/UpdateProvider with credentials) and pins cluster inference to -// the provider/model pair. Backends that need this work call it before CreateSandbox; it is a no-op -// when spec.modelConfigRef is empty. -func translateModelConfig( - ctx context.Context, - ah *v1alpha2.AgentHarness, - kube client.Client, - oc *OpenShellClients, -) error { - if ah == nil { - return fmt.Errorf("AgentHarness is required") - } - ref := strings.TrimSpace(ah.Spec.ModelConfigRef) - if ref == "" { - return nil - } - if oc == nil { - return fmt.Errorf("openshell: OpenShell clients required") - } - inference, osCli := oc.Inference, oc.OpenShell - if kube == nil { - return fmt.Errorf("openshell: Kubernetes client is required when spec.modelConfigRef is set") - } - if inference == nil { - return fmt.Errorf("openshell: inference client is required when spec.modelConfigRef is set") - } - if osCli == nil { - return fmt.Errorf("openshell: OpenShell client is required when spec.modelConfigRef is set") - } - - modelConfigRef, err := utils.ParseRefString(ref, ah.Namespace) - if err != nil { - return fmt.Errorf("failed to parse ModelConfigRef %s: %w", ref, err) - } - - modelConfig := &v1alpha2.ModelConfig{} - if err := kube.Get(ctx, modelConfigRef, modelConfig); err != nil { - return fmt.Errorf("failed to get ModelConfig %s: %w", modelConfigRef.String(), err) - } - apiKey, err := openclaw.ResolveModelConfigAPIKey(ctx, kube, modelConfig) - if err != nil { - return fmt.Errorf("openshell gateway provider: %w", err) - } - - providerRecordName := openclaw.GatewayProviderRecordName(modelConfig.Spec.Provider) - model := modelConfig.Spec.Model - - if err := UpsertGatewayProvider(ctx, osCli, GatewayProviderDef{ - Name: providerRecordName, - Type: providerRecordName, - Credentials: map[string]string{ - "apiKey": apiKey, - }, - }); err != nil { - return fmt.Errorf("upsert inference provider %s: %w", providerRecordName, err) - } - ctrllog.FromContext(ctx).Info("upserted gateway provider", "name", providerRecordName) - - if _, err := inference.SetClusterInference(ctx, &inferencev1.SetClusterInferenceRequest{ - ProviderName: providerRecordName, - ModelId: model, - NoVerify: true, - }); err != nil { - return fmt.Errorf("cluster inference for model %s: %w", model, err) - } - ctrllog.FromContext(ctx).Info("set cluster inference", "provider", providerRecordName, "model", model) - return nil -} diff --git a/go/core/pkg/sandboxbackend/openshell/translate_test.go b/go/core/pkg/sandboxbackend/openshell/translate_test.go deleted file mode 100644 index 88f7124f42..0000000000 --- a/go/core/pkg/sandboxbackend/openshell/translate_test.go +++ /dev/null @@ -1,268 +0,0 @@ -package openshell - -import ( - "testing" - - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openclaw" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openshell/hermes" - "github.com/stretchr/testify/require" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestBuildOpenshellCreateRequest_AllowedDomainsPolicy(t *testing.T) { - sbx := &v1alpha2.AgentHarness{ - ObjectMeta: metav1.ObjectMeta{Name: "s1", Namespace: "ns"}, - Spec: v1alpha2.AgentHarnessSpec{ - Backend: v1alpha2.AgentHarnessBackendOpenClaw, - Network: &v1alpha2.AgentHarnessNetwork{ - AllowedDomains: []string{ - "api.openai.com", - "https://api.anthropic.com/v1", - "*.slack.com", - "api.openai.com", - }, - }, - }, - } - req, unsupported := buildAgentHarnessOpenshellCreateRequest(sbx) - require.Empty(t, unsupported) - pol := req.GetSpec().GetPolicy() - require.NotNil(t, pol) - require.Equal(t, uint32(1), pol.GetVersion()) - net := pol.GetNetworkPolicies() - require.Len(t, net, 5) - require.Contains(t, net, openclaw.NetworkPolicyKeyClawhub) - require.Contains(t, net, openclaw.NetworkPolicyKeyAPI) - require.Contains(t, net, openclaw.NetworkPolicyKeyDocs) - require.Contains(t, net, openclaw.NetworkPolicyKeyNPMYarn) - npm := net[openclaw.NetworkPolicyKeyNPMYarn] - require.Equal(t, "npm_yarn", npm.GetName()) - require.Len(t, npm.GetEndpoints(), 2) - require.Equal(t, "registry.npmjs.org", npm.GetEndpoints()[0].GetHost()) - require.Equal(t, "skip", npm.GetEndpoints()[0].GetTls()) - require.Equal(t, "registry.yarnpkg.com", npm.GetEndpoints()[1].GetHost()) - - clawhub := net[openclaw.NetworkPolicyKeyClawhub] - require.Len(t, clawhub.GetEndpoints(), 1) - require.Equal(t, openclaw.RegistryHostClawhub, clawhub.GetEndpoints()[0].GetHost()) - require.Equal(t, []uint32{443}, clawhub.GetEndpoints()[0].GetPorts()) - require.Len(t, clawhub.GetEndpoints()[0].GetRules(), 2) - - fs := pol.GetFilesystem() - require.NotNil(t, fs) - require.True(t, fs.GetIncludeWorkdir()) - require.ElementsMatch(t, []string{"/tmp", "/dev/null", "/sandbox/.openclaw", "/sandbox/.nemoclaw"}, fs.GetReadWrite()) - require.ElementsMatch(t, []string{"/usr", "/lib", "/proc", "/dev/urandom", "/app", "/etc", "/var/log"}, fs.GetReadOnly()) - require.NotNil(t, pol.GetLandlock()) - require.Equal(t, "best_effort", pol.GetLandlock().GetCompatibility()) - require.NotNil(t, pol.GetProcess()) - require.Equal(t, "sandbox", pol.GetProcess().GetRunAsUser()) - require.Equal(t, "sandbox", pol.GetProcess().GetRunAsGroup()) - - rule := net[kagentAllowedDomainsNetworkPolicyKey] - require.NotNil(t, rule) - require.Equal(t, kagentAllowedDomainsNetworkPolicyKey, rule.GetName()) - require.Len(t, rule.GetEndpoints(), 3) - paths := make([]string, 0, len(rule.GetBinaries())) - for _, b := range rule.GetBinaries() { - paths = append(paths, b.GetPath()) - } - require.Contains(t, paths, "/usr/bin/curl") - - hosts := make([]string, 0, len(rule.GetEndpoints())) - for _, ep := range rule.GetEndpoints() { - require.Equal(t, []uint32{443, 80}, ep.GetPorts()) - require.Equal(t, "rest", ep.GetProtocol()) - require.Equal(t, "enforce", ep.GetEnforcement()) - require.Equal(t, "full", ep.GetAccess()) - hosts = append(hosts, ep.GetHost()) - } - require.ElementsMatch(t, []string{"api.openai.com", "api.anthropic.com", "*.slack.com"}, hosts) -} - -func TestBuildClawCreateRequest_PinsBaseImage(t *testing.T) { - sbx := &v1alpha2.AgentHarness{ - ObjectMeta: metav1.ObjectMeta{Name: "s1", Namespace: "ns"}, - Spec: v1alpha2.AgentHarnessSpec{Backend: v1alpha2.AgentHarnessBackendOpenClaw}, - } - req, unsupported := buildClawCreateRequest(sbx, nil) - require.Empty(t, unsupported) - require.Equal(t, openclaw.NemoclawSandboxBaseImage, req.GetSpec().GetTemplate().GetImage()) -} - -func TestBuildOpenshellCreateRequest_OpenClaw_NoAllowedDomains_HasRegistryPolicies(t *testing.T) { - sbx := &v1alpha2.AgentHarness{ - ObjectMeta: metav1.ObjectMeta{Name: "s1", Namespace: "ns"}, - Spec: v1alpha2.AgentHarnessSpec{Backend: v1alpha2.AgentHarnessBackendOpenClaw}, - } - req, _ := buildAgentHarnessOpenshellCreateRequest(sbx) - policy := req.GetSpec().GetPolicy() - require.NotNil(t, policy.GetFilesystem()) - net := policy.GetNetworkPolicies() - require.Len(t, net, 4) - require.Contains(t, net, openclaw.NetworkPolicyKeyClawhub) - require.Contains(t, net, openclaw.NetworkPolicyKeyNPMYarn) - require.Contains(t, net, openclaw.NetworkPolicyKeyAPI) - require.Contains(t, net, openclaw.NetworkPolicyKeyDocs) - require.NotContains(t, net, kagentAllowedDomainsNetworkPolicyKey) - require.Equal(t, "best_effort", policy.GetLandlock().GetCompatibility()) - require.Equal(t, "sandbox", policy.GetProcess().GetRunAsUser()) - - docs := net[openclaw.NetworkPolicyKeyDocs] - require.Len(t, docs.GetEndpoints()[0].GetRules(), 1) - require.Equal(t, "GET", docs.GetEndpoints()[0].GetRules()[0].GetAllow().GetMethod()) - require.Len(t, docs.GetBinaries(), 1) -} - -func TestBuildOpenshellCreateRequest_OpenClaw_Telegram_HasTelegramBotPolicy(t *testing.T) { - sbx := &v1alpha2.AgentHarness{ - ObjectMeta: metav1.ObjectMeta{Name: "s1", Namespace: "ns"}, - Spec: v1alpha2.AgentHarnessSpec{ - Backend: v1alpha2.AgentHarnessBackendOpenClaw, - Channels: []v1alpha2.AgentHarnessChannel{ - { - Name: "tg1", - Type: v1alpha2.AgentHarnessChannelTypeTelegram, - Telegram: &v1alpha2.AgentHarnessTelegramChannelSpec{ - BotToken: v1alpha2.AgentHarnessChannelCredential{Value: "token"}, - }, - }, - }, - }, - } - req, _ := buildAgentHarnessOpenshellCreateRequest(sbx) - net := req.GetSpec().GetPolicy().GetNetworkPolicies() - require.Len(t, net, 5) - tgPol := net[openclaw.NetworkPolicyKeyTelegramBot] - require.NotNil(t, tgPol) - require.Equal(t, "telegram_bot", tgPol.GetName()) - require.Len(t, tgPol.GetEndpoints(), 1) - ep := tgPol.GetEndpoints()[0] - require.Equal(t, "api.telegram.org", ep.GetHost()) - require.Equal(t, []uint32{443}, ep.GetPorts()) - require.Len(t, ep.GetRules(), 3) - require.Equal(t, "GET", ep.GetRules()[0].GetAllow().GetMethod()) - require.Equal(t, "/bot*/**", ep.GetRules()[0].GetAllow().GetPath()) - require.Equal(t, "POST", ep.GetRules()[1].GetAllow().GetMethod()) - require.Equal(t, "/bot*/**", ep.GetRules()[1].GetAllow().GetPath()) - require.Equal(t, "GET", ep.GetRules()[2].GetAllow().GetMethod()) - require.Equal(t, "/file/bot*/**", ep.GetRules()[2].GetAllow().GetPath()) - paths := make([]string, 0, len(tgPol.GetBinaries())) - for _, b := range tgPol.GetBinaries() { - paths = append(paths, b.GetPath()) - } - require.ElementsMatch(t, []string{"/usr/local/bin/node", "/usr/bin/node", "/usr/bin/curl"}, paths) -} - -func TestBuildOpenshellCreateRequest_OpenClaw_Slack_HasSlackPolicy(t *testing.T) { - sbx := &v1alpha2.AgentHarness{ - ObjectMeta: metav1.ObjectMeta{Name: "s1", Namespace: "ns"}, - Spec: v1alpha2.AgentHarnessSpec{ - Backend: v1alpha2.AgentHarnessBackendOpenClaw, - Channels: []v1alpha2.AgentHarnessChannel{ - { - Name: "s1", - Type: v1alpha2.AgentHarnessChannelTypeSlack, - Slack: &v1alpha2.AgentHarnessSlackChannelSpec{ - BotToken: v1alpha2.AgentHarnessChannelCredential{Value: "b"}, - AppToken: v1alpha2.AgentHarnessChannelCredential{Value: "a"}, - OpenClaw: &v1alpha2.AgentHarnessOpenClawSlackOptions{ - ChannelAccess: v1alpha2.AgentHarnessChannelAccessOpen, - }, - }, - }, - }, - }, - } - req, _ := buildAgentHarnessOpenshellCreateRequest(sbx) - net := req.GetSpec().GetPolicy().GetNetworkPolicies() - s := net[openclaw.NetworkPolicyKeySlack] - require.NotNil(t, s) - require.Equal(t, "slack", s.GetName()) - require.Len(t, s.GetEndpoints(), 5) - require.Equal(t, "slack.com", s.GetEndpoints()[0].GetHost()) - require.Equal(t, "api.slack.com", s.GetEndpoints()[1].GetHost()) - require.Equal(t, "hooks.slack.com", s.GetEndpoints()[2].GetHost()) - wssPrimary := s.GetEndpoints()[3] - require.Equal(t, "wss-primary.slack.com", wssPrimary.GetHost()) - require.Equal(t, "websocket", wssPrimary.GetProtocol()) - require.True(t, wssPrimary.GetWebsocketCredentialRewrite()) - wssBackup := s.GetEndpoints()[4] - require.Equal(t, "wss-backup.slack.com", wssBackup.GetHost()) - require.Equal(t, "websocket", wssBackup.GetProtocol()) - require.True(t, wssBackup.GetWebsocketCredentialRewrite()) - require.True(t, s.GetEndpoints()[0].GetRequestBodyCredentialRewrite()) -} - -func TestBuildOpenshellCreateRequest_OpenClaw_AllowedDomains_OmitsNPMPresetHosts(t *testing.T) { - sbx := &v1alpha2.AgentHarness{ - ObjectMeta: metav1.ObjectMeta{Name: "s1", Namespace: "ns"}, - Spec: v1alpha2.AgentHarnessSpec{ - Backend: v1alpha2.AgentHarnessBackendOpenClaw, - Network: &v1alpha2.AgentHarnessNetwork{ - AllowedDomains: []string{ - "registry.npmjs.org", - "registry.npmjs.org:443", - "registry.yarnpkg.com", - "api.openai.com", - }, - }, - }, - } - req, _ := buildAgentHarnessOpenshellCreateRequest(sbx) - net := req.GetSpec().GetPolicy().GetNetworkPolicies() - require.Contains(t, net, openclaw.NetworkPolicyKeyNPMYarn) - rule := net[kagentAllowedDomainsNetworkPolicyKey] - require.NotNil(t, rule) - hosts := make([]string, 0, len(rule.GetEndpoints())) - for _, ep := range rule.GetEndpoints() { - hosts = append(hosts, ep.GetHost()) - } - require.ElementsMatch(t, []string{"api.openai.com"}, hosts) -} - -func TestBuildOpenshellCreateRequest_Hermes_BaselinePolicy(t *testing.T) { - sbx := &v1alpha2.AgentHarness{ - ObjectMeta: metav1.ObjectMeta{Name: "h1", Namespace: "ns"}, - Spec: v1alpha2.AgentHarnessSpec{Backend: v1alpha2.AgentHarnessBackendHermes}, - } - req, unsupported := buildHermesCreateRequest(sbx, nil) - require.Empty(t, unsupported) - require.Equal(t, hermes.HermesSandboxBaseImage, req.GetSpec().GetTemplate().GetImage()) - - pol := req.GetSpec().GetPolicy() - require.NotNil(t, pol) - net := pol.GetNetworkPolicies() - require.Contains(t, net, hermes.NetworkPolicyKeyNVIDIA) - require.Contains(t, net, hermes.NetworkPolicyKeyNousResearch) - require.Contains(t, net, hermes.NetworkPolicyKeyPyPI) - require.NotContains(t, net, openclaw.NetworkPolicyKeyClawhub) - require.NotContains(t, net, openclaw.NetworkPolicyKeyNPMYarn) - - fs := pol.GetFilesystem() - require.Contains(t, fs.GetReadWrite(), hermes.HermesConfigDir) - require.Contains(t, fs.GetReadOnly(), "/opt/hermes") -} - -func TestBuildOpenshellCreateRequest_Hermes_TelegramChannel(t *testing.T) { - sbx := &v1alpha2.AgentHarness{ - ObjectMeta: metav1.ObjectMeta{Name: "h1", Namespace: "ns"}, - Spec: v1alpha2.AgentHarnessSpec{ - Backend: v1alpha2.AgentHarnessBackendHermes, - Channels: []v1alpha2.AgentHarnessChannel{ - { - Name: "tg", - Type: v1alpha2.AgentHarnessChannelTypeTelegram, - Telegram: &v1alpha2.AgentHarnessTelegramChannelSpec{ - BotToken: v1alpha2.AgentHarnessChannelCredential{Value: "tok"}, - }, - }, - }, - }, - } - req, _ := buildAgentHarnessOpenshellCreateRequest(sbx) - net := req.GetSpec().GetPolicy().GetNetworkPolicies() - require.Contains(t, net, hermes.NetworkPolicyKeyTelegram) - require.Equal(t, "telegram", net[hermes.NetworkPolicyKeyTelegram].GetName()) -} diff --git a/go/core/pkg/sandboxbackend/routing.go b/go/core/pkg/sandboxbackend/routing.go deleted file mode 100644 index 9f660a19a8..0000000000 --- a/go/core/pkg/sandboxbackend/routing.go +++ /dev/null @@ -1,97 +0,0 @@ -package sandboxbackend - -import ( - "context" - "fmt" - - "github.com/kagent-dev/kagent/go/api/v1alpha2" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -// RoutingBackend delegates to agent-sandbox or Agent Substrate based on spec.platform. -type RoutingBackend struct { - AgentSandbox Backend - Substrate Backend -} - -var _ Backend = (*RoutingBackend)(nil) - -// NewRoutingBackend returns a backend that routes SandboxAgent workloads by platform. -func NewRoutingBackend(agentSandbox, substrate Backend) *RoutingBackend { - return &RoutingBackend{AgentSandbox: agentSandbox, Substrate: substrate} -} - -func (r *RoutingBackend) backendFor(agent v1alpha2.AgentObject) (Backend, error) { - if r == nil { - return nil, fmt.Errorf("routing sandbox backend is nil") - } - if v1alpha2.AgentSandboxPlatform(agent) == v1alpha2.SandboxPlatformSubstrate { - if r.Substrate == nil { - return nil, fmt.Errorf("substrate sandbox backend is not configured") - } - return r.Substrate, nil - } - if r.AgentSandbox == nil { - return nil, fmt.Errorf("agent-sandbox backend is not configured") - } - return r.AgentSandbox, nil -} - -func (r *RoutingBackend) BuildSandbox(ctx context.Context, in BuildInput) ([]client.Object, error) { - b, err := r.backendFor(in.Agent) - if err != nil { - return nil, err - } - return b.BuildSandbox(ctx, in) -} - -func (r *RoutingBackend) GetOwnedResourceTypes() []client.Object { - var out []client.Object - if r != nil && r.AgentSandbox != nil { - out = append(out, r.AgentSandbox.GetOwnedResourceTypes()...) - } - if r != nil && r.Substrate != nil { - out = append(out, r.Substrate.GetOwnedResourceTypes()...) - } - return out -} - -// OwnedResourceTypesFor returns owned-resource types for the agent's sandbox platform. -func (r *RoutingBackend) OwnedResourceTypesFor(agent v1alpha2.AgentObject) ([]client.Object, error) { - b, err := r.backendFor(agent) - if err != nil { - return nil, err - } - return b.GetOwnedResourceTypes(), nil -} - -func (r *RoutingBackend) ComputeReady(ctx context.Context, cl client.Client, nn types.NamespacedName) (metav1.ConditionStatus, string, string) { - sa := &v1alpha2.SandboxAgent{} - if err := cl.Get(ctx, nn, sa); err != nil { - return metav1.ConditionUnknown, "SandboxAgentNotFound", err.Error() - } - b, err := r.backendFor(sa) - if err != nil { - return metav1.ConditionUnknown, "SandboxBackendNotConfigured", err.Error() - } - return b.ComputeReady(ctx, cl, nn) -} - -// ValidatePlatform reports whether this routing backend can reconcile the agent's sandbox platform. -func (r *RoutingBackend) ValidatePlatform(agent v1alpha2.AgentObject) error { - _, err := r.backendFor(agent) - return err -} - -// ValidateSandboxPlatform reports whether backend supports the agent's sandbox platform. -func ValidateSandboxPlatform(backend Backend, agent v1alpha2.AgentObject) error { - if backend == nil { - return fmt.Errorf("sandbox backend is not configured") - } - if rb, ok := backend.(*RoutingBackend); ok { - return rb.ValidatePlatform(agent) - } - return nil -} diff --git a/go/core/pkg/sandboxbackend/routing_test.go b/go/core/pkg/sandboxbackend/routing_test.go deleted file mode 100644 index 905ad8f560..0000000000 --- a/go/core/pkg/sandboxbackend/routing_test.go +++ /dev/null @@ -1,43 +0,0 @@ -package sandboxbackend_test - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/kagent-dev/kagent/go/api/v1alpha2" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/agentsxk8s" - "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/substrate" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func TestValidateSandboxPlatform(t *testing.T) { - t.Parallel() - - routing := sandboxbackend.NewRoutingBackend(agentsxk8s.New(), substrate.NewAgentsBackend(nil, nil)) - - substrateSA := &v1alpha2.SandboxAgent{ - ObjectMeta: metav1.ObjectMeta{Name: "s", Namespace: "ns"}, - Spec: v1alpha2.SandboxAgentSpec{ - Platform: v1alpha2.SandboxPlatformSubstrate, - }, - } - require.NoError(t, sandboxbackend.ValidateSandboxPlatform(routing, substrateSA)) - - k8sSA := &v1alpha2.SandboxAgent{ - ObjectMeta: metav1.ObjectMeta{Name: "s", Namespace: "ns"}, - Spec: v1alpha2.SandboxAgentSpec{ - Platform: v1alpha2.SandboxPlatformAgentSandbox, - }, - } - require.NoError(t, sandboxbackend.ValidateSandboxPlatform(routing, k8sSA)) - - substrateOnly := sandboxbackend.NewRoutingBackend(nil, substrate.NewAgentsBackend(nil, nil)) - require.NoError(t, sandboxbackend.ValidateSandboxPlatform(substrateOnly, substrateSA)) - require.Error(t, sandboxbackend.ValidateSandboxPlatform(substrateOnly, k8sSA)) - - agentSandboxOnly := sandboxbackend.NewRoutingBackend(agentsxk8s.New(), nil) - require.Error(t, sandboxbackend.ValidateSandboxPlatform(agentSandboxOnly, substrateSA)) - require.NoError(t, sandboxbackend.ValidateSandboxPlatform(agentSandboxOnly, k8sSA)) -} diff --git a/go/core/pkg/sandboxbackend/substrate/agent_actor.go b/go/core/pkg/sandboxbackend/substrate/agent_actor.go index 821cdbc669..220c69f8ef 100644 --- a/go/core/pkg/sandboxbackend/substrate/agent_actor.go +++ b/go/core/pkg/sandboxbackend/substrate/agent_actor.go @@ -43,9 +43,6 @@ func (b *SandboxAgentActorBackend) EnsureSessionActor(ctx context.Context, sa *v if b == nil || b.client == nil { return sandboxbackend.EnsureResult{}, fmt.Errorf("substrate ate-api client is required") } - if v1alpha2.AgentSandboxPlatform(sa) != v1alpha2.SandboxPlatformSubstrate { - return sandboxbackend.EnsureResult{}, fmt.Errorf("substrate actor backend called for platform %q", v1alpha2.AgentSandboxPlatform(sa)) - } actorID := SandboxAgentSessionActorID(sa, sessionID) tmplNS, tmplName := sa.Namespace, SandboxAgentActorTemplateName(sa) diff --git a/go/core/pkg/sandboxbackend/substrate/agentharness_actor.go b/go/core/pkg/sandboxbackend/substrate/agentharness_actor.go new file mode 100644 index 0000000000..8d650c28d6 --- /dev/null +++ b/go/core/pkg/sandboxbackend/substrate/agentharness_actor.go @@ -0,0 +1,206 @@ +package substrate + +import ( + "context" + "crypto/sha256" + "fmt" + "strings" + + "github.com/agent-substrate/substrate/pkg/proto/ateapipb" + "github.com/kagent-dev/kagent/go/api/v1alpha2" + "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// AgentHarnessSessionActorBackend manages per-session ate-api actors for +// AgentHarness chats. The AgentHarness itself is a template: its generated +// ActorTemplate is the golden snapshot, and every chat session is materialized +// as its own substrate actor spun from that template. +type AgentHarnessSessionActorBackend struct { + client *Client + atenetRouterURL string +} + +// NewAgentHarnessSessionActorBackend returns a backend that ensures per-session +// AgentHarness actors on ate-api. +func NewAgentHarnessSessionActorBackend(client *Client, atenetRouterURL string) *AgentHarnessSessionActorBackend { + atenetRouterURL = strings.TrimSpace(atenetRouterURL) + if atenetRouterURL == "" { + atenetRouterURL = DefaultAtenetRouterURL + } + return &AgentHarnessSessionActorBackend{ + client: client, + atenetRouterURL: atenetRouterURL, + } +} + +// EnsureSessionActor creates (if needed) and resumes the per-session actor for +// an AgentHarness chat session, then waits for it to be reachable via +// atenet-router. The actor is spun from the harness's generated ActorTemplate. +func (b *AgentHarnessSessionActorBackend) EnsureSessionActor(ctx context.Context, ah *v1alpha2.AgentHarness, sessionID string) (sandboxbackend.EnsureResult, error) { + if ah == nil { + return sandboxbackend.EnsureResult{}, fmt.Errorf("AgentHarness is required") + } + sessionID = strings.TrimSpace(sessionID) + if sessionID == "" { + return sandboxbackend.EnsureResult{}, fmt.Errorf("session id is required") + } + if b == nil || b.client == nil { + return sandboxbackend.EnsureResult{}, fmt.Errorf("substrate ate-api client is required") + } + if err := validateSubstrateSpec(ah); err != nil { + return sandboxbackend.EnsureResult{}, err + } + + actorID := AgentHarnessSessionActorID(ah, sessionID) + tmplNS, tmplName := generatedActorTemplateKey(ah) + + actor, err := b.client.GetActor(ctx, actorID) + if err != nil { + if status.Code(err) != codes.NotFound { + return sandboxbackend.EnsureResult{}, fmt.Errorf("substrate GetActor %q: %w", actorID, err) + } + actor, err = b.client.CreateActor(ctx, actorID, tmplNS, tmplName) + if err != nil { + return sandboxbackend.EnsureResult{}, fmt.Errorf("substrate CreateActor %q: %w", actorID, err) + } + } + + switch actor.GetStatus() { + case ateapipb.Actor_STATUS_RUNNING, ateapipb.Actor_STATUS_RESUMING: + // already active or waking + case ateapipb.Actor_STATUS_SUSPENDED, ateapipb.Actor_STATUS_UNSPECIFIED: + if _, err = b.client.ResumeActor(ctx, actorID); err != nil { + return sandboxbackend.EnsureResult{}, wrapResumeActorError(actorID, err) + } + } + + if err := waitForActorReachableViaAtenet(ctx, b.client, nil, b.atenetRouterURL, actorID); err != nil { + return sandboxbackend.EnsureResult{}, err + } + + host := ActorHost(actorID, "") + return sandboxbackend.EnsureResult{ + Handle: sandboxbackend.Handle{ID: actorID}, + Endpoint: fmt.Sprintf("atenet-router Host %s", host), + }, nil +} + +// SuspendSessionActor checkpoints and frees the worker for a chat session actor. +// Suspended actors are resumed automatically on the next EnsureSessionActor. +func (b *AgentHarnessSessionActorBackend) SuspendSessionActor(ctx context.Context, ah *v1alpha2.AgentHarness, sessionID string) error { + if b == nil || b.client == nil || ah == nil { + return nil + } + actorID := AgentHarnessSessionActorID(ah, sessionID) + actor, err := b.client.GetActor(ctx, actorID) + if err != nil { + if status.Code(err) == codes.NotFound { + return nil + } + return fmt.Errorf("substrate GetActor %q: %w", actorID, err) + } + switch actor.GetStatus() { + case ateapipb.Actor_STATUS_RUNNING, ateapipb.Actor_STATUS_RESUMING, ateapipb.Actor_STATUS_SUSPENDING: + if err := b.client.SuspendActor(ctx, actorID); err != nil && status.Code(err) != codes.NotFound { + return fmt.Errorf("substrate SuspendActor %q: %w", actorID, err) + } + } + return nil +} + +// DeleteSessionActor deletes a single per-session actor by id. +func (b *AgentHarnessSessionActorBackend) DeleteSessionActor(ctx context.Context, actorID string) (bool, error) { + if strings.TrimSpace(actorID) == "" { + return true, nil + } + return deleteActor(ctx, b.client, actorID) +} + +// SessionActorState is the coarse lifecycle state of a chat session's actor as +// surfaced to the UI. +type SessionActorState string + +const ( + // SessionActorStateRunning means the actor is running or waking up. + SessionActorStateRunning SessionActorState = "running" + // SessionActorStateSuspended means the actor is checkpointed/freed and will + // resume on the next connect. + SessionActorStateSuspended SessionActorState = "suspended" + // SessionActorStateMissing means no actor exists yet for the session. + SessionActorStateMissing SessionActorState = "missing" +) + +// GetSessionActorState reports whether a chat session's actor is running, +// suspended, or not yet created. +func (b *AgentHarnessSessionActorBackend) GetSessionActorState(ctx context.Context, ah *v1alpha2.AgentHarness, sessionID string) (SessionActorState, error) { + if b == nil || b.client == nil || ah == nil { + return SessionActorStateMissing, nil + } + if strings.TrimSpace(sessionID) == "" { + return SessionActorStateMissing, fmt.Errorf("session id is required") + } + actorID := AgentHarnessSessionActorID(ah, sessionID) + actor, err := b.client.GetActor(ctx, actorID) + if err != nil { + if status.Code(err) == codes.NotFound { + return SessionActorStateMissing, nil + } + return SessionActorStateMissing, fmt.Errorf("substrate GetActor %q: %w", actorID, err) + } + switch actor.GetStatus() { + case ateapipb.Actor_STATUS_RUNNING, ateapipb.Actor_STATUS_RESUMING: + return SessionActorStateRunning, nil + default: + return SessionActorStateSuspended, nil + } +} + +// DeleteAllAgentHarnessActors deletes the legacy single harness actor and every +// per-session actor belonging to the AgentHarness. It is best-effort and returns +// false while any actor is still terminating. +func (b *AgentHarnessSessionActorBackend) DeleteAllAgentHarnessActors(ctx context.Context, ah *v1alpha2.AgentHarness) (bool, error) { + if b == nil || b.client == nil || ah == nil { + return true, nil + } + prefix := agentHarnessActorPrefix(ah) + actors, err := b.client.ListActors(ctx) + if err != nil { + return false, fmt.Errorf("list substrate actors: %w", err) + } + allDone := true + for _, actor := range actors { + id := strings.TrimSpace(actor.GetActorId()) + if id == "" { + continue + } + if id != prefix && !strings.HasPrefix(id, prefix+"-") { + continue + } + done, err := deleteActor(ctx, b.client, id) + if err != nil { + return false, fmt.Errorf("delete substrate actor %q: %w", id, err) + } + if !done { + allDone = false + } + } + return allDone, nil +} + +func agentHarnessActorPrefix(ah *v1alpha2.AgentHarness) string { + return ActorID(ah) +} + +// AgentHarnessSessionActorID returns a stable DNS-1123 ate-api actor id for an +// AgentHarness chat session. +func AgentHarnessSessionActorID(ah *v1alpha2.AgentHarness, sessionID string) string { + raw := fmt.Sprintf("%s-%s", agentHarnessActorPrefix(ah), sanitizeSessionID(sessionID)) + raw = strings.ToLower(strings.ReplaceAll(raw, "_", "-")) + if len(raw) <= 63 && dns1123Label.MatchString(raw) { + return raw + } + sum := sha256.Sum256([]byte(ah.Namespace + "/" + ah.Name + "/" + sessionID)) + return fmt.Sprintf("%s-%x", actorIDPrefix, sum[:12]) +} diff --git a/go/core/pkg/sandboxbackend/substrate/agents_backend.go b/go/core/pkg/sandboxbackend/substrate/agents_backend.go index e0e557f250..be06b9cba1 100644 --- a/go/core/pkg/sandboxbackend/substrate/agents_backend.go +++ b/go/core/pkg/sandboxbackend/substrate/agents_backend.go @@ -39,9 +39,6 @@ func (b *AgentsBackend) BuildSandbox(ctx context.Context, in sandboxbackend.Buil if !ok || sa == nil { return nil, fmt.Errorf("substrate sandbox backend requires a SandboxAgent") } - if v1alpha2.AgentSandboxPlatform(sa) != v1alpha2.SandboxPlatformSubstrate { - return nil, fmt.Errorf("substrate sandbox backend called for platform %q", v1alpha2.AgentSandboxPlatform(sa)) - } if b.Lifecycle == nil { return nil, fmt.Errorf("substrate lifecycle is not configured") } diff --git a/go/core/pkg/sandboxbackend/substrate/gateway_token.go b/go/core/pkg/sandboxbackend/substrate/gateway_token.go index 10eaca47c6..90837f4960 100644 --- a/go/core/pkg/sandboxbackend/substrate/gateway_token.go +++ b/go/core/pkg/sandboxbackend/substrate/gateway_token.go @@ -2,20 +2,33 @@ package substrate import ( "context" + "crypto/rand" + "encoding/hex" "fmt" "strings" "github.com/kagent-dev/kagent/go/api/v1alpha2" corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) // GatewayTokenSecretKey is the Secret data key used for per-harness OpenClaw gateway tokens. const GatewayTokenSecretKey = "token" +// ManagedGatewayTokenSecretName returns the name of the Secret the controller +// auto-creates to hold a generated gateway token when the harness does not +// specify one inline (gatewayToken) or via gatewayTokenSecretRef. +func ManagedGatewayTokenSecretName(ah *v1alpha2.AgentHarness) string { + return ah.Name + "-gateway-token" +} + // ResolveGatewayToken returns the per-harness gateway token. -// Token source is validated at admission via AgentHarnessSubstrateSpec CEL rules. +// Precedence: gatewayTokenSecretRef, then inline gatewayToken, then the +// controller-managed Secret holding an auto-generated token. func ResolveGatewayToken(ctx context.Context, kube client.Client, ah *v1alpha2.AgentHarness) (string, error) { if ah == nil || ah.Spec.Substrate == nil { return "", fmt.Errorf("spec.substrate is required") @@ -24,7 +37,78 @@ func ResolveGatewayToken(ctx context.Context, kube client.Client, ah *v1alpha2.A if sub.GatewayTokenSecretRef != nil && strings.TrimSpace(sub.GatewayTokenSecretRef.Name) != "" { return resolveGatewayTokenSecret(ctx, kube, ah.Namespace, sub.GatewayTokenSecretRef) } - return strings.TrimSpace(sub.GatewayToken), nil + if t := strings.TrimSpace(sub.GatewayToken); t != "" { + return t, nil + } + // No token configured on the spec: fall back to the controller-managed + // Secret. EnsureManagedGatewayToken creates it during reconciliation. + ref := &v1alpha2.TypedLocalReference{Name: ManagedGatewayTokenSecretName(ah)} + return resolveGatewayTokenSecret(ctx, kube, ah.Namespace, ref) +} + +// EnsureManagedGatewayToken makes sure a gateway token exists for the harness. +// When the spec provides a token (inline or via gatewayTokenSecretRef) it is a +// no-op. Otherwise it creates a controller-owned Secret holding a randomly +// generated token exactly once, so the same token is reused across reconciles +// and can be retrieved later via +// `kubectl get secret -gateway-token`. +func (p *Lifecycle) EnsureManagedGatewayToken(ctx context.Context, ah *v1alpha2.AgentHarness) error { + if ah == nil || ah.Spec.Substrate == nil { + return fmt.Errorf("spec.substrate is required") + } + if p.Client == nil { + return fmt.Errorf("substrate lifecycle kubernetes client is required") + } + sub := ah.Spec.Substrate + if strings.TrimSpace(sub.GatewayToken) != "" { + return nil + } + if sub.GatewayTokenSecretRef != nil && strings.TrimSpace(sub.GatewayTokenSecretRef.Name) != "" { + return nil + } + + key := types.NamespacedName{Namespace: ah.Namespace, Name: ManagedGatewayTokenSecretName(ah)} + var existing corev1.Secret + if err := p.Client.Get(ctx, key, &existing); err == nil { + if len(strings.TrimSpace(string(existing.Data[GatewayTokenSecretKey]))) > 0 { + return nil // already provisioned + } + } else if !apierrors.IsNotFound(err) { + return fmt.Errorf("get managed gateway token secret %s: %w", key, err) + } + + token, err := generateGatewayToken() + if err != nil { + return fmt.Errorf("generate gateway token: %w", err) + } + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: key.Name, + Namespace: key.Namespace, + Labels: lifecycleLabels(ah), + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{GatewayTokenSecretKey: []byte(token)}, + } + if err := controllerutil.SetControllerReference(ah, secret, p.Client.Scheme()); err != nil { + return fmt.Errorf("set managed gateway token secret owner ref: %w", err) + } + if err := p.Client.Create(ctx, secret); err != nil { + if apierrors.IsAlreadyExists(err) { + return nil + } + return fmt.Errorf("create managed gateway token secret %s: %w", key, err) + } + return nil +} + +// generateGatewayToken returns a 256-bit random token as a hex string. +func generateGatewayToken() (string, error) { + buf := make([]byte, 32) + if _, err := rand.Read(buf); err != nil { + return "", fmt.Errorf("read random bytes: %w", err) + } + return hex.EncodeToString(buf), nil } func resolveGatewayTokenSecret(ctx context.Context, kube client.Client, namespace string, ref *v1alpha2.TypedLocalReference) (string, error) { diff --git a/go/core/pkg/sandboxbackend/substrate/gateway_token_test.go b/go/core/pkg/sandboxbackend/substrate/gateway_token_test.go index 9cfb7dc57f..327b23da72 100644 --- a/go/core/pkg/sandboxbackend/substrate/gateway_token_test.go +++ b/go/core/pkg/sandboxbackend/substrate/gateway_token_test.go @@ -6,8 +6,10 @@ import ( "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -80,3 +82,69 @@ func TestResolveGatewayTokenAcceptsNonemptySecretValue(t *testing.T) { require.NoError(t, err) require.Equal(t, "secret-token", token) } + +func TestEnsureManagedGatewayTokenGeneratesAndResolves(t *testing.T) { + t.Parallel() + + scheme := runtime.NewScheme() + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(v1alpha2.AddToScheme(scheme)) + + const ns = "kagent" + ah := &v1alpha2.AgentHarness{ + ObjectMeta: metav1.ObjectMeta{Name: "claw", Namespace: ns}, + Spec: v1alpha2.AgentHarnessSpec{ + Substrate: &v1alpha2.AgentHarnessSubstrateSpec{}, + }, + } + kube := fake.NewClientBuilder().WithScheme(scheme).WithObjects(ah).Build() + p := &Lifecycle{Client: kube} + + // No token set: managed secret does not exist yet. + require.NoError(t, p.EnsureManagedGatewayToken(context.Background(), ah)) + + secretName := ManagedGatewayTokenSecretName(ah) + var secret corev1.Secret + require.NoError(t, kube.Get(context.Background(), types.NamespacedName{Namespace: ns, Name: secretName}, &secret)) + generated := string(secret.Data[GatewayTokenSecretKey]) + require.NotEmpty(t, generated) + + // ResolveGatewayToken falls back to the managed secret. + resolved, err := ResolveGatewayToken(context.Background(), kube, ah) + require.NoError(t, err) + require.Equal(t, generated, resolved) + + // Idempotent: re-ensuring keeps the same token. + require.NoError(t, p.EnsureManagedGatewayToken(context.Background(), ah)) + resolvedAgain, err := ResolveGatewayToken(context.Background(), kube, ah) + require.NoError(t, err) + require.Equal(t, generated, resolvedAgain) +} + +func TestEnsureManagedGatewayTokenNoopWhenSpecProvidesToken(t *testing.T) { + t.Parallel() + + scheme := runtime.NewScheme() + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(v1alpha2.AddToScheme(scheme)) + + const ns = "kagent" + ah := &v1alpha2.AgentHarness{ + ObjectMeta: metav1.ObjectMeta{Name: "claw", Namespace: ns}, + Spec: v1alpha2.AgentHarnessSpec{ + Substrate: &v1alpha2.AgentHarnessSubstrateSpec{GatewayToken: "inline-token"}, + }, + } + kube := fake.NewClientBuilder().WithScheme(scheme).WithObjects(ah).Build() + p := &Lifecycle{Client: kube} + + require.NoError(t, p.EnsureManagedGatewayToken(context.Background(), ah)) + + var secret corev1.Secret + err := kube.Get(context.Background(), types.NamespacedName{Namespace: ns, Name: ManagedGatewayTokenSecretName(ah)}, &secret) + require.True(t, apierrors.IsNotFound(err), "managed secret must not be created when spec provides a token") + + resolved, err := ResolveGatewayToken(context.Background(), kube, ah) + require.NoError(t, err) + require.Equal(t, "inline-token", resolved) +} diff --git a/go/core/pkg/sandboxbackend/substrate/lifecycle.go b/go/core/pkg/sandboxbackend/substrate/lifecycle.go index 1590127c29..9bfaac069b 100644 --- a/go/core/pkg/sandboxbackend/substrate/lifecycle.go +++ b/go/core/pkg/sandboxbackend/substrate/lifecycle.go @@ -14,6 +14,10 @@ func (p *Lifecycle) EnsureGeneratedTemplate(ctx context.Context, ah *v1alpha2.Ag return LifecycleState{}, fmt.Errorf("spec.substrate is required") } + if err := p.EnsureManagedGatewayToken(ctx, ah); err != nil { + return LifecycleState{}, err + } + wpKey, err := p.resolveWorkerPoolRef(ctx, ah) if err != nil { return LifecycleState{}, err diff --git a/go/core/pkg/sandboxbackend/substrate/lifecycle_acp.go b/go/core/pkg/sandboxbackend/substrate/lifecycle_acp.go new file mode 100644 index 0000000000..f9fcd5cf29 --- /dev/null +++ b/go/core/pkg/sandboxbackend/substrate/lifecycle_acp.go @@ -0,0 +1,128 @@ +package substrate + +import ( + "context" + "encoding/base64" + "fmt" + "strings" + + atev1alpha1 "github.com/agent-substrate/substrate/pkg/api/v1alpha1" + "github.com/kagent-dev/kagent/go/api/v1alpha2" + "github.com/kagent-dev/kagent/go/core/internal/utils" + "github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/openclaw" + corev1 "k8s.io/api/core/v1" +) + +// Default Substrate workload images for the generic acp-shim agent targets +// (docker/acp-sandbox/Dockerfile). Substrate admission requires digest-pinned +// refs. +const ( + // AcpSandboxHermesImage is the acp-sandbox "hermes" target. + AcpSandboxHermesImage = "ttl.sh/kagent-acp-hermes@sha256:38e8f2d34ea753070ca47094cc231295d1a07b245821d40853726bd0065d8c57" +) + +// acpAgentSpec describes how to run one stdio ACP agent behind the acp-shim +// inside a Substrate actor. +type acpAgentSpec struct { + // DefaultImage is the digest-pinned acp-sandbox target image used when + // neither the harness nor cluster defaults specify a workload image. + DefaultImage string + // ChildCommand is the stdio ACP agent command the shim spawns per + // connection (shell-safe words, joined with spaces). + ChildCommand []string +} + +// acpAgentSpecs maps non-OpenClaw substrate backends to their agent commands. +// Per-connection child policy is used for all of them: Substrate actors are +// checkpointed/restored, and a child spawned per connection is always a +// fresh post-restore process (the same reason the OpenClaw path re-ensures +// its gateway per connection). +var acpAgentSpecs = map[v1alpha2.AgentHarnessBackendType]acpAgentSpec{ + v1alpha2.AgentHarnessBackendHermes: { + DefaultImage: AcpSandboxHermesImage, + ChildCommand: []string{"hermes", "acp"}, + }, +} + +// buildAcpAgentActorStartup returns the ateom workload startup script and +// container env for a generic stdio ACP agent (hermes) on +// Substrate. Unlike OpenClaw there is no in-sandbox gateway: the shim owns +// the atenet ingress port and bridges WebSocket frames to a per-connection +// agent child. Model credentials come from the harness ModelConfig as a +// provider-conventional env var (e.g. OPENAI_API_KEY, ANTHROPIC_API_KEY) +// resolved by ate-api from the referenced Secret. +func (p *Lifecycle) buildAcpAgentActorStartup(ctx context.Context, ah *v1alpha2.AgentHarness, spec acpAgentSpec) (script string, env []atev1alpha1.EnvVar, err error) { + if ah == nil { + return "", nil, fmt.Errorf("AgentHarness is required") + } + + containerEnv := []corev1.EnvVar{ + {Name: "HOME", Value: openclaw.SubstrateActorHome}, + // Substrate actors do not inherit the image's ENV (unlike docker run), + // so the shim's exec.LookPath and any subprocesses the agent spawns + // need an explicit PATH. Includes the hermes image's venv bin dir. + {Name: "PATH", Value: openclaw.SubstrateActorHome + "/.venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}, + } + + prelude := "" + if ref := strings.TrimSpace(ah.Spec.ModelConfigRef); ref != "" { + mcRef, parseErr := utils.ParseRefString(ref, ah.Namespace) + if parseErr != nil { + return "", nil, fmt.Errorf("parse modelConfigRef %q: %w", ref, parseErr) + } + mc := &v1alpha2.ModelConfig{} + if getErr := p.Client.Get(ctx, mcRef, mc); getErr != nil { + return "", nil, fmt.Errorf("get ModelConfig %s: %w", mcRef, getErr) + } + apiKeyEnv, keyErr := openclaw.ModelConfigAPIKeyEnvVar(mc) + if keyErr != nil { + return "", nil, keyErr + } + containerEnv = append(containerEnv, apiKeyEnv) + + if ah.Spec.Backend == v1alpha2.AgentHarnessBackendHermes { + prelude = hermesConfigPrelude(mc) + } + } + + // Backend-agnostic env passthrough from the harness spec. Appended last so + // it cannot shadow HOME/PATH or the shim token. + containerEnv = append(containerEnv, ah.Spec.Env...) + + containerEnv = append(containerEnv, acpShimTokenEnv(ah)...) + + script = fmt.Sprintf( + "set -e\n%sexec /usr/local/bin/acp-shim \\\n --listen :%d \\\n --child-policy per-connection \\\n -- %s", + prelude, acpListenPort, strings.Join(spec.ChildCommand, " ")) + return script, actorTemplateEnvFromPodEnv(containerEnv), nil +} + +// hermesProviderSlugs maps kagent ModelConfig providers to hermes provider +// slugs (hermes_cli CANONICAL_PROVIDERS). Hermes authenticates these via the +// provider-conventional env var already injected from the ModelConfig secret. +var hermesProviderSlugs = map[v1alpha2.ModelProvider]string{ + v1alpha2.ModelProviderOpenAI: "openai-api", + v1alpha2.ModelProviderAnthropic: "anthropic", + v1alpha2.ModelProviderGemini: "gemini", +} + +// hermesConfigPrelude returns shell lines that write ~/.hermes/config.yaml +// selecting the ModelConfig's model and provider. Without it hermes defaults +// to an unauthenticated provider and prompts silently produce no output. +// Returns "" when the ModelConfig provider has no hermes equivalent. +func hermesConfigPrelude(mc *v1alpha2.ModelConfig) string { + slug, ok := hermesProviderSlugs[mc.Spec.Provider] + if !ok || strings.TrimSpace(mc.Spec.Model) == "" { + return "" + } + cfg := fmt.Sprintf("model:\n default: %q\n provider: %q\n", mc.Spec.Model, slug) + if mc.Spec.Provider == v1alpha2.ModelProviderOpenAI { + // Hermes auto-upgrades direct api.openai.com to its codex_responses + // transport, which requests reasoning.encrypted_content — rejected + // with HTTP 400 by non-reasoning models (e.g. gpt-4.1-mini), and the + // turn ends silently. Pin the plain chat-completions transport. + cfg += " api_mode: chat_completions\n" + } + encoded := base64.StdEncoding.EncodeToString([]byte(cfg)) + return fmt.Sprintf("mkdir -p \"$HOME/.hermes\"\necho %s | base64 -d > \"$HOME/.hermes/config.yaml\"\n", encoded) +} diff --git a/go/core/pkg/sandboxbackend/substrate/lifecycle_actortemplate.go b/go/core/pkg/sandboxbackend/substrate/lifecycle_actortemplate.go index a5cb3b0351..ef64cf081c 100644 --- a/go/core/pkg/sandboxbackend/substrate/lifecycle_actortemplate.go +++ b/go/core/pkg/sandboxbackend/substrate/lifecycle_actortemplate.go @@ -42,12 +42,46 @@ func (p *Lifecycle) ensureActorTemplate(ctx context.Context, ah *v1alpha2.AgentH func (p *Lifecycle) buildActorTemplate(ctx context.Context, ah *v1alpha2.AgentHarness, wpKey types.NamespacedName) (*atev1alpha1.ActorTemplate, error) { key := types.NamespacedName{Namespace: ah.Namespace, Name: actorTemplateName(ah)} + + var ( + startupScript string + containerEnv []atev1alpha1.EnvVar + defaultImage string + containerName string + err error + ) + // clawBackend selects the OpenClaw startup path; the cluster-wide + // DefaultWorkloadImage only applies to claw backends (it points at the + // openclaw sandbox image), other backends fall back to their own image. + clawBackend := false + switch ah.Spec.Backend { + case v1alpha2.AgentHarnessBackendOpenClaw: + clawBackend = true + defaultImage = openclaw.AcpSandboxOpenClawImage + containerName = defaultOpenClawContainer + startupScript, containerEnv, err = p.buildOpenClawActorStartup(ctx, ah) + if err != nil { + return nil, fmt.Errorf("build openclaw actor startup: %w", err) + } + default: + spec, ok := acpAgentSpecs[ah.Spec.Backend] + if !ok { + return nil, fmt.Errorf("substrate runtime does not support backend %q", ah.Spec.Backend) + } + defaultImage = spec.DefaultImage + containerName = string(ah.Spec.Backend) + startupScript, containerEnv, err = p.buildAcpAgentActorStartup(ctx, ah, spec) + if err != nil { + return nil, fmt.Errorf("build %s actor startup: %w", ah.Spec.Backend, err) + } + } + workloadImage := strings.TrimSpace(ah.Spec.Substrate.WorkloadImage) - if workloadImage == "" { + if workloadImage == "" && clawBackend { workloadImage = strings.TrimSpace(p.Defaults.DefaultWorkloadImage) } if workloadImage == "" { - workloadImage = openclaw.NemoclawSandboxBaseImage + workloadImage = defaultImage } else { var err error workloadImage, err = pinImageRef(workloadImage) @@ -55,10 +89,6 @@ func (p *Lifecycle) buildActorTemplate(ctx context.Context, ah *v1alpha2.AgentHa return nil, err } } - startupScript, containerEnv, err := p.buildOpenClawActorStartup(ctx, ah) - if err != nil { - return nil, fmt.Errorf("build openclaw actor startup: %w", err) - } desired := &atev1alpha1.ActorTemplate{ ObjectMeta: metav1.ObjectMeta{ @@ -71,7 +101,7 @@ func (p *Lifecycle) buildActorTemplate(ctx context.Context, ah *v1alpha2.AgentHa Runsc: defaultRunscConfig(p.Defaults), Containers: []atev1alpha1.Container{ { - Name: defaultOpenClawContainer, + Name: containerName, Image: workloadImage, Command: []string{ "/bin/sh", diff --git a/go/core/pkg/sandboxbackend/substrate/lifecycle_openclaw.go b/go/core/pkg/sandboxbackend/substrate/lifecycle_openclaw.go index db75066fab..fde845cfc1 100644 --- a/go/core/pkg/sandboxbackend/substrate/lifecycle_openclaw.go +++ b/go/core/pkg/sandboxbackend/substrate/lifecycle_openclaw.go @@ -16,7 +16,15 @@ import ( corev1 "k8s.io/api/core/v1" ) -const defaultSubstrateOpenClawGatewayPort = 80 +// OpenClawGatewayPort is the loopback port the OpenClaw gateway listens on +// inside a substrate actor. It is a private implementation detail: the +// in-sandbox `openclaw acp` child connects to it over loopback, while the +// acp-shim owns the atenet ingress port (acpListenPort). kagent never reaches +// the gateway directly. +const OpenClawGatewayPort = 18789 + +// acpListenPort is the actor port atenet-router routes Host-based traffic to. +const acpListenPort = 80 //go:embed templates/openclaw_startup.sh.tmpl var openClawStartupScriptTmplContent string @@ -25,11 +33,11 @@ var openClawStartupScriptTmpl = template.Must(template.New("openclaw_startup").P type openClawStartupScriptData struct { OpenClawJSONBase64 string - GatewayPort int + ACPPort int } // buildOpenClawActorStartup returns the ateom workload startup script and container env for OpenClaw on Substrate. -// When spec.modelConfigRef is set, openclaw.json includes models/agents/channels like the OpenShell bootstrap path. +// When spec.modelConfigRef is set, openclaw.json includes models/agents/channels. func (p *Lifecycle) buildOpenClawActorStartup(ctx context.Context, ah *v1alpha2.AgentHarness) (script string, env []atev1alpha1.EnvVar, err error) { if ah == nil { return "", nil, fmt.Errorf("AgentHarness is required") @@ -42,7 +50,7 @@ func (p *Lifecycle) buildOpenClawActorStartup(ctx context.Context, ah *v1alpha2. if err != nil { return "", nil, fmt.Errorf("resolve gateway token: %w", err) } - gw := openclaw.SubstrateGatewayBootstrap(token, defaultSubstrateOpenClawGatewayPort, openClawControlUIBasePath(ah)) + gw := openclaw.SubstrateGatewayBootstrap(token, OpenClawGatewayPort) var jsonBytes []byte var containerEnv []corev1.EnvVar @@ -66,27 +74,65 @@ func (p *Lifecycle) buildOpenClawActorStartup(ctx context.Context, ah *v1alpha2. if err != nil { return "", nil, fmt.Errorf("build gateway-only openclaw json: %w", err) } - containerEnv = []corev1.EnvVar{{Name: "HOME", Value: "/root"}} + containerEnv = []corev1.EnvVar{{Name: "HOME", Value: openclaw.SubstrateActorHome}} } - script, err = openClawStartupScript(jsonBytes, gw.Port) + containerEnv = append(containerEnv, acpShimEnv(ah, gw.Port)...) + script, err = openClawStartupScript(jsonBytes) if err != nil { return "", nil, err } return script, actorTemplateEnvFromPodEnv(containerEnv), nil } -func openClawControlUIBasePath(ah *v1alpha2.AgentHarness) string { - if ah == nil { - return "" +// acpShimEnv returns the env vars the acp-shim and the image's +// openclaw-gateway-ensure/openclaw-acp-child scripts read. The shim reuses +// the harness gateway token as its bearer token; when the token comes from a +// Secret it stays a secretKeyRef (resolved by ate-api), never inlined. +func acpShimEnv(ah *v1alpha2.AgentHarness, gatewayPort int) []corev1.EnvVar { + env := []corev1.EnvVar{ + {Name: "OPENCLAW_GATEWAY_PORT", Value: fmt.Sprintf("%d", gatewayPort)}, + } + return append(env, acpShimTokenEnv(ah)...) +} + +// acpShimTokenEnv returns the ACP_SHIM_TOKEN env var derived from the +// harness gateway token (secretKeyRef stays a ref, resolved by ate-api). +// When the spec sets neither gatewayToken nor gatewayTokenSecretRef, it +// references the controller-managed Secret holding the auto-generated token. +func acpShimTokenEnv(ah *v1alpha2.AgentHarness) []corev1.EnvVar { + sub := ah.Spec.Substrate + if sub == nil { + return nil + } + switch { + case sub.GatewayTokenSecretRef != nil && strings.TrimSpace(sub.GatewayTokenSecretRef.Name) != "": + return []corev1.EnvVar{shimTokenSecretRefEnv(sub.GatewayTokenSecretRef.Name)} + case strings.TrimSpace(sub.GatewayToken) != "": + return []corev1.EnvVar{{Name: "ACP_SHIM_TOKEN", Value: strings.TrimSpace(sub.GatewayToken)}} + default: + return []corev1.EnvVar{shimTokenSecretRefEnv(ManagedGatewayTokenSecretName(ah))} + } +} + +// shimTokenSecretRefEnv builds an ACP_SHIM_TOKEN env var sourced from the +// "token" key of the named Secret. +func shimTokenSecretRefEnv(secretName string) corev1.EnvVar { + return corev1.EnvVar{ + Name: "ACP_SHIM_TOKEN", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{Name: secretName}, + Key: GatewayTokenSecretKey, + }, + }, } - return "/api/agentharnesses/" + ah.Namespace + "/" + ah.Name + "/gateway" } -func openClawStartupScript(jsonBytes []byte, gwPort int) (string, error) { +func openClawStartupScript(jsonBytes []byte) (string, error) { var buf bytes.Buffer if err := openClawStartupScriptTmpl.Execute(&buf, openClawStartupScriptData{ OpenClawJSONBase64: base64.StdEncoding.EncodeToString(jsonBytes), - GatewayPort: gwPort, + ACPPort: acpListenPort, }); err != nil { return "", fmt.Errorf("render openclaw startup script: %w", err) } diff --git a/go/core/pkg/sandboxbackend/substrate/lifecycle_openclaw_test.go b/go/core/pkg/sandboxbackend/substrate/lifecycle_openclaw_test.go index 6d8578f3c3..12131befc1 100644 --- a/go/core/pkg/sandboxbackend/substrate/lifecycle_openclaw_test.go +++ b/go/core/pkg/sandboxbackend/substrate/lifecycle_openclaw_test.go @@ -59,7 +59,10 @@ func TestBuildOpenClawActorStartup_WithModelConfig(t *testing.T) { script, env, err := p.buildOpenClawActorStartup(context.Background(), ah) require.NoError(t, err) require.Contains(t, script, "base64 -d") - require.Contains(t, script, "openclaw gateway run --port 80") + require.Contains(t, script, "openclaw-gateway-ensure") + require.Contains(t, script, "exec /usr/local/bin/acp-shim") + require.Contains(t, script, "--listen :80") + require.NotContains(t, script, "--passthrough") var foundKey bool for _, e := range env { @@ -75,6 +78,22 @@ func TestBuildOpenClawActorStartup_WithModelConfig(t *testing.T) { } require.True(t, foundKey, "expected OPENAI_API_KEY secretKeyRef in container env") + var foundShimToken, foundGatewayPort bool + for _, e := range env { + switch e.Name { + case "ACP_SHIM_TOKEN": + require.NotNil(t, e.Value) + require.Equal(t, "some-token", *e.Value) + foundShimToken = true + case "OPENCLAW_GATEWAY_PORT": + require.NotNil(t, e.Value) + require.Equal(t, "18789", *e.Value) + foundGatewayPort = true + } + } + require.True(t, foundShimToken, "expected ACP_SHIM_TOKEN in container env") + require.True(t, foundGatewayPort, "expected OPENCLAW_GATEWAY_PORT in container env") + // Decode embedded JSON from the base64 line in the startup script. var payload string for line := range strings.SplitSeq(script, "\n") { @@ -94,12 +113,12 @@ func TestBuildOpenClawActorStartup_WithModelConfig(t *testing.T) { require.NoError(t, json.Unmarshal(raw, &root)) gw := root["gateway"].(map[string]any) require.Equal(t, "lan", gw["bind"]) - require.Equal(t, float64(80), gw["port"]) + require.Equal(t, float64(18789), gw["port"]) auth := gw["auth"].(map[string]any) require.Equal(t, "token", auth["mode"]) require.Equal(t, "some-token", auth["token"]) - controlUI := gw["controlUi"].(map[string]any) - require.Equal(t, "/api/agentharnesses/kagent/peterj-claw/gateway", controlUI["basePath"]) + _, hasControlUI := gw["controlUi"] + require.False(t, hasControlUI, "controlUi should not be emitted") _, hasModels := root["models"] require.False(t, hasModels, "substrate bootstrap should omit models unless ModelConfig sets an explicit baseUrl") require.Contains(t, root, "agents") diff --git a/go/core/pkg/sandboxbackend/substrate/lifecycle_test.go b/go/core/pkg/sandboxbackend/substrate/lifecycle_test.go index b47ba1b825..1ca62a55e7 100644 --- a/go/core/pkg/sandboxbackend/substrate/lifecycle_test.go +++ b/go/core/pkg/sandboxbackend/substrate/lifecycle_test.go @@ -129,6 +129,7 @@ func TestEnsureActorTemplateDoesNotUpdateWhenDesiredStateMatches(t *testing.T) { UID: "00000000-0000-0000-0000-000000000001", }, Spec: v1alpha2.AgentHarnessSpec{ + Backend: v1alpha2.AgentHarnessBackendOpenClaw, Runtime: v1alpha2.AgentHarnessRuntimeSubstrate, Substrate: &v1alpha2.AgentHarnessSubstrateSpec{ GatewayToken: "test-token", diff --git a/go/core/pkg/sandboxbackend/substrate/openclaw.go b/go/core/pkg/sandboxbackend/substrate/openclaw.go index 2ccd9f0f0d..892c2e146c 100644 --- a/go/core/pkg/sandboxbackend/substrate/openclaw.go +++ b/go/core/pkg/sandboxbackend/substrate/openclaw.go @@ -23,7 +23,7 @@ const ( var dns1123Label = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`) -// ClawBackend implements AsyncBackend for OpenClaw/NemoClaw on Agent Substrate. +// ClawBackend implements AsyncBackend for OpenClaw on Agent Substrate. type ClawBackend struct { client *Client backend v1alpha2.AgentHarnessBackendType @@ -32,7 +32,7 @@ type ClawBackend struct { var _ sandboxbackend.AsyncBackend = (*ClawBackend)(nil) -// NewOpenClawBackend returns a substrate backend for openclaw/nemoclaw harness types. +// NewOpenClawBackend returns a substrate backend for openclaw harness types. func NewOpenClawBackend(client *Client, backend v1alpha2.AgentHarnessBackendType, recorder record.EventRecorder) *ClawBackend { return &ClawBackend{ client: client, @@ -163,7 +163,7 @@ func substrateConnectionEndpoint(namespace, name string, actor *ateapipb.Actor) func validateSubstrateSpec(ah *v1alpha2.AgentHarness) error { runtime := ah.Spec.Runtime if runtime == "" { - runtime = v1alpha2.AgentHarnessRuntimeOpenshell + runtime = v1alpha2.AgentHarnessRuntimeSubstrate } if runtime != v1alpha2.AgentHarnessRuntimeSubstrate { return fmt.Errorf("substrate backend called for runtime %q", runtime) diff --git a/go/core/pkg/sandboxbackend/substrate/templates/openclaw_startup.sh.tmpl b/go/core/pkg/sandboxbackend/substrate/templates/openclaw_startup.sh.tmpl index a082584ddf..4d9ae2b53a 100644 --- a/go/core/pkg/sandboxbackend/substrate/templates/openclaw_startup.sh.tmpl +++ b/go/core/pkg/sandboxbackend/substrate/templates/openclaw_startup.sh.tmpl @@ -1,4 +1,20 @@ set -e mkdir -p "${HOME}/.openclaw" echo '{{.OpenClawJSONBase64}}' | base64 -d > "${HOME}/.openclaw/openclaw.json" -openclaw gateway run --port {{.GatewayPort}} --allow-unconfigured +# Pre-warm the gateway so it lands in the golden snapshot (best effort: a +# fresh per-connection child re-ensures it after checkpoint/restore, where +# the snapshotted Node process survives but its listener does not). The +# gateway stays a private loopback detail: kagent only ever reaches the actor +# through the shim's /acp WebSocket, never the gateway directly. +/usr/local/bin/openclaw-gateway-ensure || true +# The shim owns the atenet ingress port: /acp bridges WebSocket frames to a +# per-connection `openclaw acp` child, which connects to the loopback gateway. +# +# The child must NOT pass --url: `openclaw acp` only applies the gateway +# token from gateway.remote.token when it also resolves the URL from +# gateway.remote.url (both are written to openclaw.json above). An explicit +# --url bypasses the config token and fails against a token-auth gateway. +exec /usr/local/bin/acp-shim \ + --listen :{{.ACPPort}} \ + --child-policy per-connection \ + -- /bin/sh -c '/usr/local/bin/openclaw-gateway-ensure && exec openclaw acp' diff --git a/go/go.mod b/go/go.mod index 6b689ba700..b56ea4969f 100644 --- a/go/go.mod +++ b/go/go.mod @@ -54,7 +54,6 @@ require ( k8s.io/api v0.36.2 k8s.io/apimachinery v0.36.2 k8s.io/client-go v0.36.2 - sigs.k8s.io/agent-sandbox v0.4.6 sigs.k8s.io/controller-runtime v0.24.1 sigs.k8s.io/yaml v1.6.0 trpc.group/trpc-go/trpc-a2a-go v0.2.5 @@ -79,7 +78,6 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0 go.opentelemetry.io/otel/sdk/log v0.20.0 - golang.org/x/crypto v0.53.0 google.golang.org/grpc v1.81.1 k8s.io/apiextensions-apiserver v0.36.2 ) @@ -414,6 +412,7 @@ require ( go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect go.yaml.in/yaml/v4 v4.0.0-rc.2 // indirect + golang.org/x/crypto v0.53.0 // indirect golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect golang.org/x/exp/typeparams v0.0.0-20260209203927-2842357ff358 // indirect golang.org/x/mod v0.36.0 // indirect diff --git a/go/go.sum b/go/go.sum index 4ef8f4beca..c833fea764 100644 --- a/go/go.sum +++ b/go/go.sum @@ -1086,8 +1086,6 @@ rsc.io/omap v1.2.0 h1:c1M8jchnHbzmJALzGLclfH3xDWXrPxSUHXzH5C+8Kdw= rsc.io/omap v1.2.0/go.mod h1:C8pkI0AWexHopQtZX+qiUeJGzvc8HkdgnsWK4/mAa00= rsc.io/ordered v1.1.1 h1:1kZM6RkTmceJgsFH/8DLQvkCVEYomVDJfBRLT595Uak= rsc.io/ordered v1.1.1/go.mod h1:evAi8739bWVBRG9aaufsjVc202+6okf8u2QeVL84BCM= -sigs.k8s.io/agent-sandbox v0.4.6 h1:dUUvQ+rlv4kT3CB9p2MgnPcNSMAQg6eTBrWyCiYgxYs= -sigs.k8s.io/agent-sandbox v0.4.6/go.mod h1:rrBXoA+nKZO2MIs3pTG+qOuKadX5gzMvO/nrlwC9W1w= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 h1:hSfpvjjTQXQY2Fol2CS0QHMNs/WI1MOSGzCm1KhM5ec= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/controller-runtime v0.24.1 h1:miPEwrmirImAvgME1L9qebGHrOnGJoVmVdtOU9fRfo4= diff --git a/helm/kagent-crds/templates/kagent.dev_agentharnesses.yaml b/helm/kagent-crds/templates/kagent.dev_agentharnesses.yaml index 9c4c1ee2a6..39371dfebc 100644 --- a/helm/kagent-crds/templates/kagent.dev_agentharnesses.yaml +++ b/helm/kagent-crds/templates/kagent.dev_agentharnesses.yaml @@ -38,8 +38,8 @@ spec: schema: openAPIV3Schema: description: |- - AgentHarness is a generic remote execution environment provisioned by a backend - (e.g. OpenShell) and addressable by exec/SSH. + AgentHarness is a generic remote execution environment provisioned by a + backend (OpenClaw or Hermes) running on Agent Substrate. properties: apiVersion: description: |- @@ -71,7 +71,6 @@ spec: description: Backend selects the control plane to use. Required. enum: - openclaw - - nemoclaw - hermes type: string channels: @@ -203,8 +202,8 @@ spec: exclusive rule: '!(size(self.allowedUserIDs) > 0 && has(self.allowedUserIDsFrom))' openclaw: - description: OpenClaw configures OpenClaw/NemoClaw-specific - Slack routing. + description: OpenClaw configures OpenClaw-specific Slack + routing. properties: allowlistChannels: description: AllowlistChannels is required when channelAccess @@ -492,8 +491,8 @@ spec: image: description: |- Image is the container image to run in the harness VM, if the backend - supports per-resource images. Backends openclaw and nemoclaw pin the image - to the NemoClaw sandbox base when this field is empty; backend hermes pins + supports per-resource images. Backend openclaw pins the image + to the OpenClaw sandbox base when this field is empty; backend hermes pins to the Hermes sandbox base image when empty. type: string modelConfigRef: @@ -515,11 +514,10 @@ spec: type: array type: object runtime: - default: openshell + default: substrate description: Runtime selects the harness provisioning stack. Defaults - to openshell when unset. + to substrate when unset. enum: - - openshell - substrate type: string substrate: @@ -528,6 +526,9 @@ spec: gatewayToken: description: |- GatewayToken is the OpenClaw gateway Bearer token for this harness. + Optional: when neither gatewayToken nor gatewayTokenSecretRef is set, the + controller generates a random token and stores it in a Secret named + "-gateway-token" (key "token"), which can be retrieved later. Prefer gatewayTokenSecretRef for production secrets. minLength: 1 type: string @@ -574,15 +575,13 @@ spec: - name type: object workloadImage: - description: WorkloadImage overrides the default nemoclaw/openclaw - sandbox image in the ActorTemplate. + description: WorkloadImage overrides the default openclaw sandbox + image in the ActorTemplate. type: string type: object x-kubernetes-validations: - - message: Exactly one of gatewayToken or gatewayTokenSecretRef must - be specified - rule: (has(self.gatewayToken) && !has(self.gatewayTokenSecretRef)) - || (!has(self.gatewayToken) && has(self.gatewayTokenSecretRef)) + - message: Specify at most one of gatewayToken or gatewayTokenSecretRef + rule: '!(has(self.gatewayToken) && has(self.gatewayTokenSecretRef))' required: - backend type: object @@ -590,8 +589,8 @@ spec: - message: slack backend-specific settings must match spec.backend rule: '!has(self.channels) || self.channels.all(c, c.type != ''slack'' || (has(c.slack) && ((self.backend == ''hermes'' && has(c.slack.hermes) - && !has(c.slack.openclaw)) || ((self.backend == ''openclaw'' || self.backend - == ''nemoclaw'') && has(c.slack.openclaw) && !has(c.slack.hermes)))))' + && !has(c.slack.openclaw)) || (self.backend == ''openclaw'' && has(c.slack.openclaw) + && !has(c.slack.hermes)))))' - message: spec.substrate may only be set when runtime is substrate rule: '!has(self.substrate) || self.runtime == ''substrate''' - message: spec.substrate is required when runtime is substrate @@ -610,7 +609,6 @@ spec: environment. Additional backends may be added in the future. enum: - openclaw - - nemoclaw - hermes type: string id: diff --git a/helm/kagent-crds/templates/kagent.dev_sandboxagents.yaml b/helm/kagent-crds/templates/kagent.dev_sandboxagents.yaml index 3f8f594b50..8fce16ac0b 100644 --- a/helm/kagent-crds/templates/kagent.dev_sandboxagents.yaml +++ b/helm/kagent-crds/templates/kagent.dev_sandboxagents.yaml @@ -27,7 +27,7 @@ spec: schema: openAPIV3Schema: description: SandboxAgent declares an agent that runs in an isolated sandbox - (agent-sandbox or Agent Substrate). + on Agent Substrate. properties: apiVersion: description: |- @@ -10951,14 +10951,6 @@ spec: rule: '!has(self.systemMessage) || !has(self.systemMessageFrom)' description: type: string - platform: - default: agent-sandbox - description: Platform selects the sandbox control plane. Defaults - to agent-sandbox. - enum: - - agent-sandbox - - substrate - type: string sandbox: description: |- Sandbox configures sandboxed execution behavior shared across runtimes. @@ -11296,8 +11288,7 @@ spec: type: array type: object substrate: - description: Substrate is optional substrate-specific settings when - platform is substrate. + description: Substrate is optional Agent Substrate-specific settings. properties: snapshotsConfig: description: |- @@ -11335,12 +11326,10 @@ spec: type: string type: object x-kubernetes-validations: - - message: spec.skills is not supported when spec.platform is substrate - rule: '!has(self.skills) || self.platform != ''substrate''' - - message: spec.substrate may only be set when spec.platform is substrate - rule: '!has(self.substrate) || self.platform == ''substrate''' - - message: BYO agents are not supported when spec.platform is substrate - rule: '!has(self.type) || self.type != ''BYO'' || self.platform != ''substrate''' + - message: spec.skills is not supported for sandbox agents + rule: '!has(self.skills)' + - message: BYO agents are not supported for sandbox agents + rule: '!has(self.type) || self.type != ''BYO''' - message: type must be specified rule: has(self.type) - message: type must be either Declarative or BYO diff --git a/helm/kagent/values.yaml b/helm/kagent/values.yaml index 20c79d95c6..9fc4a5519a 100644 --- a/helm/kagent/values.yaml +++ b/helm/kagent/values.yaml @@ -250,13 +250,8 @@ controller: type: ClusterIP port: 8443 - # Extra controller env (mapped to flags via SUBSTRATE_* / OPENSHELL_* env names). - # OpenShell AgentHarness: set OPENSHELL_GATEWAY_URL (or leave defaults below). - env: - # - name: OPENSHELL_GATEWAY_URL - # value: openshell.openshell.svc.cluster.local:8080 - # - name: OPENSHELL_INSECURE - # value: "true" + # Extra controller env (mapped to flags via SUBSTRATE_* env names). + env: [] # Agent Substrate (OpenClaw harness runtime=substrate). Requires ate-system installed. # kagent generates per-harness ActorTemplates and references an existing WorkerPool. diff --git a/substrate-values.yaml b/substrate-values.yaml new file mode 100644 index 0000000000..3ca90cece7 --- /dev/null +++ b/substrate-values.yaml @@ -0,0 +1,3 @@ +atelet: + extraArgs: + - --localhost-registry-replacement=kind-registry:5000 diff --git a/ui/src/app/actions/agentHarnessSession.ts b/ui/src/app/actions/agentHarnessSession.ts new file mode 100644 index 0000000000..49ce793be6 --- /dev/null +++ b/ui/src/app/actions/agentHarnessSession.ts @@ -0,0 +1,81 @@ +"use server"; + +import { BaseResponse } from "@/types"; +import { fetchApi, createErrorResponse } from "./utils"; + +export interface AgentHarnessSessionActor { + namespace: string; + name: string; + sessionId: string; + actorId?: string; + state?: AgentHarnessSessionState; +} + +/** Lifecycle state of a chat session's substrate actor. */ +export type AgentHarnessSessionState = "running" | "suspended" | "missing"; + +/** + * Provisions (creates + resumes) the substrate actor for a single AgentHarness + * chat session. Called when a new chat is started so the actor is warm before + * the /acp WebSocket connects. + */ +export async function ensureAgentHarnessSession( + namespace: string, + name: string, + sessionId: string +): Promise> { + try { + const response = await fetchApi>( + `/agentharnesses/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}/sessions/${encodeURIComponent( + sessionId + )}/ensure`, + { method: "POST" } + ); + return { message: "Session actor ready", data: response.data }; + } catch (error) { + return createErrorResponse(error, "Error provisioning session actor"); + } +} + +/** + * Checkpoints and frees the substrate actor for a single AgentHarness chat + * session. The actor is resumed automatically on the next /acp connection. + */ +export async function suspendAgentHarnessSession( + namespace: string, + name: string, + sessionId: string +): Promise> { + try { + const response = await fetchApi>( + `/agentharnesses/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}/sessions/${encodeURIComponent( + sessionId + )}/suspend`, + { method: "POST" } + ); + return { message: "Session actor suspended", data: response.data }; + } catch (error) { + return createErrorResponse(error, "Error suspending session actor"); + } +} + +/** + * Returns the lifecycle state ("running", "suspended", "missing") of a single + * AgentHarness chat session's substrate actor. + */ +export async function getAgentHarnessSessionStatus( + namespace: string, + name: string, + sessionId: string +): Promise> { + try { + const response = await fetchApi>( + `/agentharnesses/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}/sessions/${encodeURIComponent( + sessionId + )}/status` + ); + return { message: "Session actor state", data: response.data }; + } catch (error) { + return createErrorResponse(error, "Error reading session actor state"); + } +} diff --git a/ui/src/app/actions/agents.ts b/ui/src/app/actions/agents.ts index 2ba29c4101..0c23e19cdd 100644 --- a/ui/src/app/actions/agents.ts +++ b/ui/src/app/actions/agents.ts @@ -19,10 +19,10 @@ import { isMcpTool } from "@/lib/toolUtils"; import { k8sRefUtils } from "@/lib/k8sUtils"; import { formRowsToGitRepos, type GitSkillFormRow } from "@/lib/agentSkillsForm"; import { buildAgentHarnessCRDraft } from "@/lib/agentHarnessForm"; -import { buildSandboxPlatformFromForm, buildSandboxSubstrateFromForm } from "@/lib/sandboxAgentForm"; +import { buildSandboxSubstrateFromForm } from "@/lib/sandboxAgentForm"; function declarativeRuntimeFromForm(agentFormData: AgentFormData): DeclarativeRuntime { - if (agentFormData.sandboxPlatform === "substrate") { + if (agentFormData.runInSandbox) { return "go"; } return agentFormData.declarativeRuntime === "go" ? "go" : "python"; @@ -236,7 +236,6 @@ function fromAgentFormDataToAgent(agentFormData: AgentFormData): Agent { function fromAgentFormDataToSandboxAgent(agentFormData: AgentFormData): SandboxAgent { const substrate = buildSandboxSubstrateFromForm(agentFormData); - const platform = buildSandboxPlatformFromForm(agentFormData); const kind = agentFormData.type || "Declarative"; if (kind === "BYO") { @@ -251,7 +250,6 @@ function fromAgentFormDataToSandboxAgent(agentFormData: AgentFormData): SandboxA type: "BYO", description: agentFormData.description, // BYO agents are not supported on Agent Substrate. - platform: undefined, substrate: undefined, byo: { deployment: { @@ -394,9 +392,6 @@ function fromAgentFormDataToSandboxAgent(agentFormData: AgentFormData): SandboxA spec.skills = skills; } - if (platform) { - spec.platform = platform; - } if (substrate) { spec.substrate = substrate; } diff --git a/ui/src/app/actions/sessions.ts b/ui/src/app/actions/sessions.ts index 3f987076f8..bc3c732989 100644 --- a/ui/src/app/actions/sessions.ts +++ b/ui/src/app/actions/sessions.ts @@ -76,6 +76,27 @@ export async function createSession(session: CreateSessionRequest): Promise> { + try { + const response = await fetchApi>(`/sessions/${sessionId}`, { + method: "PATCH", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ name }), + }); + return { message: "Session renamed successfully", data: response.data }; + } catch (error) { + return createErrorResponse(error, "Error renaming session"); + } +} + /** * Gets all messages for a session * @param sessionId The session ID diff --git a/ui/src/app/agents/[namespace]/[name]/chat/[chatId]/page.tsx b/ui/src/app/agents/[namespace]/[name]/chat/[chatId]/page.tsx index 42ba1f5a18..1e3fa7f18d 100644 --- a/ui/src/app/agents/[namespace]/[name]/chat/[chatId]/page.tsx +++ b/ui/src/app/agents/[namespace]/[name]/chat/[chatId]/page.tsx @@ -1,13 +1,62 @@ "use client"; -import { use } from "react"; +import { use, useEffect, useState } from "react"; +import { useSearchParams } from "next/navigation"; import ChatInterface from "@/components/chat/ChatInterface"; +import AcpHarnessChat from "@/components/chat/AcpHarnessChat"; +import { getAgentWithResolvedKind } from "@/app/actions/agents"; +import { Loader2 } from "lucide-react"; export default function ChatPageView({ params }: { params: Promise<{ name: string; namespace: string; chatId: string }> }) { const { name, namespace, chatId } = use(params); + const searchParams = useSearchParams(); + // A brand-new chat (just created via "New Chat") arrives with ?new=1 and stays + // idle until the user sends a message; any other navigation (sidebar click, + // reload) auto-connects and resumes the actor's prior transcript. + const isNew = searchParams.get("new") === "1"; + const [gate, setGate] = useState<"loading" | "ready">("loading"); + const [harnessAcpPath, setHarnessAcpPath] = useState(null); - return ; + useEffect(() => { + let cancelled = false; + (async () => { + try { + const agentRes = await getAgentWithResolvedKind(name, namespace); + if (cancelled) return; + const substrateHarness = agentRes.data?.substrateAgentHarness; + if (substrateHarness) { + const acpBase = + substrateHarness.acpPath || + `/api/agentharnesses/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}/acp`; + setHarnessAcpPath(`${acpBase}/${encodeURIComponent(chatId)}`); + } + } catch { + /* fall through to the standard chat interface */ + } finally { + if (!cancelled) setGate("ready"); + } + })(); + return () => { + cancelled = true; + }; + }, [name, namespace, chatId]); + + if (gate === "loading") { + return ( +
+ + Preparing chat… +
+ ); + } + + if (harnessAcpPath) { + return ; + } + + return ; } diff --git a/ui/src/app/agents/[namespace]/[name]/chat/page.tsx b/ui/src/app/agents/[namespace]/[name]/chat/page.tsx index a32c9d0982..97858938fd 100644 --- a/ui/src/app/agents/[namespace]/[name]/chat/page.tsx +++ b/ui/src/app/agents/[namespace]/[name]/chat/page.tsx @@ -1,12 +1,14 @@ "use client"; import { use, useEffect, useState } from "react"; -import { useRouter } from "next/navigation"; +import { useRouter, useSearchParams } from "next/navigation"; import ChatInterface from "@/components/chat/ChatInterface"; +import AcpHarnessChat from "@/components/chat/AcpHarnessChat"; import { getAgentWithResolvedKind } from "@/app/actions/agents"; -import { getSessionsForAgent, createSession } from "@/app/actions/sessions"; -import { isSingleSessionSandboxAgent, isSubstrateSandboxAgent } from "@/lib/sandboxAgentForm"; -import { Loader2 } from "lucide-react"; +import { createSession } from "@/app/actions/sessions"; +import { isSubstrateSandboxAgent } from "@/lib/sandboxAgentForm"; +import { Button } from "@/components/ui/button"; +import { Loader2, PlusCircle } from "lucide-react"; import type { Session } from "@/types"; function notifySidebarSession(agentRef: string, session: Session) { @@ -21,7 +23,14 @@ function notifySidebarSession(agentRef: string, session: Session) { export default function ChatAgentPage({ params }: { params: Promise<{ name: string; namespace: string }> }) { const { name, namespace } = use(params); const router = useRouter(); + const searchParams = useSearchParams(); + const apcSessionId = searchParams.get("sessionId") || undefined; const [gate, setGate] = useState<"loading" | "ready">("loading"); + const [harnessSession, setHarnessSession] = useState<{ acpPath: string; sessionId?: string } | null>(null); + // Harness landing: user is on the bare /chat page with no session selected. + // We don't create a session or start an actor here; the user picks an existing + // chat from the sidebar or clicks "New Chat" (which creates + opens one). + const [harnessLanding, setHarnessLanding] = useState(false); useEffect(() => { let cancelled = false; @@ -33,6 +42,27 @@ export default function ChatAgentPage({ params }: { params: Promise<{ name: stri setGate("ready"); return; } + // Substrate AgentHarness: chat over ACP through the controller's + // same-origin WebSocket proxy instead of the A2A session flow. Each chat + // session maps to its own substrate actor, keyed by the DB session id. + const substrateHarness = agentRes.data.substrateAgentHarness; + if (substrateHarness) { + const acpBase = + substrateHarness.acpPath || + `/api/agentharnesses/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}/acp`; + // Existing chat opened via ?sessionId= (legacy ACP picker links). + if (apcSessionId) { + setHarnessSession({ acpPath: `${acpBase}/${encodeURIComponent(apcSessionId)}`, sessionId: apcSessionId }); + setGate("ready"); + return; + } + // Bare /chat landing: don't create a session, don't spin up an actor, + // and don't show a spinner. Let the user pick an existing chat from + // the sidebar or click "New Chat" (which creates + opens one). + setHarnessLanding(true); + setGate("ready"); + return; + } if (agentRes.data.workloadMode !== "sandbox") { setGate("ready"); return; @@ -52,33 +82,6 @@ export default function ChatAgentPage({ params }: { params: Promise<{ name: stri setGate("ready"); return; } - if (!isSingleSessionSandboxAgent(agentRes.data)) { - setGate("ready"); - return; - } - const sessRes = await getSessionsForAgent(namespace, name); - if (cancelled) return; - if (sessRes.error || !sessRes.data) { - setGate("ready"); - return; - } - const list = sessRes.data; - const agentRef = `${namespace}/${name}`; - if (list.length >= 1) { - notifySidebarSession(agentRef, list[0]); - router.replace(`/agents/${namespace}/${name}/chat/${list[0].id}`); - return; - } - const created = await createSession({ - agent_ref: agentRef, - name: "Chat", - }); - if (cancelled) return; - if (!created.error && created.data) { - notifySidebarSession(agentRef, created.data); - router.replace(`/agents/${namespace}/${name}/chat/${created.data.id}`); - return; - } } catch { /* fall through to chat */ } @@ -87,7 +90,18 @@ export default function ChatAgentPage({ params }: { params: Promise<{ name: stri return () => { cancelled = true; }; - }, [name, namespace, router]); + }, [name, namespace, router, apcSessionId]); + + const startNewHarnessChat = async () => { + const created = await createSession({ agent_ref: `${namespace}/${name}` }); + if (created.error || !created.data) return; + // Navigate straight to the new chat. We deliberately don't dispatch + // new-session-created here: that synchronous parent re-render can drop the + // first router transition. The destination's pathname-keyed refreshSessions + // lists the new session in the sidebar. Stay idle until the first message; + // ?new=1 signals that. + window.location.href = `/agents/${namespace}/${name}/chat/${created.data.id}?new=1`; + }; if (gate === "loading") { return ( @@ -105,5 +119,34 @@ export default function ChatAgentPage({ params }: { params: Promise<{ name: stri ); } + if (harnessSession) { + return ( + + ); + } + + if (harnessLanding) { + return ( +
+
+

Start chatting

+

+ Pick a conversation from the sidebar, or start a new chat to begin. +

+ +
+
+ ); + } + return ; } diff --git a/ui/src/app/agents/new-harness/page.tsx b/ui/src/app/agents/new-harness/page.tsx index d4dcaa8808..292c8f181c 100644 --- a/ui/src/app/agents/new-harness/page.tsx +++ b/ui/src/app/agents/new-harness/page.tsx @@ -7,6 +7,7 @@ import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { defaultAgentHarnessFormSlice, + isSubstrateOnlyHarnessBackend, type AgentHarnessFormSlice, } from "@/lib/agentHarnessForm"; import type { AgentHarnessCrBackend, ModelConfig } from "@/types"; @@ -26,7 +27,7 @@ import { focusFirstFormError } from "@/components/agent-form/focusFirstFormError import { PageHeader } from "@/components/layout/PageHeader"; const HARNESS_OPTIONS = [ - { value: "nemoclaw-openclaw", label: "NemoClaw (OpenClaw)", backend: "openclaw" as const }, + { value: "openclaw", label: "OpenClaw", backend: "openclaw" as const }, { value: "hermes", label: "Hermes", backend: "hermes" as const }, ] as const; @@ -263,10 +264,14 @@ function AgentHarnessPageContent() { onValueChange={(val) => { const harnessType = val as FormState["harnessType"]; setState((prev) => { + const backend = harnessBackendForType(harnessType); const nextHarnessForm = { ...prev.harnessForm, - backend: harnessBackendForType(harnessType), + backend, }; + if (isSubstrateOnlyHarnessBackend(backend)) { + nextHarnessForm.runtime = "substrate"; + } if (harnessType === "hermes" && !prev.harnessForm.image.trim()) { nextHarnessForm.image = HERMES_DEFAULT_IMAGE; } diff --git a/ui/src/app/agents/new/page.tsx b/ui/src/app/agents/new/page.tsx index 6146277fb7..663c206e65 100644 --- a/ui/src/app/agents/new/page.tsx +++ b/ui/src/app/agents/new/page.tsx @@ -11,10 +11,7 @@ import { type AgentFormWorkloadKind, } from "@/lib/agentFormLayout"; import { - defaultDeclarativeRuntimeForSandboxPlatform, - defaultSandboxPlatform, sandboxFieldsFromApiSpec, - skillsSupportedForSandboxPlatform, substrateSupportedForAgentType, } from "@/lib/sandboxAgentForm"; import { ModelConfig, ContextConfig, type DeclarativeRuntime } from "@/types"; @@ -110,7 +107,6 @@ function AgentPageContent({ isEditMode, agentName, agentNamespace }: AgentPageCo isSubmitting: boolean; isLoading: boolean; errors: AgentFormValidationErrors; - sandboxPlatform: "agent-sandbox" | "substrate"; substrateWorkerPoolRefName: string; substrateSnapshotsLocation: string; } @@ -146,42 +142,18 @@ function AgentPageContent({ isEditMode, agentName, agentNamespace }: AgentPageCo isSubmitting: false, isLoading: isEditMode, errors: {}, - sandboxPlatform: "agent-sandbox", substrateWorkerPoolRefName: "", substrateSnapshotsLocation: "", }); const substrateEnabled = useSubstrateEnabled(); - // When substrate becomes available, prefer it for sandbox agents still on the default platform. - useEffect(() => { - if (isEditMode || !substrateEnabled) { - return; - } - setState((prev) => { - if ( - !prev.runInSandbox || - prev.sandboxPlatform === "substrate" || - !substrateSupportedForAgentType(prev.agentType) - ) { - return prev; - } - return { - ...prev, - sandboxPlatform: defaultSandboxPlatform(true), - declarativeRuntime: defaultDeclarativeRuntimeForSandboxPlatform("substrate"), - }; - }); - }, [isEditMode, substrateEnabled]); - const useDeclarativeAgentFields = formUsesDeclarativeSections(state.agentType); - const substrateSandboxAgent = state.runInSandbox && state.sandboxPlatform === "substrate"; + const substrateSandboxAgent = state.runInSandbox; const showDeclarativeRuntimeField = useDeclarativeAgentFields && !substrateSandboxAgent; const showByoFields = formUsesByoSections(state.agentType); const showModelAndBehaviorSection = useDeclarativeAgentFields; - const skillsEnabled = - useDeclarativeAgentFields && - skillsSupportedForSandboxPlatform(state.runInSandbox, state.sandboxPlatform); + const skillsEnabled = useDeclarativeAgentFields && !state.runInSandbox; const disabled = state.isSubmitting || state.isLoading; useEffect(() => { @@ -251,11 +223,9 @@ function AgentPageContent({ isEditMode, agentName, agentNamespace }: AgentPageCo }; const sandboxFields = agentResponse.workloadMode === "sandbox" - ? sandboxFieldsFromApiSpec(agent.spec?.platform, agent.spec?.substrate) + ? sandboxFieldsFromApiSpec(agent.spec?.substrate) : {}; - const isSubstrateSandbox = - agentResponse.workloadMode === "sandbox" && - agent.spec?.platform === "substrate"; + const isSubstrateSandbox = agentResponse.workloadMode === "sandbox"; const useDeclarativeForm = agent.spec.type === "Declarative"; if (useDeclarativeForm) { const decl = agent.spec?.declarative; @@ -381,7 +351,6 @@ function AgentPageContent({ isEditMode, agentName, agentNamespace }: AgentPageCo ...(useDeclarativeAgentFields ? { declarativeRuntime: state.declarativeRuntime } : {}), ...(state.runInSandbox ? { - sandboxPlatform: state.sandboxPlatform, substrateWorkerPoolRefName: state.substrateWorkerPoolRefName, substrateSnapshotsLocation: state.substrateSnapshotsLocation, } @@ -548,7 +517,6 @@ function AgentPageContent({ isEditMode, agentName, agentNamespace }: AgentPageCo serviceAccountName: state.serviceAccountName.trim() || undefined, ...(state.runInSandbox ? { - sandboxPlatform: state.sandboxPlatform, substrateWorkerPoolRefName: state.substrateWorkerPoolRefName, substrateSnapshotsLocation: state.substrateSnapshotsLocation, } @@ -681,9 +649,9 @@ function AgentPageContent({ isEditMode, agentName, agentNamespace }: AgentPageCo setState((prev) => ({ ...prev, agentType: next, - // BYO agents are not supported on Agent Substrate. - ...(!substrateSupportedForAgentType(next) && prev.sandboxPlatform === "substrate" - ? { sandboxPlatform: "agent-sandbox" as const } + // BYO agents are not supported in a sandbox (Agent Substrate). + ...(!substrateSupportedForAgentType(next) && prev.runInSandbox + ? { runInSandbox: false } : {}), errors: { ...prev.errors, type: undefined }, })); @@ -711,16 +679,19 @@ function AgentPageContent({ isEditMode, agentName, agentNamespace }: AgentPageCo setState((prev) => ({ ...prev, runInSandbox: !!checked, + // Sandbox agents run on Agent Substrate (Go runtime, no skills). ...(checked ? { - sandboxPlatform: defaultSandboxPlatform( - substrateEnabled && substrateSupportedForAgentType(prev.agentType) - ), + declarativeRuntime: "go", + skillRefs: [""], + skillGitRepos: [newEmptyGitSkillRow()], + skillsGitAuthSecretName: "", + errors: { ...prev.errors, skills: undefined }, } : {}), })) } - disabled={disabled || isEditMode} + disabled={disabled || isEditMode || !substrateSupportedForAgentType(state.agentType)} />
@@ -739,77 +710,44 @@ function AgentPageContent({ isEditMode, agentName, agentNamespace }: AgentPageCo {state.runInSandbox && substrateEnabled && substrateSupportedForAgentType(state.agentType) && ( - Sandbox platform + Agent Substrate settings Agent Substrate runs declarative agents as ate.dev actors using the Go ADK runtime. Skills are not supported on substrate yet. A new substrate actor is started for each chat session. - - {state.sandboxPlatform === "substrate" && ( -
-
- - - setState((prev) => ({ ...prev, substrateWorkerPoolRefName: e.target.value })) - } - placeholder="default pool name in agent namespace" - disabled={disabled} - className="mt-1" - /> -
-
- - - setState((prev) => ({ ...prev, substrateSnapshotsLocation: e.target.value })) - } - placeholder="gs://ate-snapshots/…" - disabled={disabled} - className="mt-1" - /> -
+
+
+ + + setState((prev) => ({ ...prev, substrateWorkerPoolRefName: e.target.value })) + } + placeholder="default pool name in agent namespace" + disabled={disabled} + className="mt-1" + />
- )} +
+ + + setState((prev) => ({ ...prev, substrateSnapshotsLocation: e.target.value })) + } + placeholder="gs://ate-snapshots/…" + disabled={disabled} + className="mt-1" + /> +
+
)} diff --git a/ui/src/app/openshell/OpenshellTerminalPage.tsx b/ui/src/app/openshell/OpenshellTerminalPage.tsx deleted file mode 100644 index e937186f38..0000000000 --- a/ui/src/app/openshell/OpenshellTerminalPage.tsx +++ /dev/null @@ -1,326 +0,0 @@ -"use client"; - -import { Button } from "@/components/ui/button"; -import { Checkbox } from "@/components/ui/checkbox"; -import { Label } from "@/components/ui/label"; -import { FitAddon } from "@xterm/addon-fit"; -import { Terminal } from "@xterm/xterm"; -import "@xterm/xterm/css/xterm.css"; -import { - defaultHarnessSSHLaunchCommand, - isAgentHarnessBackend, - type AgentHarnessBackend, -} from "@/lib/agentHarness"; -import { useSearchParams } from "next/navigation"; -import { useCallback, useEffect, useRef, useState } from "react"; - -function terminalApiBase(): string { - const envOnly = process.env.NEXT_PUBLIC_SANDBOX_SSH_HTTP_BASE?.trim(); - if (envOnly) { - return envOnly.replace(/\/+$/, ""); - } - const backend = process.env.NEXT_PUBLIC_BACKEND_URL?.trim() ?? ""; - if (backend.startsWith("/")) { - return new URL(backend, window.location.origin).href.replace(/\/+$/, ""); - } - if ( - (backend.startsWith("http://") || backend.startsWith("https://")) && - !backend.includes(".svc.cluster.local") - ) { - return backend.replace(/\/+$/, ""); - } - return new URL("/api", window.location.origin).href.replace(/\/+$/, ""); -} - -function sandboxSshWebSocketURL(apiBase: string): string { - const u = new URL(apiBase); - u.protocol = u.protocol === "https:" ? "wss:" : "ws:"; - const basePath = u.pathname.replace(/\/?$/, ""); - u.pathname = `${basePath}/sandbox/ssh`; - return u.toString(); -} - -export function OpenshellTerminalPage() { - const searchParams = useSearchParams(); - - const gatewaySandboxName = searchParams.get("sandbox")?.trim() ?? ""; - const harnessBackendParam = searchParams.get("harnessBackend")?.trim() ?? ""; - const harnessBackend: AgentHarnessBackend | undefined = isAgentHarnessBackend(harnessBackendParam) - ? harnessBackendParam - : undefined; - const clawHarnessSession = searchParams.get("clawHarness") === "1"; - const harnessTerminalSession = clawHarnessSession || harnessBackend === "hermes"; - const namespace = searchParams.get("ns")?.trim() ?? ""; - const crName = searchParams.get("name")?.trim() ?? ""; - const autoConnect = Boolean(gatewaySandboxName); - const modelConfigRef = searchParams.get("modelConfigRef")?.trim() ?? ""; - const [plainShellOnly, setPlainShellOnly] = useState(() => searchParams.get("plainShell") === "1"); - /** Plain-shell mode the active SSH session was opened with (null when disconnected). */ - const [appliedPlainShell, setAppliedPlainShell] = useState(null); - - const displayTitle = - namespace && crName ? `${namespace}/${crName}` : gatewaySandboxName || "OpenShell"; - - const [termError, setTermError] = useState(null); - const [sessionActive, setSessionActive] = useState(false); - const [connecting, setConnecting] = useState(() => Boolean(autoConnect)); - - const termHostRef = useRef(null); - const termRef = useRef(null); - const wsRef = useRef(null); - - useEffect(() => { - const el = termHostRef.current; - if (!el) return; - - const term = new Terminal({ - cursorBlink: true, - fontSize: 13, - theme: { - background: "#0c0c0c", - }, - }); - const fit = new FitAddon(); - term.loadAddon(fit); - term.open(el); - fit.fit(); - termRef.current = term; - - const ro = new ResizeObserver(() => { - fit.fit(); - const ws = wsRef.current; - if (ws?.readyState === WebSocket.OPEN) { - ws.send( - JSON.stringify({ - type: "resize", - cols: term.cols, - rows: term.rows, - }), - ); - } - }); - ro.observe(el); - - term.onData((data) => { - const ws = wsRef.current; - if (ws?.readyState === WebSocket.OPEN) ws.send(data); - }); - - return () => { - ro.disconnect(); - wsRef.current?.close(); - term.dispose(); - termRef.current = null; - }; - }, []); - - const onDisconnect = useCallback(() => { - wsRef.current?.close(); - }, []); - - const connectTerminal = useCallback(() => { - const term = termRef.current; - if (!term) { - setConnecting(false); - return; - } - - setTermError(null); - setConnecting(true); - setSessionActive(false); - wsRef.current?.close(); - - const name = gatewaySandboxName.trim(); - if (!name) { - setConnecting(false); - setTermError("Missing gateway sandbox name."); - return; - } - - const apiBase = terminalApiBase(); - const url = sandboxSshWebSocketURL(apiBase); - - let ws: WebSocket; - try { - ws = new WebSocket(url); - } catch (e) { - setConnecting(false); - setTermError(e instanceof Error ? e.message : String(e)); - return; - } - ws.binaryType = "arraybuffer"; - wsRef.current = ws; - - ws.onopen = () => { - if (wsRef.current !== ws) return; - setTermError(null); - const usePlainShell = plainShellOnly; - const launchCommand = - !usePlainShell && harnessBackend - ? defaultHarnessSSHLaunchCommand(harnessBackend) - : undefined; - setAppliedPlainShell(usePlainShell); - term.reset(); - ws.send( - JSON.stringify({ - sandbox_name: name, - plain_shell: usePlainShell, - ...(launchCommand ? { launch_command: launchCommand } : {}), - ...(harnessBackend ? { harness_backend: harnessBackend } : {}), - cols: term.cols, - rows: term.rows, - }), - ); - }; - - ws.onmessage = (ev) => { - if (wsRef.current !== ws) return; - if (typeof ev.data === "string") { - try { - const msg = JSON.parse(ev.data) as { type?: string; message?: string }; - if (msg.type === "error") { - term.writeln(`\r\n\x1b[31m${msg.message ?? "error"}\x1b[0m`); - return; - } - if (msg.type === "ready") { - setConnecting(false); - setSessionActive(true); - return; - } - } catch { - term.write(ev.data); - } - return; - } - term.write(new Uint8Array(ev.data as ArrayBuffer)); - }; - - ws.onerror = () => { - if (wsRef.current !== ws) return; - setConnecting(false); - setTermError("WebSocket error — check Network → WS and that /api reaches the controller."); - }; - - ws.onclose = (ev) => { - if (wsRef.current !== ws) return; - wsRef.current = null; - setConnecting(false); - setSessionActive(false); - setAppliedPlainShell(null); - term.writeln(`\r\n\x1b[90m(disconnected)\x1b[0m`); - if (!ev.wasClean && ev.code === 1006) { - setTermError("Connection closed abnormally (1006)."); - } - }; - }, - [plainShellOnly, harnessBackend, gatewaySandboxName], - ); - - const restartSession = useCallback(() => { - wsRef.current?.close(); - window.setTimeout(() => connectTerminal(), 120); - }, [connectTerminal]); - - useEffect(() => { - if (!autoConnect) return; - const t = window.setTimeout(() => { - if (!termRef.current) return; - connectTerminal(); - }, 400); - return () => window.clearTimeout(t); - }, [autoConnect, connectTerminal]); - - const showReconnect = Boolean(gatewaySandboxName) && !sessionActive && !connecting; - const plainShellPendingRestart = - harnessTerminalSession && - sessionActive && - appliedPlainShell !== null && - plainShellOnly !== appliedPlainShell; - - return ( -
-
-
-

OpenShell

-

{displayTitle}

-
- {modelConfigRef ? ( -
-
Model config
-
- Model config:{" "} - {modelConfigRef} -
-
- ) : null} - {gatewaySandboxName ? ( -
-
Gateway sandbox
-
- Gateway:{" "} - {gatewaySandboxName} -
-
- ) : null} -
-
-
- {harnessTerminalSession && gatewaySandboxName ? ( -
-
- setPlainShellOnly(v === true)} - disabled={connecting} - /> - -
- {plainShellPendingRestart ? ( -

Restart session to apply.

- ) : null} -
- ) : null} -
- {showReconnect ? ( - - ) : null} - {sessionActive ? ( - - ) : null} - {connecting ? ( - - ) : null} -
-
-
- - {!gatewaySandboxName ? ( -

- Open a harness from the Agents list to start a terminal session. -

- ) : null} - - {termError ?

{termError}

: null} - -
-
-
-
- ); -} diff --git a/ui/src/app/openshell/page.tsx b/ui/src/app/openshell/page.tsx deleted file mode 100644 index a453b085cb..0000000000 --- a/ui/src/app/openshell/page.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { Suspense } from "react"; -import { OpenshellTerminalPage } from "./OpenshellTerminalPage"; - -export default function OpenshellPage() { - return ( - Loading…
} - > - - - ); -} diff --git a/ui/src/components/AgentCard.tsx b/ui/src/components/AgentCard.tsx index 9786507f73..d5aedd3a0e 100644 --- a/ui/src/components/AgentCard.tsx +++ b/ui/src/components/AgentCard.tsx @@ -16,7 +16,7 @@ import { MemoriesDialog } from "@/components/MemoriesDialog"; import KagentLogo from "@/components/kagent-logo"; import Link from "next/link"; import { useRouter } from "next/navigation"; -import { Brain, MoreHorizontal, Pencil, Terminal, Trash2 } from "lucide-react"; +import { Brain, MoreHorizontal, Pencil, Trash2 } from "lucide-react"; import { k8sRefUtils } from "@/lib/k8sUtils"; import { agentHarnessIcon, @@ -26,11 +26,6 @@ import { getAgentHarnessRuntime, isAgentHarness, } from "@/lib/agentHarness"; -import { - isOpenshellSandboxRow, - isSubstrateHarnessRow, - openshellTerminalHref, -} from "@/lib/openshellSandboxAgents"; import { cn } from "@/lib/utils"; interface AgentCardProps { @@ -44,8 +39,6 @@ export function AgentCard({ agentResponse, onAgentsChanged }: AgentCardProps) { const [memoriesOpen, setMemoriesOpen] = useState(false); const [deleteOpen, setDeleteOpen] = useState(false); - const sshSandbox = isOpenshellSandboxRow(agentResponse); - const substrateHarness = isSubstrateHarnessRow(agentResponse); const agentHarness = isAgentHarness(agentResponse); const harnessBackend = getAgentHarnessBackend(agentResponse); const harnessRuntime = getAgentHarnessRuntime(agentResponse); @@ -92,22 +85,18 @@ export function AgentCard({ agentResponse, onAgentsChanged }: AgentCardProps) { )}> - {sshSandbox ? ( - agentHarness ? ( - - {harnessBackend ? agentHarnessIcon(harnessBackend) : "🦞"} - - ) : ( - - ) + {agentHarness ? ( + + {harnessBackend ? agentHarnessIcon(harnessBackend) : "🦞"} + ) : ( )} @@ -185,19 +174,7 @@ export function AgentCard({ agentResponse, onAgentsChanged }: AgentCardProps) { ); - const substrateGatewayPath = agentResponse.substrateAgentHarness?.gatewayUIPath; - const chatHref = - substrateHarness && substrateGatewayPath - ? substrateGatewayPath - : sshSandbox && agentResponse.openshellAgentHarness - ? openshellTerminalHref({ - gatewaySandboxName: agentResponse.openshellAgentHarness.gatewaySandboxName, - namespace: agent.metadata.namespace, - crName: agent.metadata.name, - modelConfigRef: agentResponse.modelConfigRef, - harnessBackend, - }) - : `/agents/${agent.metadata.namespace}/${agent.metadata.name}/chat`; + const chatHref = `/agents/${agent.metadata.namespace}/${agent.metadata.name}/chat`; return ( <> diff --git a/ui/src/components/AgentListView.tsx b/ui/src/components/AgentListView.tsx index 1fe756639d..ef63e91f26 100644 --- a/ui/src/components/AgentListView.tsx +++ b/ui/src/components/AgentListView.tsx @@ -17,20 +17,14 @@ import { import { countAgentToolBindings } from "@/lib/countAgentTools"; import { k8sRefUtils } from "@/lib/k8sUtils"; import { cn } from "@/lib/utils"; -import { ArrowDown, ArrowUp, Brain, Lock, MoreHorizontal, Pencil, Terminal, Trash2 } from "lucide-react"; +import { ArrowDown, ArrowUp, Brain, Lock, MoreHorizontal, Pencil, Trash2 } from "lucide-react"; import { agentHarnessIcon, agentHarnessTypeLabel, getAgentHarnessBackend, isAgentHarness, } from "@/lib/agentHarness"; -import { - isOpenshellSandboxRow, - isSubstrateHarnessRow, - openshellTerminalHref, -} from "@/lib/openshellSandboxAgents"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; -import type { SandboxPlatform } from "@/types"; interface AgentListViewProps { agentResponse: AgentResponse[]; @@ -62,26 +56,15 @@ function baseTypeLabel(type: string | undefined): string { } } -function sandboxPlatformLabel(platform: SandboxPlatform | undefined): string { - switch (platform) { - case "substrate": - return "Substrate"; - case "agent-sandbox": - return "Agent Sandbox"; - default: - return "Sandbox"; - } -} - -function SandboxBadge({ platform }: { platform: SandboxPlatform | undefined }) { +function SandboxBadge() { return ( - + - {sandboxPlatformLabel(platform)} + Agent Substrate ); } @@ -91,14 +74,11 @@ function RowTypeCell({ item }: { item: AgentResponse }) { if (harnessBackend) { return {agentHarnessTypeLabel(harnessBackend)}; } - if (isOpenshellSandboxRow(item)) { - return Agent harness; - } if (item.workloadMode === "sandbox") { return ( {baseTypeLabel(item.agent.spec?.type)} - + ); } @@ -108,7 +88,6 @@ function RowTypeCell({ item }: { item: AgentResponse }) { function rowTypeSortKey(item: AgentResponse): string { const harnessBackend = getAgentHarnessBackend(item); if (harnessBackend) return agentHarnessTypeLabel(harnessBackend); - if (isOpenshellSandboxRow(item)) return "Agent harness"; if (item.workloadMode === "sandbox") return `${baseTypeLabel(item.agent.spec?.type)} (sandbox)`; return baseTypeLabel(item.agent.spec?.type); } @@ -270,8 +249,6 @@ function AgentListRow({ item, onAgentsChanged }: { item: AgentResponse; onAgents const [memoriesOpen, setMemoriesOpen] = useState(false); const [deleteOpen, setDeleteOpen] = useState(false); - const sshSandbox = isOpenshellSandboxRow(item); - const substrateHarness = isSubstrateHarnessRow(item); const agentHarness = isAgentHarness(item); const harnessBackend = getAgentHarnessBackend(item); @@ -283,31 +260,9 @@ function AgentListRow({ item, onAgentsChanged }: { item: AgentResponse; onAgents const nTools = countAgentToolBindings(item); const nSkills = countSkills(agent); - const substrateGatewayPath = item.substrateAgentHarness?.gatewayUIPath; - const gatewaySandboxName = item.openshellAgentHarness?.gatewaySandboxName; const chatPath = useMemo( - () => - substrateHarness && substrateGatewayPath - ? substrateGatewayPath - : sshSandbox && gatewaySandboxName - ? openshellTerminalHref({ - gatewaySandboxName, - namespace, - crName: name, - modelConfigRef: item.modelConfigRef, - harnessBackend, - }) - : `/agents/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}/chat`, - [ - substrateHarness, - substrateGatewayPath, - sshSandbox, - gatewaySandboxName, - namespace, - name, - item.modelConfigRef, - harnessBackend, - ], + () => `/agents/${encodeURIComponent(namespace)}/${encodeURIComponent(name)}/chat`, + [namespace, name], ); const goChat = useCallback(() => { @@ -344,11 +299,7 @@ function AgentListRow({ item, onAgentsChanged }: { item: AgentResponse; onAgents tabIndex={isReady ? 0 : -1} role={isReady ? "link" : undefined} aria-label={ - isReady - ? sshSandbox - ? `Open SSH terminal for ${k8sRefUtils.toRef(namespace, name)}` - : `Open chat for ${k8sRefUtils.toRef(namespace, name)}` - : undefined + isReady ? `Open chat for ${k8sRefUtils.toRef(namespace, name)}` : undefined } > @@ -358,18 +309,14 @@ function AgentListRow({ item, onAgentsChanged }: { item: AgentResponse; onAgents />
- {sshSandbox ? ( - agentHarness ? ( - - {harnessBackend ? agentHarnessIcon(harnessBackend) : "🦞"} - - ) : ( - - ) + {agentHarness ? ( + + {harnessBackend ? agentHarnessIcon(harnessBackend) : "🦞"} + ) : ( )} diff --git a/ui/src/components/AgentsProvider.tsx b/ui/src/components/AgentsProvider.tsx index 1f9a2acc81..b808e82ce0 100644 --- a/ui/src/components/AgentsProvider.tsx +++ b/ui/src/components/AgentsProvider.tsx @@ -51,7 +51,7 @@ export interface AgentFormData { // Context management context?: ContextConfig; promptSources?: Array<{ name: string; alias: string }>; - /** AgentHarness CR (kagent.dev/v1alpha2 AgentHarness; openclaw, nemoclaw, or hermes backend). */ + /** AgentHarness CR (kagent.dev/v1alpha2 AgentHarness; openclaw or hermes backend). */ agentHarness?: AgentHarnessFormSlice; // BYO fields byoImage?: string; @@ -67,8 +67,7 @@ export interface AgentFormData { env?: EnvVar[]; imagePullPolicy?: string; serviceAccountName?: string; - /** Sandbox workload platform when type is Sandbox. */ - sandboxPlatform?: "agent-sandbox" | "substrate"; + /** Optional Agent Substrate settings when runInSandbox is true. */ substrateWorkerPoolRefName?: string; substrateSnapshotsLocation?: string; } diff --git a/ui/src/components/agent-form/AgentHarnessFields.tsx b/ui/src/components/agent-form/AgentHarnessFields.tsx index 8cc7f9f520..fba523340a 100644 --- a/ui/src/components/agent-form/AgentHarnessFields.tsx +++ b/ui/src/components/agent-form/AgentHarnessFields.tsx @@ -16,7 +16,12 @@ import type { AgentHarnessFormSlice, AgentHarnessFormValidationError, } from "@/lib/agentHarnessForm"; -import { isClawHarnessBackend, newAgentHarnessChannelRow } from "@/lib/agentHarnessForm"; +import { + agentHarnessBackendSupportsMessengerChannels, + isClawHarnessBackend, + isSubstrateOnlyHarnessBackend, + newAgentHarnessChannelRow, +} from "@/lib/agentHarnessForm"; import type { AgentHarnessCrBackend } from "@/types"; import { useSubstrateEnabled } from "@/contexts/SubstrateFeaturesContext"; @@ -113,7 +118,7 @@ function ChannelTypeSetupHints({ lines={[ <> Bot + app tokens: Same Socket Mode tokens as OpenClaw (xoxb- and{" "} - xapp-). Stored as OpenShell providers and resolved at egress. + xapp-). Stored as secret providers and resolved at egress. , <> Allowed Slack users: Optional Slack member IDs (U…) who may DM the @@ -153,15 +158,18 @@ export function AgentHarnessFields({ const substrateEnabled = useSubstrateEnabled(); const harnessBackend = value.backend; const clawBackend = isClawHarnessBackend(harnessBackend); + const substrateOnly = isSubstrateOnlyHarnessBackend(harnessBackend); + const channelsSupported = agentHarnessBackendSupportsMessengerChannels(harnessBackend); const set = (patch: Partial) => onChange({ ...value, ...patch }); const [advancedOpen, setAdvancedOpen] = React.useState(false); const section = validationError?.section ?? null; React.useEffect(() => { - if (!substrateEnabled && value.runtime === "substrate") { - set({ runtime: "openshell" }); + // Ensure runtime is always substrate (only supported runtime) + if (value.runtime !== "substrate") { + set({ runtime: "substrate" }); } - }, [substrateEnabled, value.runtime]); + }, [value.runtime]); return (
@@ -169,26 +177,22 @@ export function AgentHarnessFields({ {section === "general" ? validationError?.message : null} - {substrateEnabled ? ( + {substrateEnabled || substrateOnly ? ( Control plane - +
+ Agent Substrate +
+ {substrateOnly ? ( +

+ This harness type runs on Agent Substrate only. +

+ ) : null}
{value.runtime === "substrate" ? (
@@ -198,11 +202,14 @@ export function AgentHarnessFields({ id="agent-field-substrate-gateway-token" disabled={disabled} type="password" + placeholder="Leave empty to auto-generate" value={value.substrateGatewayToken} onChange={(e) => set({ substrateGatewayToken: e.target.value })} />

- Bearer token used by kagent when proxying the generated OpenClaw gateway. + Optional bearer token used by kagent when proxying the generated sandbox + gateway. Leave empty to have the controller generate one and store it in a + Secret named <harness-name>-gateway-token.

@@ -236,6 +243,7 @@ export function AgentHarnessFields({ ) : null} + {channelsSupported ? ( + ) : null} set({ image: e.target.value })} className="font-mono text-sm" - placeholder="e.g. ghcr.io/kagent-dev/nemoclaw/sandbox-base:2026.5.4" + placeholder="e.g. ghcr.io/kagent-dev/openclaw/sandbox-base:2026.5.4" disabled={disabled} autoComplete="off" /> diff --git a/ui/src/components/chat/AcpHarnessChat.tsx b/ui/src/components/chat/AcpHarnessChat.tsx new file mode 100644 index 0000000000..c84aa32855 --- /dev/null +++ b/ui/src/components/chat/AcpHarnessChat.tsx @@ -0,0 +1,725 @@ +"use client"; + +// ACP harness chat — drives the standard kagent chat experience over the +// Agent Client Protocol for substrate AgentHarness actors, through the +// controller's same-origin WebSocket proxy (/api/agentharnesses/{ns}/{name}/acp). +// +// On connect the ACP handshake runs automatically (initialize → session/new), +// then each chat message is a session/prompt; streaming output arrives as +// session/update notifications (agent_message_chunk, agent_thought_chunk, +// tool_call, tool_call_update, plan) and is mapped onto the same A2A Message +// shapes the rest of the chat UI renders (ChatMessage / ToolCallDisplay / +// StreamingMessage). Agent-initiated session/request_permission requests are +// auto-approved. +// +// When the agent advertises loadSession + sessionCapabilities.list, past +// sessions are fetched via session/list and offered in a "Previous chats" +// picker; selecting one calls session/load, which replays the transcript as +// user_message_chunk / agent_message_chunk updates before the chat resumes. + +import type React from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { ArrowBigUp, RefreshCw, X } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { Textarea } from "@/components/ui/textarea"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import ChatMessage from "@/components/chat/ChatMessage"; +import StreamingMessage from "./StreamingMessage"; +import StatusDisplay from "./StatusDisplay"; +import { createMessage, ProcessedToolCallData, ProcessedToolResultData } from "@/lib/messageHandlers"; +import { getStatusPlaceholder } from "@/lib/statusUtils"; +import type { ChatStatus } from "@/types"; +import type { Message } from "@a2a-js/sdk"; +import { v4 as uuidv4 } from "uuid"; +import { toast } from "sonner"; +import { renameSession } from "@/app/actions/sessions"; + +type ConnState = "idle" | "connecting" | "initializing" | "creating-session" | "loading-session" | "ready" | "running" | "disconnected"; + +type JsonRpcMessage = { + jsonrpc?: string; + id?: number | string; + method?: string; + params?: Record; + result?: Record; + error?: { code: number; message: string; data?: unknown }; +}; + +type SessionUpdate = { + sessionUpdate?: string; + content?: unknown; + toolCallId?: string; + title?: string; + kind?: string; + status?: string; + rawInput?: Record; + rawOutput?: unknown; + entries?: { content?: string; status?: string }[]; +}; + +type PermissionOption = { optionId?: string; name?: string; kind?: string }; + +/** SessionInfo entries returned by ACP session/list. */ +type AcpSessionInfo = { sessionId: string; title?: string; updatedAt?: string }; + +/** Strip the "[Working directory: ...]" prefix some backends prepend to titles. */ +function cleanSessionTitle(title: string): string { + return title.replace(/^\s*\[Working directory:[^\]]*\]\s*/i, "").trim(); +} + +function chunkText(content: unknown): string { + if (content && typeof content === "object" && "text" in content) { + const text = (content as { text?: unknown }).text; + return typeof text === "string" ? text : ""; + } + return ""; +} + +function toolResultText(update: SessionUpdate): string { + if (Array.isArray(update.content)) { + const parts = (update.content as { content?: { text?: string } }[]) + .map((c) => c?.content?.text ?? "") + .filter(Boolean); + if (parts.length > 0) return parts.join("\n"); + } + if (update.rawOutput !== undefined) { + return typeof update.rawOutput === "string" ? update.rawOutput : JSON.stringify(update.rawOutput, null, 2); + } + return ""; +} + +function connToChatStatus(conn: ConnState): ChatStatus { + switch (conn) { + case "idle": + case "ready": + return "ready"; + case "running": + return "working"; + case "disconnected": + return "error"; + default: + return "thinking"; + } +} + +interface AcpHarnessChatProps { + /** Same-origin WebSocket path, e.g. /api/agentharnesses/kagent/my-claw/acp */ + acpPath: string; + namespace: string; + agentName: string; + /** The kagent/DB session id this chat maps to (its substrate actor key). */ + sessionId?: string; + /** Callback when ACP sessions are updated from session/list. */ + onSessionsUpdate?: (sessions: AcpSessionInfo[]) => void; + /** The session ID to load on mount (from sidebar click). */ + initialLoadSessionId?: string; + /** Connect on mount and resume the actor's prior transcript (existing chats). + * New chats pass false so they stay idle until the first message. */ + autoConnect?: boolean; +} + +export default function AcpHarnessChat({ + acpPath, + namespace, + agentName, + sessionId, + onSessionsUpdate, + initialLoadSessionId, + autoConnect, +}: AcpHarnessChatProps) { + // Connection is deferred until the user sends their first message (or opens a + // past session) so loading the chat page never blocks on a cold actor + // create+resume — the actor is only provisioned once the chat is actually used. + const [conn, setConn] = useState("idle"); + const [messages, setMessages] = useState([]); + const [streamingContent, setStreamingContent] = useState(""); + const [currentInputMessage, setCurrentInputMessage] = useState(""); + const [pastSessions, setPastSessions] = useState([]); + const [loadedSessionId, setLoadedSessionId] = useState(""); + + const wsRef = useRef(null); + const nextIdRef = useRef(1); + const pendingRef = useRef(new Map()); + const sessionIdRef = useRef(null); + // Set when we close the socket ourselves (unmount/navigation/reconnect) so + // onclose doesn't surface a spurious "disconnected" toast. + const intentionalCloseRef = useRef(false); + const streamBufRef = useRef(""); + const streamKindRef = useRef<"agent" | "thought" | "user" | null>(null); + const toolNamesRef = useRef(new Map()); + const toolResultsSentRef = useRef(new Set()); + const planMessageIdRef = useRef(null); + const authMethodsRef = useRef([]); + const authTriedRef = useRef(false); + const authQueueRef = useRef([]); + // session/load: replay in progress (user_message_chunk only rendered then), + // and the session id we asked to load (the load response carries no id). + const replayingRef = useRef(false); + const pendingLoadRef = useRef(null); + // A first message typed before the socket connected; sent automatically once + // session/new completes after the lazy connect. + const pendingPromptRef = useRef(null); + // On the first session/list after connect, decide whether to resume the + // actor's existing conversation (session/load) or start fresh (session/new). + const needResumeDecisionRef = useRef(false); + const canListSessionsRef = useRef(false); + const bottomRef = useRef(null); + // Last title persisted to the DB session name, to avoid redundant renames. + const lastTitleRef = useRef(""); + // DB session id (stable per mount) read from a ref so handleMessage stays stable. + const dbSessionIdRef = useRef(sessionId); + dbSessionIdRef.current = sessionId; + + const chatStatus = connToChatStatus(conn); + const agentContext = { namespace, agentName }; + + useEffect(() => { + bottomRef.current?.scrollIntoView({ behavior: "smooth" }); + }, [messages, streamingContent, conn]); + + const appendMessage = useCallback((message: Message) => { + setMessages((prev) => [...prev, message]); + }, []); + + const flushStream = useCallback(() => { + const buf = streamBufRef.current; + const kind = streamKindRef.current; + streamBufRef.current = ""; + streamKindRef.current = null; + setStreamingContent(""); + if (buf.trim()) { + const role = kind === "thought" ? "thinking" : kind === "user" ? "user" : "assistant"; + appendMessage(createMessage(buf, role, { originalType: "TextMessage" })); + } + }, [appendMessage]); + + const addStreamChunk = useCallback( + (kind: "agent" | "thought" | "user", text: string) => { + if (!text) return; + if (streamKindRef.current !== null && streamKindRef.current !== kind) { + flushStream(); + } + streamKindRef.current = kind; + streamBufRef.current += text; + setStreamingContent(streamBufRef.current); + }, + [flushStream], + ); + + const sendRaw = useCallback((msg: Record) => { + const ws = wsRef.current; + if (!ws || ws.readyState !== WebSocket.OPEN) return; + ws.send(JSON.stringify(msg)); + }, []); + + const rpc = useCallback( + (method: string, params: Record) => { + const id = nextIdRef.current++; + pendingRef.current.set(id, method); + sendRaw({ jsonrpc: "2.0", id, method, params }); + }, + [sendRaw], + ); + + const handleSessionUpdate = useCallback( + (update: SessionUpdate) => { + switch (update.sessionUpdate) { + case "agent_message_chunk": + addStreamChunk("agent", chunkText(update.content)); + break; + case "agent_thought_chunk": + addStreamChunk("thought", chunkText(update.content)); + break; + case "user_message_chunk": + // Only rendered during session/load replay; live user messages are + // already appended locally when the prompt is sent. + if (replayingRef.current) { + addStreamChunk("user", chunkText(update.content)); + } + break; + case "tool_call": { + flushStream(); + const id = update.toolCallId || uuidv4(); + const name = update.title || update.kind || "tool"; + toolNamesRef.current.set(id, name); + const toolCallData: ProcessedToolCallData[] = [{ id, name, args: update.rawInput ?? {} }]; + appendMessage( + createMessage("", "assistant", { + originalType: "ToolCallRequestEvent", + additionalMetadata: { toolCallData }, + }), + ); + break; + } + case "tool_call_update": { + const id = update.toolCallId; + if (!id) break; + const status = update.status ?? ""; + if (status !== "completed" && status !== "failed") break; + if (toolResultsSentRef.current.has(id)) break; + toolResultsSentRef.current.add(id); + const name = toolNamesRef.current.get(id) || update.title || "tool"; + const toolResultData: ProcessedToolResultData[] = [ + { + call_id: id, + name, + content: toolResultText(update) || (status === "failed" ? "tool call failed" : "done"), + is_error: status === "failed", + }, + ]; + appendMessage( + createMessage("", "assistant", { + originalType: "ToolCallExecutionEvent", + additionalMetadata: { toolResultData }, + }), + ); + break; + } + case "plan": { + const text = (update.entries ?? []) + .map((e) => `${e.status === "completed" ? "✓" : e.status === "in_progress" ? "▸" : "○"} ${e.content ?? ""}`) + .join("\n"); + if (!text) break; + if (planMessageIdRef.current === null) { + planMessageIdRef.current = uuidv4(); + } + const planId = planMessageIdRef.current; + const planMessage = createMessage(text, "plan", { messageId: planId, originalType: "TextMessage" }); + setMessages((prev) => + prev.some((m) => m.messageId === planId) + ? prev.map((m) => (m.messageId === planId ? planMessage : m)) + : [...prev, planMessage], + ); + break; + } + default: + break; + } + }, + [addStreamChunk, appendMessage, flushStream], + ); + + const handleAgentRequest = useCallback( + (msg: JsonRpcMessage) => { + if (msg.method === "session/request_permission") { + const params = msg.params as { options?: PermissionOption[]; toolCall?: { title?: string } } | undefined; + const options = params?.options ?? []; + const allow = + options.find((o) => o.kind === "allow_once") ?? + options.find((o) => o.kind === "allow_always") ?? + options[0]; + if (allow?.optionId) { + sendRaw({ + jsonrpc: "2.0", + id: msg.id, + result: { outcome: { outcome: "selected", optionId: allow.optionId } }, + }); + } else { + sendRaw({ jsonrpc: "2.0", id: msg.id, result: { outcome: { outcome: "cancelled" } } }); + } + return; + } + // fs/* and anything else: we advertised no client capabilities. + sendRaw({ + jsonrpc: "2.0", + id: msg.id, + error: { code: -32601, message: `method not supported by this client: ${msg.method}` }, + }); + }, + [sendRaw], + ); + + const handleMessage = useCallback( + (msg: JsonRpcMessage) => { + // Response to one of our requests. + if (msg.id !== undefined && msg.method === undefined) { + const method = pendingRef.current.get(msg.id); + pendingRef.current.delete(msg.id); + if (msg.error) { + // Some agents (e.g. codex-acp) refuse session/new until an explicit + // authenticate call, even when credentials are already present as + // env vars. Try each advertised auth method (API-key ones first) + // until one succeeds. + if (method === "session/new" && !authTriedRef.current && authMethodsRef.current.length > 0) { + authTriedRef.current = true; + const ids = authMethodsRef.current; + authQueueRef.current = [ + ...ids.filter((id) => id.includes("api-key")), + ...ids.filter((id) => !id.includes("api-key")), + ]; + rpc("authenticate", { methodId: authQueueRef.current.shift() }); + return; + } + // An auth method can fail (e.g. its env var is unset); fall through + // to the next one before giving up. + if (method === "authenticate" && authQueueRef.current.length > 0) { + rpc("authenticate", { methodId: authQueueRef.current.shift() }); + return; + } + // Listing sessions is best-effort; degrade silently to no picker. + if (method === "session/list") { + console.debug("acp: session/list failed", msg.error.message); + return; + } + toast.error(`${method ?? "request"} failed: ${msg.error.message}`); + if (method === "session/prompt") { + flushStream(); + setConn("ready"); + } else if (method === "session/load") { + replayingRef.current = false; + pendingLoadRef.current = null; + flushStream(); + // Keep the previous session usable; only the load failed. + setConn("ready"); + } else if (method === "initialize" || method === "session/new" || method === "authenticate") { + wsRef.current?.close(); + } + return; + } + if (method === "initialize") { + const methods = (msg.result?.authMethods as { id?: string }[] | undefined) ?? []; + authMethodsRef.current = methods.map((m) => m.id).filter((id): id is string => typeof id === "string"); + // Previous chats: remember whether the agent supports session + // listing + loading; the list itself is fetched after session/new + // succeeds (codex requires authenticate before session/list). + const caps = (msg.result?.agentCapabilities ?? {}) as { + loadSession?: boolean; + sessionCapabilities?: { list?: unknown }; + }; + canListSessionsRef.current = caps.loadSession === true && caps.sessionCapabilities?.list !== undefined; + // When the agent supports listing and needs no authenticate step + // (e.g. OpenClaw), list first so we can resume this actor's existing + // conversation instead of always starting a brand-new ACP session. + if (canListSessionsRef.current && authMethodsRef.current.length === 0) { + needResumeDecisionRef.current = true; + setConn("loading-session"); + rpc("session/list", {}); + } else { + setConn("creating-session"); + rpc("session/new", { cwd: "/home/agent", mcpServers: [] }); + } + } else if (method === "authenticate") { + authQueueRef.current = []; + rpc("session/new", { cwd: "/home/agent", mcpServers: [] }); + } else if (method === "session/list") { + const sessions = (msg.result?.sessions as AcpSessionInfo[] | undefined) ?? []; + const sorted = sessions + .filter((s) => typeof s.sessionId === "string" && s.sessionId.length > 0) + .sort((a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")); + setPastSessions(sorted); + onSessionsUpdate?.(sorted); + // Emit event for sidebar to pick up + if (typeof window !== "undefined") { + window.dispatchEvent( + new CustomEvent("acp-sessions-updated", { + detail: { agentRef: `${namespace}/${agentName}`, sessions: sorted }, + }) + ); + } + // First list after connect: resume the actor's existing conversation + // if it has one, otherwise start a fresh session. + if (needResumeDecisionRef.current) { + needResumeDecisionRef.current = false; + if (sorted.length > 0) { + const resumeId = sorted[0].sessionId; + flushStream(); + setMessages([]); + toolNamesRef.current.clear(); + toolResultsSentRef.current.clear(); + planMessageIdRef.current = null; + replayingRef.current = true; + pendingLoadRef.current = resumeId; + setConn("loading-session"); + rpc("session/load", { sessionId: resumeId, cwd: "/home/agent", mcpServers: [] }); + return; + } + setConn("creating-session"); + rpc("session/new", { cwd: "/home/agent", mcpServers: [] }); + return; + } + // Adopt the agent-generated title as this chat's DB session name so the + // sidebar shows a meaningful label instead of "Untitled". + const dbSessionId = dbSessionIdRef.current; + const currentAcpId = sessionIdRef.current; + // session/list returns prefixed ids (e.g. "agent:main:acp:") while + // session/new hands back the bare id, so match by suffix; each actor + // holds a single chat, so fall back to the only session if present. + const match = + sorted.find( + (s) => currentAcpId && (s.sessionId === currentAcpId || s.sessionId.endsWith(currentAcpId)), + ) ?? (sorted.length === 1 ? sorted[0] : undefined); + const title = match?.title ? cleanSessionTitle(match.title) : ""; + if (dbSessionId && title && title !== lastTitleRef.current) { + lastTitleRef.current = title; + void renameSession(dbSessionId, title); + if (typeof window !== "undefined") { + window.dispatchEvent( + new CustomEvent("harness-session-titled", { + detail: { sessionId: dbSessionId, title }, + }) + ); + } + } + } else if (method === "session/load") { + replayingRef.current = false; + flushStream(); + if (pendingLoadRef.current) { + sessionIdRef.current = pendingLoadRef.current; + setLoadedSessionId(pendingLoadRef.current); + } + pendingLoadRef.current = null; + setConn("ready"); + // A message typed before the transcript finished replaying is sent now. + const pendingAfterLoad = pendingPromptRef.current; + const loadedSid = sessionIdRef.current; + if (pendingAfterLoad && loadedSid) { + pendingPromptRef.current = null; + toolResultsSentRef.current.clear(); + setConn("running"); + rpc("session/prompt", { sessionId: loadedSid, prompt: [{ type: "text", text: pendingAfterLoad }] }); + } + } else if (method === "session/new") { + const sid = msg.result?.sessionId as string | undefined; + if (!sid) { + toast.error("session/new returned no sessionId"); + wsRef.current?.close(); + return; + } + sessionIdRef.current = sid; + setConn("ready"); + // Fetch previous chats now that any required authenticate has run. + if (canListSessionsRef.current) { + rpc("session/list", {}); + } + // If the user typed their first message before we lazily connected, + // send it now that the session is live. + const pending = pendingPromptRef.current; + if (pending) { + pendingPromptRef.current = null; + toolResultsSentRef.current.clear(); + setConn("running"); + rpc("session/prompt", { sessionId: sid, prompt: [{ type: "text", text: pending }] }); + } + } else if (method === "session/prompt") { + const stop = msg.result?.stopReason as string | undefined; + flushStream(); + planMessageIdRef.current = null; + setConn("ready"); + if (stop && stop !== "end_turn") { + toast.info(`Turn ended: ${stop}`); + } + // Pick up the current session and freshly generated titles. + if (canListSessionsRef.current) { + rpc("session/list", {}); + } + } + return; + } + // Agent-initiated request. + if (msg.id !== undefined && msg.method !== undefined) { + handleAgentRequest(msg); + return; + } + // Notification. + if (msg.method === "session/update") { + const update = (msg.params as { update?: SessionUpdate } | undefined)?.update; + if (update) handleSessionUpdate(update); + } + }, + [flushStream, handleAgentRequest, handleSessionUpdate, rpc], + ); + + const connect = useCallback(() => { + const proto = window.location.protocol === "https:" ? "wss:" : "ws:"; + const url = `${proto}//${window.location.host}${acpPath}`; + intentionalCloseRef.current = false; + setConn("connecting"); + const ws = new WebSocket(url); + wsRef.current = ws; + ws.onopen = () => { + authMethodsRef.current = []; + authTriedRef.current = false; + authQueueRef.current = []; + replayingRef.current = false; + pendingLoadRef.current = null; + needResumeDecisionRef.current = false; + setPastSessions([]); + setLoadedSessionId(""); + setConn("initializing"); + rpc("initialize", { + protocolVersion: 1, + clientCapabilities: { fs: { readTextFile: false, writeTextFile: false } }, + }); + }; + ws.onmessage = (ev) => { + try { + handleMessage(JSON.parse(String(ev.data)) as JsonRpcMessage); + } catch { + console.debug("acp: received non-JSON frame", ev.data); + } + }; + ws.onclose = (ev) => { + flushStream(); + setConn("disconnected"); + sessionIdRef.current = null; + pendingRef.current.clear(); + pendingPromptRef.current = null; + // Only warn on genuinely unexpected drops: a normal close (1000/1001) or + // one we triggered ourselves (navigation/unmount/reconnect) is silent. + if (!intentionalCloseRef.current && ev.code !== 1000 && ev.code !== 1001) { + toast.error("Disconnected from the agent. The actor may still be starting — try reconnecting."); + } + }; + }, [acpPath, flushStream, handleMessage, rpc]); + + // Connect lazily: auto-connect on mount for existing chats (autoConnect) or + // when a specific past session was requested, resuming the actor's prior + // transcript. A fresh chat stays idle until the user sends their first + // message, so the page never blocks on a cold actor. + useEffect(() => { + if (autoConnect || initialLoadSessionId) connect(); + return () => { + intentionalCloseRef.current = true; + wsRef.current?.close(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [acpPath]); + + // If a session ID is passed (e.g., from sidebar click), load it once ready. + useEffect(() => { + if (initialLoadSessionId && conn === "ready") { + handleLoadSession(initialLoadSessionId); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [initialLoadSessionId, conn]); + + const handleLoadSession = (sid: string) => { + if (conn !== "ready" || !sid || sid === sessionIdRef.current) return; + // Clear the transcript; the agent replays the conversation as + // session/update notifications before answering session/load. + flushStream(); + setMessages([]); + setLoadedSessionId(sid); + toolNamesRef.current.clear(); + toolResultsSentRef.current.clear(); + planMessageIdRef.current = null; + replayingRef.current = true; + pendingLoadRef.current = sid; + setConn("loading-session"); + rpc("session/load", { sessionId: sid, cwd: "/home/agent", mcpServers: [] }); + }; + + const handleSendMessage = (e: React.FormEvent) => { + e.preventDefault(); + const text = currentInputMessage.trim(); + if (!text) return; + // First message on a fresh chat: provision/resume the actor now by + // connecting, queue the prompt, and let session/new send it once live. + if (conn === "idle") { + appendMessage(createMessage(text, "user", { originalType: "TextMessage" })); + setCurrentInputMessage(""); + pendingPromptRef.current = text; + connect(); + return; + } + const sid = sessionIdRef.current; + if (!sid || conn !== "ready") return; + appendMessage(createMessage(text, "user", { originalType: "TextMessage" })); + setCurrentInputMessage(""); + toolResultsSentRef.current.clear(); + setConn("running"); + rpc("session/prompt", { sessionId: sid, prompt: [{ type: "text", text }] }); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "Enter" && !e.shiftKey) { + e.preventDefault(); + e.currentTarget.form?.requestSubmit(); + } + }; + + const handleCancel = () => { + const sid = sessionIdRef.current; + if (!sid) return; + sendRaw({ jsonrpc: "2.0", method: "session/cancel", params: { sessionId: sid } }); + }; + + const connectingHint = + conn === "connecting" || conn === "initializing" || conn === "creating-session" || conn === "loading-session"; + + return ( +
+
+ +
+ {messages.length === 0 && !streamingContent ? ( +
+
+

+ {connectingHint ? "Connecting to the agent…" : conn === "disconnected" ? "Disconnected" : "Start a conversation"} +

+

+ {connectingHint + ? "The first connection can take up to a minute while the sandbox resumes." + : conn === "disconnected" + ? "Use Reconnect below to start a new session." + : "To begin chatting with the agent, type your message in the input box below."} +

+
+
+ ) : ( + <> + {messages.map((message, index) => ( + + ))} + {streamingContent && } + + )} +
+
+ +
+ +
+
+ +
+ {conn === "disconnected" && ( + + )} +
+
+ +
+