Skip to content
Merged
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
48 changes: 48 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Copilot instructions for dev-sidecar

## Commands

- Dependency management:
- Install dependencies for the entire workspace from the repository root using `pnpm install`. Avoid using `npm install` for dependency installation in this project.
- If `pnpm install` reports dependency conflicts or version mismatches, update the relevant `package.json` entries to compatible versions (or adjust `peerDependencies`), then re-run `pnpm install`.

- Linting:
- Lint the repository from the root with `pnpm lint`.
- Auto-fix lint issues from the root with `pnpm lint:fix`.
- If `pnpm lint` or `pnpm lint:fix` fails, review the reported errors and manually fix the files indicated by the linter, then re-run the command.

- Testing:
- Run package tests where they live:
- `pnpm --filter @docmirror/dev-sidecar test`
- `pnpm --filter @docmirror/mitmproxy test`
- Run a single test file by passing it after `--`, for example:
- `pnpm --filter @docmirror/dev-sidecar test -- test/regex.test.js`
- `pnpm --filter @docmirror/mitmproxy test -- test/proxyTest.js`

- GUI development and packaging (from `packages/gui`):
- `npm run electron`
- `npm run electron:build`
- `npm run serve`
- `npm run lint`
- For GUI debugging: run `npm run electron` and open the Electron developer tools (application menu View → Toggle Developer Tools or the platform shortcut) to inspect renderer pages, console logs, and IPC traffic.

## High-level architecture

- This is a pnpm workspace monorepo with four packages:
- `packages/core`: shared app logic, config, shell helpers, system proxy handling, and plugin/module code.
- `packages/mitmproxy`: the HTTP(S) proxy, DNS, interception, PAC, and response/request rewrite layer.
- `packages/gui`: the Electron + Vue 2 desktop app.
- `packages/cli`: a small CLI entrypoint that loads user config and starts the proxy service.
- `packages/core/src/index.js` exposes the main API and owns process-level error handling plus config/state wiring.
- `packages/mitmproxy/src/index.js` creates the proxy server(s), applies proxy options, and reports status/errors back to the host process.
- `packages/gui/src/background.js` is the Electron main process: it loads config, creates the main window, tray, IPC bridges, and Windows-specific power-monitor behavior.
- Renderer code lives under `packages/gui/src/view/`; IPC/bridge code lives under `packages/gui/src/bridge/`.
- The CLI reads `packages/cli/src/user_config.json5`, prints a banner, and starts the core API with the mitmproxy service path.

## Repo-specific conventions

- Most runtime code in `core`, `mitmproxy`, and `cli` uses CommonJS; GUI code is the Electron/Vue app and is organized around the Electron main process plus renderer/bridge split.
- Keep GUI source imports aligned with the existing file layout and `.js` module naming used in the Electron entrypoints.
- Preserve the startup/shutdown flow: ensure the sequence remains where `core` initializes and manages the proxy lifecycle, `mitmproxy` performs network interception, and the GUI communicates exclusively through IPC bridges and the `core` API; avoid direct cross-component calls that bypass these boundaries.
- Native/module setup matters here: the repo uses a root `.npmrc` with a PhantomJS mirror and C++17 build flags for native modules.
- The project documents Node 22.x, Python 3.11/setuptools, and VS 2022 C++ tooling as the expected local environment for Windows builds.
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"lodash": "^4.18.1",
"log4js": "^6.9.1",
"node-powershell": "^4.0.0",
"request": "^2.88.2",
"spawn-sync": "^2.0.0",
"winreg": "^1.2.5"
},
Expand Down
31 changes: 5 additions & 26 deletions packages/core/src/modules/plugin/free-eye/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ function resolveTestsDir (customDir) {
return fs.existsSync(candidate) ? candidate : fallbackDir
}

