Skip to content

Commit 063be18

Browse files
committed
Merge remote-tracking branch 'origin/main' into theo/endorsements
# Conflicts: # .gitignore
2 parents 7f8cd49 + 90eea04 commit 063be18

688 files changed

Lines changed: 44305 additions & 18963 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/pr-size.yml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,9 @@ jobs:
124124
group: pr-size-${{ github.event.pull_request.number }}
125125
cancel-in-progress: true
126126
steps:
127+
# This pull_request_target job may fetch untrusted PR commits only as passive
128+
# git data. Do not add dependency installs, build/test scripts, or cache
129+
# actions here; use pull_request plus workflow_run for that pattern instead.
127130
- name: Checkout base repository
128131
uses: actions/checkout@v4
129132
with:

‎.github/workflows/release.yml‎

Lines changed: 161 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ on:
2323
type: string
2424

2525
permissions:
26-
contents: write
27-
id-token: write
26+
contents: read
27+
id-token: none
2828

2929
jobs:
3030
check_changes:
@@ -244,6 +244,68 @@ jobs:
244244
exit $code
245245
}
246246
247+
- name: Prepare Azure Trusted Signing
248+
if: matrix.platform == 'win'
249+
shell: pwsh
250+
env:
251+
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
252+
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
253+
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
254+
AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }}
255+
AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }}
256+
AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }}
257+
AZURE_TRUSTED_SIGNING_PUBLISHER_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_PUBLISHER_NAME }}
258+
run: |
259+
$ErrorActionPreference = "Stop"
260+
261+
$requiredSecrets = @(
262+
$env:AZURE_TENANT_ID,
263+
$env:AZURE_CLIENT_ID,
264+
$env:AZURE_CLIENT_SECRET,
265+
$env:AZURE_TRUSTED_SIGNING_ENDPOINT,
266+
$env:AZURE_TRUSTED_SIGNING_ACCOUNT_NAME,
267+
$env:AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME,
268+
$env:AZURE_TRUSTED_SIGNING_PUBLISHER_NAME
269+
)
270+
if ($requiredSecrets | Where-Object { [string]::IsNullOrWhiteSpace($_) }) {
271+
Write-Host "Azure Trusted Signing disabled; skipping TrustedSigning module preparation."
272+
exit 0
273+
}
274+
275+
try {
276+
Install-PackageProvider `
277+
-Name NuGet `
278+
-MinimumVersion 2.8.5.201 `
279+
-Force `
280+
-Scope CurrentUser `
281+
-ErrorAction Stop
282+
} catch {
283+
Write-Warning "Could not bootstrap NuGet package provider. Continuing because the runner may already have a usable provider. $($_.Exception.Message)"
284+
}
285+
286+
Install-Module `
287+
-Name TrustedSigning `
288+
-MinimumVersion 0.5.0 `
289+
-Force `
290+
-AllowClobber `
291+
-Repository PSGallery `
292+
-Scope CurrentUser `
293+
-ErrorAction Stop
294+
295+
Import-Module TrustedSigning -MinimumVersion 0.5.0 -Force
296+
Get-Command Invoke-TrustedSigning -ErrorAction Stop
297+
298+
$moduleRoots = @(
299+
[System.IO.Path]::Combine([Environment]::GetFolderPath("MyDocuments"), "PowerShell", "Modules"),
300+
[System.IO.Path]::Combine([Environment]::GetFolderPath("MyDocuments"), "WindowsPowerShell", "Modules"),
301+
[System.IO.Path]::Combine($env:ProgramFiles, "PowerShell", "Modules"),
302+
[System.IO.Path]::Combine($env:ProgramFiles, "WindowsPowerShell", "Modules")
303+
)
304+
$modulePathEntries = @($moduleRoots + ($env:PSModulePath -split ";")) |
305+
Where-Object { $_ -and (Test-Path $_) } |
306+
Select-Object -Unique
307+
"PSModulePath=$($modulePathEntries -join ';')" >> $env:GITHUB_ENV
308+
247309
- name: Build desktop artifact
248310
shell: bash
249311
env:
@@ -358,6 +420,9 @@ jobs:
358420
if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.build.result == 'success' }}
359421
runs-on: ubuntu-24.04 # blacksmith-8vcpu-ubuntu-2404
360422
timeout-minutes: 10
423+
permissions:
424+
contents: read
425+
id-token: write
361426
steps:
362427
- name: Checkout
363428
uses: actions/checkout@v6
@@ -511,6 +576,98 @@ jobs:
511576
fail_on_unmatched_files: true
512577
token: ${{ steps.app_token.outputs.token }}
513578

