From 42eaf77ff0c7db974c844f24582cf6a153b198c0 Mon Sep 17 00:00:00 2001 From: Jeremy Lam Date: Tue, 12 May 2026 07:33:57 +0000 Subject: [PATCH 01/19] Add AGENTS.md for waveterm-remote fork --- AGENTS.md | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..f40f15dec2 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,78 @@ +# AGENTS.md — waveterm-remote Fork + +This fork of Wave Terminal is optimized for remote development workflows. The local machine is a thin client; remote SSH environments are primary workspaces. + +## Git Remotes + +- `origin` → `https://github.com/whoisjeremylam/waveterm-remote` (this fork) +- `upstream` → `https://github.com/wavetermdev/waveterm` (original) +- Do not run `git push` — the user handles pushes interactively with 2FA + +## Dev Environment Status + +| Tool | Status | +|------|--------| +| NodeJS v24.14.0 | Available | +| npm 11.9.0 | Available | +| git 2.43.0 | Available | +| Go 1.25+ | **Missing** — must install before building | +| Task (build runner) | **Missing** — must install before building | + +Install Task and Go before attempting builds. Then run `task init` and `task dev`. + +## Planning Documents + +All fork planning lives in `.pi/`: +- `.pi/index.md` — entry point +- `.pi/context.md` — fork purpose and problem statement +- `.pi/todos.md` — active tasks and backlog +- `.pi/decisions.md` — architecture decisions +- `.pi/specs/` — feature specifications + +Current active spec: `.pi/specs/portforwarding.md` + +## Architecture + +- **Frontend**: React/TypeScript in `frontend/` +- **Backend**: Go in `pkg/` and `cmd/` +- **Electron main**: `emain/` (Node.js bridge between frontend and Go) +- **Go backend runs as separate process** — Electron main process bridges to it via IPC + +## Priorities + +1. Install missing build tools (Task, Go) +2. Verify `task dev` and `task start` work +3. Implement SSH port forwarding (`LocalForward`/`RemoteForward`) — spec ready +4. Later: remove/disable AI features, MOSH support, vertical tabs, UX improvements + +## Conventions + +- Follow existing code patterns: `panichandler` on goroutines, `WithLock` for struct mutations, table-driven tests with `t.Run`, manual `if` assertions (no testify) +- `docs/docs/` is public-facing documentation (Docusaurus) — do not mix fork planning with user docs +- `README.md` stays close to upstream; fork differences go in `.pi/` or `README-FORK.md` if needed +- All new SSH config keywords follow the parsing pattern in `pkg/remote/sshclient.go` +- ConnKeywords fields use `json:"ssh:..."` tags for SSH config and `json:"conn:..."` for internal config + +## Key Files for SSH Work + +| File | Purpose | +|------|---------| +| `pkg/wconfig/settingsconfig.go` | `ConnKeywords` struct — add new SSH fields here | +| `pkg/remote/sshclient.go` | Config parsing (`findSshConfigKeywords`), merging (`mergeKeywords`), `ConnectToClient` | +| `pkg/remote/conncontroller/conncontroller.go` | Connection lifecycle — start forwarding after connect, cleanup on disconnect | +| `pkg/genconn/ssh-impl.go` | SSH session implementation | +| `cmd/wsh/cmd/wshcmd-ssh.go` | `wsh ssh` CLI command | +| `docs/docs/connections.mdx` | Public docs for connections and SSH config | + +## Testing + +- No existing tests for `sshclient.go` or `conncontroller.go` — new tests would be first coverage +- Use `t.TempDir()` for filesystem fixtures, not external fixture files +- Use hand-written inline mocks, not gomock +- `t.Parallel()` on independent tests only + +## Out of Scope (Current) + +- DynamicForward (needs SOCKS5 handler) +- `wsh ssh -L`/`-R` CLI flags +- UI status indicators for port forwarding From 4828633ac4e628b3ad945f1062fc0bc145d39561 Mon Sep 17 00:00:00 2001 From: Jeremy Lam Date: Tue, 12 May 2026 08:54:48 +0000 Subject: [PATCH 02/19] set up local build toolchain and macOS CI workflow - Add local Go 1.26.2 and Zig 0.14.0 installs (gitignored) - Add @go-task/cli as local npm dependency - Update Taskfile.yml to use {{.GO}} and {{.ZIG}} vars instead of PATH - Add go.mod stub in golang dir to prevent module scan errors - Add manual macOS build workflow (.github/workflows/build-macos.yml) - Update AGENTS.md with dev environment notes - Add fork notes to README.md - Add .pi/ to .gitignore --- .github/workflows/build-macos.yml | 51 ++ .gitignore | 3 + AGENTS.md | 19 +- README.md | 8 + Taskfile.yml | 32 +- frontend/types/gotypes.d.ts | 1 + package-lock.json | 949 ++++++++++++------------------ package.json | 1 + 8 files changed, 461 insertions(+), 603 deletions(-) create mode 100644 .github/workflows/build-macos.yml diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml new file mode 100644 index 0000000000..bf32fdb113 --- /dev/null +++ b/.github/workflows/build-macos.yml @@ -0,0 +1,51 @@ +name: Build macOS +on: + workflow_dispatch: + +env: + GO_VERSION: "1.26.2" + NODE_VERSION: 22 + NODE_OPTIONS: --max-old-space-size=4096 + +jobs: + build: + runs-on: macos-latest + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-go@v6 + with: + go-version: ${{ env.GO_VERSION }} + cache-dependency-path: go.sum + + - uses: actions/setup-node@v6 + with: + node-version: ${{ env.NODE_VERSION }} + cache: npm + cache-dependency-path: package-lock.json + + - name: Install Task + uses: arduino/setup-task@v2 + with: + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install FPM + run: sudo gem install fpm + + - name: Install deps + run: npm ci --no-audit --no-fund + env: + GIT_ASKPASS: "echo" + GIT_TERMINAL_PROMPT: "0" + + - name: Build + run: task package + env: + USE_SYSTEM_FPM: true + + - name: Upload artifacts + uses: actions/upload-artifact@v5 + with: + name: macos-build + path: make/ diff --git a/.gitignore b/.gitignore index 7bd717e540..faa9b6deea 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,6 @@ docsite/ .superpowers docs/superpowers .claude +golang-*/ +zig-*/ +.pi/ diff --git a/AGENTS.md b/AGENTS.md index f40f15dec2..64bf1784d6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,17 +8,21 @@ This fork of Wave Terminal is optimized for remote development workflows. The lo - `upstream` → `https://github.com/wavetermdev/waveterm` (original) - Do not run `git push` — the user handles pushes interactively with 2FA -## Dev Environment Status +## Dev Environment | Tool | Status | |------|--------| | NodeJS v24.14.0 | Available | | npm 11.9.0 | Available | | git 2.43.0 | Available | -| Go 1.25+ | **Missing** — must install before building | -| Task (build runner) | **Missing** — must install before building | +| Go 1.26.2 | Local install in `golang-1.26.2/` | +| Task (build runner) | Local npm dep (`@go-task/cli`) | -Install Task and Go before attempting builds. Then run `task init` and `task dev`. +Go and Task are installed locally (not globally). The Taskfile uses `{{.GO_DIR}}` and `{{.GO}}` vars to reference the local Go binary. + +**When upgrading Go**: download to `golang-/`, update `GO_DIR` in Taskfile.yml vars, and run `echo "module golang" > golang-/go.mod` (prevents `go mod tidy` from scanning the Go install dir as part of the project module). + +Run `./node_modules/.bin/task init` then `./node_modules/.bin/task dev`. ## Planning Documents @@ -40,10 +44,9 @@ Current active spec: `.pi/specs/portforwarding.md` ## Priorities -1. Install missing build tools (Task, Go) -2. Verify `task dev` and `task start` work -3. Implement SSH port forwarding (`LocalForward`/`RemoteForward`) — spec ready -4. Later: remove/disable AI features, MOSH support, vertical tabs, UX improvements +1. Verify `task dev` and `task start` work (build tools installed) +2. Implement SSH port forwarding (`LocalForward`/`RemoteForward`) — spec ready +3. Later: remove/disable AI features, MOSH support, vertical tabs, UX improvements ## Conventions diff --git a/README.md b/README.md index a9f406725c..67c8908f6b 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,14 @@ Want to provide input to our future releases? Connect with us on [Discord](https See [Building Wave Terminal](BUILD.md). +### Fork Notes + +This fork is optimized for remote development workflows with a focus on macOS. + +- **Local toolchain** — Go and Task are installed locally (not global), no system dependencies required +- **macOS builds** — CI builds macOS `.dmg` via GitHub Actions (manual trigger) +- **Planned changes** — SSH port forwarding, reduced AI features, MOSH support, vertical tabs, SSH config as source of truth for connections + ## Contributing Wave uses GitHub Issues for issue tracking. diff --git a/Taskfile.yml b/Taskfile.yml index bf37a83e45..76154d1b91 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -13,6 +13,10 @@ vars: ARTIFACTS_BUCKET: waveterm-github-artifacts/staging-w2 RELEASES_BUCKET: dl.waveterm.dev/releases-w2 WINGET_PACKAGE: CommandLine.Wave + GO_DIR: "golang-1.26.2" + GO: "{{.ROOT_DIR}}/{{.GO_DIR}}/bin/go" + ZIG_DIR: "zig-0.14.0" + ZIG: "{{.ROOT_DIR}}/{{.ZIG_DIR}}/zig" tasks: electron:dev: @@ -166,7 +170,7 @@ tasks: generates: - "dist/schema/**/*" cmds: - - go run cmd/generateschema/main-generateschema.go + - "{{.GO}} run cmd/generateschema/main-generateschema.go" - cmd: '{{.RMRF}} "dist/schema"' ignore_error: true - task: copyfiles:'schema':'dist/schema' @@ -225,7 +229,7 @@ tasks: - task: build:server:internal vars: ARCHS: amd64 - GO_ENV_VARS: CC="zig cc -target x86_64-windows-gnu" + GO_ENV_VARS: CC="{{.ZIG}} cc -target x86_64-windows-gnu" deps: - go:mod:tidy sources: @@ -248,7 +252,7 @@ tasks: ARCHS: sh: echo {{if eq "arm" ARCH}}arm64{{else}}{{ARCH}}{{end}} GO_ENV_VARS: - sh: echo "{{if eq "amd64" ARCH}}CC=\"zig cc -target x86_64-windows-gnu\"{{else}}CC=\"zig cc -target aarch64-windows-gnu\"{{end}}" + sh: echo "{{if eq "amd64" ARCH}}CC=\"{{.ZIG}} cc -target x86_64-windows-gnu\"{{else}}CC=\"{{.ZIG}} cc -target aarch64-windows-gnu\"{{end}}" build:server:linux: desc: Build the wavesrv component for Linux platforms (only generates artifacts for the current architecture). @@ -261,14 +265,14 @@ tasks: ARCHS: sh: echo {{if eq "arm" ARCH}}arm64{{else}}{{ARCH}}{{end}} GO_ENV_VARS: - sh: echo "{{if eq "amd64" ARCH}}CC=\"zig cc -target x86_64-linux-gnu.2.28\"{{else}}CC=\"zig cc -target aarch64-linux-gnu.2.28\"{{end}}" + sh: echo "{{if eq "amd64" ARCH}}CC=\"{{.ZIG}} cc -target x86_64-linux-gnu.2.28\"{{else}}CC=\"{{.ZIG}} cc -target aarch64-linux-gnu.2.28\"{{end}}" build:server:internal: requires: vars: - ARCHS cmd: - cmd: CGO_ENABLED=1 GOARCH={{.GOARCH}} {{.GO_ENV_VARS}} go build -tags "osusergo,sqlite_omit_load_extension" -ldflags "{{.GO_LDFLAGS}} -X main.BuildTime=$({{.DATE}} +'%Y%m%d%H%M') -X main.WaveVersion={{.VERSION}}" -o dist/bin/wavesrv.{{if eq .GOARCH "amd64"}}x64{{else}}{{.GOARCH}}{{end}}{{exeExt}} cmd/server/main-server.go + cmd: CGO_ENABLED=1 GOARCH={{.GOARCH}} {{.GO_ENV_VARS}} {{.GO}} build -tags "osusergo,sqlite_omit_load_extension" -ldflags "{{.GO_LDFLAGS}} -X main.BuildTime=$({{.DATE}} +'%Y%m%d%H%M') -X main.WaveVersion={{.VERSION}}" -o dist/bin/wavesrv.{{if eq .GOARCH "amd64"}}x64{{else}}{{.GOARCH}}{{end}}{{exeExt}} cmd/server/main-server.go for: var: ARCHS split: "," @@ -342,7 +346,7 @@ tasks: - GOOS - GOARCH - VERSION - cmd: (CGO_ENABLED=0 GOOS={{.GOOS}} GOARCH={{.GOARCH}} go build -ldflags="-s -w -X main.BuildTime=$({{.DATE}} +'%Y%m%d%H%M') -X main.WaveVersion={{.VERSION}}" -o dist/bin/wsh-{{.VERSION}}-{{.GOOS}}.{{.NORMALIZEDARCH}}{{.EXT}} cmd/wsh/main-wsh.go) + cmd: (CGO_ENABLED=0 GOOS={{.GOOS}} GOARCH={{.GOARCH}} {{.GO}} build -ldflags="-s -w -X main.BuildTime=$({{.DATE}} +'%Y%m%d%H%M') -X main.WaveVersion={{.VERSION}}" -o dist/bin/wsh-{{.VERSION}}-{{.GOOS}}.{{.NORMALIZEDARCH}}{{.EXT}} cmd/wsh/main-wsh.go) internal: true build:tsunamiscaffold: @@ -363,8 +367,8 @@ tasks: generate: desc: Generate Typescript bindings for the Go backend. cmds: - - go run cmd/generatets/main-generatets.go - - go run cmd/generatego/main-generatego.go + - "{{.GO}} run cmd/generatets/main-generatets.go" + - "{{.GO}} run cmd/generatego/main-generatego.go" deps: - build:schema sources: @@ -469,7 +473,7 @@ tasks: desc: Initialize the project for development. cmds: - npm install - - go mod tidy + - "{{.GO}} mod tidy" - cd docs && npm install dev:cleardata:windows: @@ -505,7 +509,7 @@ tasks: - go.sum sources: - go.mod - cmd: go mod tidy + cmd: "{{.GO}} mod tidy" copyfiles:*:*: desc: Recursively copy directory and its contents. @@ -522,7 +526,7 @@ tasks: tsunami:demo:todo: desc: Run the tsunami todo demo application - cmd: go run demo/todo/*.go + cmd: "{{.GO}} run demo/todo/*.go" dir: tsunami env: TSUNAMI_LISTENADDR: "localhost:12026" @@ -632,7 +636,7 @@ tasks: platforms: [windows] ignore_error: true - mkdir -p bin - - cd tsunami && go build -ldflags "-X main.BuildTime=$({{.DATE}} +'%Y%m%d%H%M') -X main.TsunamiVersion={{.VERSION}}" -o ../bin/tsunami{{exeExt}} cmd/main-tsunami.go + - cd tsunami && {{.GO}} build -ldflags "-X main.BuildTime=$({{.DATE}} +'%Y%m%d%H%M') -X main.TsunamiVersion={{.VERSION}}" -o ../bin/tsunami{{exeExt}} cmd/main-tsunami.go sources: - "tsunami/**/*.go" - "tsunami/go.mod" @@ -651,9 +655,9 @@ tasks: godoc: desc: Start the Go documentation server for the root module - cmd: $(go env GOPATH)/bin/pkgsite -http=:6060 + cmd: $({{.GO}} env GOPATH)/bin/pkgsite -http=:6060 tsunami:godoc: desc: Start the Go documentation server for the tsunami module - cmd: $(go env GOPATH)/bin/pkgsite -http=:6060 + cmd: $({{.GO}} env GOPATH)/bin/pkgsite -http=:6060 dir: tsunami diff --git a/frontend/types/gotypes.d.ts b/frontend/types/gotypes.d.ts index c5b870d7ed..b3220ab6d1 100644 --- a/frontend/types/gotypes.d.ts +++ b/frontend/types/gotypes.d.ts @@ -1589,6 +1589,7 @@ declare global { "debug:panictype"?: string; "block:view"?: string; "block:controller"?: string; + "block:subblock"?: boolean; "ai:backendtype"?: string; "ai:local"?: boolean; "wsh:cmd"?: string; diff --git a/package-lock.json b/package-lock.json index 1798a0fa38..b4995a359b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "dependencies": { "@ai-sdk/react": "^2.0.104", "@floating-ui/react": "^0.27.16", + "@go-task/cli": "^3.50.0", "@observablehq/plot": "^0.6.17", "@react-hook/resize-observer": "^2.0.2", "@table-nav/core": "^0.0.7", @@ -4848,18 +4849,6 @@ "node": ">=14.14" } }, - "node_modules/@emnapi/runtime": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", - "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", @@ -5475,6 +5464,21 @@ "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", "license": "MIT" }, + "node_modules/@go-task/cli": { + "version": "3.50.0", + "resolved": "https://registry.npmjs.org/@go-task/cli/-/cli-3.50.0.tgz", + "integrity": "sha512-xERwU5ul6fpv5yVzAOBW8feqcQSFSHNHOZVbOwcZdIz3BSmbXrAJlDM1E2pVdIi7SoZj2L2XZwrlsvWZiF5q/w==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "jszip": "^3.10.1", + "proxy-agent": "^7.0.0", + "tar": "^7.4.3" + }, + "bin": { + "task": "run-task.js" + } + }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -5576,468 +5580,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", - "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.0" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", - "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.0" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", - "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", - "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", - "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", - "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", - "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", - "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", - "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", - "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", - "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", - "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.0" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", - "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.0" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", - "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.0" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", - "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.0" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", - "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.0" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", - "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", - "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.0" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", - "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "peer": true, - "dependencies": { - "@emnapi/runtime": "^1.4.4" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", - "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", - "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", - "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -6060,7 +5602,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "dev": true, "license": "ISC", "dependencies": { "minipass": "^7.0.4" @@ -11309,6 +10850,18 @@ "node": ">=12" } }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -11615,6 +11168,15 @@ "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/basic-ftp": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz", + "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -12377,7 +11939,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": ">=18" @@ -14198,6 +13759,15 @@ "lodash-es": "^4.17.21" } }, + "node_modules/data-uri-to-buffer": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-7.0.0.tgz", + "integrity": "sha512-CuRUx0TXGSbbWdEci3VK/XOZGP3n0P4pIKpsqpVtBqaIIuj3GKK8H45oAqA4Rg8FHipc+CzRdUzmD4YQXxv66Q==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/dayjs": { "version": "1.11.19", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", @@ -14395,10 +13965,27 @@ "object-keys": "^1.1.1" }, "engines": { - "node": ">= 0.4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/degenerator": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-6.0.0.tgz", + "integrity": "sha512-j5MdXdefrecJeSqTpUrgZd4fBsD2IxZx0JlJD+n1Q7+aTf7/HcyXSfHsicPW6ekPurX159v1ZYla6OJgSPh2Dw==", + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "quickjs-wasi": "^0.0.1" } }, "node_modules/delaunator": { @@ -15426,6 +15013,37 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/eslint": { "version": "9.39.3", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.3.tgz", @@ -16804,6 +16422,20 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/get-uri": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-7.0.0.tgz", + "integrity": "sha512-ZsC7KQxm1Hra8yO0RvMZ4lGJT7vnBtSNpEHKq39MPN7vjuvCiu1aQ8rkXUaIXG1y/TSDez97Gmv04ibnYqCp/A==", + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "7.0.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -18037,6 +17669,12 @@ "node": ">=16.x" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, "node_modules/immer": { "version": "10.1.3", "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.3.tgz", @@ -18177,7 +17815,6 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", - "dev": true, "license": "MIT", "engines": { "node": ">= 12" @@ -18890,6 +18527,54 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/katex": { "version": "0.16.38", "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.38.tgz", @@ -19036,6 +18721,15 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lightningcss": { "version": "1.31.1", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.31.1.tgz", @@ -22443,7 +22137,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -22583,7 +22276,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", - "dev": true, "license": "MIT", "dependencies": { "minipass": "^7.1.2" @@ -22819,6 +22511,15 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "license": "MIT" }, + "node_modules/netmask": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.1.1.tgz", + "integrity": "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -23539,6 +23240,90 @@ "node": ">=8" } }, + "node_modules/pac-proxy-agent": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-8.0.0.tgz", + "integrity": "sha512-HyCoVbyQ/nbVlQ/R6wBu0YXhbG2oAnEK5BQ3xMyj1OffQmU5NoOnpLzgPlKHaobUzz5NK0+AZHby4TdydAEBUA==", + "license": "MIT", + "dependencies": { + "agent-base": "8.0.0", + "debug": "^4.3.4", + "get-uri": "7.0.0", + "http-proxy-agent": "8.0.0", + "https-proxy-agent": "8.0.0", + "pac-resolver": "8.0.0", + "quickjs-wasi": "^0.0.1", + "socks-proxy-agent": "9.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/agent-base": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-8.0.0.tgz", + "integrity": "sha512-QT8i0hCz6C/KQ+KTAbSNwCHDGdmUJl2tp2ZpNlGSWCfhUNVbYG2WLE3MdZGBAgXPV4GAvjGMxo+C1hroyxmZEg==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/http-proxy-agent": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-8.0.0.tgz", + "integrity": "sha512-7pose0uGgrCJeH2Qh4JcNhWZp3u/oNrWjNYDK4ydOLxOpTw8V8ogHFAmkz0VWq96JBFj4umVJpvmQi287rSYLg==", + "license": "MIT", + "dependencies": { + "agent-base": "8.0.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-8.0.0.tgz", + "integrity": "sha512-YYeW+iCnAS3xhvj2dvVoWgsbca3RfQy/IlaNHHOtDmU0jMqPI9euIq3Y9BJETdxk16h9NHHCKqp/KB9nIMStCQ==", + "license": "MIT", + "dependencies": { + "agent-base": "8.0.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/socks-proxy-agent": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-9.0.0.tgz", + "integrity": "sha512-fFlbMlfsXhK02ZB8aZY7Hwxh/IHBV9b1Oq9bvBk6tkFWXvdAxUgA0wbw/NYR5liU3Y5+KI6U4FH3kYJt9QYv0w==", + "license": "MIT", + "dependencies": { + "agent-base": "8.0.0", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-8.0.0.tgz", + "integrity": "sha512-SVNzOxVq2zuTew3WAt7U8UghwzJzuWYuJryd3y8FxyLTZdjVoCzY8kLP39PpEqQCDvlMWdQXwViu0sYT3eiU2w==", + "license": "MIT", + "dependencies": { + "degenerator": "6.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "quickjs-wasi": "^0.0.1" + } + }, "node_modules/package-json": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz", @@ -23719,6 +23504,12 @@ "integrity": "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==", "license": "MIT" }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, "node_modules/papaparse": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz", @@ -26021,6 +25812,89 @@ "node": ">= 0.10" } }, + "node_modules/proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-7.0.0.tgz", + "integrity": "sha512-okTgt79rHTvMHkr/Ney5rZpgCHh3g1g3tI5uhkgN5b7OeI3n0Q/ui1uv9OdrnZNJM9WIZJqZPh/UJs+YtO/TMQ==", + "license": "MIT", + "dependencies": { + "agent-base": "8.0.0", + "debug": "^4.3.4", + "http-proxy-agent": "8.0.0", + "https-proxy-agent": "8.0.0", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "8.0.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "9.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/agent-base": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-8.0.0.tgz", + "integrity": "sha512-QT8i0hCz6C/KQ+KTAbSNwCHDGdmUJl2tp2ZpNlGSWCfhUNVbYG2WLE3MdZGBAgXPV4GAvjGMxo+C1hroyxmZEg==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/http-proxy-agent": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-8.0.0.tgz", + "integrity": "sha512-7pose0uGgrCJeH2Qh4JcNhWZp3u/oNrWjNYDK4ydOLxOpTw8V8ogHFAmkz0VWq96JBFj4umVJpvmQi287rSYLg==", + "license": "MIT", + "dependencies": { + "agent-base": "8.0.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/https-proxy-agent": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-8.0.0.tgz", + "integrity": "sha512-YYeW+iCnAS3xhvj2dvVoWgsbca3RfQy/IlaNHHOtDmU0jMqPI9euIq3Y9BJETdxk16h9NHHCKqp/KB9nIMStCQ==", + "license": "MIT", + "dependencies": { + "agent-base": "8.0.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-agent/node_modules/socks-proxy-agent": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-9.0.0.tgz", + "integrity": "sha512-fFlbMlfsXhK02ZB8aZY7Hwxh/IHBV9b1Oq9bvBk6tkFWXvdAxUgA0wbw/NYR5liU3Y5+KI6U4FH3kYJt9QYv0w==", + "license": "MIT", + "dependencies": { + "agent-base": "8.0.0", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/pump": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", @@ -26103,6 +25977,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/quickjs-wasi": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/quickjs-wasi/-/quickjs-wasi-0.0.1.tgz", + "integrity": "sha512-fBWNLTBkxkLAhe1AzF1hyXEvuA+N+vV1WMP2D6iiMUblvmOt8Pp5t8zUcgvz7aYA1ldUdxDlgUse15dmcKjkNg==", + "license": "MIT" + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -28850,6 +28730,12 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -28874,61 +28760,6 @@ "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", "license": "MIT" }, - "node_modules/sharp": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", - "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", - "hasInstallScript": true, - "license": "Apache-2.0", - "optional": true, - "peer": true, - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.4", - "semver": "^7.7.2" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.3", - "@img/sharp-darwin-x64": "0.34.3", - "@img/sharp-libvips-darwin-arm64": "1.2.0", - "@img/sharp-libvips-darwin-x64": "1.2.0", - "@img/sharp-libvips-linux-arm": "1.2.0", - "@img/sharp-libvips-linux-arm64": "1.2.0", - "@img/sharp-libvips-linux-ppc64": "1.2.0", - "@img/sharp-libvips-linux-s390x": "1.2.0", - "@img/sharp-libvips-linux-x64": "1.2.0", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", - "@img/sharp-libvips-linuxmusl-x64": "1.2.0", - "@img/sharp-linux-arm": "0.34.3", - "@img/sharp-linux-arm64": "0.34.3", - "@img/sharp-linux-ppc64": "0.34.3", - "@img/sharp-linux-s390x": "0.34.3", - "@img/sharp-linux-x64": "0.34.3", - "@img/sharp-linuxmusl-arm64": "0.34.3", - "@img/sharp-linuxmusl-x64": "0.34.3", - "@img/sharp-wasm32": "0.34.3", - "@img/sharp-win32-arm64": "0.34.3", - "@img/sharp-win32-ia32": "0.34.3", - "@img/sharp-win32-x64": "0.34.3" - } - }, - "node_modules/sharp/node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", - "license": "Apache-2.0", - "optional": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -29222,7 +29053,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 6.0.0", @@ -29263,7 +29093,6 @@ "version": "2.8.7", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", - "dev": true, "license": "MIT", "dependencies": { "ip-address": "^10.0.1", @@ -29805,46 +29634,6 @@ "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", "license": "MIT" }, - "node_modules/svgo": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.0.tgz", - "integrity": "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "commander": "^11.1.0", - "css-select": "^5.1.0", - "css-tree": "^3.0.1", - "css-what": "^6.1.0", - "csso": "^5.0.5", - "picocolors": "^1.1.1", - "sax": "^1.4.1" - }, - "bin": { - "svgo": "bin/svgo.js" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" - } - }, - "node_modules/svgo/node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=16" - } - }, "node_modules/swr": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/swr/-/swr-2.4.1.tgz", @@ -29914,7 +29703,6 @@ "version": "7.5.11", "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.11.tgz", "integrity": "sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", @@ -29956,7 +29744,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": ">=18" diff --git a/package.json b/package.json index 4c1d56798f..3379025e8f 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "dependencies": { "@ai-sdk/react": "^2.0.104", "@floating-ui/react": "^0.27.16", + "@go-task/cli": "^3.50.0", "@observablehq/plot": "^0.6.17", "@react-hook/resize-observer": "^2.0.2", "@table-nav/core": "^0.0.7", From 5866e986a6198653811262c1365e77479e1727e9 Mon Sep 17 00:00:00 2001 From: Jeremy Lam Date: Tue, 12 May 2026 09:06:38 +0000 Subject: [PATCH 03/19] clean up upstream CI workflows - Delete deploy-docsite.yml, testdriver-build.yml, testdriver.yml - Change codeql.yml to manual trigger only --- .github/workflows/codeql.yml | 20 +--- .github/workflows/deploy-docsite.yml | 80 -------------- .github/workflows/testdriver-build.yml | 83 --------------- .github/workflows/testdriver.yml | 141 ------------------------- 4 files changed, 1 insertion(+), 323 deletions(-) delete mode 100644 .github/workflows/deploy-docsite.yml delete mode 100644 .github/workflows/testdriver-build.yml delete mode 100644 .github/workflows/testdriver.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 30a8979b9b..9adf8336d1 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -12,25 +12,7 @@ name: "CodeQL" on: - push: - branches: ["main"] - paths: - - "**/*.go" - - "**/*.ts" - - "**/*.tsx" - pull_request: - branches: ["main"] - paths: - - "**/*.go" - - "**/*.ts" - - "**/*.tsx" - types: - - opened - - synchronize - - reopened - - ready_for_review - schedule: - - cron: "36 5 * * 5" + workflow_dispatch: env: NODE_VERSION: 22 diff --git a/.github/workflows/deploy-docsite.yml b/.github/workflows/deploy-docsite.yml deleted file mode 100644 index 092b024cb5..0000000000 --- a/.github/workflows/deploy-docsite.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: Docsite CI/CD - -run-name: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && 'Build and Deploy' || 'Test Build' }} Docsite - -env: - NODE_VERSION: 22 - -on: - push: - branches: - - main - workflow_dispatch: - # Also run any time a PR is opened targeting the docs - pull_request: - branches: - - main - types: - - opened - - synchronize - - reopened - - ready_for_review - paths: - - "docs/**" - - ".github/workflows/deploy-docsite.yml" - - "Taskfile.yml" - -jobs: - build: - name: Build Docsite - runs-on: ubuntu-latest - if: github.event.pull_request.draft == false - steps: - - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - uses: actions/setup-node@v6 - with: - node-version: ${{env.NODE_VERSION}} - cache: npm - cache-dependency-path: package-lock.json - - name: Install Task - uses: arduino/setup-task@v2 - with: - version: 3.x - repo-token: ${{ secrets.GITHUB_TOKEN }} - - uses: nick-fields/retry@v4 - name: npm ci - with: - command: npm ci --no-audit --no-fund - retry_on: error - max_attempts: 3 - timeout_minutes: 5 - - name: Build docsite - run: task docsite:build:public - - name: Upload Build Artifact - # Only upload the build artifact when pushed to the main branch - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - uses: actions/upload-pages-artifact@v4 - with: - path: docs/build - deploy: - name: Deploy to GitHub Pages - # Only deploy when pushed to the main branch - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - needs: build - # Grant GITHUB_TOKEN the permissions required to make a Pages deployment - permissions: - pages: write # to deploy to Pages - id-token: write # to verify the deployment originates from an appropriate source - - # Deploy to the github-pages environment - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - - runs-on: ubuntu-latest - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v5 diff --git a/.github/workflows/testdriver-build.yml b/.github/workflows/testdriver-build.yml deleted file mode 100644 index da190073e6..0000000000 --- a/.github/workflows/testdriver-build.yml +++ /dev/null @@ -1,83 +0,0 @@ -name: TestDriver.ai Build - -on: - push: - branches: - - main - tags: - - "v[0-9]+.[0-9]+.[0-9]+*" - pull_request: - # branches: - # - main - # paths-ignore: - # - "docs/**" - # - ".storybook/**" - # - ".vscode/**" - # - ".editorconfig" - # - ".gitignore" - # - ".prettierrc" - # - ".eslintrc.js" - # - "**/*.md" - types: - - opened - - synchronize - - reopened - - ready_for_review - schedule: - - cron: 0 21 * * * - workflow_dispatch: null - -env: - GO_VERSION: "1.25.6" - NODE_VERSION: 22 - -permissions: - contents: read # To allow the action to read repository contents - pull-requests: write # To allow the action to create/update pull request comments - -jobs: - build_and_upload: - name: Build for TestDriver.ai - runs-on: windows-latest - if: github.event.pull_request.draft == false - steps: - - uses: actions/checkout@v6 - - # General build dependencies - - uses: actions/setup-go@v6 - with: - go-version: ${{env.GO_VERSION}} - - uses: actions/setup-node@v6 - with: - node-version: ${{env.NODE_VERSION}} - cache: npm - cache-dependency-path: package-lock.json - - uses: nick-fields/retry@v4 - name: npm ci - with: - command: npm ci --no-audit --no-fund - retry_on: error - max_attempts: 3 - timeout_minutes: 5 - - name: Install Task - uses: arduino/setup-task@v2 - with: - version: 3.x - repo-token: ${{ secrets.GITHUB_TOKEN }} - - name: Install Zig - uses: mlugg/setup-zig@v2 - - - name: Build - run: task package - env: - USE_SYSTEM_FPM: true # Ensure that the installed version of FPM is used rather than the bundled one. - CSC_IDENTITY_AUTO_DISCOVERY: false # disable codesign - shell: powershell # electron-builder's Windows code signing package has some compatibility issues with pwsh, so we need to use Windows Powershell - - # Upload .exe as an artifact - - name: Upload .exe artifact - id: upload - uses: actions/upload-artifact@v5 - with: - name: windows-exe - path: make/*.exe diff --git a/.github/workflows/testdriver.yml b/.github/workflows/testdriver.yml deleted file mode 100644 index 9d51ec7659..0000000000 --- a/.github/workflows/testdriver.yml +++ /dev/null @@ -1,141 +0,0 @@ -name: TestDriver.ai Run - -on: - workflow_run: - workflows: ["TestDriver.ai Build"] - types: - - completed - -env: - GO_VERSION: "1.25.6" - NODE_VERSION: 22 - -permissions: - contents: read - statuses: write - -jobs: - context: - runs-on: ubuntu-22.04 - steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - name: Dump job context - env: - JOB_CONTEXT: ${{ toJson(job) }} - run: echo "$JOB_CONTEXT" - - name: Dump steps context - env: - STEPS_CONTEXT: ${{ toJson(steps) }} - run: echo "$STEPS_CONTEXT" - - name: Dump runner context - env: - RUNNER_CONTEXT: ${{ toJson(runner) }} - run: echo "$RUNNER_CONTEXT" - - name: Dump strategy context - env: - STRATEGY_CONTEXT: ${{ toJson(strategy) }} - run: echo "$STRATEGY_CONTEXT" - - name: Dump matrix context - env: - MATRIX_CONTEXT: ${{ toJson(matrix) }} - run: echo "$MATRIX_CONTEXT" - run_testdriver: - name: Run TestDriver.ai - runs-on: windows-latest - if: github.event.workflow_run.conclusion == 'success' - steps: - - uses: testdriverai/action@main - id: testdriver - env: - FORCE_COLOR: "3" - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - key: ${{ secrets.DASHCAM_API }} - prerun: | - $headers = @{ - Authorization = "token ${{ secrets.GITHUB_TOKEN }}" - } - - $downloadFolder = "./download" - $artifactFileName = "waveterm.exe" - $artifactFilePath = "$downloadFolder/$artifactFileName" - - Write-Host "Starting the artifact download process..." - - # Create the download directory if it doesn't exist - if (-not (Test-Path -Path $downloadFolder)) { - Write-Host "Creating download folder..." - mkdir $downloadFolder - } else { - Write-Host "Download folder already exists." - } - - # Fetch the artifact upload URL - Write-Host "Fetching the artifact upload URL..." - $artifactUrl = (Invoke-RestMethod -Uri "https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}/artifacts" -Headers $headers).artifacts[0].archive_download_url - - if ($artifactUrl) { - Write-Host "Artifact URL successfully fetched: $artifactUrl" - } else { - Write-Error "Failed to fetch the artifact URL." - exit 1 - } - - # Download the artifact (zipped file) - Write-Host "Starting artifact download..." - $artifactZipPath = "$env:TEMP\artifact.zip" - try { - Invoke-WebRequest -Uri $artifactUrl ` - -Headers $headers ` - -OutFile $artifactZipPath ` - -MaximumRedirection 5 - - Write-Host "Artifact downloaded successfully to $artifactZipPath" - } catch { - Write-Error "Error downloading artifact: $_" - exit 1 - } - - # Unzip the artifact - $artifactUnzipPath = "$env:TEMP\artifact" - Write-Host "Unzipping the artifact to $artifactUnzipPath..." - try { - Expand-Archive -Path $artifactZipPath -DestinationPath $artifactUnzipPath -Force - Write-Host "Artifact unzipped successfully to $artifactUnzipPath" - } catch { - Write-Error "Failed to unzip the artifact: $_" - exit 1 - } - - # Find the installer or app executable - $artifactInstallerPath = Get-ChildItem -Path $artifactUnzipPath -Filter *.exe -Recurse | Select-Object -First 1 - - if ($artifactInstallerPath) { - Write-Host "Executable file found: $($artifactInstallerPath.FullName)" - } else { - Write-Error "Executable file not found. Exiting." - exit 1 - } - - # Run the installer and log the result - Write-Host "Running the installer: $($artifactInstallerPath.FullName)..." - try { - Start-Process -FilePath $artifactInstallerPath.FullName -Wait - Write-Host "Installer ran successfully." - } catch { - Write-Error "Failed to run the installer: $_" - exit 1 - } - - # Optional: If the app executable is different from the installer, find and launch it - $wavePath = Join-Path $env:USERPROFILE "AppData\Local\Programs\waveterm\Wave.exe" - - Write-Host "Launching the application: $($wavePath)" - Start-Process -FilePath $wavePath - Write-Host "Application launched." - - prompt: | - 1. /run testdriver/onboarding.yml From 95347af62b2694339f18b62cd571b88affc6945b Mon Sep 17 00:00:00 2001 From: Jeremy Lam Date: Tue, 12 May 2026 09:08:14 +0000 Subject: [PATCH 04/19] move fork notice above title in README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 67c8908f6b..e385a7f000 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@

