Files
platform/package.json
pastilhasandClaude Opus 5 035a1ba8f6 add photos, an immich-backed library behind its own sidecar
officer-photos owns the whole Immich contract: the instance URL and the API
key live there and nowhere else, and the platform side is an auth-gated
forwarder holding no credentials. The route surface is an allow-list keyed on
the first path segment, so admin, auth, api-keys, sessions, jobs, system-config
and libraries are unreachable by construction rather than by enumeration.

The UI mirrors Immich's own sidebar — timeline, explore, map, search, albums,
people, favorites, sharing, archive, trash — because the point of a sidecar
screen is to reproduce what the upstream already ships, then extend it. The
timeline reads Immich's columnar time-bucket format directly; selection lives
in the URL per docs/navigation-audit.md.

Two things worth knowing for anyone touching this later:

- `duration` is an integer count of milliseconds in Immich 3.0. It was an
  HH:MM:SS.mmm string before, and every stale example still shows that form.
- the map container is sized with h-full/w-full, never `absolute inset-0`.
  maplibre's stylesheet sets `position: relative; overflow: hidden` on the
  element it is given, and an unlayered vendor rule beats Tailwind 4's layered
  `.absolute` regardless of source order — so the div collapses to height 0 and
  clips its own canvas away. Nothing errors: the GL context is healthy, tiles
  download and pixels are drawn into a buffer nobody ever composites.

maplibre-gl is pinned to 5.x deliberately; 6.0 resolves a separate worker file
from import.meta.url, which Officer's index.html fallback answers with HTML.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 00:18:33 +00:00

188 lines
6.5 KiB
JSON