579+
deploy_web:
580+
name: Deploy hosted web app
581+
needs: [preflight, release]
582+
if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.release.result == 'success' }}
583+
runs-on: blacksmith-8vcpu-ubuntu-2404
584+
timeout-minutes: 10
585+
env:
586+
VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
587+
VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
588+
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
589+
T3CODE_WEB_ROUTER_URL: ${{ vars.T3CODE_WEB_ROUTER_URL }}
590+
T3CODE_WEB_LATEST_DOMAIN: ${{ vars.T3CODE_WEB_LATEST_DOMAIN }}
591+
T3CODE_WEB_NIGHTLY_DOMAIN: ${{ vars.T3CODE_WEB_NIGHTLY_DOMAIN }}
592+
VERCEL_TEAM_SLUG: ${{ vars.VERCEL_TEAM_SLUG }}
593+
steps:
594+
- name: Checkout
595+
uses: actions/checkout@v6
596+
with:
597+
ref: ${{ needs.preflight.outputs.ref }}
598+
599+
- name: Setup Bun
600+
uses: oven-sh/setup-bun@v2
601+
with:
602+
bun-version-file: package.json
603+
604+
- name: Setup Node
605+
uses: actions/setup-node@v6
606+
with:
607+
node-version-file: package.json
608+
609+
- name: Install release tooling dependencies
610+
run: bun install --frozen-lockfile --filter=@t3tools/scripts --filter=@t3tools/web
611+
612+
- name: Align package versions to release version
613+
run: node scripts/update-release-package-versions.ts "${{ needs.preflight.outputs.version }}"
614+
615+
- name: Refresh release lockfile
616+
run: bun install --lockfile-only --ignore-scripts
617+
618+
- name: Deploy and alias channel
619+
shell: bash
620+
run: |
621+
set -euo pipefail
622+
623+
if [[ -z "${VERCEL_TOKEN:-}" || -z "${VERCEL_ORG_ID:-}" || -z "${VERCEL_PROJECT_ID:-}" ]]; then
624+
echo "Missing one or more required Vercel secrets: VERCEL_TOKEN, VERCEL_ORG_ID, VERCEL_PROJECT_ID." >&2
625+
exit 1
626+
fi
627+
628+
router_url="${T3CODE_WEB_ROUTER_URL:-https://app.t3.codes}"
629+
latest_domain="${T3CODE_WEB_LATEST_DOMAIN:-latest.app.t3.codes}"
630+
nightly_domain="${T3CODE_WEB_NIGHTLY_DOMAIN:-nightly.app.t3.codes}"
631+
router_domain="${router_url#http://}"
632+
router_domain="${router_domain#https://}"
633+
router_domain="${router_domain%%/*}"
634+
635+
if [[ "${{ needs.preflight.outputs.release_channel }}" == "stable" ]]; then
636+
channel_domain="$latest_domain"
637+
channel_name="latest"
638+
else
639+
channel_domain="$nightly_domain"
640+
channel_name="nightly"
641+
fi
642+
643+
vercel_scope="${VERCEL_TEAM_SLUG:-$VERCEL_ORG_ID}"
644+
vercel_scope_args=(--scope "$vercel_scope")
645+
646+
echo "Deploying hosted web app for $channel_name channel."
647+
deployment_url="$(
648+
bunx [email protected] deploy \
649+
--prod \
650+
--skip-domain \
651+
--yes \
652+
--token "$VERCEL_TOKEN" \
653+
"${vercel_scope_args[@]}" \
654+
--build-env "APP_VERSION=${{ needs.preflight.outputs.version }}" \
655+
--build-env "VITE_HOSTED_APP_URL=$router_url" \
656+
--build-env "VITE_HOSTED_APP_CHANNEL=$channel_name"
657+
)"
658+
659+
echo "Aliasing $deployment_url to $channel_domain."
660+
bunx [email protected] alias set "$deployment_url" "$channel_domain" \
661+
--token "$VERCEL_TOKEN" \
662+
"${vercel_scope_args[@]}"
663+
664+
if [[ "$channel_name" == "latest" && -n "$router_domain" && "$router_domain" != "$channel_domain" ]]; then
665+
echo "Aliasing $deployment_url to router domain $router_domain."
666+
bunx [email protected] alias set "$deployment_url" "$router_domain" \
667+
--token "$VERCEL_TOKEN" \
668+
"${vercel_scope_args[@]}"
669+
fi
670+
514671
finalize:
515672
name: Finalize release
516673
if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.release.result == 'success' && needs.preflight.outputs.release_channel == 'stable' }}
@@ -595,8 +752,9 @@ jobs:
595752
always() && !cancelled() &&
596753
needs.preflight.result == 'success' &&
597754
needs.release.result == 'success' &&
755+
needs.deploy_web.result == 'success' &&
598756
(needs.finalize.result == 'success' || needs.finalize.result == 'skipped')
599-
needs: [preflight, release, finalize]
757+
needs: [preflight, release, deploy_web, finalize]
600758
runs-on: blacksmith-8vcpu-ubuntu-2404
601759
timeout-minutes: 10
602760
steps:

‎.gitignore‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,5 @@ __screenshots__/
2424
squashfs-root/
2525
.vercel
2626
.gstack/
27+
dist-electron/
28+
.electron-runtime/

