Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ VITE_FRONTEND_PORT="3001" # Port to run the frontend application
VITE_USE_TLS="false" # Use HTTPS/WSS for proxied backend connections
VITE_INSECURE_SKIP_VERIFY="false" # Skip TLS certificate verification for proxied backend requests

# Deployment environment — controls which PostHog project key is compiled in.
# Set to "production" or "staging" only in real deployment build pipelines.
# Leave unset locally so developers never accidentally send telemetry.
# VITE_APP_ENV="production"

# Mocking / test helpers
VITE_MOCK_API="false" # Enable/disable API mocking with MSW

Expand Down
8 changes: 0 additions & 8 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,6 @@ jobs:

echo "tags=$TAGS" >> "$GITHUB_OUTPUT"

# Use production PostHog key only for tagged releases
if [[ "$RELEVANT_REF" == refs/tags/v* ]]; then
echo "vite_app_env=production" >> "$GITHUB_OUTPUT"
else
echo "vite_app_env=" >> "$GITHUB_OUTPUT"
fi

echo "=== Build outputs ==="
echo "Short SHA: $SHORT_SHA"
echo "Tags: $TAGS"
Expand All @@ -186,7 +179,6 @@ jobs:
AUTOMATION_VERSION=${{ steps.prep.outputs.automation_version }}
OPENHANDS_BUILD_GIT_SHA=${{ env.RELEVANT_SHA }}
OPENHANDS_BUILD_GIT_REF=${{ env.RELEVANT_REF }}
VITE_APP_ENV=${{ steps.prep.outputs.vite_app_env }}
cache-from: type=gha
cache-to: type=gha,mode=max
provenance: true
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,6 @@ return new ConversationClient(getAgentServerClientOptions()).someMethod(...);
- CI workflow: a `Read defaults from config/defaults.json` step uses `node -p` to extract values into `$GITHUB_OUTPUT`.
- Dockerfile ARG defaults are kept as fallbacks for local `docker build` without the CI workflow; CI always passes `--build-arg` overrides from the JSON.
- To bump a version, edit `config/defaults.json` only — the JS scripts, Docker build, and CI workflow all derive their values from it.
- Docker all-in-one image: `.github/workflows/docker.yml` builds and publishes `ghcr.io/openhands/agent-canvas` — a combined image that bundles the agent-server (from `ghcr.io/openhands/agent-server`), the automation server (`openhands-automation` via pip), and the agent-canvas frontend (static build). The Dockerfile lives at `docker/Dockerfile`, the entrypoint at `docker/entrypoint.sh`. The workflow structure mirrors the SDK repo's `server.yml`: a `build-and-push-image` matrix job (2 × arch: amd64 on `ubuntu-24.04`, arm64 on `ubuntu-24.04-arm`) pushes arch-suffixed tags, then `merge-manifests` creates multi-arch manifests via `docker buildx imagetools create`, then `consolidate-build-info` aggregates artifacts, and `update-pr-description` updates the PR body (using `<!-- AGENT_CANVAS_DOCKER_START -->` / `<!-- AGENT_CANVAS_DOCKER_END -->` markers). The workflow triggers on push to main, `v*` tags (releases), PRs, and `workflow_dispatch`. On release tags it also pushes semver tags (e.g. `1.2.3`, `1.2`, `1`, `latest`). Fork PRs are skipped (no GHCR auth). The image exposes port 8000 as a unified entry point: `/api/automation/*` → automation (:18001), `/api/*` → agent-server (:18000), `/*` → static frontend. The Dockerfile accepts a `VITE_APP_ENV` build arg (default empty → staging PostHog key); the CI workflow passes `VITE_APP_ENV=production` only for tagged releases (`refs/tags/v*`), so PR and main-branch images use the staging key while release images use the production key, matching the `build:lib` npm path. The entrypoint auto-generates **both** the session API key and `OH_SECRET_KEY` (persisted to `~/.openhands/agent-canvas/session-api-key.txt` and `secret-key.txt` respectively) when none is provided, so the image runs secure by default. Users can override either via env var (`OH_SECRET_KEY`, `SESSION_API_KEY` / `OH_SESSION_API_KEYS_0`). Unlike `scripts/dev-safe.mjs` (which uses a static default secret key for local dev convenience), the Docker entrypoint never falls back to a known default.
- Docker all-in-one image: `.github/workflows/docker.yml` builds and publishes `ghcr.io/openhands/agent-canvas` — a combined image that bundles the agent-server (from `ghcr.io/openhands/agent-server`), the automation server (`openhands-automation` via pip), and the agent-canvas frontend (static build). The Dockerfile lives at `docker/Dockerfile`, the entrypoint at `docker/entrypoint.sh`. The workflow structure mirrors the SDK repo's `server.yml`: a `build-and-push-image` matrix job (2 × arch: amd64 on `ubuntu-24.04`, arm64 on `ubuntu-24.04-arm`) pushes arch-suffixed tags, then `merge-manifests` creates multi-arch manifests via `docker buildx imagetools create`, then `consolidate-build-info` aggregates artifacts, and `update-pr-description` updates the PR body (using `<!-- AGENT_CANVAS_DOCKER_START -->` / `<!-- AGENT_CANVAS_DOCKER_END -->` markers). The workflow triggers on push to main, `v*` tags (releases), PRs, and `workflow_dispatch`. On release tags it also pushes semver tags (e.g. `1.2.3`, `1.2`, `1`, `latest`). Fork PRs are skipped (no GHCR auth). The image exposes port 8000 as a unified entry point: `/api/automation/*` → automation (:18001), `/api/*` → agent-server (:18000), `/*` → static frontend. The entrypoint auto-generates **both** the session API key and `OH_SECRET_KEY` (persisted to `~/.openhands/agent-canvas/session-api-key.txt` and `secret-key.txt` respectively) when none is provided, so the image runs secure by default. Users can override either via env var (`OH_SECRET_KEY`, `SESSION_API_KEY` / `OH_SESSION_API_KEYS_0`). Unlike `scripts/dev-safe.mjs` (which uses a static default secret key for local dev convenience), the Docker entrypoint never falls back to a known default.

