Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
1a82038
feat(desktop): Windows support
waleedlatif1 Nov 12, 2026
7030eeb
fix(desktop): withhold the browser sign-in hand-off channels on Windows
waleedlatif1 Nov 12, 2026
8abaa83
fix(desktop): cover the Windows silent install across the restart pro…
waleedlatif1 Nov 12, 2026
8f93313
fix(desktop): label Browser and Terminal shortcuts with Ctrl on Windows
waleedlatif1 Nov 12, 2026
22bbc9c
fix(desktop): drop Import Passwords from the Windows browser menu
waleedlatif1 Nov 12, 2026
76b1b0f
fix(desktop): bring the README's Windows section in line with the shell
waleedlatif1 Nov 12, 2026
f948075
fix(desktop): cycle Browser tabs with Ctrl+Tab from inside the page
waleedlatif1 Nov 12, 2026
0430d49
style(desktop): format the in-flight terminal command labels
waleedlatif1 Nov 13, 2026
e28b307
test(desktop): give the login-landing window stub isMinimized
waleedlatif1 Nov 13, 2026
6fc0811
fix(desktop): build the login-landing window from the shared Electron…
waleedlatif1 Nov 13, 2026
61a4b9d
fix(desktop): keep a command's output when its markers share a pty chunk
waleedlatif1 Nov 13, 2026
07af952
fix(desktop): await the executor's registration and sleep blocker in …
waleedlatif1 Nov 13, 2026
4405292
fix(desktop): keep the shell parser's segment type module-private
waleedlatif1 Nov 13, 2026
08fa25e
chore(audits): drop the Browser shortcut exports from the unused-expo…
waleedlatif1 Nov 13, 2026
f1aa923
Merge remote-tracking branch 'origin/staging' into feat/desktop-windo…
waleedlatif1 Nov 13, 2026
e292b7d
fix(desktop): read a failing PowerShell cmdlet as exit 1, never an ea…
waleedlatif1 Nov 13, 2026
6d47379
fix(desktop): scope Windows Stop to the command's own processes and k…
waleedlatif1 Nov 13, 2026
2d3aaf1
fix(desktop): mark a pending run interrupted on every Ctrl-C path, no…
waleedlatif1 Nov 13, 2026
3b2f326
fix(desktop): refuse a run into a busy terminal before waiting for it…
waleedlatif1 Nov 13, 2026
5b6e49b
fix(desktop): log build and platform-rejection messages through creat…
waleedlatif1 Nov 13, 2026
fc2ee52
fix(desktop): fail the IPC audit when a feature-gated channel is also…
waleedlatif1 Nov 13, 2026
8d31345
fix(desktop): keep a quoted program path whole when naming a running …
waleedlatif1 Nov 13, 2026
970b754
fix(desktop): verify every native file the Windows node-pty prebuild …
waleedlatif1 Nov 13, 2026
7983de7
fix(desktop): keep one taskbar-flash focus listener per window
waleedlatif1 Nov 13, 2026
a402e94
fix(desktop): report the theme preference from the storage key the pa…
waleedlatif1 Nov 13, 2026
1ff76b0
Merge remote-tracking branch 'origin/staging' into feat/desktop-windo…
waleedlatif1 Nov 13, 2026
51fd4a8
fix(desktop): tell a stale PowerShell exit code from this line's with…
waleedlatif1 Nov 13, 2026
aef4a50
fix(desktop): report a page's forced theme so light pages keep a ligh…
waleedlatif1 Nov 13, 2026
bf3d23f
Merge remote-tracking branch 'origin/staging' into feat/desktop-windo…
waleedlatif1 Nov 13, 2026
5a17797
fix(desktop): ship the Computer Use ask-for-apps channel only where C…
waleedlatif1 Nov 13, 2026
4718f8f
fix(desktop): drive the process-wide Windows theme source from the ap…
waleedlatif1 Nov 13, 2026
fc95f13
fix(desktop): join bash PROMPT_COMMAND with newlines and keep the use…
waleedlatif1 Nov 13, 2026
7f795c6
fix(desktop): on macOS and Linux read a stopped command's process gro…
waleedlatif1 Nov 13, 2026
ceb0999
fix(desktop): refuse Windows links to network shares before realpath …
waleedlatif1 Nov 13, 2026
7ea69c8
fix(desktop): keep prefixed Windows download names within the cap and…
waleedlatif1 Nov 13, 2026
e1a1363
docs(desktop): hold the Windows download claims until a signed Window…
waleedlatif1 Nov 13, 2026
0c1d3d0
fix(sim-setup): refuse a non-Windows installer resolved for Windows
waleedlatif1 Nov 13, 2026
12afe3c
docs(desktop): describe what forceCodeSigning, signExts and the Windo…
waleedlatif1 Nov 13, 2026
357f7cf
fix(desktop): guard the password copy capability check like its siblings
waleedlatif1 Nov 13, 2026
a6bdda5
fix(desktop): end a terminating kill's command instead of the shell, …
waleedlatif1 Nov 13, 2026
66bda4c
Merge branch 'feat/desktop-windows-support' of https://github.com/sim…
waleedlatif1 Nov 13, 2026
4aa9612
test(desktop): replace the granted folder before the open, which Wind…
waleedlatif1 Nov 13, 2026
bccb75d
fix(desktop): look up the filesystem calls at call time in localRealp…
waleedlatif1 Nov 13, 2026
d01ebf8
Merge remote-tracking branch 'origin/staging' into feat/desktop-windo…
waleedlatif1 Nov 13, 2026
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
113 changes: 108 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1308,8 +1308,12 @@ jobs:
needs.desktop-signing.result == 'success' &&
needs.version.result == 'success' &&
needs.desktop-signing.outputs.configured == 'true'
# The reusable workflow's Windows job requests id-token: write for Azure
# OIDC, and GitHub checks every called job against the caller's grant at
# startup, even one this macOS call skips.
permissions:
contents: write
id-token: write
uses: ./.github/workflows/desktop-release.yml
with:
version: ${{ needs.version.outputs.version }}
Expand All @@ -1323,6 +1327,57 @@ jobs:
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}

