app
SOLIDWORKS Bridge
Public Made by Adomby adom
Launch, model and quit SOLIDWORKS from any Adom container. Nothing is ever lost.
← Commit history
0.9.27 source
10 files changed
+138−49
BRIDGE_VERSION+1−1@@ -1 +1 @@-0.9.26+0.9.27
Cargo.lock+1−1@@ -122,7 +122,7 @@ dependencies = [ [[package]] name = "swx-bridge"-version = "0.9.26"+version = "0.9.27" dependencies = [ "miniz_oxide", "serde",
Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "swx-bridge"-version = "0.9.26"+version = "0.9.27" edition = "2021" rust-version = "1.80" description = "Adom SOLIDWORKS bridge — swx_* verbs over the SOLIDWORKS COM API, as a single native binary"
README.md+8−1@@ -492,7 +492,14 @@ not. ## Roadmap -- v0.9.26 (this): **page 2, read right.** Measured on the live window, which+- v0.9.27 (this): **the local sign-in helper.** ab hands a saved password only to+ a local caller, never to a bridge or over the relay, and a cloud agent is not+ allowed to start that local caller either (both measured 2026-10-12). So the+ page now ships `tools/local-sign-in.ps1`: run on the PC, it asks the vault+ behind ab's consent card, sets the password into the login window, ticks+ Remember me, clicks Log in and verifies. The sign-in skill says exactly who may+ touch the password and how. Docs and a tool; no runtime change.+- v0.9.26: **page 2, read right.** Measured on the live window, which was on page 2 with the name filled. 0.9.25 called it page 1 because the user-name box is there too. It tried to set that box, which is prefilled and read-only, and blamed the refusal on a cookie banner that was really a
bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "solidworks", "displayName": "SOLIDWORKS",- "version": "0.9.26",+ "version": "0.9.27", "description": "Bridge for SOLIDWORKS desktop CAD — create and open documents, a modeling DSL, file management (find/open/save/export/Pack and Go), custom properties, BOM generation, design analysis (mass properties, bodies, parameters, interference, rebuild health) and background window/dialog etiquette, over the SOLIDWORKS COM API. Single native binary: no interpreter, no PowerShell, ~10ms cold start.", "author": "Adom Inc.", "license": "MIT",
install.shpackage.json+1−1@@ -1,6 +1,6 @@ { "slug": "solidworks-bridge",- "version": "0.9.26",+ "version": "0.9.27", "type": "app", "description": "Skills for your container so your AI knows how to drive the SOLIDWORKS bridge (swx_* verbs). The bridge runtime itself is the release zip — a single native swx-bridge.exe; Adom Bridge loads that.", "tags": [
skills/solidworks-sign-in/SKILL.md+34−43@@ -164,47 +164,38 @@ Every line below is marked **[measured]** (seen on a real machine, 2026-10-02 to ## Credentials -- [measured] 2026-10-12: Adom Bridge refused to hand the saved password to the- SOLIDWORKS bridge: "pup_credential_reveal requires a VERIFIED local transport- (ab 2.0.96+ X-Adom-Verified-Origin: local); this request arrived with origin- "bridge"". That is Adom Bridge's POLICY: a saved password goes only to a local- desktop assistant, and a bridge calls back with origin "bridge". It is not a- bug, and nothing may work around it (no shelling out to a local command, no- other route).-- What the bridge does instead: `swx_sign_in` PREPARES the page. It fills the- user name (pup's host list carries it, with no secret and no toast), presses- Continue, waits for the password page, and only then asks for the password.- When that is refused, the reply is `sign_in_vault_policy`, with `abSaid` (Adom- Bridge's exact words), `pagePrepared {page, userNameFilled}` and `waysOut`.- The window is left on the password page for the user to type into. Nothing is- retried.-- The way out that keeps the secret inside Adom Bridge: issue #237 on- adom/adom-bridge asks for a fill verb (`desktop_credential_fill`) that types a- saved login into a control without handing it to anyone, or a verified-local- origin for the bridges it hosts.+Where the password comes from, and who is allowed to touch it — all measured on+2026-10-12 on a real login window (ab 2.1.174, bridge 0.9.26): -This skill carries NO login, and it never will. The login comes from the user's-own secret store on their desktop, in general terms:--- the pup vault on the user's desktop, through `swx_sign_in {reason}` — the- bridge reads it locally, the user clicks one consent toast per use, and the- password never reaches the AI, a reply or a log;-- growing that vault, by the user on their desktop: `pup_import_browser_logins- {"hostFilter": "3ds.com"}` (it found nothing on one machine because the- browser held no saved 3DEXPERIENCE login), or signing in to 3DEXPERIENCE once- in a pup window so the vault captures it;-- or the user's own PRIVATE skill pack, if they keep one, for where their logins- live — never this skill.--An agent must never write a password into this skill, the bridge repository, a-wiki page, a log line or a chat message.--`swx_sign_in` is the path: it does every step above, in order, and fails closed-with a reason when it cannot. For a login window the bridge's own launch did not-raise (SOLIDWORKS Visualize's, the platform updater's, or one already up), pass-`userAuthorized:true` (only on the user's say-so) and `window:"visualize"`,-`"solidworks"`, `"desktopUpdate"` or the window handle when more than one is-open; the reply names the process that owns the window. A window handle the-user names is checked by its content and never refused because a census missed-it. If it has neither field, the reply is `sign_in_window_not_a_login` with-what was found.+- The login lives in the Adom Bridge vault on the user's PC (`pup_credentials {}`+ lists hosts + user names, never a secret; the 3DEXPERIENCE host is+ `iam.3ds.com`). You never write a password into this skill, the bridge, a reply, a+ log or a screenshot — [measured] every value that leaves the bridge is+ scrubbed of it; [unmeasured] whether any ab verb can read a typed value back+ on the updater host.+- **The bridge prepares the page and stops.** `swx_sign_in {userAuthorized:true,+ window:<hwnd>}` fills the user name from the vault's listing, presses Continue,+ reaches the password page, asks the vault ONCE for the password — and ab+ refuses a bridge: *"pup_credential_reveal requires a VERIFIED local transport+ … this request arrived with origin \"bridge\""*. The reply is+ `sign_in_vault_policy` with ab's words (`abSaid`), `pagePrepared+ {page:2, userNameFilled:true}` and `waysOut`. Nothing is retried, nothing is+ worked around. ab issue #237 asks for a fill verb (ab sets the control itself,+ the secret never leaves ab); until it lands the password step is outside the+ bridge.+- **A cloud agent cannot type it either.** Sending the password through ab from a+ container is refused by the agent's own policy (measured twice: typing it+ directly, and starting a local helper that would reveal it). Do not retry by+ any route; do not ask the user to paste it into chat.+- **What works today: a LOCAL caller.** ab hands a vault secret to a local desktop+ assistant (its bundled CLI on the same PC, Hydrogen, a local agent or script)+ behind one consent card. `tools/local-sign-in.ps1` in this page's repo is that+ assistant: run it ON the PC that shows the login (double-click, or+ `powershell -ExecutionPolicy Bypass -File local-sign-in.ps1`); it finds the+ window through `swx_status`, asks the vault (ab's consent card appears), sets+ the password into the box by automation id, ticks Remember me, clicks Log in and+ verifies the window is gone. It never prints the secret. Tick **Remember me** so+ the platform session persists and the window stops coming back every couple of+ weeks.+- The user may instead type the password into the prepared window; the bridge+ reports `pagePrepared` so an agent can say exactly that.
tools/local-sign-in.ps1added+91@@ -0,0 +1,91 @@+# local-sign-in.ps1 — the "local desktop assistant" for a 3DEXPERIENCE ID login window.+#+# RUN THIS ON THE PC THAT SHOWS THE LOGIN (double-click, or:+# powershell -ExecutionPolicy Bypass -File local-sign-in.ps1 [-Hwnd <hwnd>] [-VaultHost iam.3ds.com]).+#+# It uses Adom Bridge's bundled local CLI, a trusted LOCAL caller: ab shows its own consent card for the+# vault reveal, the password is set straight into the window's password box (UIA, by automation id),+# "Remember me" is ticked, "Log in" is clicked, and the window is checked to be gone.+# The password is never printed, logged or sent anywhere; the variable is cleared right after the set.+#+# Why a script and not the bridge: ab hands a saved password only to a verified local caller — never to a+# bridge (origin "bridge") and never over the relay — and a cloud agent is not allowed to start this helper+# for you either (both measured 2026-10-12). adom-bridge issue #237 asks ab for a fill verb so that no+# helper is needed.+param(+ [int]$Hwnd = 0,+ [string]$VaultHost = 'iam.3ds.com',+ [int]$OwnerPid = 0+)+$ErrorActionPreference = 'Continue'+$cli = Join-Path $env:LOCALAPPDATA 'Adom Bridge\adom-bridge-cli.exe'+if (-not (Test-Path $cli)) { "Adom Bridge CLI not found at $cli"; exit 1 }++function Ab($verb, $obj) {+ $json = ($obj | ConvertTo-Json -Compress -Depth 6)+ $out = & $cli --ai-thread 'local sign-in' $verb $json 2>&1 | Out-String+ try { return ($out | ConvertFrom-Json) } catch { return [pscustomobject]@{ status = 'unparsed'; error = $out.Substring(0, [Math]::Min(300, $out.Length)) } }+}++# 0. the login window: by content, through the bridge's own census, unless given+if ($Hwnd -eq 0) {+ $st = Ab 'swx_status' @{ reason = 'find the 3DEXPERIENCE login window' }+ $w = $null+ try { $w = $st.result.solidworks.signInWindow } catch {}+ if (-not $w) { try { $w = $st.solidworks.signInWindow } catch {} }+ if ($w -and $w.hwnd) { $Hwnd = [int]$w.hwnd; if ($w.ownerPid) { $OwnerPid = [int]$w.ownerPid } }+ if ($Hwnd -eq 0) { "no 3DEXPERIENCE login window is open (swx_status.solidworks.signInWindow is empty); pass -Hwnd"; exit 1 }+}+"login window: hwnd=$Hwnd owner pid=$OwnerPid"++# 1. the user name, if the page is still on page 1 (prefilled on page 2 — then this set is refused read-only, which is fine)+$cred = Ab 'pup_credentials' @{}+$user = $null+foreach ($c in @($cred.credentials)) { if ($c.host -eq $VaultHost) { $user = $c.username } }+if ($user) {+ $u = Ab 'desktop_find_control' @{ hwnd = $Hwnd; role = 'Edit'; name = 'Email or username'; reason = 'is the user name box here' }+ if ($u.best) {+ $null = Ab 'desktop_ui_set' @{ hwnd = $Hwnd; automationId = 'username'; text = ''; reason = 'clear the user name box' }+ $s0 = Ab 'desktop_ui_set' @{ hwnd = $Hwnd; automationId = 'username'; text = $user; reason = 'set the saved user name' }+ "user name: status=$($s0.status) $($s0.error)"+ $cont = Ab 'desktop_find_control' @{ hwnd = $Hwnd; name = 'Continue'; reason = 'page 1?' }+ if ($cont.best) { $null = Ab 'desktop_ui_click' @{ hwnd = $Hwnd; name = 'Continue'; reason = 'go to the password page' }; Start-Sleep -Seconds 3 }+ }+}++# 2. the secret, from ab's vault, behind ab's own consent card (this script is a local caller)+$r = Ab 'pup_credential_reveal' @{ host = $VaultHost; reason = 'Sign in to the 3DEXPERIENCE window on this PC; the password is set into the login window by this local helper and is never printed, logged or sent anywhere' }+$pw = $null+foreach ($k in 'password', 'secret', 'value') { if ($r.$k) { $pw = [string]$r.$k; break } }+if (-not $pw -and $r.credential) { foreach ($k in 'password', 'secret') { if ($r.credential.$k) { $pw = [string]$r.credential.$k; break } } }+if (-not $pw -and $r.result) { foreach ($k in 'password', 'secret') { if ($r.result.$k) { $pw = [string]$r.result.$k; break } } }+if (-not $pw) { "reveal: no secret (status=$($r.status) errorCode=$($r.errorCode) error=$($r.error) granted=$($r.granted) pending=$($r.pending))"; exit 2 }+"reveal: ok"++# 3. the password box, by automation id; clear, then set+$null = Ab 'desktop_ui_set' @{ hwnd = $Hwnd; automationId = 'password'; text = ''; reason = 'clear the password box' }+$s = Ab 'desktop_ui_set' @{ hwnd = $Hwnd; automationId = 'password'; text = $pw; sensitive = $true; reason = 'set the saved password into the 3DEXPERIENCE login (local helper)' }+$pw = $null; $r = $null; [GC]::Collect()+"set password: status=$($s.status) $($s.error)"+if ($s.status -ne 'ok') { exit 3 }++# 4. Remember me (best effort: a real checkbox on the updater host, an off-screen one on the Visualize host), then Log in+$c = Ab 'desktop_find_control' @{ hwnd = $Hwnd; role = 'CheckBox'; contains = 'Remember me'; reason = 'find the Remember me checkbox' }+if ($c.best -and $c.best.name) {+ $t = Ab 'desktop_ui_click' @{ hwnd = $Hwnd; name = $c.best.name; reason = 'tick Remember me so the platform session persists' }+ "remember me: status=$($t.status) invoked=$($t.invoked)"+} else {+ $lbl = Ab 'desktop_find_control' @{ hwnd = $Hwnd; role = 'Text'; contains = 'Remember me'; reason = 'find the Remember me label' }+ if ($lbl.best -and $lbl.best.rect) { $t = Ab 'desktop_click' @{ x = $lbl.best.rect.centerX; y = $lbl.best.rect.centerY; reason = 'tick Remember me by its label' }; "remember me (label click): status=$($t.status)" } else { "remember me: not found" }+}+$l = Ab 'desktop_ui_click' @{ hwnd = $Hwnd; name = 'Log in'; reason = 'submit the 3DEXPERIENCE sign-in' }+"log in: status=$($l.status) invoked=$($l.invoked) $($l.error)"++# 5. verify by effect: the password box must be gone from this window+Start-Sleep -Seconds 10+$f = Ab 'desktop_find_control' @{ hwnd = $Hwnd; automationId = 'password'; reason = 'verify the login window is gone' }+$alive = $false+if ($OwnerPid -ne 0) { $alive = (Get-Process -Id $OwnerPid -ErrorAction SilentlyContinue) -ne $null }+"after 10 s: password box still found=$([bool]($f.best)) owner process alive=$alive"+if ($f.best) { "the window is still up: the password may have been rejected — it is NOT retried (lockout risk); sign in by hand this once"; exit 4 }+"signed in"
uninstall.sh