{
"name": "officer",
"version": "0.1.0",
"private": true,
"type": "module",
"workspaces": [
"src/databases/*",
"src/workspaces/*"
],
"scripts": {
"preinstall": "node -e \"var v = +process.versions.node.split('.')[0]; if (v < 22 || v > 22) { console.error('Node 22 required (got ' + process.versions.node + '). Run: nvm use 22'); process.exit(1); }\"",
"gen:index": "bun run ./scripts/gen-index.ts",
"predev": "bun run ./scripts/gen-index.ts",
"dev": "bun --env-file=.env --watch src/server.tsx",
"prestart": "bun run ./scripts/gen-index.ts",
"start": "NODE_ENV=production bun src/server.tsx",
"prebuild": "bun run ./scripts/prebuild.ts",
"build:web": "bun run ./scripts/build/web.ts",
"build:dashboard": "bun run ./scripts/build/dashboard.ts",
"build:landing": "bun run ./scripts/build/landing.ts",
"db:gen": "cd src/databases/officer_db && bun run generate",
"db:push": "cd src/databases/officer_db && bun run push",
"db:migrate": "cd src/databases/officer_db && bun run migrate",
"dev:emailer": "cd src/workspaces/emailer && bun run dev",
"format": "{ git diff --name-only HEAD -- 'src/**/*.ts' 'src/**/*.tsx'; git ls-files --others --exclude-standard -- 'src/**/*.ts' 'src/**/*.tsx'; } | xargs -r prettier --write",
"format:all": "prettier --write \"src/**/*.{ts,tsx}\"",
"format:check": "prettier --check \"src/**/*.{ts,tsx}\"",
"setup": "bash scripts/setup.sh"
},
"dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.2.41",
"@bitcoinerlab/secp256k1": "1.2.0",
"@getalby/sdk": "^8.0.3",
"@hookform/resolvers": "^5.2.2",
"@modelcontextprotocol/sdk": "^1.27.1",
"@monaco-editor/react": "^4.7.0",
"@noble/hashes": "2.2.0",
"@noble/secp256k1": "^3.1.0",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-alert-dialog": "^1.1.15",
"@radix-ui/react-aspect-ratio": "^1.1.7",
"@radix-ui/react-avatar": "^1.1.10",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-collapsible": "^1.1.12",
"@radix-ui/react-context-menu": "^2.2.16",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-hover-card": "^1.1.15",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-menubar": "^1.1.16",
"@radix-ui/react-navigation-menu": "^1.2.14",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-progress": "^1.1.7",
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slider": "^1.3.6",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-toggle": "^1.1.10",
"@radix-ui/react-toggle-group": "^1.1.11",
"@radix-ui/react-tooltip": "^1.2.8",
"@react-oauth/google": "^0.13.4",
"@react-three/drei": "^10.7.7",
"@react-three/fiber": "^9.5.0",
"@scure/bip32": "^2.2.0",
"@scure/bip39": "1.6.0",
"@simplewebauthn/browser": "^13.2.2",
"@simplewebauthn/server": "^13.2.2",
"@tabler/icons-react": "^3.36.0",
"@tanstack/react-query": "^5.90.5",
"@tanstack/react-virtual": "^3.13.18",
"@tiptap/extension-text-align": "^3.15.3",
"@types/mailparser": "^3.4.6",
"@types/three": "^0.182.0",
"@typescript/native-preview": "^7.0.0-dev.20260107.1",
"@uiw/react-textarea-code-editor": "^3.1.1",
"@visual-json/core": "^0.1.1",
"@visual-json/react": "^0.1.1",
"@xterm/addon-fit": "^0.11.0",
"@xterm/addon-search": "^0.16.0",
"@xterm/addon-serialize": "^0.14.0",
"@xterm/addon-unicode11": "^0.9.0",
"@xterm/addon-web-links": "^0.12.0",
"@xterm/addon-webgl": "^0.19.0",
"@xterm/xterm": "^6.0.0",
"argon2": "^0.44.0",
"bech32": "2.0.0",
"bitcoinjs-lib": "6.1.5",
"bun-plugin-tailwind": "^0.1.2",
"check-password-strength": "^3.0.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"config": "workspace:*",
"cron": "^4.3.3",
"date-fns": "^4.1.0",
"definitions": "workspace:*",
"dotenv": "^17.2.3",
"drizzle-orm": "^0.45.1",
"ecpair": "3.0.1",
"emailer": "workspace:*",
"embla-carousel-react": "^8.6.0",
"googleapis": "^169.0.0",
"helpers": "workspace:*",
"hono": "^4.11.1",
"hooks": "workspace:*",
"html-to-image": "^1.11.13",
"html2canvas": "^1.4.1",
"i18n": "workspace:*",
"idb-keyval": "^6.2.2",
"imapflow": "^1.2.10",
"injector": "workspace:*",
"input-otp": "^1.4.2",
"js-beautify": "^1.15.4",
"jsqr": "^1.4.0",
"jwt-decode": "^4.0.0",
"lucide-react": "^0.562.0",
"mailparser": "^3.9.3",
"maplibre-gl": "^5.24.0",
"markdown-it": "^14.1.0",
"material-file-icons": "^2.4.0",
"monaco-editor": "^0.55.1",
"next-themes": "^0.4.6",
"node-pty": "^1.1.0",
"nodemailer": "^7.0.12",
"officerdb": "workspace:*",
"officerdev": "workspace:*",
"pg": "^8.16.3",
"postgres": "^3.4.5",
"qrcode": "^1.5.4",
"react": "^19",
"react-countup": "^6.5.3",
"react-day-picker": "^9.13.0",
"react-dom": "^19",
"react-hook-form": "^7.69.0",
"react-markdown": "^10.1.0",
"react-resizable-panels": "^4.0.15",
"react-router": "^7.11.0",
"react-spinners": "^0.17.0",
"recharts": "3.6.0",
"redis": "^5.8.3",
"rehype-raw": "^7.0.0",
"rehype-sanitize": "^6.0.0",
"rehype-slug": "^6.0.0",
"remark-gfm": "^4.0.1",
"shiki": "^3.22.0",
"sonner": "^2.0.7",
"sounds": "workspace:*",
"state": "workspace:*",
"tailwind-merge": "^3.3.1",
"tailwindcss-animate": "^1.0.7",
"three": "^0.182.0",
"types": "workspace:*",
"varuint-bitcoin": "^2.0.0",
"vaul": "^1.1.2",
"widgets": "workspace:*",
"ws": "^8.18.1",
"zod": "^4.2.1"
},
"devDependencies": {
"@playwright/test": "^1.57.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/react": "^16.3.2",
"@types/bun": "1.3.5",
"@types/chrome": "^0.1.36",
"@types/markdown-it": "^14.1.2",
"@types/node-telegram-bot-api": "^0.64.14",
"@types/nodemailer": "^7.0.5",
"@types/pg": "^8.15.5",
"@types/qrcode": "^1.5.6",
"@types/react": "^19",
"@types/react-dom": "^19",
"drizzle-kit": "^0.31.8",
"happy-dom": "^20.3.7",
"playwright": "^1.57.0",
"prettier": "^3.6.2",
"tailwindcss": "^4.1.11",
"tsx": "^4.20.6",
"tw-animate-css": "^1.4.0",
"typescript": "^5.9.3",
"vite": "^7.3.0",
"vite-plugin-compression": "^0.5.1"
}
}