+> **Fork:** This is a fork of [Wave Terminal](https://github.com/wavetermdev/waveterm) optimized for remote development workflows. + # Wave Terminal
From 18c93b6b8eb81d505fa85f51377d7c9b45e77e0f Mon Sep 17 00:00:00 2001 From: Jeremy Lam Date: Tue, 12 May 2026 09:15:11 +0000 Subject: [PATCH 05/19] fix macOS CI workflow to download Go and Zig locally --- .github/workflows/build-macos.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index bf32fdb113..a40e424966 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -13,10 +13,16 @@ jobs: steps: - uses: actions/checkout@v6 - - uses: actions/setup-go@v6 - with: - go-version: ${{ env.GO_VERSION }} - cache-dependency-path: go.sum + - name: Install Go + run: | + curl -sL "https://go.dev/dl/go${{ env.GO_VERSION }}.darwin-arm64.tar.gz" | tar -xzf - -C . + mv go golang-${{ env.GO_VERSION }} + echo "module golang" > golang-${{ env.GO_VERSION }}/go.mod + + - name: Install Zig + run: | + curl -sL "https://ziglang.org/download/0.14.0/zig-macos-aarch64-0.14.0.tar.xz" | tar -xJf - + mv zig-macos-aarch64-0.14.0 zig-0.14.0 - uses: actions/setup-node@v6 with: @@ -30,9 +36,6 @@ jobs: version: 3.x repo-token: ${{ secrets.GITHUB_TOKEN }} - - name: Install FPM - run: sudo gem install fpm - - name: Install deps run: npm ci --no-audit --no-fund env: From 23127297ec4ddf6b049b33dffe30aaa9ee2fe2fd Mon Sep 17 00:00:00 2001 From: Jeremy Lam Date: Wed, 13 May 2026 12:57:45 +0000 Subject: [PATCH 06/19] remove telemetry, analytics, and user tracking from waveterm Completely removes all telemetry collection, local storage, and cloud upload functionality per the remove-telemetry spec. Key changes: - Delete pkg/telemetry/, pkg/telemetry/telemetrydata/, pkg/wcloud/ - Remove telemetry loops and event recording from cmd/server/main-server.go - Remove 5 telemetry RPC commands and ActivityUpdate type from wshrpc layer - Remove telemetry call sites from wcore, conncontroller, wslconn, jobcontroller, aiusechat, panichandler, clientservice - Remove TelemetryClear/TelemetryEnabled from SettingsConfig and regenerate metaconsts.go, gotypes.d.ts, services.ts, wshclientapi.ts, wshclient.go - Remove wsh CLI activity tracking (activityWrap, sendActivity) - Remove frontend recordTEvent, ActivityCommand, RecordTEventCommand call sites - Remove Electron activity tracking (logActiveState, sendDisplaysTDataEvent, increment-term-commands IPC) - Simplify onboarding: remove telemetry toggle, NoTelemetryStarPage, and telemetry consent flow - Delete telemetry docs (telemetry.mdx, telemetry-old.mdx) - Add migration to drop db_tevent and db_activity tables Compilation fixes: - Restore services import in onboarding.tsx for AgreeTos - Fix emain/preload.ts and emain-ipc.ts corruption from incrementTermCommands removal (restore nativePaste, openBuilder, saveTextFile, setIsActive) - Fix malformed custom.d.ts type declaration - Replace remaining telemetry:enabled reads in AI panel files with true --- cmd/generatego/main-generatego.go | 1 - cmd/server/main-server.go | 274 ---------- cmd/wsh/cmd/wshcmd-ai.go | 1 - cmd/wsh/cmd/wshcmd-badge.go | 1 - cmd/wsh/cmd/wshcmd-debug.go | 13 - cmd/wsh/cmd/wshcmd-debugterm.go | 1 - cmd/wsh/cmd/wshcmd-deleteblock.go | 1 - cmd/wsh/cmd/wshcmd-editconfig.go | 1 - cmd/wsh/cmd/wshcmd-editor.go | 1 - cmd/wsh/cmd/wshcmd-file.go | 16 +- cmd/wsh/cmd/wshcmd-focusblock.go | 1 - cmd/wsh/cmd/wshcmd-getmeta.go | 1 - cmd/wsh/cmd/wshcmd-getvar.go | 1 - cmd/wsh/cmd/wshcmd-launch.go | 1 - cmd/wsh/cmd/wshcmd-notify.go | 1 - cmd/wsh/cmd/wshcmd-root.go | 26 - cmd/wsh/cmd/wshcmd-run.go | 1 - cmd/wsh/cmd/wshcmd-secret.go | 5 - cmd/wsh/cmd/wshcmd-setbg.go | 1 - cmd/wsh/cmd/wshcmd-setconfig.go | 1 - cmd/wsh/cmd/wshcmd-setmeta.go | 1 - cmd/wsh/cmd/wshcmd-setvar.go | 1 - cmd/wsh/cmd/wshcmd-ssh.go | 1 - cmd/wsh/cmd/wshcmd-tabindicator.go | 1 - cmd/wsh/cmd/wshcmd-term.go | 1 - cmd/wsh/cmd/wshcmd-termscrollback.go | 1 - cmd/wsh/cmd/wshcmd-view.go | 1 - cmd/wsh/cmd/wshcmd-wavepath.go | 1 - cmd/wsh/cmd/wshcmd-web.go | 1 - cmd/wsh/cmd/wshcmd-wsl.go | 1 - .../000012_drop_telemetry.down.sql | 1 + .../000012_drop_telemetry.up.sql | 2 + docs/docs/config.mdx | 5 +- docs/docs/faq.mdx | 12 - docs/docs/index.mdx | 1 - docs/docs/telemetry-old.mdx | 130 ----- docs/docs/telemetry.mdx | 71 --- emain/emain-activity.ts | 42 -- emain/emain-ipc.ts | 23 +- emain/emain.ts | 123 +---- emain/preload.ts | 2 - frontend/app/aipanel/aimode.tsx | 13 +- frontend/app/aipanel/aipanel-contextmenu.ts | 10 - frontend/app/aipanel/aipanel.tsx | 10 +- frontend/app/aipanel/aitooluse.tsx | 4 +- frontend/app/aipanel/byokannouncement.tsx | 22 - frontend/app/aipanel/restorebackupmodal.tsx | 4 +- frontend/app/aipanel/telemetryrequired.tsx | 106 ---- frontend/app/aipanel/waveai-model.tsx | 14 +- frontend/app/block/blockenv.ts | 4 +- frontend/app/block/blockframe-header.tsx | 9 +- frontend/app/block/connectionbutton.tsx | 5 +- .../app/block/durable-session-flyover.tsx | 5 +- frontend/app/modals/about.tsx | 8 +- .../app/onboarding/onboarding-durable.tsx | 10 +- .../app/onboarding/onboarding-features.tsx | 31 -- .../app/onboarding/onboarding-starask.tsx | 44 +- .../onboarding/onboarding-upgrade-minor.tsx | 33 +- frontend/app/onboarding/onboarding.tsx | 148 +----- frontend/app/store/global.ts | 9 - frontend/app/store/keymodel.ts | 8 +- frontend/app/store/services.ts | 3 - frontend/app/store/wshclientapi.ts | 30 -- frontend/app/tab/tab.tsx | 4 +- frontend/app/tab/tabbarenv.ts | 4 +- frontend/app/tab/tabcontextmenu.ts | 12 +- frontend/app/tab/vtabbarenv.ts | 4 +- frontend/app/view/term/osc-handlers.ts | 28 +- frontend/app/view/term/term-model.ts | 8 +- .../app/view/waveconfig/waveconfig-model.ts | 26 +- frontend/app/view/waveconfig/waveconfigenv.ts | 4 +- .../app/workspace/workspace-layout-model.ts | 6 +- frontend/preview/mock/preview-electron-api.ts | 2 +- .../preview/previews/onboarding.preview.tsx | 7 +- frontend/types/custom.d.ts | 1 - frontend/types/gotypes.d.ts | 177 ------- pkg/aiusechat/usechat.go | 38 -- pkg/jobcontroller/jobcontroller.go | 35 -- pkg/panichandler/panichandler.go | 20 - pkg/remote/conncontroller/conncontroller.go | 33 +- pkg/service/clientservice/clientservice.go | 12 - pkg/telemetry/telemetry.go | 466 ------------------ pkg/telemetry/telemetrydata/telemetrydata.go | 303 ------------ pkg/wcloud/wcloud.go | 323 ------------ pkg/wcloud/wclouddata.go | 23 - pkg/wconfig/metaconsts.go | 3 - pkg/wconfig/settingsconfig.go | 9 +- pkg/wcore/block.go | 38 -- pkg/wcore/layout.go | 6 +- pkg/wcore/wcore.go | 18 - pkg/wcore/workspace.go | 9 +- pkg/wshrpc/wshclient/wshclient.go | 31 -- pkg/wshrpc/wshrpctypes.go | 43 +- pkg/wshrpc/wshserver/wshserver.go | 82 --- pkg/wslconn/wslconn.go | 20 - schema/settings.json | 6 - 96 files changed, 77 insertions(+), 3025 deletions(-) create mode 100644 db/migrations-wstore/000012_drop_telemetry.down.sql create mode 100644 db/migrations-wstore/000012_drop_telemetry.up.sql delete mode 100644 docs/docs/telemetry-old.mdx delete mode 100644 docs/docs/telemetry.mdx delete mode 100644 frontend/app/aipanel/telemetryrequired.tsx delete mode 100644 pkg/telemetry/telemetry.go delete mode 100644 pkg/telemetry/telemetrydata/telemetrydata.go delete mode 100644 pkg/wcloud/wcloud.go delete mode 100644 pkg/wcloud/wclouddata.go diff --git a/cmd/generatego/main-generatego.go b/cmd/generatego/main-generatego.go index ab7e338439..49cab8010f 100644 --- a/cmd/generatego/main-generatego.go +++ b/cmd/generatego/main-generatego.go @@ -26,7 +26,6 @@ func GenerateWshClient() error { gogen.GenerateBoilerplate(&buf, "wshclient", []string{ "github.com/wavetermdev/waveterm/pkg/aiusechat/uctypes", "github.com/wavetermdev/waveterm/pkg/baseds", - "github.com/wavetermdev/waveterm/pkg/telemetry/telemetrydata", "github.com/wavetermdev/waveterm/pkg/vdom", "github.com/wavetermdev/waveterm/pkg/waveobj", "github.com/wavetermdev/waveterm/pkg/wconfig", diff --git a/cmd/server/main-server.go b/cmd/server/main-server.go index b204643ee8..323deaca87 100644 --- a/cmd/server/main-server.go +++ b/cmd/server/main-server.go @@ -22,19 +22,13 @@ import ( "github.com/wavetermdev/waveterm/pkg/filestore" "github.com/wavetermdev/waveterm/pkg/jobcontroller" "github.com/wavetermdev/waveterm/pkg/panichandler" - "github.com/wavetermdev/waveterm/pkg/remote/conncontroller" "github.com/wavetermdev/waveterm/pkg/remote/fileshare/wshfs" - "github.com/wavetermdev/waveterm/pkg/secretstore" "github.com/wavetermdev/waveterm/pkg/service" - "github.com/wavetermdev/waveterm/pkg/telemetry" - "github.com/wavetermdev/waveterm/pkg/telemetry/telemetrydata" "github.com/wavetermdev/waveterm/pkg/util/envutil" "github.com/wavetermdev/waveterm/pkg/util/shellutil" "github.com/wavetermdev/waveterm/pkg/util/sigutil" - "github.com/wavetermdev/waveterm/pkg/util/utilfn" "github.com/wavetermdev/waveterm/pkg/wavebase" "github.com/wavetermdev/waveterm/pkg/waveobj" - "github.com/wavetermdev/waveterm/pkg/wcloud" "github.com/wavetermdev/waveterm/pkg/wconfig" "github.com/wavetermdev/waveterm/pkg/wcore" "github.com/wavetermdev/waveterm/pkg/web" @@ -44,7 +38,6 @@ import ( "github.com/wavetermdev/waveterm/pkg/wshrpc/wshremote" "github.com/wavetermdev/waveterm/pkg/wshrpc/wshserver" "github.com/wavetermdev/waveterm/pkg/wshutil" - "github.com/wavetermdev/waveterm/pkg/wslconn" "github.com/wavetermdev/waveterm/pkg/wstore" "net/http" @@ -55,15 +48,8 @@ import ( var WaveVersion = "0.0.0" var BuildTime = "0" -const InitialTelemetryWait = 10 * time.Second -const TelemetryTick = 2 * time.Minute -const TelemetryInterval = 4 * time.Hour -const TelemetryInitialCountsWait = 5 * time.Second -const TelemetryCountsInterval = 1 * time.Hour const BackupCleanupTick = 2 * time.Minute const BackupCleanupInterval = 4 * time.Hour -const InitialDiagnosticWait = 5 * time.Minute -const DiagnosticTick = 10 * time.Minute var shutdownOnce sync.Once @@ -81,8 +67,6 @@ func doShutdown(reason string) { ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) defer cancelFn() go blockcontroller.StopAllBlockControllersForShutdown() - shutdownActivityUpdate() - sendTelemetryWrapper() // TODO deal with flush in progress clearTempFiles() filestore.WFS.FlushCache(ctx) @@ -118,74 +102,6 @@ func startConfigWatcher() { } } -func telemetryLoop() { - defer func() { - panichandler.PanicHandler("telemetryLoop", recover()) - }() - var nextSend int64 - time.Sleep(InitialTelemetryWait) - for { - if time.Now().Unix() > nextSend { - nextSend = time.Now().Add(TelemetryInterval).Unix() - sendTelemetryWrapper() - } - time.Sleep(TelemetryTick) - } -} - -func diagnosticLoop() { - defer func() { - panichandler.PanicHandler("diagnosticLoop", recover()) - }() - if os.Getenv("WAVETERM_NOPING") != "" { - log.Printf("WAVETERM_NOPING set, disabling diagnostic ping\n") - return - } - var lastSentDate string - time.Sleep(InitialDiagnosticWait) - for { - currentDate := time.Now().Format("2006-01-02") - if lastSentDate == "" || lastSentDate != currentDate { - if sendDiagnosticPing() { - lastSentDate = currentDate - } - } - time.Sleep(DiagnosticTick) - } -} - -func sendDiagnosticPing() bool { - ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) - defer cancelFn() - - rpcClient := wshclient.GetBareRpcClient() - isOnline, err := wshclient.NetworkOnlineCommand(rpcClient, &wshrpc.RpcOpts{Route: "electron", Timeout: 2000}) - if err != nil || !isOnline { - return false - } - clientId := wstore.GetClientId() - usageTelemetry := telemetry.IsTelemetryEnabled() - wcloud.SendDiagnosticPing(ctx, clientId, usageTelemetry) - return true -} - -func setupTelemetryConfigHandler() { - watcher := wconfig.GetWatcher() - if watcher == nil { - return - } - currentConfig := watcher.GetFullConfig() - currentTelemetryEnabled := currentConfig.Settings.TelemetryEnabled - - watcher.RegisterUpdateHandler(func(newConfig wconfig.FullConfigType) { - newTelemetryEnabled := newConfig.Settings.TelemetryEnabled - if newTelemetryEnabled != currentTelemetryEnabled { - currentTelemetryEnabled = newTelemetryEnabled - wcore.GoSendNoTelemetryUpdate(newTelemetryEnabled) - } - }) -} - func backupCleanupLoop() { defer func() { panichandler.PanicHandler("backupCleanupLoop", recover()) @@ -203,185 +119,6 @@ func backupCleanupLoop() { } } -func panicTelemetryHandler(panicName string) { - activity := wshrpc.ActivityUpdate{NumPanics: 1} - err := telemetry.UpdateActivity(context.Background(), activity) - if err != nil { - log.Printf("error updating activity (panicTelemetryHandler): %v\n", err) - } - telemetry.RecordTEvent(context.Background(), telemetrydata.MakeTEvent("debug:panic", telemetrydata.TEventProps{ - PanicType: panicName, - })) -} - -func sendTelemetryWrapper() { - defer func() { - panichandler.PanicHandler("sendTelemetryWrapper", recover()) - }() - ctx, cancelFn := context.WithTimeout(context.Background(), 15*time.Second) - defer cancelFn() - beforeSendActivityUpdate(ctx) - clientId := wstore.GetClientId() - err := wcloud.SendAllTelemetry(clientId) - if err != nil { - log.Printf("[error] sending telemetry: %v\n", err) - } -} - -func updateTelemetryCounts(lastCounts telemetrydata.TEventProps) telemetrydata.TEventProps { - ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) - defer cancelFn() - var props telemetrydata.TEventProps - props.CountBlocks, _ = wstore.DBGetCount[*waveobj.Block](ctx) - props.CountTabs, _ = wstore.DBGetCount[*waveobj.Tab](ctx) - props.CountWindows, _ = wstore.DBGetCount[*waveobj.Window](ctx) - props.CountWorkspaces, _, _ = wstore.DBGetWSCounts(ctx) - props.CountSSHConn = conncontroller.GetNumSSHHasConnected() - props.CountWSLConn = wslconn.GetNumWSLHasConnected() - props.CountJobs = jobcontroller.GetNumJobsRunning() - props.CountJobsConnected = jobcontroller.GetNumJobsConnected() - props.CountViews, _ = wstore.DBGetBlockViewCounts(ctx) - - fullConfig := wconfig.GetWatcher().GetFullConfig() - customWidgets := fullConfig.CountCustomWidgets() - customAIPresets := fullConfig.CountCustomAIPresets() - customSettings := wconfig.CountCustomSettings() - customAIModes := fullConfig.CountCustomAIModes() - - props.UserSet = &telemetrydata.TEventUserProps{ - SettingsCustomWidgets: customWidgets, - SettingsCustomAIPresets: customAIPresets, - SettingsCustomSettings: customSettings, - SettingsCustomAIModes: customAIModes, - } - - secretsCount, err := secretstore.CountSecrets() - if err == nil { - props.UserSet.SettingsSecretsCount = secretsCount - } - - if utilfn.CompareAsMarshaledJson(props, lastCounts) { - return lastCounts - } - tevent := telemetrydata.MakeTEvent("app:counts", props) - err = telemetry.RecordTEvent(ctx, tevent) - if err != nil { - log.Printf("error recording counts tevent: %v\n", err) - } - return props -} - -func updateTelemetryCountsLoop() { - defer func() { - panichandler.PanicHandler("updateTelemetryCountsLoop", recover()) - }() - var nextSend int64 - var lastCounts telemetrydata.TEventProps - time.Sleep(TelemetryInitialCountsWait) - for { - if time.Now().Unix() > nextSend { - nextSend = time.Now().Add(TelemetryCountsInterval).Unix() - lastCounts = updateTelemetryCounts(lastCounts) - } - time.Sleep(TelemetryTick) - } -} - -func beforeSendActivityUpdate(ctx context.Context) { - activity := wshrpc.ActivityUpdate{} - activity.NumTabs, _ = wstore.DBGetCount[*waveobj.Tab](ctx) - activity.NumBlocks, _ = wstore.DBGetCount[*waveobj.Block](ctx) - activity.Blocks, _ = wstore.DBGetBlockViewCounts(ctx) - activity.NumWindows, _ = wstore.DBGetCount[*waveobj.Window](ctx) - activity.NumSSHConn = conncontroller.GetNumSSHHasConnected() - activity.NumWSLConn = wslconn.GetNumWSLHasConnected() - activity.NumWSNamed, activity.NumWS, _ = wstore.DBGetWSCounts(ctx) - err := telemetry.UpdateActivity(ctx, activity) - if err != nil { - log.Printf("error updating before activity: %v\n", err) - } -} - -func startupActivityUpdate(firstLaunch bool) { - defer func() { - panichandler.PanicHandler("startupActivityUpdate", recover()) - }() - ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second) - defer cancelFn() - activity := wshrpc.ActivityUpdate{Startup: 1} - err := telemetry.UpdateActivity(ctx, activity) // set at least one record into activity (don't use go routine wrap here) - if err != nil { - log.Printf("error updating startup activity: %v\n", err) - } - autoUpdateChannel := telemetry.AutoUpdateChannel() - autoUpdateEnabled := telemetry.IsAutoUpdateEnabled() - shellType, shellVersion, shellErr := shellutil.DetectShellTypeAndVersion() - if shellErr != nil { - shellType = "error" - shellVersion = "" - } - userSetOnce := &telemetrydata.TEventUserProps{ - ClientInitialVersion: "v" + WaveVersion, - } - tosTs := telemetry.GetTosAgreedTs() - var cohortTime time.Time - if tosTs > 0 { - cohortTime = time.UnixMilli(tosTs) - } else { - cohortTime = time.Now() - } - cohortMonth := cohortTime.Format("2006-01") - year, week := cohortTime.ISOWeek() - cohortISOWeek := fmt.Sprintf("%04d-W%02d", year, week) - userSetOnce.CohortMonth = cohortMonth - userSetOnce.CohortISOWeek = cohortISOWeek - fullConfig := wconfig.GetWatcher().GetFullConfig() - props := telemetrydata.TEventProps{ - UserSet: &telemetrydata.TEventUserProps{ - ClientVersion: "v" + wavebase.WaveVersion, - ClientBuildTime: wavebase.BuildTime, - ClientArch: wavebase.ClientArch(), - ClientOSRelease: wavebase.UnameKernelRelease(), - ClientIsDev: wavebase.IsDevMode(), - ClientPackageType: wavebase.ClientPackageType(), - ClientMacOSVersion: wavebase.ClientMacOSVersion(), - AutoUpdateChannel: autoUpdateChannel, - AutoUpdateEnabled: autoUpdateEnabled, - LocalShellType: shellType, - LocalShellVersion: shellVersion, - SettingsTransparent: fullConfig.Settings.WindowTransparent, - }, - UserSetOnce: userSetOnce, - } - if firstLaunch { - props.AppFirstLaunch = true - } - tevent := telemetrydata.MakeTEvent("app:startup", props) - err = telemetry.RecordTEvent(ctx, tevent) - if err != nil { - log.Printf("error recording startup event: %v\n", err) - } -} - -func shutdownActivityUpdate() { - ctx, cancelFn := context.WithTimeout(context.Background(), 1*time.Second) - defer cancelFn() - activity := wshrpc.ActivityUpdate{Shutdown: 1} - err := telemetry.UpdateActivity(ctx, activity) // do NOT use the go routine wrap here (this needs to be synchronous) - if err != nil { - log.Printf("error updating shutdown activity: %v\n", err) - } - err = telemetry.TruncateActivityTEventForShutdown(ctx) - if err != nil { - log.Printf("error truncating activity t-event for shutdown: %v\n", err) - } - tevent := telemetrydata.MakeTEvent("app:shutdown", telemetrydata.TEventProps{}) - err = telemetry.RecordTEvent(ctx, tevent) - if err != nil { - log.Printf("error recording shutdown event: %v\n", err) - } -} - func createMainWshClient() { rpc := wshserver.GetMainRpcClient() wshutil.DefaultRouter.RegisterTrustedLeaf(rpc, wshutil.DefaultRoute) @@ -405,11 +142,6 @@ func grabAndRemoveEnvVars() error { if err != nil { return err } - err = wcloud.CacheAndRemoveEnvVars() - if err != nil { - return err - } - // Remove WAVETERM env vars that leak from prod => dev os.Unsetenv("WAVETERM_CLIENTID") os.Unsetenv("WAVETERM_WORKSPACEID") @@ -525,7 +257,6 @@ func main() { log.Printf("error initializing wstore: %v\n", err) return } - panichandler.PanicTelemetryHandler = panicTelemetryHandler go func() { defer func() { panichandler.PanicHandler("InitCustomShellStartupFiles", recover()) @@ -566,12 +297,7 @@ func main() { aiusechat.InitAIModeConfigWatcher() maybeStartPprofServer() go stdinReadWatch() - go telemetryLoop() - go diagnosticLoop() - setupTelemetryConfigHandler() - go updateTelemetryCountsLoop() go backupCleanupLoop() - go startupActivityUpdate(firstLaunch) // must be after startConfigWatcher() blocklogger.InitBlockLogger() jobcontroller.InitJobController() blockcontroller.InitBlockController() diff --git a/cmd/wsh/cmd/wshcmd-ai.go b/cmd/wsh/cmd/wshcmd-ai.go index 643c80ee7a..79293c634e 100644 --- a/cmd/wsh/cmd/wshcmd-ai.go +++ b/cmd/wsh/cmd/wshcmd-ai.go @@ -68,7 +68,6 @@ func getMaxFileSize(mimeType string) (int, string) { func aiRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("ai", rtnErr == nil) }() if len(args) == 0 && aiMessageFlag == "" { diff --git a/cmd/wsh/cmd/wshcmd-badge.go b/cmd/wsh/cmd/wshcmd-badge.go index 590ed1e40b..f2b17a0f24 100644 --- a/cmd/wsh/cmd/wshcmd-badge.go +++ b/cmd/wsh/cmd/wshcmd-badge.go @@ -44,7 +44,6 @@ func init() { func badgeRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("badge", rtnErr == nil) }() if badgePid > 0 && runtime.GOOS == "windows" { diff --git a/cmd/wsh/cmd/wshcmd-debug.go b/cmd/wsh/cmd/wshcmd-debug.go index 9efac0ff87..29a1857689 100644 --- a/cmd/wsh/cmd/wshcmd-debug.go +++ b/cmd/wsh/cmd/wshcmd-debug.go @@ -24,24 +24,11 @@ var debugBlockIdsCmd = &cobra.Command{ Hidden: true, } -var debugSendTelemetryCmd = &cobra.Command{ - Use: "send-telemetry", - Short: "send telemetry", - RunE: debugSendTelemetryRun, - Hidden: true, -} - func init() { debugCmd.AddCommand(debugBlockIdsCmd) - debugCmd.AddCommand(debugSendTelemetryCmd) rootCmd.AddCommand(debugCmd) } -func debugSendTelemetryRun(cmd *cobra.Command, args []string) error { - err := wshclient.SendTelemetryCommand(RpcClient, nil) - return err -} - func debugBlockIdsRun(cmd *cobra.Command, args []string) error { oref, err := resolveBlockArg() if err != nil { diff --git a/cmd/wsh/cmd/wshcmd-debugterm.go b/cmd/wsh/cmd/wshcmd-debugterm.go index 66346c460a..dabea2540e 100644 --- a/cmd/wsh/cmd/wshcmd-debugterm.go +++ b/cmd/wsh/cmd/wshcmd-debugterm.go @@ -50,7 +50,6 @@ func init() { func debugTermRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("debugterm", rtnErr == nil) }() mode, err := getDebugTermMode() if err != nil { diff --git a/cmd/wsh/cmd/wshcmd-deleteblock.go b/cmd/wsh/cmd/wshcmd-deleteblock.go index 76518e721c..c6406f928e 100644 --- a/cmd/wsh/cmd/wshcmd-deleteblock.go +++ b/cmd/wsh/cmd/wshcmd-deleteblock.go @@ -24,7 +24,6 @@ func init() { func deleteBlockRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("deleteblock", rtnErr == nil) }() fullORef, err := resolveBlockArg() if err != nil { diff --git a/cmd/wsh/cmd/wshcmd-editconfig.go b/cmd/wsh/cmd/wshcmd-editconfig.go index cbd4015bae..cedba81ad0 100644 --- a/cmd/wsh/cmd/wshcmd-editconfig.go +++ b/cmd/wsh/cmd/wshcmd-editconfig.go @@ -30,7 +30,6 @@ func init() { func editConfigRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("editconfig", rtnErr == nil) }() configFile := "settings.json" // default diff --git a/cmd/wsh/cmd/wshcmd-editor.go b/cmd/wsh/cmd/wshcmd-editor.go index 4968b17509..9459dcc065 100644 --- a/cmd/wsh/cmd/wshcmd-editor.go +++ b/cmd/wsh/cmd/wshcmd-editor.go @@ -32,7 +32,6 @@ func init() { func editorRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("editor", rtnErr == nil) }() if len(args) == 0 { OutputHelpMessage(cmd) diff --git a/cmd/wsh/cmd/wshcmd-file.go b/cmd/wsh/cmd/wshcmd-file.go index e40eb324d2..7c668ef2dd 100644 --- a/cmd/wsh/cmd/wshcmd-file.go +++ b/cmd/wsh/cmd/wshcmd-file.go @@ -90,7 +90,7 @@ var fileListCmd = &cobra.Command{ Short: "list files", Long: "List files in a directory. By default, lists files in the current directory." + UriHelpText, Example: " wsh file ls wsh://user@ec2/home/user/", - RunE: activityWrap("file", fileListRun), + RunE: fileListRun, PreRunE: preRunSetupRpcClient, } @@ -100,7 +100,7 @@ var fileCatCmd = &cobra.Command{ Long: "Display the contents of a file." + UriHelpText, Example: " wsh file cat wsh://user@ec2/home/user/config.txt", Args: cobra.ExactArgs(1), - RunE: activityWrap("file", fileCatRun), + RunE: fileCatRun, PreRunE: preRunSetupRpcClient, } @@ -110,7 +110,7 @@ var fileInfoCmd = &cobra.Command{ Long: "Show information about a file." + UriHelpText, Example: " wsh file info wsh://user@ec2/home/user/config.txt", Args: cobra.ExactArgs(1), - RunE: activityWrap("file", fileInfoRun), + RunE: fileInfoRun, PreRunE: preRunSetupRpcClient, } @@ -120,7 +120,7 @@ var fileRmCmd = &cobra.Command{ Long: "Remove a file." + UriHelpText, Example: " wsh file rm wsh://user@ec2/home/user/config.txt", Args: cobra.ExactArgs(1), - RunE: activityWrap("file", fileRmRun), + RunE: fileRmRun, PreRunE: preRunSetupRpcClient, } @@ -130,7 +130,7 @@ var fileWriteCmd = &cobra.Command{ Long: "Write stdin into a file, buffering input (10MB total file size limit)." + UriHelpText, Example: " echo 'hello' | wsh file write ./greeting.txt", Args: cobra.ExactArgs(1), - RunE: activityWrap("file", fileWriteRun), + RunE: fileWriteRun, PreRunE: preRunSetupRpcClient, } @@ -140,7 +140,7 @@ var fileAppendCmd = &cobra.Command{ Long: "Append stdin to a file, buffering input (10MB total file size limit)." + UriHelpText, Example: " tail -f log.txt | wsh file append ./app.log", Args: cobra.ExactArgs(1), - RunE: activityWrap("file", fileAppendRun), + RunE: fileAppendRun, PreRunE: preRunSetupRpcClient, } @@ -151,7 +151,7 @@ var fileCpCmd = &cobra.Command{ Long: "Copy files between different storage systems." + UriHelpText, Example: " wsh file cp wsh://user@ec2/home/user/config.txt ./local-config.txt\n wsh file cp ./local-config.txt wsh://user@ec2/home/user/config.txt", Args: cobra.ExactArgs(2), - RunE: activityWrap("file", fileCpRun), + RunE: fileCpRun, PreRunE: preRunSetupRpcClient, } @@ -162,7 +162,7 @@ var fileMvCmd = &cobra.Command{ Long: "Move files between different storage systems. The source file will be deleted once the operation completes successfully." + UriHelpText, Example: " wsh file mv wsh://user@ec2/home/user/config.txt ./local-config.txt\n wsh file mv ./local-config.txt wsh://user@ec2/home/user/config.txt", Args: cobra.ExactArgs(2), - RunE: activityWrap("file", fileMvRun), + RunE: fileMvRun, PreRunE: preRunSetupRpcClient, } diff --git a/cmd/wsh/cmd/wshcmd-focusblock.go b/cmd/wsh/cmd/wshcmd-focusblock.go index 3f6603a3e2..ff5f224d9d 100644 --- a/cmd/wsh/cmd/wshcmd-focusblock.go +++ b/cmd/wsh/cmd/wshcmd-focusblock.go @@ -26,7 +26,6 @@ func init() { func focusBlockRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("focusblock", rtnErr == nil) }() tabId := os.Getenv("WAVETERM_TABID") diff --git a/cmd/wsh/cmd/wshcmd-getmeta.go b/cmd/wsh/cmd/wshcmd-getmeta.go index f5e1e40f67..b713bbef4f 100644 --- a/cmd/wsh/cmd/wshcmd-getmeta.go +++ b/cmd/wsh/cmd/wshcmd-getmeta.go @@ -74,7 +74,6 @@ func filterMetaKeys(meta map[string]interface{}, keys []string) map[string]inter func getMetaRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("getmeta", rtnErr == nil) }() fullORef, err := resolveBlockArg() if err != nil { diff --git a/cmd/wsh/cmd/wshcmd-getvar.go b/cmd/wsh/cmd/wshcmd-getvar.go index 9391c4f5f2..1a42be43a3 100644 --- a/cmd/wsh/cmd/wshcmd-getvar.go +++ b/cmd/wsh/cmd/wshcmd-getvar.go @@ -53,7 +53,6 @@ func shouldPrintNewline() bool { func getVarRun(cmd *cobra.Command, args []string) error { defer func() { - sendActivity("getvar", WshExitCode == 0) }() // Resolve block to get zoneId diff --git a/cmd/wsh/cmd/wshcmd-launch.go b/cmd/wsh/cmd/wshcmd-launch.go index 3ec582a6cd..d7d29385e1 100644 --- a/cmd/wsh/cmd/wshcmd-launch.go +++ b/cmd/wsh/cmd/wshcmd-launch.go @@ -28,7 +28,6 @@ func init() { func launchRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("launch", rtnErr == nil) }() widgetId := args[0] diff --git a/cmd/wsh/cmd/wshcmd-notify.go b/cmd/wsh/cmd/wshcmd-notify.go index de2086e1f7..a73f2b9551 100644 --- a/cmd/wsh/cmd/wshcmd-notify.go +++ b/cmd/wsh/cmd/wshcmd-notify.go @@ -31,7 +31,6 @@ func init() { func notifyRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("notify", rtnErr == nil) }() message := args[0] notificationOptions := &wshrpc.WaveNotificationOptions{ diff --git a/cmd/wsh/cmd/wshcmd-root.go b/cmd/wsh/cmd/wshcmd-root.go index 9534d2e5f5..a477f4c5a4 100644 --- a/cmd/wsh/cmd/wshcmd-root.go +++ b/cmd/wsh/cmd/wshcmd-root.go @@ -103,15 +103,6 @@ func getIsTty() bool { type RunEFnType = func(*cobra.Command, []string) error -func activityWrap(activityStr string, origRunE RunEFnType) RunEFnType { - return func(cmd *cobra.Command, args []string) (rtnErr error) { - defer func() { - sendActivity(activityStr, rtnErr == nil) - }() - return origRunE(cmd, args) - } -} - func resolveBlockArg() (*waveobj.ORef, error) { oref := blockArg if oref == "" { @@ -213,23 +204,6 @@ func getTabIdFromEnv() string { return os.Getenv("WAVETERM_TABID") } -// this will send wsh activity to the client running on *your* local machine (it does not contact any wave cloud infrastructure) -// if you've turned off telemetry in your local client, this data never gets sent to us -// no parameters or timestamps are sent, as you can see below, it just sends the name of the command (and if there was an error) -// (e.g. "wsh ai ..." would send "ai") -// this helps us understand which commands are actually being used so we know where to concentrate our effort -func sendActivity(wshCmdName string, success bool) { - if RpcClient == nil || wshCmdName == "" { - return - } - dataMap := make(map[string]int) - dataMap[wshCmdName] = 1 - if !success { - dataMap[wshCmdName+"#"+"error"] = 1 - } - wshclient.WshActivityCommand(RpcClient, dataMap, nil) -} - // Execute executes the root command. func Execute() { defer func() { diff --git a/cmd/wsh/cmd/wshcmd-run.go b/cmd/wsh/cmd/wshcmd-run.go index 6faf424c99..dffa24f515 100644 --- a/cmd/wsh/cmd/wshcmd-run.go +++ b/cmd/wsh/cmd/wshcmd-run.go @@ -40,7 +40,6 @@ func init() { func runRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("run", rtnErr == nil) }() flags := cmd.Flags() diff --git a/cmd/wsh/cmd/wshcmd-secret.go b/cmd/wsh/cmd/wshcmd-secret.go index 916e3ae4a5..cbb45c45a9 100644 --- a/cmd/wsh/cmd/wshcmd-secret.go +++ b/cmd/wsh/cmd/wshcmd-secret.go @@ -77,7 +77,6 @@ func init() { func secretGetRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("secret", rtnErr == nil) }() name := args[0] @@ -101,7 +100,6 @@ func secretGetRun(cmd *cobra.Command, args []string) (rtnErr error) { func secretSetRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("secret", rtnErr == nil) }() parts := strings.SplitN(args[0], "=", 2) @@ -137,7 +135,6 @@ func secretSetRun(cmd *cobra.Command, args []string) (rtnErr error) { func secretListRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("secret", rtnErr == nil) }() names, err := wshclient.GetSecretsNamesCommand(RpcClient, &wshrpc.RpcOpts{Timeout: 2000}) @@ -153,7 +150,6 @@ func secretListRun(cmd *cobra.Command, args []string) (rtnErr error) { func secretDeleteRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("secret", rtnErr == nil) }() name := args[0] @@ -173,7 +169,6 @@ func secretDeleteRun(cmd *cobra.Command, args []string) (rtnErr error) { func secretUiRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("secret", rtnErr == nil) }() tabId := getTabIdFromEnv() diff --git a/cmd/wsh/cmd/wshcmd-setbg.go b/cmd/wsh/cmd/wshcmd-setbg.go index 4385409187..68215a8c17 100644 --- a/cmd/wsh/cmd/wshcmd-setbg.go +++ b/cmd/wsh/cmd/wshcmd-setbg.go @@ -90,7 +90,6 @@ func validateColor(color string) error { func setBgRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("setbg", rtnErr == nil) }() borderColorChanged := cmd.Flags().Changed("border-color") diff --git a/cmd/wsh/cmd/wshcmd-setconfig.go b/cmd/wsh/cmd/wshcmd-setconfig.go index 3fcd1f94b2..6ce1b5297c 100644 --- a/cmd/wsh/cmd/wshcmd-setconfig.go +++ b/cmd/wsh/cmd/wshcmd-setconfig.go @@ -25,7 +25,6 @@ func init() { func setConfigRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("setconfig", rtnErr == nil) }() metaSetsStrs := args[:] diff --git a/cmd/wsh/cmd/wshcmd-setmeta.go b/cmd/wsh/cmd/wshcmd-setmeta.go index 79faa7e78c..2f2524e46f 100644 --- a/cmd/wsh/cmd/wshcmd-setmeta.go +++ b/cmd/wsh/cmd/wshcmd-setmeta.go @@ -158,7 +158,6 @@ func simpleMergeMeta(meta map[string]interface{}, metaUpdate map[string]interfac func setMetaRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("setmeta", rtnErr == nil) }() var jsonMeta map[string]interface{} if setMetaJsonFilePath != "" { diff --git a/cmd/wsh/cmd/wshcmd-setvar.go b/cmd/wsh/cmd/wshcmd-setvar.go index bbfb3e15a1..ea0918fee5 100644 --- a/cmd/wsh/cmd/wshcmd-setvar.go +++ b/cmd/wsh/cmd/wshcmd-setvar.go @@ -58,7 +58,6 @@ func parseKeyValue(arg string) (key, value string, err error) { func setVarRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("setvar", rtnErr == nil) }() // Resolve block to get zoneId diff --git a/cmd/wsh/cmd/wshcmd-ssh.go b/cmd/wsh/cmd/wshcmd-ssh.go index 4eb1d42a4e..cdcdff319a 100644 --- a/cmd/wsh/cmd/wshcmd-ssh.go +++ b/cmd/wsh/cmd/wshcmd-ssh.go @@ -39,7 +39,6 @@ func init() { func sshRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("ssh", rtnErr == nil) }() sshArg := args[0] diff --git a/cmd/wsh/cmd/wshcmd-tabindicator.go b/cmd/wsh/cmd/wshcmd-tabindicator.go index c3fa499cf9..56d892b1c3 100644 --- a/cmd/wsh/cmd/wshcmd-tabindicator.go +++ b/cmd/wsh/cmd/wshcmd-tabindicator.go @@ -43,7 +43,6 @@ func init() { func tabIndicatorRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("tabindicator", rtnErr == nil) }() fmt.Fprintf(os.Stderr, "tabindicator is deprecated, use 'wsh badge' instead\n") diff --git a/cmd/wsh/cmd/wshcmd-term.go b/cmd/wsh/cmd/wshcmd-term.go index f2119ad5b7..6a5f83ab2a 100644 --- a/cmd/wsh/cmd/wshcmd-term.go +++ b/cmd/wsh/cmd/wshcmd-term.go @@ -32,7 +32,6 @@ func init() { func termRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("term", rtnErr == nil) }() var cwd string diff --git a/cmd/wsh/cmd/wshcmd-termscrollback.go b/cmd/wsh/cmd/wshcmd-termscrollback.go index 6368e1559d..995a07eb0a 100644 --- a/cmd/wsh/cmd/wshcmd-termscrollback.go +++ b/cmd/wsh/cmd/wshcmd-termscrollback.go @@ -45,7 +45,6 @@ func init() { func termScrollbackRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("termscrollback", rtnErr == nil) }() // Resolve the block argument diff --git a/cmd/wsh/cmd/wshcmd-view.go b/cmd/wsh/cmd/wshcmd-view.go index 1ba84b516f..2347cea407 100644 --- a/cmd/wsh/cmd/wshcmd-view.go +++ b/cmd/wsh/cmd/wshcmd-view.go @@ -43,7 +43,6 @@ func init() { func viewRun(cmd *cobra.Command, args []string) (rtnErr error) { cmdName := cmd.Name() defer func() { - sendActivity(cmdName, rtnErr == nil) }() if len(args) == 0 { OutputHelpMessage(cmd) diff --git a/cmd/wsh/cmd/wshcmd-wavepath.go b/cmd/wsh/cmd/wshcmd-wavepath.go index 9a5ad6af39..bdb54d4367 100644 --- a/cmd/wsh/cmd/wshcmd-wavepath.go +++ b/cmd/wsh/cmd/wshcmd-wavepath.go @@ -30,7 +30,6 @@ func init() { func wavepathRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("wavepath", rtnErr == nil) }() if len(args) == 0 { diff --git a/cmd/wsh/cmd/wshcmd-web.go b/cmd/wsh/cmd/wshcmd-web.go index bfda76b82c..3ffdc93d13 100644 --- a/cmd/wsh/cmd/wshcmd-web.go +++ b/cmd/wsh/cmd/wshcmd-web.go @@ -97,7 +97,6 @@ func webGetRun(cmd *cobra.Command, args []string) error { func webOpenRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("web", rtnErr == nil) }() var replaceBlockORef *waveobj.ORef diff --git a/cmd/wsh/cmd/wshcmd-wsl.go b/cmd/wsh/cmd/wshcmd-wsl.go index cfe9cd47d8..ee67f7a945 100644 --- a/cmd/wsh/cmd/wshcmd-wsl.go +++ b/cmd/wsh/cmd/wshcmd-wsl.go @@ -30,7 +30,6 @@ func init() { func wslRun(cmd *cobra.Command, args []string) (rtnErr error) { defer func() { - sendActivity("wsl", rtnErr == nil) }() var err error diff --git a/db/migrations-wstore/000012_drop_telemetry.down.sql b/db/migrations-wstore/000012_drop_telemetry.down.sql new file mode 100644 index 0000000000..bcdcbcff58 --- /dev/null +++ b/db/migrations-wstore/000012_drop_telemetry.down.sql @@ -0,0 +1 @@ +-- Recreating these tables is not necessary; the data is obsolete diff --git a/db/migrations-wstore/000012_drop_telemetry.up.sql b/db/migrations-wstore/000012_drop_telemetry.up.sql new file mode 100644 index 0000000000..2650b693e0 --- /dev/null +++ b/db/migrations-wstore/000012_drop_telemetry.up.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS db_tevent; +DROP TABLE IF EXISTS db_activity; diff --git a/docs/docs/config.mdx b/docs/docs/config.mdx index 05389e99ef..57c379fd20 100644 --- a/docs/docs/config.mdx +++ b/docs/docs/config.mdx @@ -115,7 +115,6 @@ wsh editconfig | window:savelastwindow | bool | when `true`, the last window that is closed is preserved and is reopened the next time the app is launched (defaults to `true`) | | window:confirmonclose | bool | when `true`, a prompt will ask a user to confirm that they want to close a window if it has an unsaved workspace with more than one tab (defaults to `true`) | | window:dimensions | string | set the default dimensions for new windows using the format "WIDTHxHEIGHT" (e.g. "1920x1080"). when a new window is created, these dimensions will be automatically applied. The width and height values should be specified in pixels. | -| telemetry:enabled | bool | set to enable/disable telemetry | For reference, this is the current default configuration (v0.14.0): @@ -148,9 +147,7 @@ For reference, this is the current default configuration (v0.14.0): "window:fullscreenonlaunch": false, "window:magnifiedblockblursecondarypx": 2, "window:confirmclose": true, - "window:savelastwindow": true, - "telemetry:enabled": true, - "term:bellsound": false, + "window:savelastwindow": true, "term:bellsound": false, "term:bellindicator": false, "term:osc52": "always", "term:cursor": "block", diff --git a/docs/docs/faq.mdx b/docs/docs/faq.mdx index 61dc80beb4..69dc81cb7f 100644 --- a/docs/docs/faq.mdx +++ b/docs/docs/faq.mdx @@ -56,15 +56,3 @@ If you've installed via Snap, you can use the following command: ```sh sudo snap install waveterm --classic --beta ``` - -## Can I use Wave AI without enabling telemetry? - - - -Yes! Wave AI is normally disabled when telemetry is not enabled. However, you can enable Wave AI features without telemetry by configuring your own custom AI model (either a local model or using your own API key). - -To enable Wave AI without telemetry: -1. Configure a custom AI mode (see [Wave AI documentation](./waveai-modes)) -2. Set `waveai:defaultmode` to your custom mode's key in your Wave settings - -Once you've completed both steps, Wave AI will be enabled and you can use it completely privately without telemetry. This allows you to use local models like Ollama or your own API keys with providers like OpenAI, OpenRouter, or others. diff --git a/docs/docs/index.mdx b/docs/docs/index.mdx index f1665faae8..ef1af63d6a 100644 --- a/docs/docs/index.mdx +++ b/docs/docs/index.mdx @@ -78,7 +78,6 @@ Other References: - [Configuration](./config) - [Custom Widgets](./customwidgets) - [Full wsh reference](./wsh-reference) -- [Telemetry](./telemetry) - [FAQ](./faq) - [Release Notes](./releasenotes) diff --git a/docs/docs/telemetry-old.mdx b/docs/docs/telemetry-old.mdx deleted file mode 100644 index dba263dacb..0000000000 --- a/docs/docs/telemetry-old.mdx +++ /dev/null @@ -1,130 +0,0 @@ ---- -id: "telemetry-old" -title: "Legacy Telemetry" -sidebar_class_name: hidden ---- - -Wave Terminal collects telemetry data to help us track feature use, direct future product efforts, and generate aggregate metrics on Wave's popularity and usage. We do not collect or store any PII (personal identifiable information) and all metric data is only associated with and aggregated using your randomly generated _ClientId_. You may opt out of collection at any time. - -If you would like to turn telemetry on or off, the first opportunity is a button on the initial welcome page. After this, it can be turned off by adding `"telemetry:enabled": false` to the `config/settings.json` file. It can alternatively be turned on by adding `"telemetry:enabled": true` to the `config/settings.json` file. - -:::info - -You can also change your telemetry setting by running the wsh command: - -``` -wsh setconfig telemetry:enabled=true -``` - -::: - ---- - -## Sending Telemetry - -Provided that telemetry is enabled, it is sent 10 seconds after Waveterm is first booted and then again every 4 hours thereafter. It can also be sent in response to a few special cases listed below. When telemetry is sent, it is grouped into individual days as determined by your time zone. Any data from a previous day is marked as `Uploaded` so it will not need to be sent again. - -### Sending Once Telemetry is Enabled - -As soon as telemetry is enabled, a telemetry update is sent regardless of how long it has been since the last send. This does not reset the usual timer for telemetry sends. - -### Notifying that Telemetry is Disabled - -As soon as telemetry is disabled, Waveterm sends a special update that notifies us of this change. See [When Telemetry is Turned Off](#when-telemetry-is-turned-off) for more info. The timer still runs in the background but no data is sent. - -### When Waveterm is Closed - -Provided that telemetry is enabled, it will be sent when Waveterm is closed. - ---- - -## Telemetry Data - -When telemetry is active, we collect the following data. It is stored in the `telemetry.TelemetryData` type in the source code. - -| Name | Description | -| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| ActiveMinutes | The number of minutes that the user has actively used Waveterm on a given day. This requires the terminal window to be in focus while the user is actively interacting with it. | -| FgMinutes | The number of minutes that Waveterm has been in the foreground on a given day. This requires the terminal window to be in focus regardless of user interaction. | -| OpenMinutes | The number of minutes that Waveterm has been open on a given day. This only requires that the terminal is open, even if the window is out of focus. | -| NumBlocks | The number of existing blocks open on a given day | -| NumTabs | The number of existing tabs open on a given day. | -| NewTab | The number of new tabs created on a given day | -| NumWindows | The number of existing windows open on a given day. | -| NumWS | The number of existing workspaces on a given day. | -| NumWSNamed | The number of named workspaces on a give day. | -| NewTab | The number of new tabs opened on a given day. | -| NumStartup | The number of times waveterm has been started on a given day. | -| NumShutdown | The number of times waveterm has been shut down on a given day. | -| SetTabTheme | The number of times the tab theme is changed from the context menu | -| NumMagnify | The number of times any block is magnified | -| NumPanics | The number of backend (golang) panics caught in the current day | -| NumAIReqs | The number of AI requests made in the current day | -| NumSSHConn | The number of distinct SSH connections that have been made to distinct hosts | -| NumWSLConns | The number of distinct WSL connections that have been made to distinct distros | -| Renderers | The number of new block views of each type are open on a given day. | -| WshCmds | The number of wsh commands of each type run on a given day | -| Blocks | The number of blocks of different view types open on a given day | -| Conn | The number of successful remote connections made (and errors) on a given day | - -## Associated Data - -In addition to the telemetry data collected, the following is also reported. It is stored in the `telemetry.ActivityType` type in the source code. - -| Name | Description | -| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| Day | The date the telemetry is associated with. It does not include the time. | -| Uploaded | A boolean that indicates if the telemetry for this day is finalized. It is false during the day the telemetry is associated with, but gets set true at the first telemetry upload after that. Once it is true, the data for that particular day will not be sent up with the telemetry any more. | -| TzName | The code for the timezone the user's OS is reporting (e.g. PST, GMT, JST) | -| TzOffset | The offset for the timezone the user's OS is reporting (e.g. -08:00, +00:00, +09:00) | -| ClientVersion | Which version of Waveterm is installed. | -| ClientArch | This includes the user's operating system (e.g. linux or darwin) and architecture (e.g. x86_64 or arm64). It does not include data for any Connections at this time. | -| BuildTime | This serves as a more accurate version number that keeps track of when we built the version. It has no bearing on when that version was installed by you. | -| OSRelease | This lists the version of the operating system the user has installed. | -| Displays | Display resolutions (added in v0.9.3 to help us understand what screen resolutions to optimize for) | - -## Telemetry Metadata - -Lastly, some data is sent along with the telemetry that describes how to classify it. It is stored in the `wcloud.TelemetryInputType` in the source code. - -| Name | Description | -| ----------------- | --------------------------------------------------------------------------------------------------------------------------- | -| UserId | Currently Unused. This is an anonymous UUID intended for use in future features. | -| ClientId | This is an anonymous UUID created when Waveterm is first launched. It is used for telemetry and sending prompts to Open AI. | -| AppType | This is used to differentiate the current version of waveterm from the legacy app. | -| AutoUpdateEnabled | Whether or not auto update is turned on. | -| AutoUpdateChannel | The type of auto update in use. This specifically refers to whether a latest or beta channel is selected. | -| CurDay | The current day (in your time zone) when telemetry is sent. It does not include the time of day. | - -## Geo Data - -We do not store IP addresses in our telemetry table. However, CloudFlare passes us Geo-Location headers. We store these two header values: - -| Name | Description | -| ------------ | ----------------------------------------------------------------- | -| CFCountry | 2-letter country code (e.g. "US", "FR", or "JP") | -| CFRegionCode | region code (often a provence, region, or state within a country) | - ---- - -## When Telemetry is Turned Off - -When a user disables telemetry, Waveterm sends a notification that their anonymous _ClientId_ has had its telemetry disabled. This is done with the `wcloud.NoTelemetryInputType` type in the source code. Beyond that, no further information is sent unless telemetry is turned on again. If it is turned on again, the previous 30 days of telemetry will be sent. - ---- - -## A Note on IP Addresses - -Telemetry is uploaded via https, which means your IP address is known to the telemetry server. We **do not** store your IP address in our telemetry table and **do not** associate it with your _ClientId_. - ---- - -## Previously Collected Telemetry Data - -While we believe the data we collect with telemetry is fairly minimal, we cannot make that decision for every user. If you ever change your mind about what has been collected previously, you may request that your data be deleted by emailing us at [support@waveterm.dev](mailto:support@waveterm.dev). If you do, we will need your _ClientId_ to remove it. - ---- - -## Privacy Policy - -For a summary of the above, you can take a look at our [Privacy Policy](https://www.waveterm.dev/privacy). diff --git a/docs/docs/telemetry.mdx b/docs/docs/telemetry.mdx deleted file mode 100644 index 2f9132276d..0000000000 --- a/docs/docs/telemetry.mdx +++ /dev/null @@ -1,71 +0,0 @@ ---- -sidebar_position: 100 -title: Telemetry -id: "telemetry" ---- - -## tl;dr - -Wave Terminal collects telemetry data to help us track feature use, direct future product efforts, and generate aggregate metrics on Wave's popularity and usage. We do NOT collect personal information (PII), keystrokes, file contents, AI prompts, IP addresses, hostnames, or commands. We attach all information to an anonymous, randomly generated _ClientId_ (UUID). You may opt out of collection at any time. - -Here's a quick summary of what is collected: - -- Basic App/System Info - OS, architecture, app version, update settings -- Usage Metrics - App start/shutdown, active minutes, foreground time, tab/block counts/usage -- Feature Interactions - When you create tabs, run commands, change settings, etc. -- Display Info - Monitor resolution, number of displays -- Connection Events - SSH/WSL connection attempts (but NOT hostnames/IPs) -- Wave AI Usage - Model/provider selection, token counts, request metrics, latency (but NOT prompts or responses) -- Error Reports - Crash/panic events with minimal debugging info, but no stack traces or detailed errors - -Telemetry can be disabled at any time in settings. If not disabled it is sent on startup, on shutdown, and every 4-hours. - -## How to Disable Telemetry - -Telemetry can be enabled or disabled on the initial welcome screen when Wave first starts. After setup, telemetry can be disabled by setting the `telemetry:enabled` key to `false` in Wave’s general configuration file. It can also be disabled using the CLI command `wsh setconfig telemetry:enabled=false`. - -:::info - -This document outlines the current telemetry system as of v0.11.1. As of v0.12.5, Wave Terminal no longer sends legacy telemetry. The previous telemetry documentation can be found in our [Legacy Telemetry Documentation](./telemetry-old.mdx) for historical reference. - -::: - -## Diagnostics Ping - -Wave sends a small, anonymous diagnostics ping after the app has been running for a short time and at most once per day thereafter. This is used to estimate active installs and understand which versions are still in use, so we can make informed decisions about ongoing support and deprecations. - -The ping includes only: your Wave version, OS/CPU arch, local date (yyyy-mm-dd, no timezone or clock time), your randomly generated anonymous client ID, and whether usage telemetry is enabled or disabled. - -It does not include usage data, commands, files, or any telemetry events. - -This ping is intentionally separate from telemetry so Wave can count active installs. If you'd like to disable it, set the WAVETERM_NOPING environment variable. - -## Sending Telemetry - -Provided that telemetry is enabled, it is sent shortly after Wave is first launched and then again every 4 hours thereafter. It can also be sent in response to a few special cases listed below. When telemetry is sent, events are marked as sent to prevent duplicate transmissions. - -### Sending Once Telemetry is Enabled - -As soon as telemetry is enabled, a telemetry update is sent regardless of how long it has been since the last send. This does not reset the usual timer for telemetry sends. - -### When Wave is Closed - -Provided that telemetry is enabled, it will be sent when Waveterm is closed. - -## Event Types and Properties - -Wave collects the event types and properties described in the summary above. As we add features, new events and properties may be added to track their usage. - -For the complete, current list of all telemetry events and properties, see the source code: [telemetrydata.go](https://github.com/wavetermdev/waveterm/blob/main/pkg/telemetry/telemetrydata/telemetrydata.go) - -## GDPR Opt-Out Compliance - -When telemetry is disabled, Wave sends a single minimal opt-out record associated with the anonymous client ID, recording that telemetry was turned off and when it occurred. This record is retained for compliance purposes. After that, no telemetry or usage data is sent. - -## Deleting Your Data - -If you want your previously collected telemetry data deleted, email us at support (at) waveterm.dev with your _ClientId_ and we'll remove it. - -## Privacy Policy - -For a summary of the above, you can take a look at our [Privacy Policy](https://www.waveterm.dev/privacy). diff --git a/emain/emain-activity.ts b/emain/emain-activity.ts index 17dde466ae..360d93217f 100644 --- a/emain/emain-activity.ts +++ b/emain/emain-activity.ts @@ -9,10 +9,6 @@ let globalIsStarting = true; let globalIsRelaunching = false; let forceQuit = false; let userConfirmedQuit = false; -let termCommandsRun = 0; -let termCommandsRemote = 0; -let termCommandsWsl = 0; -let termCommandsDurable = 0; export function setWasActive(val: boolean) { wasActive = val; @@ -66,42 +62,4 @@ export function getUserConfirmedQuit(): boolean { return userConfirmedQuit; } -export function incrementTermCommandsRun() { - termCommandsRun++; -} - -export function getAndClearTermCommandsRun(): number { - const count = termCommandsRun; - termCommandsRun = 0; - return count; -} - -export function incrementTermCommandsRemote() { - termCommandsRemote++; -} - -export function getAndClearTermCommandsRemote(): number { - const count = termCommandsRemote; - termCommandsRemote = 0; - return count; -} -export function incrementTermCommandsWsl() { - termCommandsWsl++; -} - -export function getAndClearTermCommandsWsl(): number { - const count = termCommandsWsl; - termCommandsWsl = 0; - return count; -} - -export function incrementTermCommandsDurable() { - termCommandsDurable++; -} - -export function getAndClearTermCommandsDurable(): number { - const count = termCommandsDurable; - termCommandsDurable = 0; - return count; -} diff --git a/emain/emain-ipc.ts b/emain/emain-ipc.ts index 5e5f15b302..618342104d 100644 --- a/emain/emain-ipc.ts +++ b/emain/emain-ipc.ts @@ -12,12 +12,7 @@ import { RpcApi } from "../frontend/app/store/wshclientapi"; import { getWebServerEndpoint } from "../frontend/util/endpoints"; import * as keyutil from "../frontend/util/keyutil"; import { fireAndForget, parseDataUrl } from "../frontend/util/util"; -import { - incrementTermCommandsDurable, - incrementTermCommandsRemote, - incrementTermCommandsRun, - incrementTermCommandsWsl, - setWasActive, +import { setWasActive, } from "./emain-activity"; import { createBuilderWindow, getAllBuilderWindows, getBuilderWindowByWebContentsId } from "./emain-builder"; import { callWithOriginalXdgCurrentDesktopAsync, unamePlatform } from "./emain-platform"; @@ -438,22 +433,6 @@ export function initIpcHandlers() { console.log("fe-log", logStr); }); - electron.ipcMain.on( - "increment-term-commands", - (event, opts?: { isRemote?: boolean; isWsl?: boolean; isDurable?: boolean }) => { - incrementTermCommandsRun(); - if (opts?.isRemote) { - incrementTermCommandsRemote(); - } - if (opts?.isWsl) { - incrementTermCommandsWsl(); - } - if (opts?.isDurable) { - incrementTermCommandsDurable(); - } - } - ); - electron.ipcMain.on("native-paste", (event) => { event.sender.paste(); }); diff --git a/emain/emain.ts b/emain/emain.ts index 8b08178aec..c1c343fb9b 100644 --- a/emain/emain.ts +++ b/emain/emain.ts @@ -12,10 +12,6 @@ import { fireAndForget, sleep } from "../frontend/util/util"; import { AuthKey, configureAuthKeyRequestInjection } from "./authkey"; import { getActivityState, - getAndClearTermCommandsDurable, - getAndClearTermCommandsRemote, - getAndClearTermCommandsRun, - getAndClearTermCommandsWsl, getForceQuit, getGlobalIsRelaunching, getUserConfirmedQuit, @@ -121,123 +117,8 @@ function handleWSEvent(evtMsg: WSEventType) { }); } -// we try to set the primary display as index [0] -function getActivityDisplays(): ActivityDisplayType[] { - const displays = electron.screen.getAllDisplays(); - const primaryDisplay = electron.screen.getPrimaryDisplay(); - const rtn: ActivityDisplayType[] = []; - for (const display of displays) { - const adt = { - width: display.size.width, - height: display.size.height, - dpr: display.scaleFactor, - internal: display.internal, - }; - if (display.id === primaryDisplay?.id) { - rtn.unshift(adt); - } else { - rtn.push(adt); - } - } - return rtn; -} - -async function sendDisplaysTDataEvent() { - const displays = getActivityDisplays(); - if (displays.length === 0) { - return; - } - const props: TEventProps = {}; - props["display:count"] = displays.length; - props["display:height"] = displays[0].height; - props["display:width"] = displays[0].width; - props["display:dpr"] = displays[0].dpr; - props["display:all"] = displays; - try { - await RpcApi.RecordTEventCommand( - ElectronWshClient, - { - event: "app:display", - props, - }, - { noresponse: true } - ); - } catch (e) { - console.log("error sending display tdata event", e); - } -} - -function logActiveState() { - fireAndForget(async () => { - const astate = getActivityState(); - const activity: ActivityUpdate = { openminutes: 1 }; - const ww = focusedWaveWindow; - const activeTabView = ww?.activeTabView; - const isWaveAIOpen = activeTabView?.isWaveAIOpen ?? false; - - if (astate.wasInFg) { - activity.fgminutes = 1; - } - if (astate.wasActive) { - activity.activeminutes = 1; - } - activity.displays = getActivityDisplays(); - - const termCmdCount = getAndClearTermCommandsRun(); - if (termCmdCount > 0) { - activity.termcommandsrun = termCmdCount; - } - const termCmdRemoteCount = getAndClearTermCommandsRemote(); - const termCmdWslCount = getAndClearTermCommandsWsl(); - const termCmdDurableCount = getAndClearTermCommandsDurable(); - - const props: TEventProps = { - "activity:activeminutes": activity.activeminutes, - "activity:fgminutes": activity.fgminutes, - "activity:openminutes": activity.openminutes, - }; - if (termCmdCount > 0) { - props["activity:termcommandsrun"] = termCmdCount; - } - if (termCmdRemoteCount > 0) { - props["activity:termcommands:remote"] = termCmdRemoteCount; - } - if (termCmdWslCount > 0) { - props["activity:termcommands:wsl"] = termCmdWslCount; - } - if (termCmdDurableCount > 0) { - props["activity:termcommands:durable"] = termCmdDurableCount; - } - if (astate.wasActive && isWaveAIOpen) { - props["activity:waveaiactiveminutes"] = 1; - } - if (astate.wasInFg && isWaveAIOpen) { - props["activity:waveaifgminutes"] = 1; - } - - try { - await RpcApi.ActivityCommand(ElectronWshClient, activity, { noresponse: true }); - await RpcApi.RecordTEventCommand( - ElectronWshClient, - { - event: "app:activity", - props, - }, - { noresponse: true } - ); - } catch (e) { - console.log("error logging active state", e); - } finally { - setWasInFg(ww?.isFocused() ?? false); - setWasActive(false); - } - }); -} - // this isn't perfect, but gets the job done without being complicated -function runActiveTimer() { - logActiveState(); - setTimeout(runActiveTimer, 60000); +function runActiveTimer() { setTimeout(runActiveTimer, 60000); } function hideWindowWithCatch(window: WaveBrowserWindow) { @@ -417,8 +298,6 @@ async function appMain() { ensureHotSpareTab(fullConfig); await relaunchBrowserWindows(); setTimeout(runActiveTimer, 5000); // start active timer, wait 5s just to be safe - setTimeout(sendDisplaysTDataEvent, 5000); - makeAndSetAppMenu(); makeDockTaskbar(); await configureAutoUpdater(); diff --git a/emain/preload.ts b/emain/preload.ts index 8d2b18a308..d9cfa4230f 100644 --- a/emain/preload.ts +++ b/emain/preload.ts @@ -64,8 +64,6 @@ contextBridge.exposeInMainWorld("api", { clearWebviewStorage: (webContentsId: number) => ipcRenderer.invoke("clear-webview-storage", webContentsId), setWaveAIOpen: (isOpen: boolean) => ipcRenderer.send("set-waveai-open", isOpen), closeBuilderWindow: () => ipcRenderer.send("close-builder-window"), - incrementTermCommands: (opts?: { isRemote?: boolean; isWsl?: boolean; isDurable?: boolean }) => - ipcRenderer.send("increment-term-commands", opts), nativePaste: () => ipcRenderer.send("native-paste"), openBuilder: (appId?: string) => ipcRenderer.send("open-builder", appId), setBuilderWindowAppId: (appId: string) => ipcRenderer.send("set-builder-window-appid", appId), diff --git a/frontend/app/aipanel/aimode.tsx b/frontend/app/aipanel/aimode.tsx index 3602cdd360..4467f78502 100644 --- a/frontend/app/aipanel/aimode.tsx +++ b/frontend/app/aipanel/aimode.tsx @@ -144,7 +144,7 @@ export const AIModeDropdown = memo(({ compatibilityMode = false }: AIModeDropdow const widgetContextEnabled = useAtomValue(model.widgetAccessAtom); const hasPremium = useAtomValue(model.hasPremiumAtom); const showCloudModes = useAtomValue(getSettingsKeyAtom("waveai:showcloudmodes")); - const telemetryEnabled = useAtomValue(getSettingsKeyAtom("telemetry:enabled")) ?? false; + const telemetryEnabled = true; const [isOpen, setIsOpen] = useState(false); const dropdownRef = useRef(null); @@ -186,16 +186,6 @@ export const AIModeDropdown = memo(({ compatibilityMode = false }: AIModeDropdow const handleConfigureClick = () => { fireAndForget(async () => { - RpcApi.RecordTEventCommand( - TabRpcClient, - { - event: "action:other", - props: { - "action:type": "waveai:configuremodes:contextmenu", - }, - }, - { noresponse: true } - ); await model.openWaveAIConfig(); setIsOpen(false); }); @@ -203,7 +193,6 @@ export const AIModeDropdown = memo(({ compatibilityMode = false }: AIModeDropdow const handleEnableTelemetry = () => { fireAndForget(async () => { - await RpcApi.WaveAIEnableTelemetryCommand(TabRpcClient); setTimeout(() => { model.focusInput(); }, 100); diff --git a/frontend/app/aipanel/aipanel-contextmenu.ts b/frontend/app/aipanel/aipanel-contextmenu.ts index 4e78389198..4e3ec6cc4f 100644 --- a/frontend/app/aipanel/aipanel-contextmenu.ts +++ b/frontend/app/aipanel/aipanel-contextmenu.ts @@ -130,16 +130,6 @@ export async function handleWaveAIContextMenu(e: React.MouseEvent, showCopy: boo menu.push({ label: "Configure Modes", click: () => { - RpcApi.RecordTEventCommand( - TabRpcClient, - { - event: "action:other", - props: { - "action:type": "waveai:configuremodes:contextmenu", - }, - }, - { noresponse: true } - ); model.openWaveAIConfig(); }, }); diff --git a/frontend/app/aipanel/aipanel.tsx b/frontend/app/aipanel/aipanel.tsx index 32b8582141..d3484b7971 100644 --- a/frontend/app/aipanel/aipanel.tsx +++ b/frontend/app/aipanel/aipanel.tsx @@ -27,7 +27,7 @@ import { AIPanelMessages } from "./aipanelmessages"; import { AIRateLimitStrip } from "./airatelimitstrip"; import { WaveUIMessage } from "./aitypes"; import { BYOKAnnouncement } from "./byokannouncement"; -import { TelemetryRequiredMessage } from "./telemetryrequired"; + import { WaveAIModel } from "./waveai-model"; const AIBlockMask = memo(() => { @@ -235,7 +235,7 @@ AIErrorMessage.displayName = "AIErrorMessage"; const ConfigChangeModeFixer = memo(() => { const model = WaveAIModel.getInstance(); - const telemetryEnabled = jotai.useAtomValue(getSettingsKeyAtom("telemetry:enabled")) ?? false; + const telemetryEnabled = true; const aiModeConfigs = jotai.useAtomValue(model.aiModeConfigs); useEffect(() => { @@ -262,7 +262,7 @@ const AIPanelComponentInner = memo(({ roundTopLeft }: AIPanelComponentInnerProps const showOverlayBlockNums = jotai.useAtomValue(getSettingsKeyAtom("app:showoverlayblocknums")) ?? true; const isFocused = jotai.useAtomValue(model.isWaveAIFocusedAtom); const focusFollowsCursorMode = jotai.useAtomValue(getSettingsKeyAtom("app:focusfollowscursor")) ?? "off"; - const telemetryEnabled = jotai.useAtomValue(getSettingsKeyAtom("telemetry:enabled")) ?? false; + const telemetryEnabled = true; const isPanelVisible = jotai.useAtomValue(model.getPanelVisibleAtom()); const tabModel = useTabModelMaybe(); const [tabBorderColor, tabActiveBorderColor] = useTabBackground(waveEnv, tabModel?.tabId); @@ -586,9 +586,7 @@ const AIPanelComponentInner = memo(({ roundTopLeft }: AIPanelComponentInnerProps
- {!allowAccess ? ( - - ) : ( + {( <> {messages.length === 0 && initialLoadDone ? (
{ }; const handleOpenDiff = () => { - recordTEvent("waveai:showdiff"); fireAndForget(() => WaveAIModel.getInstance().openDiff(toolData.inputfilename, toolData.toolcallid)); }; @@ -282,7 +281,6 @@ const AIToolUse = memo(({ part, isStreaming }: AIToolUseProps) => { Date.now() - toolData.runts < BackupRetentionDays * 24 * 60 * 60 * 1000 && ( -
-
-
- - - - -
- - ); -}; - -TelemetryRequiredMessage.displayName = "TelemetryRequiredMessage"; - -export { TelemetryRequiredMessage }; diff --git a/frontend/app/aipanel/waveai-model.tsx b/frontend/app/aipanel/waveai-model.tsx index 194005adc6..b870f89f2e 100644 --- a/frontend/app/aipanel/waveai-model.tsx +++ b/frontend/app/aipanel/waveai-model.tsx @@ -141,7 +141,7 @@ export class WaveAIModel { }); this.defaultModeAtom = jotai.atom((get) => { - const telemetryEnabled = get(getSettingsKeyAtom("telemetry:enabled")) ?? false; + const telemetryEnabled = true; if (this.inBuilder) { return telemetryEnabled ? "waveaibuilder@default" : "invalid"; } @@ -419,7 +419,7 @@ export class WaveAIModel { } isValidMode(mode: string): boolean { - const telemetryEnabled = globalStore.get(getSettingsKeyAtom("telemetry:enabled")) ?? false; + const telemetryEnabled = true; if (mode.startsWith("waveai@") && !telemetryEnabled) { return false; } @@ -598,16 +598,6 @@ export class WaveAIModel { } handleAIFeedback(feedback: "good" | "bad") { - RpcApi.RecordTEventCommand( - TabRpcClient, - { - event: "waveai:feedback", - props: { - "waveai:feedback": feedback, - }, - }, - { noresponse: true } - ); } requestWaveAIFocus() { diff --git a/frontend/app/block/blockenv.ts b/frontend/app/block/blockenv.ts index 8a529be11b..dec7559eeb 100644 --- a/frontend/app/block/blockenv.ts +++ b/frontend/app/block/blockenv.ts @@ -25,9 +25,7 @@ export type BlockEnv = WaveEnvSubset<{ electron: { openExternal: WaveEnv["electron"]["openExternal"]; }; - rpc: { - ActivityCommand: WaveEnv["rpc"]["ActivityCommand"]; - ConnEnsureCommand: WaveEnv["rpc"]["ConnEnsureCommand"]; + rpc: { ConnEnsureCommand: WaveEnv["rpc"]["ConnEnsureCommand"]; ConnDisconnectCommand: WaveEnv["rpc"]["ConnDisconnectCommand"]; ConnConnectCommand: WaveEnv["rpc"]["ConnConnectCommand"]; SetConnectionsConfigCommand: WaveEnv["rpc"]["SetConnectionsConfigCommand"]; diff --git a/frontend/app/block/blockframe-header.tsx b/frontend/app/block/blockframe-header.tsx index a70f323e71..6d77767872 100644 --- a/frontend/app/block/blockframe-header.tsx +++ b/frontend/app/block/blockframe-header.tsx @@ -13,9 +13,7 @@ import { DurableSessionFlyover } from "@/app/block/durable-session-flyover"; import { getBlockBadgeAtom } from "@/app/store/badge"; import { createBlockSplitHorizontally, - createBlockSplitVertically, - recordTEvent, - refocusNode, + createBlockSplitVertically, refocusNode, WOS, } from "@/app/store/global"; import { globalStore } from "@/app/store/jotaiStore"; @@ -237,10 +235,7 @@ const BlockFrame_Header = ({ viewIconUnion = metaFrameIcon ?? viewIconUnion; React.useEffect(() => { - if (magnified && !preview && !prevMagifiedState.current) { - waveEnv.rpc.ActivityCommand(TabRpcClient, { nummagnify: 1 }); - recordTEvent("action:magnify", { "block:view": viewName }); - } + if (magnified && !preview && !prevMagifiedState.current) { } prevMagifiedState.current = magnified; }, [magnified]); diff --git a/frontend/app/block/connectionbutton.tsx b/frontend/app/block/connectionbutton.tsx index c5a9b635c3..dade9ba3c2 100644 --- a/frontend/app/block/connectionbutton.tsx +++ b/frontend/app/block/connectionbutton.tsx @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import { computeConnColorNum } from "@/app/block/blockutil"; -import { recordTEvent } from "@/app/store/global"; import { useWaveEnv } from "@/app/waveenv/waveenv"; import { IconButton } from "@/element/iconbutton"; import * as util from "@/util/util"; @@ -29,9 +28,7 @@ export const ConnectionButton = React.memo( let connIconElem: React.ReactNode = null; const connColorNum = computeConnColorNum(connStatus); let color = `var(--conn-icon-color-${connColorNum})`; - const clickHandler = function () { - recordTEvent("action:other", { "action:type": "conndropdown", "action:initiator": "mouse" }); - setConnModalOpen(true); + const clickHandler = function () { setConnModalOpen(true); }; let titleText = null; let shouldSpin = false; diff --git a/frontend/app/block/durable-session-flyover.tsx b/frontend/app/block/durable-session-flyover.tsx index 7ab7fa0b10..205f4430ae 100644 --- a/frontend/app/block/durable-session-flyover.tsx +++ b/frontend/app/block/durable-session-flyover.tsx @@ -1,7 +1,6 @@ // Copyright 2026, Command Line Inc. // SPDX-License-Identifier: Apache-2.0 -import { recordTEvent } from "@/app/store/global"; import { TermViewModel } from "@/app/view/term/term-model"; import { useWaveEnv } from "@/app/waveenv/waveenv"; import * as util from "@/util/util"; @@ -43,9 +42,7 @@ interface StandardSessionContentProps { } function StandardSessionContent({ viewModel, onClose }: StandardSessionContentProps) { - const handleRestartAsDurable = () => { - recordTEvent("action:termdurable", { "action:type": "restartdurable" }); - onClose(); + const handleRestartAsDurable = () => { onClose(); util.fireAndForget(() => viewModel.restartSessionWithDurability(true)); }; diff --git a/frontend/app/modals/about.tsx b/frontend/app/modals/about.tsx index d3a43d386d..d949722435 100644 --- a/frontend/app/modals/about.tsx +++ b/frontend/app/modals/about.tsx @@ -90,13 +90,7 @@ const AboutModal = () => { const updaterChannel = fullConfig?.settings?.["autoupdate:channel"] ?? "latest"; useEffect(() => { - fireAndForget(async () => { - RpcApi.RecordTEventCommand( - TabRpcClient, - { event: "action:other", props: { "action:type": "about" } }, - { noresponse: true } - ); - }); + fireAndForget(async () => { }); }, []); return ( diff --git a/frontend/app/onboarding/onboarding-durable.tsx b/frontend/app/onboarding/onboarding-durable.tsx index b716b3d7da..13acbb5b44 100644 --- a/frontend/app/onboarding/onboarding-durable.tsx +++ b/frontend/app/onboarding/onboarding-durable.tsx @@ -23,15 +23,7 @@ export const DurableSessionPage = ({ const handleFireClick = () => { setFireClicked(!fireClicked); - if (!fireClicked) { - RpcApi.RecordTEventCommand(TabRpcClient, { - event: "onboarding:fire", - props: { - "onboarding:feature": "durable", - "onboarding:version": CurrentOnboardingVersion, - }, - }); - } + if (!fireClicked) { } }; return ( diff --git a/frontend/app/onboarding/onboarding-features.tsx b/frontend/app/onboarding/onboarding-features.tsx index b47bbc4e1a..97836d2bc3 100644 --- a/frontend/app/onboarding/onboarding-features.tsx +++ b/frontend/app/onboarding/onboarding-features.tsx @@ -27,13 +27,6 @@ export const WaveAIPage = ({ onNext, onSkip }: { onNext: () => void; onSkip: () const handleFireClick = () => { setFireClicked(!fireClicked); if (!fireClicked) { - RpcApi.RecordTEventCommand(TabRpcClient, { - event: "onboarding:fire", - props: { - "onboarding:feature": "waveai", - "onboarding:version": CurrentOnboardingVersion, - }, - }); } }; @@ -122,13 +115,6 @@ export const MagnifyBlocksPage = ({ const handleFireClick = () => { setFireClicked(!fireClicked); if (!fireClicked) { - RpcApi.RecordTEventCommand(TabRpcClient, { - event: "onboarding:fire", - props: { - "onboarding:feature": "magnify", - "onboarding:version": CurrentOnboardingVersion, - }, - }); } }; @@ -180,13 +166,6 @@ export const FilesPage = ({ onFinish, onPrev }: { onFinish: () => void; onPrev?: const handleFireClick = () => { setFireClicked(!fireClicked); if (!fireClicked) { - RpcApi.RecordTEventCommand(TabRpcClient, { - event: "onboarding:fire", - props: { - "onboarding:feature": "wsh", - "onboarding:version": CurrentOnboardingVersion, - }, - }); } }; @@ -273,12 +252,6 @@ export const OnboardingFeatures = ({ onComplete }: { onComplete: () => void }) = oref: WOS.makeORef("client", clientId), meta: { "onboarding:lastversion": CurrentOnboardingVersion }, }); - RpcApi.RecordTEventCommand(TabRpcClient, { - event: "onboarding:start", - props: { - "onboarding:version": CurrentOnboardingVersion, - }, - }); }, []); const handleNext = () => { @@ -302,10 +275,6 @@ export const OnboardingFeatures = ({ onComplete }: { onComplete: () => void }) = }; const handleSkip = () => { - RpcApi.RecordTEventCommand(TabRpcClient, { - event: "onboarding:skip", - props: {}, - }); onComplete(); }; diff --git a/frontend/app/onboarding/onboarding-starask.tsx b/frontend/app/onboarding/onboarding-starask.tsx index bb7678ab2a..7297e0a100 100644 --- a/frontend/app/onboarding/onboarding-starask.tsx +++ b/frontend/app/onboarding/onboarding-starask.tsx @@ -14,16 +14,7 @@ type StarAskPageProps = { }; export function StarAskPage({ onClose, page = "upgrade" }: StarAskPageProps) { - const handleStarClick = async () => { - RpcApi.RecordTEventCommand( - TabRpcClient, - { - event: "onboarding:githubstar", - props: { "onboarding:githubstar": "star", "onboarding:page": page }, - }, - { noresponse: true } - ); - const clientId = ClientModel.getInstance().clientId; + const handleStarClick = async () => { const clientId = ClientModel.getInstance().clientId; await RpcApi.SetMetaCommand(TabRpcClient, { oref: WOS.makeORef("client", clientId), meta: { "onboarding:githubstar": true }, @@ -32,16 +23,7 @@ export function StarAskPage({ onClose, page = "upgrade" }: StarAskPageProps) { onClose(); }; - const handleAlreadyStarred = async () => { - RpcApi.RecordTEventCommand( - TabRpcClient, - { - event: "onboarding:githubstar", - props: { "onboarding:githubstar": "already", "onboarding:page": page }, - }, - { noresponse: true } - ); - const clientId = ClientModel.getInstance().clientId; + const handleAlreadyStarred = async () => { const clientId = ClientModel.getInstance().clientId; await RpcApi.SetMetaCommand(TabRpcClient, { oref: WOS.makeORef("client", clientId), meta: { "onboarding:githubstar": true }, @@ -49,28 +31,10 @@ export function StarAskPage({ onClose, page = "upgrade" }: StarAskPageProps) { onClose(); }; - const handleRepoLinkClick = () => { - RpcApi.RecordTEventCommand( - TabRpcClient, - { - event: "action:link", - props: { "action:type": "githubrepo", "onboarding:page": page }, - }, - { noresponse: true } - ); - window.open("https://github.com/wavetermdev/waveterm", "_blank"); + const handleRepoLinkClick = () => { window.open("https://github.com/wavetermdev/waveterm", "_blank"); }; - const handleMaybeLater = async () => { - RpcApi.RecordTEventCommand( - TabRpcClient, - { - event: "onboarding:githubstar", - props: { "onboarding:githubstar": "later", "onboarding:page": page }, - }, - { noresponse: true } - ); - const clientId = ClientModel.getInstance().clientId; + const handleMaybeLater = async () => { const clientId = ClientModel.getInstance().clientId; await RpcApi.SetMetaCommand(TabRpcClient, { oref: WOS.makeORef("client", clientId), meta: { "onboarding:githubstar": false }, diff --git a/frontend/app/onboarding/onboarding-upgrade-minor.tsx b/frontend/app/onboarding/onboarding-upgrade-minor.tsx index b165c0ce7f..d9f0489dc1 100644 --- a/frontend/app/onboarding/onboarding-upgrade-minor.tsx +++ b/frontend/app/onboarding/onboarding-upgrade-minor.tsx @@ -130,16 +130,7 @@ const UpgradeOnboardingMinor = () => { }; }, []); - const handleStarClick = async () => { - RpcApi.RecordTEventCommand( - TabRpcClient, - { - event: "onboarding:githubstar", - props: { "onboarding:githubstar": "star", "onboarding:page": "minorupgrade" }, - }, - { noresponse: true } - ); - const clientId = ClientModel.getInstance().clientId; + const handleStarClick = async () => { const clientId = ClientModel.getInstance().clientId; await RpcApi.SetMetaCommand(TabRpcClient, { oref: WOS.makeORef("client", clientId), meta: { "onboarding:githubstar": true }, @@ -148,16 +139,7 @@ const UpgradeOnboardingMinor = () => { setPageName("features"); }; - const handleAlreadyStarred = async () => { - RpcApi.RecordTEventCommand( - TabRpcClient, - { - event: "onboarding:githubstar", - props: { "onboarding:githubstar": "already", "onboarding:page": "minorupgrade" }, - }, - { noresponse: true } - ); - const clientId = ClientModel.getInstance().clientId; + const handleAlreadyStarred = async () => { const clientId = ClientModel.getInstance().clientId; await RpcApi.SetMetaCommand(TabRpcClient, { oref: WOS.makeORef("client", clientId), meta: { "onboarding:githubstar": true }, @@ -165,16 +147,7 @@ const UpgradeOnboardingMinor = () => { setPageName("features"); }; - const handleMaybeLater = async () => { - RpcApi.RecordTEventCommand( - TabRpcClient, - { - event: "onboarding:githubstar", - props: { "onboarding:githubstar": "later", "onboarding:page": "minorupgrade" }, - }, - { noresponse: true } - ); - const clientId = ClientModel.getInstance().clientId; + const handleMaybeLater = async () => { const clientId = ClientModel.getInstance().clientId; await RpcApi.SetMetaCommand(TabRpcClient, { oref: WOS.makeORef("client", clientId), meta: { "onboarding:githubstar": false }, diff --git a/frontend/app/onboarding/onboarding.tsx b/frontend/app/onboarding/onboarding.tsx index ba139e81df..ad5886737e 100644 --- a/frontend/app/onboarding/onboarding.tsx +++ b/frontend/app/onboarding/onboarding.tsx @@ -7,49 +7,31 @@ import { FlexiModal } from "@/app/modals/modal"; import { OnboardingGradientBg } from "@/app/onboarding/onboarding-common"; import { OnboardingFeatures } from "@/app/onboarding/onboarding-features"; import { ClientModel } from "@/app/store/client-model"; -import { useSettingsKeyAtom } from "@/app/store/global"; import { disableGlobalKeybindings, enableGlobalKeybindings, globalRefocus } from "@/app/store/keymodel"; import { modalsModel } from "@/app/store/modalmodel"; import * as WOS from "@/app/store/wos"; import { RpcApi } from "@/app/store/wshclientapi"; import { TabRpcClient } from "@/app/store/wshrpcutil"; -import { WorkspaceLayoutModel } from "@/app/workspace/workspace-layout-model"; -import * as services from "@/store/services"; import { fireAndForget } from "@/util/util"; +import * as services from "@/store/services"; import { atom, PrimitiveAtom, useAtom, useAtomValue, useSetAtom } from "jotai"; import { OverlayScrollbarsComponent } from "overlayscrollbars-react"; import { useEffect, useRef, useState } from "react"; import { debounce } from "throttle-debounce"; -// Page flow: -// init -> (telemetry enabled) -> features -// init -> (telemetry disabled) -> notelemetrystar -> features - -type PageName = "init" | "notelemetrystar" | "features"; +type PageName = "init" | "features"; const pageNameAtom: PrimitiveAtom = atom("init"); const InitPage = ({ isCompact, - telemetryUpdateFn, }: { isCompact: boolean; - telemetryUpdateFn: (value: boolean) => Promise; }) => { - const telemetrySetting = useSettingsKeyAtom("telemetry:enabled"); const clientData = useAtomValue(ClientModel.getInstance().clientAtom); - const [telemetryEnabled, setTelemetryEnabled] = useState(!!telemetrySetting); const setPageName = useSetAtom(pageNameAtom); const handleStarClick = async () => { - RpcApi.RecordTEventCommand( - TabRpcClient, - { - event: "onboarding:githubstar", - props: { "onboarding:githubstar": "star", "onboarding:page": "init" }, - }, - { noresponse: true } - ); const clientId = ClientModel.getInstance().clientId; await RpcApi.SetMetaCommand(TabRpcClient, { oref: WOS.makeORef("client", clientId), @@ -61,22 +43,9 @@ const InitPage = ({ if (!clientData?.tosagreed) { fireAndForget(() => services.ClientService.AgreeTos()); } - if (telemetryEnabled) { - WorkspaceLayoutModel.getInstance().setAIPanelVisible(true); - } - setPageName(telemetryEnabled ? "features" : "notelemetrystar"); - }; - - const setTelemetry = (value: boolean) => { - fireAndForget(() => - telemetryUpdateFn(value).then(() => { - setTelemetryEnabled(value); - }) - ); + setPageName("features"); }; - const label = telemetryEnabled ? "Enabled" : "Disabled"; - return (
-
-
- -
-
-
- Anonymous usage data helps us improve features you use. -
- - Privacy Policy - -
- -
-