# AGENTS.md — web-extensions ## Repository Overview A collection of supplementary ownCloud Web extensions maintained outside the main web repository. Each extension in `packages/web-app-*` is a standalone Vue 3 + TypeScript app that integrates into the oCIS (ownCloud Infinite Scale) platform. - **Classification:** oCIS - **Activity Status:** Active - **License:** Apache-2.0 - **Language:** Vue.js, TypeScript ## Architecture & Key Paths - `packages/` — pnpm workspace packages. `packages/web-app-*` are the extensions themselves (file viewers/editors such as `web-app-draw-io` and `web-app-json-viewer`; workflow additions such as `web-app-advanced-search`, `web-app-cast`, `web-app-importer`, `web-app-unzip`; and a family of `web-app-ai-*` sidebars and actions). `packages/ai-llm-proxy` is not a Vue app — see **AI extensions** below. Enumerate `packages/` rather than trusting a list in a doc; extensions are added often. - `docs/` — Documentation (starting guide, release workflow) - `dev/` — Development environment configuration, including `dev/docker/csp.yaml` and `dev/docker/ocis.apps.yaml` - `support/` — Support scripts - `Makefile` — Build orchestration - `package.json` — Root package with scripts - `pnpm-workspace.yaml` — pnpm monorepo workspace configuration - `playwright.config.ts` — Root Playwright config; each package extends it - `gettext.config.cjs` — Configures translation-string extraction - `eslint.config.js` — ESLint configuration ## Development Conventions - pnpm monorepo with workspace packages - Prettier for code formatting - Vite for building individual extensions - Playwright for e2e testing - GitHub Actions CI - Each extension is independently versioned and releasable - Release workflow documented in `docs/RELEASE_WORKFLOW.md` ## Build & Test Commands ```bash pnpm install # Install all dependencies pnpm build # Build all extensions pnpm build:w # Build all extensions in watch mode (development) pnpm lint # Run ESLint across all packages pnpm check:types # Run TypeScript checks across all packages pnpm test:unit # Run all unit tests pnpm test:e2e # Run all Playwright E2E tests ``` **Per-package** (replace `` with the package name, e.g. `draw-io`, `advanced-search`): ```bash pnpm --filter build pnpm --filter test:unit pnpm --filter test:e2e pnpm --filter check:types ``` **Local development environment:** ```bash pnpm build && docker compose up -d # Start full oCIS stack with all extensions mounted docker compose down --remove-orphans # Stop docker compose pull # Update non-extension images ``` Access the dev environment at `https://host.docker.internal:9200` (login `admin`/`admin`). Requires `127.0.1.1 host.docker.internal` in `/etc/hosts`. **Translations:** ```bash make l10n-read # Extract gettext strings and create .pot template make l10n-write # Generate translations.json from .po files make l10n-push # Push source strings to Transifex make l10n-pull # Pull translations from Transifex ``` ## Extension Anatomy Every `web-app-*` package follows the same pattern: - **`src/index.ts`** — entry point; calls `defineWebApplication()` from `@ownclouders/web-pkg` and returns `{ appInfo, routes, translations, extensions }` - **`vite.config.ts`** — uses `defineConfig` from `@ownclouders/extension-sdk` (a Vite wrapper) - **`l10n/translations.json`** — compiled translation strings consumed by the app - **`tests/unit/`** — Vitest unit tests using `@ownclouders/web-test-helpers` - **`tests/e2e/`** — Playwright tests; each package has a `playwright.config.ts` that extends the root one ### Extension registration Extensions register capabilities via the `extensions` array returned from `defineWebApplication`. The key extension types are: | Type | Purpose | Example | |------|---------|---------| | `AppWrapperRoute` | File editor (opens a file in the app) | draw-io, json-viewer | | `sidebarPanel` | Adds a panel to the file detail sidebar | ai-doc-summary, chat-with-file | | `action` | Context menu / file action | ai-doc-summary's "Summarize" action | | `appMenuItem` | Entry in the global app switcher | draw-io | ### Key dependencies - **`@ownclouders/web-pkg`** — core Web SDK: `defineWebApplication`, stores (Pinia), composables, routing helpers, design system components (`oc-*`) - **`@ownclouders/web-client`** — typed WebDAV/API client, `Resource` types - **`@ownclouders/extension-sdk`** — Vite `defineConfig` wrapper for extension builds - **`@ownclouders/web-test-helpers`** — `mount()` + `defaultPlugins()` for Vitest - **`vue3-gettext`** — i18n; use `$gettext`/`$pgettext`/`$ngettext` in templates and `useGettext()` in setup ### Design system All UI must use the ownCloud Design System components from `@ownclouders/web-pkg`. Do not use custom SVG icons when an `oc-icon` equivalent exists. Available icons: `packages/design-system/src/assets/icons` in the `owncloud/web` repo. ### Translations All user-facing strings must be wrapped with `$gettext()`, `$pgettext()` or `$ngettext()` (for count-aware plurals). The `gettext.config.cjs` at the repo root configures extraction, and translation files live in each package's `l10n/` directory. Translations are managed via Transifex and synced automatically — do not edit `.po` files or `translations.json` by hand. ### AI extensions The `web-app-ai-*` packages use the sidebar and action extension patterns to add AI-powered panels. They talk to an LLM through `packages/ai-llm-proxy`, which is a plain Node.js HTTP server (not a Vue app) that validates oCIS OIDC tokens and proxies requests to a configured LLM endpoint. The proxy is configured entirely via environment variables (`LLM_ENDPOINT`, `LLM_API_KEY`, `OCIS_URL`, etc.). **Security requirement — origin validation:** any code path that calls the LLM proxy (or any LLM endpoint) **must** validate the `Origin` header of incoming requests against `OCIS_URL` and reject requests whose origin does not match. CORS headers alone are browser-enforced and insufficient — the proxy must perform an explicit server-side origin check and return `403` for unexpected origins. Never skip this check when adding new LLM-calling code. ### Docker Compose and CSP `docker-compose.yml` mounts each extension's `dist/` directory into the oCIS container. When adding a new extension, add its `dist/` mount and update `dev/docker/csp.yaml` if it needs additional CSP directives. ### App configuration (`ocis.apps.yaml`) `dev/docker/ocis.apps.yaml` and `support/actions/ocis.apps.yaml` supply per-app config (e.g. LLM endpoint, companion URL) to oCIS. The **key for each entry must match the mount target directory**, not the package directory name or the app's internal `applicationId`. These two files use different conventions because their mounts differ: - **`dev/docker/ocis.apps.yaml`** (used by `docker-compose.yml`) — mounts strip the `web-app-` prefix (e.g. `./packages/web-app-chat-with-file/dist:/web/apps/chat-with-file`), so the key is `chat-with-file`. - **`support/actions/ocis.apps.yaml`** (used by CI at `.github/workflows/test.yml`) — the workflow mounts using `${{matrix.app}}` verbatim as the target directory (e.g. `/apps/web-app-chat-with-file`), so the key is `web-app-chat-with-file`. ## Adding a New Extension 1. Create `packages/web-app-/` following the structure of an existing extension 2. Add the `dist/` volume mount in `docker-compose.yml` 3. Provide an `l10n/` directory with the same structure as other extensions 4. Run `pnpm install` from the repo root after creating `package.json` ## Releasing Releases use per-app signed tags on the merge commit: `{app-name}-v{version}` (e.g. `draw-io-v0.4.1`). Each tag independently triggers CI to build, package and publish that extension. Push tags one at a time — pushing all at once does not reliably trigger the release workflow. See `docs/RELEASE_WORKFLOW.md` for the full process. ## Important Constraints - **Monorepo structure:** Each extension in `packages/` can be built and released independently. - **oCIS dependency:** Extensions require oCIS and ownCloud Web to function. - **Docker image:** Built and published as `owncloud/web-extensions` from [owncloud-docker/web-extensions](https://github.com/owncloud-docker/web-extensions), not from this repository. - **External dependencies:** Some extensions (draw.io, external sites) embed or connect to third-party services. ## OSPO Policy Constraints ### GitHub Actions - **Only** use actions owned by `owncloud`, created by GitHub (`actions/*`), verified on the GitHub Marketplace, or verified by the ownCloud Maintainers. - Pin all actions to their full commit SHA (not tags): `uses: actions/checkout@ # vX.Y.Z` - Never introduce actions from unverified third parties. ### Dependency Management - Dependabot is configured for automated dependency updates. - Review and merge Dependabot PRs as part of regular maintenance. - Do not introduce new dependencies without discussion in an issue first. ### Git Workflow - **Rebase policy**: Always rebase; never create merge commits. Use `git pull --rebase` and `git rebase` before pushing. - **Signed commits**: All commits **must** be PGP/GPG signed (`git commit -S -s`). - **DCO sign-off**: Every commit needs a `Signed-off-by` line (`git commit -s`). - **Conventional Commits & Squash Merge**: Use the [Conventional Commits](https://www.conventionalcommits.org/) format where the repository enforces it. Many repos use squash merge, where the PR title becomes the commit message on the default branch — apply Conventional Commits format to PR titles as well. A reusable GitHub Actions workflow enforces this. ## Context for AI Agents - This is a pnpm monorepo containing multiple independent web extensions. - Each extension in `packages/` has its own `package.json`, source code and build config. - The `docs/starting_guide.md` explains how to add new extensions to the repository. - Extensions register with the oCIS Web runtime via the extension system API. - Development environment uses Docker Compose with an oCIS backend.