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
44 changes: 28 additions & 16 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ name: Build Windows Installer
on:
push:
tags: ['v*']
workflow_dispatch: # 允许手动触发
# P132: a manual "Run workflow" now builds the same Setup installer + zip and attaches
# them to the run (no Release). Before, the packaging steps were tag-only, so a manual
# run produced nothing to download.
workflow_dispatch:

permissions:
contents: write
Expand Down Expand Up @@ -70,19 +73,18 @@ jobs:
# vendor/python/ before electron-builder runs. Idempotent — safe to re-run.
# This is a build-time network op; end users have no network requirement.
- name: Prepare bundled Python runtime
if: startsWith(github.ref, 'refs/tags/')
if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
shell: pwsh
run: ./scripts/prepare-python.ps1

- name: Build
run: npm run build

# P110: NSIS setup retired (2026-08-04) — users found the installer repeatedly
# unusable, so tag builds now ship ONLY the portable single-file exe.
# npm run dist = prepare-python + build + electron-builder (target: portable
# from electron-builder.yml) → release/Millwright-Portable-<ver>-x64.exe
- name: Package (portable / 免安装版)
if: startsWith(github.ref, 'refs/tags/')
# npm run dist = prepare-python + build + electron-builder (targets from
# electron-builder.yml, P129): release/Millwright-Setup-<ver>-x64.exe (NSIS
# installer, auto-update) + release/Millwright-<ver>-x64.zip (extract-and-run).
- name: Package (Setup installer + zip)
if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
run: npm run dist
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -93,13 +95,15 @@ jobs:
# that class of accident impossible. It also confirms the installer filename carries
# the version from package.json (the old electron-builder cache bug).
- name: Verify packaged payload
if: startsWith(github.ref, 'refs/tags/')
id: verify
if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$res = 'release/win-unpacked/resources'
$version = (Get-Content package.json -Raw | ConvertFrom-Json).version
Write-Host "package.json version: ${version}"
"version=${version}" >> $env:GITHUB_OUTPUT

