mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-14 17:40:37 +02:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Environments are now managed as instance-level runtime resources rather than per-company rows > - The custom environment image setup tables were introduced with their own `company_id` columns and route query parameters > - That split made one saved environment image state depend on an extra company context even though the environment itself is the durable owner > - It also made saved-environment probes harder because applying the active custom image template could require a company context when no secret-backed config needed one > - This pull request scopes custom image templates and setup sessions directly to the saved environment > - The benefit is that reusable environment images follow the same instance-scoped model as environments while secret resolution still uses company context only when secrets require it ## Linked Issues or Issue Description No matching public GitHub issue was found. Bug report: ### What happened? saved environment custom-image routes and persistence required a `companyId` even though environments are instance-scoped, and saved sandbox probes did not opt into active custom-image template application unless a company context was present. ### Expected behavior custom-image templates and setup sessions should be owned by the saved environment, and saved sandbox probes should apply the active template while still requiring a company context only for secret-backed runtime config. ### Steps to reproduce 1. Configure an instance-scoped sandbox environment with custom-image setup support. 2. Start or inspect a custom-image session or template for that saved environment. 3. Probe the saved environment without a custom-image-specific `companyId` query parameter. ### Paperclip version or commit current `master` after the environment custom-image template migration. ### Deployment mode Local dev (pnpm dev) or authenticated local Paperclip instance. ### Installation method Built from source (pnpm dev / pnpm build). ### Agent adapter(s) involved Not adapter-specific (core bug). ### Database mode Embedded PGlite/Postgres dev database. ### Access context Board human operator. ### Privacy checklist No logs, secrets, tokens, private URLs, or local machine paths are included. Duplicate search performed: - `gh search prs "environment custom image companyId repo:paperclipai/paperclip" --state open --limit 20` - `gh search prs "custom image environment scoped repo:paperclipai/paperclip" --state open --limit 20` - `gh search issues "environment custom image repo:paperclipai/paperclip" --state open --limit 20` The returned results were unrelated adapter, Docker, auth, or stale-workspace items. ## What Changed - Removed redundant `company_id` columns from environment custom-image templates and setup sessions. - Added migration `0127_environment_custom_images_instance_scoped` to collapse duplicate active rows per environment before dropping the old company-scoped indexes/columns. - Updated custom-image services, route handlers, shared validators, and UI API/query keys to use environment-scoped custom-image state. - Kept runtime secret resolution company-aware only when secret refs or bindings require a company context. - Made saved sandbox environment probes opt into active custom-image template application. - Updated DB, shared, server, and UI tests for the new environment-scoped contract. ## Verification - `pnpm --filter @paperclipai/db run check:migrations` - `pnpm exec vitest run packages/db/src/environment-custom-images-schema.test.ts packages/shared/src/environment-custom-images.test.ts server/src/__tests__/environment-custom-image-routes.test.ts server/src/__tests__/environment-custom-images-service.test.ts server/src/__tests__/environment-routes.test.ts ui/src/pages/CompanyEnvironments.test.tsx` - `pnpm -r typecheck` - `pnpm test:run` before rebasing onto latest `master`; after the rebase only the migration number changed, and the migration check plus focused suite, typecheck, and build were rerun. - `pnpm build` ## Risks - Migration safety: the migration supersedes duplicate active templates per environment and fails duplicate active setup sessions before adding environment-only unique indexes. Operators with duplicate historical active rows should review which active template is kept. - Behavior shift: plugin custom-image setup calls now receive `companyId: "instance"` when no secret binding determines a concrete company context. - Secret-backed configs still require an explicit or uniquely inferable company context; environments with secret bindings spread across multiple companies continue to fail fast. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used OpenAI Codex via the `codex_local` adapter, GPT-5-based coding model with tool-enabled repository inspection, editing, testing, git, and GitHub CLI access. Exact context-window metadata was not exposed by the runtime. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>
113 lines
4.3 KiB
TypeScript
113 lines
4.3 KiB
TypeScript
import type {
|
|
CancelEnvironmentCustomImageSetupSession,
|
|
Environment,
|
|
EnvironmentCapabilities,
|
|
EnvironmentLease,
|
|
EnvironmentProbeResult,
|
|
EnvironmentCustomImageSetupSession,
|
|
EnvironmentCustomImageTemplate,
|
|
FinishEnvironmentCustomImageSetupSession,
|
|
StartEnvironmentCustomImageSetupSession,
|
|
} from "@paperclipai/shared";
|
|
import { api } from "./client";
|
|
|
|
export interface EnvironmentCustomImageOverview {
|
|
activeTemplate: EnvironmentCustomImageTemplate | null;
|
|
activeSession: EnvironmentCustomImageSetupSession | null;
|
|
latestSession: EnvironmentCustomImageSetupSession | null;
|
|
}
|
|
|
|
export interface EnvironmentCustomImageConnectionPayload {
|
|
type: string;
|
|
command?: string | null;
|
|
token?: string | null;
|
|
expiresAt?: string | null;
|
|
metadata?: Record<string, unknown> | null;
|
|
}
|
|
|
|
export interface EnvironmentCustomImageSetupSessionResult {
|
|
session: EnvironmentCustomImageSetupSession;
|
|
connectionPayload: EnvironmentCustomImageConnectionPayload | null;
|
|
}
|
|
|
|
export interface EnvironmentCustomImageFinishResult extends EnvironmentCustomImageSetupSessionResult {
|
|
template: EnvironmentCustomImageTemplate;
|
|
}
|
|
|
|
export interface EnvironmentCustomImageRollbackResult {
|
|
activeTemplate: EnvironmentCustomImageTemplate;
|
|
supersededTemplate: EnvironmentCustomImageTemplate;
|
|
}
|
|
|
|
export const environmentsApi = {
|
|
list: (companyId: string) => api.get<Environment[]>(`/companies/${companyId}/environments`),
|
|
capabilities: (companyId: string) =>
|
|
api.get<EnvironmentCapabilities>(`/companies/${companyId}/environments/capabilities`),
|
|
lease: (leaseId: string) => api.get<EnvironmentLease>(`/environment-leases/${leaseId}`),
|
|
create: (companyId: string, body: {
|
|
name: string;
|
|
description?: string | null;
|
|
driver: "local" | "ssh" | "sandbox" | "plugin";
|
|
config?: Record<string, unknown>;
|
|
metadata?: Record<string, unknown> | null;
|
|
}) => api.post<Environment>(`/companies/${companyId}/environments`, body),
|
|
update: (environmentId: string, body: {
|
|
name?: string;
|
|
description?: string | null;
|
|
driver?: "local" | "ssh" | "sandbox" | "plugin";
|
|
status?: "active" | "archived";
|
|
config?: Record<string, unknown>;
|
|
metadata?: Record<string, unknown> | null;
|
|
}) => api.patch<Environment>(`/environments/${environmentId}`, body),
|
|
probe: (environmentId: string) => api.post<EnvironmentProbeResult>(`/environments/${environmentId}/probe`, {}),
|
|
probeConfig: (companyId: string, body: {
|
|
name?: string;
|
|
driver: "local" | "ssh" | "sandbox" | "plugin";
|
|
description?: string | null;
|
|
config?: Record<string, unknown>;
|
|
metadata?: Record<string, unknown> | null;
|
|
}) => api.post<EnvironmentProbeResult>(`/companies/${companyId}/environments/probe-config`, body),
|
|
customImageTemplate: (environmentId: string) =>
|
|
api.get<EnvironmentCustomImageOverview>(`/environments/${environmentId}/custom-image-template`),
|
|
startCustomImageSetupSession: (
|
|
environmentId: string,
|
|
body: StartEnvironmentCustomImageSetupSession = {},
|
|
) =>
|
|
api.post<EnvironmentCustomImageSetupSessionResult>(
|
|
`/environments/${environmentId}/custom-image-setup-sessions`,
|
|
body,
|
|
),
|
|
customImageSetupSession: (sessionId: string) =>
|
|
api.get<EnvironmentCustomImageSetupSessionResult>(
|
|
`/environment-custom-image-setup-sessions/${sessionId}`,
|
|
),
|
|
finishCustomImageSetupSession: (
|
|
sessionId: string,
|
|
body: FinishEnvironmentCustomImageSetupSession = {},
|
|
) =>
|
|
api.post<EnvironmentCustomImageFinishResult>(
|
|
`/environment-custom-image-setup-sessions/${sessionId}/finish`,
|
|
body,
|
|
),
|
|
cancelCustomImageSetupSession: (
|
|
sessionId: string,
|
|
body: CancelEnvironmentCustomImageSetupSession = {},
|
|
) =>
|
|
api.post<EnvironmentCustomImageSetupSession>(
|
|
`/environment-custom-image-setup-sessions/${sessionId}/cancel`,
|
|
body,
|
|
),
|
|
rollbackCustomImageTemplate: (environmentId: string) =>
|
|
api.post<EnvironmentCustomImageRollbackResult>(
|
|
`/environments/${environmentId}/custom-image-template/rollback`,
|
|
{},
|
|
),
|
|
disableCustomImageTemplate: (
|
|
environmentId: string,
|
|
options: { deleteProviderTemplate?: boolean } = {},
|
|
) =>
|
|
api.delete<EnvironmentCustomImageTemplate>(
|
|
`/environments/${environmentId}/custom-image-template?deleteProviderTemplate=${options.deleteProviderTemplate === true ? "true" : "false"}`,
|
|
),
|
|
};
|