- Cloud conversation resume gating: when a cloud conversation is closed from the UI (`pauseCloudSandbox` is called), the conversation's `conversation_url` is NOT cleared -- it still points to the old sandbox host. `WebSocketProviderWrapper` must suppress the URL (pass `null` to `ConversationWebSocketProvider`) while `sandbox_status === "PAUSED"`, otherwise the WebSocket immediately tries the stale URL before the sandbox wakes. Symmetrically, `useActiveConversation`'s refetch interval must fast-poll (3 s) on both `!conversation_url` AND `sandbox_status === "PAUSED"` -- checking only the missing URL would leave the hook on the 30 s interval while the sandbox is resuming. The resume sequence: navigate -> sandbox PAUSED detected -> `resumeCloudSandbox` called (in `conversation.tsx`) -> fast-poll detects RUNNING -> `conversationUrl` unblocked -> WebSocket connects.
5 changes: 0 additions & 5 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,6 @@ RUN npm ci
COPY . .

# Build the static frontend.
# VITE_APP_ENV controls the PostHog telemetry key baked into the bundle:
# "production" → prod key (set by CI for tagged releases)
# anything else → staging key (default for PR / main / local builds)
ARG VITE_APP_ENV=""
ENV VITE_APP_ENV=${VITE_APP_ENV}
RUN npm run build

# ── Stage 1b: Generate shell-sourceable defaults from config/defaults.json ──
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
"typecheck:staged": "react-router typegen && npx tsc --noEmit --skipLibCheck",
"check-translation-completeness": "node scripts/check-translation-completeness.cjs",
"build:app": "npm run make-i18n && react-router build",
"build:lib": "npm run make-i18n && react-router typegen && cross-env BUILD_LIB=true VITE_APP_ENV=production vite build && tsc -p tsconfig.lib.json",
"build:lib": "npm run make-i18n && react-router typegen && cross-env BUILD_LIB=true vite build && tsc -p tsconfig.lib.json",
"build:docker": "node scripts/docker-build.mjs"
},
"lint-staged": {
Expand Down
16 changes: 4 additions & 12 deletions src/services/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,12 @@ const TELEMETRY_CONSENT_KEY = "openhands-telemetry-consent";
const TELEMETRY_FIRST_USE_KEY = "openhands-telemetry-first-use";
const TELEMETRY_SESSION_KEY = "openhands-telemetry-session";

// PostHog project keys — one per deployment environment, hardcoded so they
// are baked into the static bundle at build time and cannot drift at runtime.
// Replace POSTHOG_STAGING_KEY with a dedicated project key once provisioned.
const POSTHOG_PROD_KEY = "phc_BgzfxKdgsYMLFTmJqt424ZoyVHvKFfrwttLimzdYTKFK";
const POSTHOG_STAGING_KEY = "phc_kBtz5nKmxVRRQ7HtPwr2QX9eMC5j65zE86QKocVNwb4U";

// Always use the staging key unless VITE_APP_ENV is explicitly set to
// "production" at bundle time (hardcoded in build:lib and production CI).
// Library consumers can always override with VITE_POSTHOG_API_KEY.
// Default to the OpenHands PostHog project key, hardcoded so it is baked into
// the static bundle at build time and cannot drift at runtime. Library
// consumers can always override with VITE_POSTHOG_API_KEY.
const POSTHOG_API_KEY: string =
(import.meta.env.VITE_POSTHOG_API_KEY as string | undefined) ||
(import.meta.env.VITE_APP_ENV === "production"
? POSTHOG_PROD_KEY
: POSTHOG_STAGING_KEY);
"phc_BgzfxKdgsYMLFTmJqt424ZoyVHvKFfrwttLimzdYTKFK";

// Default to OpenHands' reverse proxy to bypass ad blockers.
// The proxy at z.openhands.dev routes to PostHog's US region.
Expand Down
Loading