Files
electron-opencode/.junie/AGENTS.md

5.0 KiB

Zhiju AI Assistant — Developer Notes

Project Overview

Electron desktop app (Electron Forge + Vite + Vue 3) that wraps a locally-spawned opencode binary (located in resources/). The renderer is a Vue 3 SPA; the main process manages the opencode child process and exposes IPC to the renderer via a preload script.


Build & Configuration

Prerequisites

  • Node.js ≥ 18
  • The opencode binary must exist at resources/windows/x64/opencode.exe (Windows) before packaging. It is not committed to the repo — obtain it separately.

Install dependencies

npm install

Run in development

npm start

This uses electron-forge start, which runs Vite for the renderer and launches Electron. Hot-reload is active for the renderer; changes to src/main/index.js or src/preload/index.js require a manual restart.

Package / distribute

npm run make        # builds installers for the current platform
npm run package     # produces an unpackaged app directory

Packaged output lands in out/. The opencode.exe binary is bundled via extraResource in forge.config.js and unpacked from ASAR at runtime.

Vite configs

File Purpose
vite.main.config.mjs Main process bundle
vite.preload.config.mjs Preload script bundle
vite.renderer.config.mjs Renderer (Vue SPA), alias @src/renderer

Architecture Notes

  • Main process (src/main/index.js): spawns opencode binary, resolves a free port starting at 4096, waits for TCP readiness, then injects window.__opencodeBaseUrl into the renderer via executeJavaScript. Also runs Bonjour service discovery.
  • Preload (src/preload/index.js): bridges IPC between main and renderer using contextBridge.
  • Renderer (src/renderer/): Vue 3 + Pinia + Vue Router. HTTP calls go through src/renderer/http/ (axios-based). SSE streaming is handled in src/renderer/http/sse.js.
  • URL constants (src/renderer/http/url.js): single source of truth for all API endpoint paths. getBaseUrl() reads window.__opencodeBaseUrl (injected by main) with fallback to http://127.0.0.1:4096.
  • Crypto (src/renderer/utils/crypto.js): passwords are Base64-encoded then RSA-encrypted (public key hardcoded) before being sent to the login API.

Testing

Framework

Vitest — chosen for native Vite/ESM compatibility, zero extra config needed.

Run all tests

npm test              # vitest run (single pass, CI-friendly)
npm run test:watch    # vitest watch mode (development)

Or directly:

npx vitest run

Writing tests

  • Place test files alongside the source file as *.test.js (Vitest picks them up automatically).
  • For pure utility/logic modules (e.g. url.js, crypto.js) no additional setup is needed.
  • Modules that depend on window, electron, or Pinia require mocking. Use vi.stubGlobal for window properties and vi.mock for module mocks.

Example: testing url.js

// src/renderer/http/url.test.js
import { describe, it, expect } from 'vitest';
import url from './url.js';

describe('url constants', () => {
  it('session.detail returns correct path', () => {
    expect(url.session.detail('abc123')).toBe('/session/abc123');
  });

  it('message.send returns correct path', () => {
    expect(url.message.send('sess1')).toBe('/session/sess1/message');
  });
});

Run with:

npx vitest run src/renderer/http/url.test.js

Testing modules that use window.__opencodeBaseUrl

import { vi, describe, it, expect, beforeEach } from 'vitest';

beforeEach(() => {
  vi.stubGlobal('__opencodeBaseUrl', 'http://127.0.0.1:5000');
});

// import and test getBaseUrl() etc.

Code Style

  • Formatter: Prettier (config in package.json defaults). Run npm run format to auto-fix, npm run format:check for CI check.
  • Commit messages: enforced by commitlint (@commitlint/config-conventional). Use conventional commits: feat:, fix:, chore:, etc.
  • Pre-commit hook: husky + lint-staged runs Prettier on staged *.js and *.vue files automatically.
  • No ESLint is configured — only Prettier for formatting.
  • Vue components use the Composition API (<script setup> style in newer components, options-style setup() returning refs in stores).
  • Pinia stores use the Setup Store pattern (function returning refs/actions), not the Options Store pattern.
  • Chinese comments are common throughout the codebase — maintain them in Chinese when editing existing files.

Key Dependencies

Package Role
electron v41 Desktop shell
electron-forge v7 Build/package toolchain
vite v5 + @vitejs/plugin-vue Renderer bundler
vue v3 + pinia + vue-router UI framework
axios HTTP client (renderer)
element-plus UI component library
bonjour-service LAN service discovery
jsencrypt + js-base64 Password encryption before login
unified / remark / rehype Markdown rendering pipeline
katex Math formula rendering in chat