# Windows desktop builds are signed with Azure Artifact Signing over GitHub
# OIDC. Gated like the macOS build: until the Azure identity and signing
# variables exist, the Windows build skips with a warning, and nothing is
# ever published unsigned. Windows jobs are separate workflow calls, and a
# Windows failure never blocks a macOS release; the update feed treats a
# release without latest.yml as having no Windows build. The prerelease path
# still waits for them: prerelease-publish needs prerelease-build-windows, so
# it starts only once that job finishes, whatever its result.
desktop-signing-windows:
name: desktop-signing-windows
runs-on: *runner-2vcpu
timeout-minutes: 2
needs: [version, desktop-changes]
if: ${{ !cancelled() && (needs.version.outputs.is_release == 'true' || needs.desktop-changes.outputs.changed == 'true') }}
outputs:
configured: ${{ steps.check.outputs.configured }}
steps:
- name: Probe Azure signing configuration
id: check
env:
CONFIGURED: ${{ secrets.AZURE_CLIENT_ID != '' && secrets.AZURE_TENANT_ID != '' && secrets.AZURE_SUBSCRIPTION_ID != '' && vars.AZURE_SIGNING_ENDPOINT != '' && vars.AZURE_SIGNING_ACCOUNT != '' && vars.AZURE_SIGNING_PROFILE != '' && vars.AZURE_SIGNING_PUBLISHER != '' }}
run: |
echo "configured=${CONFIGURED}" >> "$GITHUB_OUTPUT"
if [ "$CONFIGURED" != "true" ]; then
echo "::warning::Windows desktop release skipped: Azure signing is not configured (secrets AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_SUBSCRIPTION_ID; variables AZURE_SIGNING_ENDPOINT, AZURE_SIGNING_ACCOUNT, AZURE_SIGNING_PROFILE, AZURE_SIGNING_PUBLISHER)."
fi

desktop-release-windows:
name: desktop-release-windows
needs: [release, desktop-signing-windows, version]
# See desktop-release for why the implicit success() check is suppressed.
if: >-
!cancelled() &&
needs.release.result == 'success' &&
needs.desktop-signing-windows.result == 'success' &&
needs.version.result == 'success' &&
needs.desktop-signing-windows.outputs.configured == 'true'
permissions:
contents: write
id-token: write
uses: ./.github/workflows/desktop-release.yml
with:
version: ${{ needs.version.outputs.version }}
publish: true
platform: windows
secrets:
DESKTOP_RELEASE_TOKEN: ${{ secrets.DESKTOP_RELEASE_TOKEN }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

# Per-env desktop prereleases: a dev/staging push that touches shell code
# publishes an environment-tagged GitHub prerelease to the public,
# release-only simstudioai/sim-desktop-releases repository. Keeping these
Expand All @@ -1333,15 +1388,20 @@ jobs:
name: prerelease-create
runs-on: *runner-2vcpu
timeout-minutes: 5
needs: [desktop-changes, desktop-signing]
# Requires the signing probe to have actually succeeded (not just "not
needs: [desktop-changes, desktop-signing, desktop-signing-windows]
# Requires a signing probe to have actually succeeded (not just "not
# cancelled") so a probe failure can't produce a release with no build.
# Either platform's signing is enough: each build job gates on its own.
if: >-
${{
!cancelled() &&
needs.desktop-changes.outputs.changed == 'true' &&
needs.desktop-signing.result == 'success' &&
needs.desktop-signing.outputs.configured == 'true'
(
(needs.desktop-signing.result == 'success' &&
needs.desktop-signing.outputs.configured == 'true') ||
(needs.desktop-signing-windows.result == 'success' &&
needs.desktop-signing-windows.outputs.configured == 'true')
)
}}
permissions:
contents: read
Expand Down Expand Up @@ -1403,11 +1463,19 @@ jobs:
prerelease-build:
name: prerelease-build
needs: [prerelease-create, desktop-signing]
if: >-
!cancelled() &&
needs.prerelease-create.result == 'success' &&
needs.desktop-signing.result == 'success' &&
needs.desktop-signing.outputs.configured == 'true'
# The reusable workflow declares contents: write for its stable-release
# path. GitHub cannot elevate a caller's token, even though this prerelease
# path uses the dedicated cross-repository token for its actual upload.
# id-token: write is required the same way by the Windows job (see
# desktop-release above).
permissions:
contents: write
id-token: write
uses: ./.github/workflows/desktop-release.yml
with:
version: ${{ needs.prerelease-create.outputs.version }}
Expand All @@ -1422,14 +1490,49 @@ jobs:
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}