$required = @(
"$res/sidecar/sw_agent/server.py",
Expand Down Expand Up @@ -151,16 +155,24 @@ jobs:
if ($y -notmatch [regex]::Escape($version)) { Write-Host "::error::latest.yml version mismatch"; exit 1 }
Write-Host "installer + latest.yml: OK"

# P110 v3: extract-and-run zip (nsis + portable exe both retired).
# npm run dist with the zip target produces release/*.zip containing the
# full win-unpacked tree — download, extract, run Millwright.exe.
# P132: the run page offers the INSTALLER. The old artifact was release/win-unpacked/**,
# which GitHub serves as one zip that extracts to a bare app folder — easy to mistake
# for "the build has no installer". archive:false uploads each file as-is, so the
# artifact downloads as the .exe / .zip itself (the artifact is named after the file).
- name: Upload artifact (Setup installer)
if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
uses: actions/upload-artifact@v7
with:
path: release/Millwright-Setup-${{ steps.verify.outputs.version }}-x64.exe
archive: false
retention-days: 30

- name: Upload artifact (portable directory)
if: startsWith(github.ref, 'refs/tags/')
- name: Upload artifact (zip, extract-and-run)
if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
uses: actions/upload-artifact@v7
with:
name: Millwright
path: release/win-unpacked/**
path: release/Millwright-${{ steps.verify.outputs.version }}-x64.zip
archive: false
retention-days: 30

- name: Create Release
Expand Down
89 changes: 89 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,95 @@

## [Unreleased]

## [0.2.132] - 2026-10-02

### Fixed (P133 — tools built the part in a SolidWorks the user could not see)

**What the user saw:** a full session reported every step built and verified (the
`build_part` volume matched, faces were listed, a screenshot was taken), yet the user's
SolidWorks window stayed empty.

**Cause:** the sidecar connects with `Dispatch("SldWorks.Application")`. That attaches to the
running SolidWorks only when COM can reach it. When it cannot, COM silently **starts a new,
invisible SolidWorks** and every tool runs there. The usual reason COM cannot reach it is that
Millwright and SolidWorks run at different privilege levels, e.g. one of them "Run as
administrator" (the running-object table is separate per integrity level). Nothing ever set
`Visible`, so the user could not see it.

**Fix:**
- **On connect:** if the instance is not visible, the sidecar shows it (`Visible`,
`UserControl`), checks the SLDWORKS.exe process list before and after to tell "started a
second instance" from "SolidWorks was not running", and records a note that names the
likely cause and the fix. The note also says whether Millwright itself is elevated.
- **Where the note appears:** the sidecar attaches it to tool results as `_connection`, and
the chat shows it once per session.

**`mass_properties` "CreateMassProperty/2 unavailable":** a COM object is always callable
(`__call__` forwards to its default member). Under late binding, `getattr(ext,
"CreateMassProperty")` already returns the MassProperty object, and the code then called it
again. `sw_get` now returns a COM object instead of calling it, which fixes the same trap
for every object-returning member read through it. `mass_properties` uses `sw_get`.

### Tests
- **`sidecar/tests/test_p133_visibility.py`:** `sw_get` with late-bound objects,
`mass_properties` with a late-bound `CreateMassProperty`, a hidden second instance shown
and reported, and a hidden only instance shown. Fails 5/5 on 0.2.131.
- **Totals:** 197 JS + 63 Python tests.

## [0.2.131] - 2026-10-02

### Changed (P132 — providers × protocols, installer on the run page)

**Settings: providers now serve both protocols.** Most providers expose an
OpenAI-compatible URL and an Anthropic-compatible one. DeepSeek, for example, serves
`https://api.deepseek.com` and `https://api.deepseek.com/anthropic`.
- **Quick-fill buttons, both protocols:** before, they only appeared under the OpenAI
protocol and always filled the OpenAI URL. They now appear under both protocols and fill
the URL **for the selected protocol**.
- **Model list per provider:** the model list shows only the active provider's models plus
*Custom model*. Before, it listed every vendor's models under a protocol.
- **Protocol switch:** switching protocol keeps the provider when it serves both, and
keeps the model if that provider lists it. Otherwise it falls back to the protocol's
official endpoint.
- **Data:** presets are reorganised as `PROVIDERS` (per-protocol URLs, models, defaults).
New pure helpers `providerForURL` / `modelOptions` / `switchProtocol` /
`applyProviderPreset` are covered by tests. `MODEL_PRESETS` and
`OPENAI_COMPATIBLE_PROVIDERS` remain as derived lists.
- **Anthropic-compatible URLs added:**
- DeepSeek `…/anthropic`
- Kimi `api.moonshot.cn/anthropic`
- MiniMax `api.minimax.io/anthropic`
- GLM `open.bigmodel.cn/api/anthropic`
- Qwen `dashscope.aliyuncs.com/apps/anthropic`
- Ollama `localhost:11434`

**Model IDs**
- **DeepSeek:** V4.1 Flash (2026-09-10) is `deepseek-flash`. DeepSeek retired
`deepseek-v4-flash`, which is now only routed over, so it is removed from the presets.
`deepseek-v4-pro` stays.
- **Kimi:** `kimi-k3` is added and suggested.
- **Qwen:** `qwen3.8-max` is added and suggested.

**Build: the installer is on the run page.**
- **What looked like "only a zip":** the Actions artifact was `release/win-unpacked/**`.
GitHub serves that as one zip which extracts to a bare app folder, so it was easy to read
as "the build has no installer". The Release itself did carry the Setup exe.
- **Artifacts now:** they are the Setup installer and the zip, uploaded with
`archive: false`, so they download as the `.exe` / `.zip` themselves.
- **Manual runs:** a manual **Run workflow** now packages and verifies too, and attaches
the installer to the run without creating a Release. Before, it only ran the checks.

### Tests
- **`tests/presets.test.mjs`:**
- every provider URL is valid
- suggested models are listed
- no retired DeepSeek id remains
- URL → provider resolution
- the per-provider model list
- protocol switching
- quick-fill by protocol
- **Totals:** 197 JS + 58 Python tests.

## [0.2.130] - 2026-10-01

### Fixed (P131 — current models, and what P129/P130 broke)
Expand Down
34 changes: 18 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@
</p>

<p align="center">
<img src="https://img.shields.io/badge/version-0.2.130-blue" alt="version" />
<img src="https://img.shields.io/badge/version-0.2.132-blue" alt="version" />
<img src="https://img.shields.io/badge/electron-28-47848F?logo=electron" alt="electron" />
<img src="https://img.shields.io/badge/react-18-61DAFB?logo=react" alt="react" />
<img src="https://img.shields.io/badge/typescript-5.3-3178C6?logo=typescript" alt="typescript" />
<img src="https://img.shields.io/badge/python-3.9%2B-3776AB?logo=python&logoColor=white" alt="python" />
<img src="https://img.shields.io/badge/tests-191_JS_%2B_58_Python-brightgreen" alt="tests" />
<img src="https://img.shields.io/badge/tests-197_JS_%2B_63_Python-brightgreen" alt="tests" />
<img src="https://img.shields.io/badge/license-Apache_2.0-orange" alt="license" />
</p>

Expand Down Expand Up @@ -86,7 +86,7 @@ Millwright:
- **Agentic tool loop.** Observe → reason → act. The model chains multiple tool calls, reads structured JSON back from each one, and recovers from errors instead of failing silently.
- **Visual understanding.** Reorient, rotate, screenshot, and analyze the model — via a multimodal main model or a dedicated vision model.
- **Resident execution engine.** A persistent Python sidecar holds one COM connection open across an entire multi-step task.
- **Developer-friendly.** 191 TypeScript/Node tests plus a Python suite (`pytest sidecar/tests`) for the sidecar, a typed IPC boundary, and a `SKIP_SW_CONNECT` mode for UI-only development without SolidWorks installed.
- **Developer-friendly.** 197 TypeScript/Node tests plus a Python suite (`pytest sidecar/tests`) for the sidecar, a typed IPC boundary, and a `SKIP_SW_CONNECT` mode for UI-only development without SolidWorks installed.

## Cross-version compatibility

Expand Down Expand Up @@ -158,19 +158,21 @@ A `Millwright-*-x64.zip` is also published alongside the Setup installer for use

## Supported AI providers

| Provider | Protocol | Base URL | Suggested model |
| Provider | OpenAI-compatible URL | Anthropic-compatible URL | Suggested model |
|---|---|---|---|
| OpenAI | OpenAI | `https://api.openai.com/v1` | `gpt-6-astra` (GPT-6 Astra) |
| Anthropic | Anthropic | `https://api.anthropic.com` | `claude-opus-5-5` (Opus 5.5, default) / `claude-fable-5-1` (Fable 5.1, most capable) |
| DeepSeek | OpenAI-compatible | `https://api.deepseek.com` | `deepseek-v4-pro` |
| Kimi / Moonshot | OpenAI-compatible | `https://api.moonshot.cn/v1` | `kimi-k3` |
| MiniMax | OpenAI-compatible | `https://api.minimaxi.com/v1` | `minimax-m3` |
| Alibaba Bailian (Qwen) | OpenAI-compatible | `https://dashscope.aliyuncs.com/compatible-mode/v1` | `qwen-3.8max` |
| Zhipu (GLM) | OpenAI-compatible | `https://open.bigmodel.cn/api/paas/v4` | `glm-4.6` |
| SiliconFlow | OpenAI-compatible | `https://api.siliconflow.cn/v1` | — |
| Ollama (local) | OpenAI-compatible | `http://localhost:11434/v1` | — |

> Model IDs move fast — check your provider's docs for the current lineup. Agentic tool calling requires a model that supports function calling; GPT-6 Astra, Claude Opus 5.5 / Fable 5.1, DeepSeek V4, Kimi K3, MiniMax M3, and GLM-4.6 are first-class targets.
| OpenAI | `https://api.openai.com/v1` | — | `gpt-6-astra` (GPT-6 Astra) |
| Anthropic | — | `https://api.anthropic.com` | `claude-opus-5-5` (Opus 5.5, default) / `claude-fable-5-1` (Fable 5.1, most capable) |
| DeepSeek | `https://api.deepseek.com` | `https://api.deepseek.com/anthropic` | `deepseek-v4-pro` (strong) / `deepseek-flash` (V4.1 Flash, fast) |
| Kimi / Moonshot | `https://api.moonshot.cn/v1` | `https://api.moonshot.cn/anthropic` | `kimi-k3` |
| MiniMax | `https://api.minimax.io/v1` | `https://api.minimax.io/anthropic` | `minimax-m3` |
| Alibaba Bailian (Qwen) | `https://dashscope.aliyuncs.com/compatible-mode/v1` | `https://dashscope.aliyuncs.com/apps/anthropic` | `qwen3.8-max` |
| Zhipu (GLM) | `https://open.bigmodel.cn/api/paas/v4` | `https://open.bigmodel.cn/api/anthropic` | `glm-4.6` |
| SiliconFlow | `https://api.siliconflow.cn/v1` | — | — |
| Ollama (local) | `http://localhost:11434/v1` | `http://localhost:11434` | — |

> In ⚙️ Settings, pick the protocol first: the provider quick-fill buttons then fill that provider's URL **for the selected protocol**, and the model list shows only that provider's models (plus *Custom model*). Switching protocol keeps the provider when it serves both.

> Model IDs move fast — check your provider's docs for the current lineup. Agentic tool calling requires a model that supports function calling; GPT-6 Astra, Claude Opus 5.5 / Fable 5.1, DeepSeek V4 Pro / V4.1 Flash, Kimi K3, MiniMax M3, and GLM-4.6 are first-class targets.
>
> The newest models reject request fields that older ones accepted: GPT-6 Astra needs `max_completion_tokens`, takes no `temperature`, and refuses `reasoning_effort` alongside tools on `/chat/completions`; Claude Opus 5.5 and Fable 5.1 reject `temperature` and `budget_tokens` and always think (depth is set with `effort`). Millwright detects these models by ID and sends the right fields — the **Reasoning depth** setting maps onto each model's own controls.

Expand Down Expand Up @@ -246,7 +248,7 @@ Contributions welcome — see [CONTRIBUTING.md](docs/CONTRIBUTING.md). We especi

- [x] **v0.1** — MVP: Electron shell, LLM adapters, COM bridge, first tool set
- [x] **v0.2** — Python sidecar, agentic tool loop, dual-engine fallback, vision feedback, confirmation cards, Apache-2.0 open source
- [x] **v0.2.4 → v0.2.130** — Extensive hardening against real SolidWorks installs ← *current*: the sketch → feature → cut → visual-verification loop now runs end to end on real hardware
- [x] **v0.2.4 → v0.2.132** — Extensive hardening against real SolidWorks installs ← *current*: the sketch → feature → cut → visual-verification loop now runs end to end on real hardware
- [ ] **v0.3** — Streaming tool calls, sketching on model faces (not just reference planes), hole wizard, sheet metal, drawing annotations, remaining `#VERIFY` parameters confirmed
- [ ] **v1.0** — MCP server, multi-CAD support

Expand Down
Loading
Loading