‎.oxlintrc.json‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"**/routeTree.gen.ts"
1010
],
1111
"plugins": ["eslint", "oxc", "react", "unicorn", "typescript"],
12+
"jsPlugins": ["./oxlint-plugin-t3code/index.ts"],
1213
"categories": {
1314
"correctness": "warn",
1415
"suspicious": "warn",
@@ -17,6 +18,8 @@
1718
"rules": {
1819
"react-in-jsx-scope": "off",
1920
"eslint/no-shadow": "off",
20-
"eslint/no-await-in-loop": "off"
21+
"eslint/no-await-in-loop": "off",
22+
"eslint/no-underscore-dangle": "off",
23+
"t3code/no-inline-schema-compile": "warn"
2124
}
2225
}

‎.plans/19-remote-endpoints-hosted-static.md‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,4 +347,3 @@ Each implementation PR should run:
347347
- `bun typecheck`
348348
- focused tests for changed backend/web behavior
349349
- backend tests for any server-side endpoint discovery or auth changes using `bun run test`, never `bun test`
350-

‎REMOTE.md‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,43 @@ After setup, the renderer connects to a local forwarded HTTP/WebSocket endpoint.
115115

116116
SSH launch is a desktop feature because it needs local process and SSH access. Once the environment is paired and saved, it uses the same environment list and connection model as direct LAN, Tailscale, HTTPS, or future tunnel-backed environments.
117117

118+
#### SSH Launch Troubleshooting
119+
120+
The desktop SSH launcher connects with a non-interactive `sh` session, writes a small launcher script under `~/.t3/ssh-launch/<host-key>/`, starts or reuses a remote T3 server, and forwards the remote loopback port back to your desktop.
121+
122+
The remote host must have a compatible Node.js runtime. T3 Code uses the server package's `engines.node` requirement:
123+
124+
```text
125+
^22.16 || ^23.11 || >=24.10
126+
```
127+
128+
During SSH launch, T3 Code first checks whether `node` is already available on `PATH`. If it is missing, the launcher tries common non-interactive shell locations and version-manager shims/activation hooks:
129+
130+
- `~/.local/bin`, `~/bin`, `/opt/homebrew/bin`, `/usr/local/bin`, `/usr/bin`, `/bin`
131+
- Volta via `~/.volta/bin`
132+
- asdf via `~/.asdf/shims`, `~/.asdf/bin`, or `~/.asdf/asdf.sh`
133+
- mise via `~/.local/share/mise/shims`, `~/.mise/shims`, or `mise activate sh`
134+
- fnm via `fnm env --use-on-cd --shell sh` or `fnm env --shell sh`
135+
- nodenv via `~/.nodenv/bin`, `~/.nodenv/shims`, or `nodenv init -`
136+
- nvm via `$NVM_DIR/nvm.sh`, then `nvm use default`, `nvm use node`, or `nvm use --lts`
137+
- installed nvm versions under `$NVM_DIR/versions/node/*/bin`
138+
139+
If launch fails with `node: command not found`, a port-scan failure, or a message that the remote Node version does not satisfy the required range, SSH into the host and check the same non-interactive shell path T3 Code uses:
140+
141+
```bash
142+
ssh [email protected] 'sh -lc "command -v node && node --version"'
143+
```
144+
145+
If that does not print a compatible Node version, configure your version manager for non-interactive shells or install a compatible Node binary in one of the searched locations. For example, with nvm you may need a default alias:
146+
147+
```bash
148+
nvm alias default 24
149+
```
150+
151+
With mise/asdf/fnm/nodenv, make sure the tool's shim directory is installed and points at a Node version satisfying the range above.
152+
153+
If reconnecting after an app update fails, retry the SSH launch once. The launcher now compares its generated runner script, stops stale launcher-managed remote servers, clears the SSH launch PID/port state, and starts a fresh remote server. You should not normally need to delete `~/.t3/ssh-launch` or kill `t3` processes manually.
154+
118155
## How Pairing Works
119156

120157
The remote device does not need a long-lived secret up front.

‎apps/desktop/.gitignore‎

Lines changed: 0 additions & 2 deletions
This file was deleted.

‎apps/desktop/package.json‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@t3tools/desktop",
3-
"version": "0.0.21",
3+
"version": "0.0.23",
44
"private": true,
55
"type": "module",
66
"main": "dist-electron/main.cjs",
@@ -16,18 +16,18 @@
1616
},
1717
"dependencies": {
1818
"@effect/platform-node": "catalog:",
19-
"effect": "catalog:",
20-
"electron": "40.9.3",
21-
"electron-updater": "^6.6.2"
22-
},
23-
"devDependencies": {
24-
"@t3tools/client-runtime": "workspace:*",
2519
"@t3tools/contracts": "workspace:*",
2620
"@t3tools/shared": "workspace:*",
2721
"@t3tools/ssh": "workspace:*",
2822
"@t3tools/tailscale": "workspace:*",
23+
"effect": "catalog:",
24+
"electron": "41.5.0",
25+
"electron-updater": "^6.6.2"
26+
},
27+
"devDependencies": {
28+
"@effect/language-service": "catalog:",
29+
"@effect/vitest": "catalog:",
2930
"@types/node": "catalog:",
30-
"effect-acp": "workspace:*",
3131
"tsdown": "catalog:",
3232
"typescript": "catalog:",
3333
"vitest": "catalog:"

0 commit comments

Comments
 (0)