async function loadAllTests (globalConfig, testsDir) {
async function loadAllTests (testsDir, globalConfig) {
const tests = []
const resolvedDir = resolveTestsDir(testsDir)
if (!fs.existsSync(resolvedDir)) {
Expand Down Expand Up @@ -85,37 +85,16 @@ function getNextTest (todoTests, doneTests) {
}

async function runTests (options = {}) {
const { configPath, testsDir } = options
const preferredConfigPath = configPath && configPath.length > 0
? (path.isAbsolute(configPath) ? configPath : path.join(PLUGIN_ROOT, configPath))
: null
const fallbackConfigPath = path.join(PLUGIN_ROOT, 'config.json')

const configCandidates = Array.from(new Set([preferredConfigPath, fallbackConfigPath].filter(Boolean)))

let globalConfig
let lastError
for (const candidatePath of configCandidates) {
try {
if (!fs.existsSync(candidatePath)) {
lastError = new Error(`Config file not found: ${candidatePath}`)
continue
}
const configData = fs.readFileSync(candidatePath, 'utf8')
globalConfig = JSON.parse(configData)
break
} catch (error) {
lastError = new Error(`Error reading config file (${candidatePath}): ${error.message}`)
}
}
const { testsDir, config } = options

const globalConfig = (config && typeof config === 'object') ? config : null
if (!globalConfig) {
throw lastError || new Error('Unable to load FreeEye config.')
throw new Error('FreeEye runtime config is required.')
}

const globalResults = {}
const summaries = []
const todoTests = await loadAllTests(globalConfig, testsDir)
const todoTests = await loadAllTests(testsDir, globalConfig)
console.log(
`Loaded ${todoTests.length} tests: ${
todoTests.map(t => t.getTestTag()).join(' ')}`,
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/modules/plugin/free-eye/config.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import mainConfig from '../../../config.js'
import mainConfig from '../../../config/index.js'

export default {
name: '网络检测',
statusOff: true,
enabled: false,
tip: '运行网络检测来评估当前网络环境',
startup: {},
// FreeEye 自带一套 tests(位于本目录的 checkpoints/ 和 config.json),
// FreeEye 自带一套 tests(位于本目录的 checkpoints/),
// 这里保留最小配置以便在 dev-sidecar 中显示和切换插件。
setting: {
testsDir: 'checkpoints',
// 默认网络请求超时时间(秒),插件内部的测试可以参考或覆盖
defaultTimeout: 3,
// 复用主配置里的 free_eye 默认值,避免重复维护两份配置
config: (mainConfig.configFromFiles ? mainConfig.configFromFiles.plugin.free_eye : mainConfig.plugin.free_eye),
},
// 复用主配置里的 free_eye 默认值,避免重复维护两份配置
...mainConfig.plugin.free_eye,
}
10 changes: 3 additions & 7 deletions packages/core/src/modules/plugin/free-eye/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import fs from 'node:fs'
import path from 'node:path'
import clientModule from './client.js'

const runTests = (clientModule && (clientModule.runTests || (clientModule.default && clientModule.default.runTests)))
const runTests = clientModule.runTests
import freeEyeConfig from './config.js'

const PLUGIN_STATUS_KEY = 'plugin.free_eye'
Expand Down Expand Up @@ -85,13 +85,9 @@ const FreeEyePlugin = function (context) {

const executeTests = async () => {
const currentConfig = config.get()
const pluginConfig = currentConfig.plugin.free_eye || {}
const setting = pluginConfig.setting || {}
const configFilePath = resolvePath(setting.testsConfigFile, 'config.json')
const testsDir = resolvePath(setting.testsDir, 'checkpoints')
log.info(`FreeEye tests triggering, config=${configFilePath}, testsDir=${testsDir}`)
const setting = currentConfig.plugin.free_eye.setting || {}
try {
const { result, logs } = await captureLogs(() => runTests({ configPath: configFilePath, testsDir }))
const { result, logs } = await captureLogs(() => runTests(setting))
const payload = {
finishedAt: new Date().toISOString(),
totalTests: result.totalTests,
Expand Down
Loading
Loading