# Attaches the Windows build to the same prerelease. A Windows failure never
# holds back a macOS build that succeeded (see prerelease-publish).
prerelease-build-windows:
name: prerelease-build-windows
needs: [prerelease-create, desktop-signing-windows]
if: >-
!cancelled() &&
needs.prerelease-create.result == 'success' &&
needs.desktop-signing-windows.result == 'success' &&
needs.desktop-signing-windows.outputs.configured == 'true'
permissions:
contents: write
id-token: write
uses: ./.github/workflows/desktop-release.yml
with:
version: ${{ needs.prerelease-create.outputs.version }}
publish: true
sign: true
platform: windows
secrets:
DESKTOP_RELEASE_TOKEN: ${{ secrets.DESKTOP_RELEASE_TOKEN }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

# The draft only becomes visible to the update feed once its artifacts are
# attached — this is what makes a dev/staging push atomic from the shell's
# point of view.
prerelease-publish:
name: prerelease-publish
runs-on: *runner-2vcpu
timeout-minutes: 5
needs: [prerelease-create, prerelease-build]
needs: [prerelease-create, prerelease-build, prerelease-build-windows]
# Publishes once every configured platform has finished: the macOS build
# when Apple signing is configured, otherwise the Windows build alone.
if: >-
!cancelled() &&
needs.prerelease-create.result == 'success' &&
(
needs.prerelease-build.result == 'success' ||
(needs.prerelease-build.result == 'skipped' &&
needs.prerelease-build-windows.result == 'success')
)
permissions:
contents: read
env:
Expand Down
69 changes: 69 additions & 0 deletions .github/workflows/desktop-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -323,3 +323,72 @@ jobs:
exit 1
fi
SIM_DESKTOP_EXECUTABLE="$EXECUTABLE" bunx playwright test e2e/packaged-smoke.spec.ts

# The Windows leg: the unit suite on Windows, then an unsigned unpacked x64
# build through the same packaged smoke suite (fuses, launch telemetry, and
# the bundled offline page).
package-smoke-windows:
name: package-smoke-windows
if: github.event_name != 'schedule' && github.event_name != 'push'
runs-on: windows-latest
timeout-minutes: 30
defaults:
run:
shell: bash
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
with:
persist-credentials: false

- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: 1.4.4

- name: Setup Node
uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v6
with:
node-version: 22

- name: Cache Electron downloads
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v5
with:
path: |
~/AppData/Local/electron/Cache
~/AppData/Local/electron-builder/Cache
key: desktop-downloads-${{ runner.os }}-${{ hashFiles('bun.lock') }}
restore-keys: desktop-downloads-${{ runner.os }}-

- name: Install dependencies
env:
HUSKY: '0'
run: bun install --frozen-lockfile

- name: Run desktop unit tests
working-directory: apps/desktop
run: bun run test

- name: Bundle and package unsigned
working-directory: apps/desktop
run: |
bun run build
bunx electron-builder --win dir --x64 --publish never -c.win.signExecutable=false

- name: Run packaged Electron smoke suite
working-directory: apps/desktop
run: |
EXECUTABLE=release/win-unpacked/Sim.exe
if [ ! -f "$EXECUTABLE" ]; then
echo "::error::Packaged app executable was not found."
exit 1
fi
SIM_DESKTOP_EXECUTABLE="$EXECUTABLE" bunx playwright test e2e/packaged-smoke.spec.ts

- name: Upload test results
if: failure()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: desktop-package-smoke-windows-results
path: apps/desktop/test-results
retention-days: 7
Loading
Loading