Skip to content

Commit 6ec6745

Browse files
committed
Merge remote-tracking branch 'origin/main' into fix/issue-2890-warn-unsaved-changes-waveconfig
2 parents ec9356b + 6a287e4 commit 6ec6745

69 files changed

Lines changed: 1752 additions & 1096 deletions

Some content is hidden

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

.kilocode/skills/waveenv/SKILL.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Create a narrowing whenever you are writing a component (or group of components)
3030

3131
```ts
3232
import {
33-
BlockMetaKeyAtomFnType, // only if you use getBlockMetaKeyAtom
33+
MetaKeyAtomFnType, // only if you use getBlockMetaKeyAtom or getTabMetaKeyAtom
3434
ConnConfigKeyAtomFnType, // only if you use getConnConfigKeyAtom
3535
SettingsKeyAtomFnType, // only if you use getSettingsKeyAtom
3636
WaveEnv,
@@ -77,12 +77,14 @@ export type MyEnv = WaveEnvSubset<{
7777

7878
// --- key-parameterized atom factories: enumerate the keys you use ---
7979
getSettingsKeyAtom: SettingsKeyAtomFnType<"app:focusfollowscursor" | "window:magnifiedblockopacity">;
80-
getBlockMetaKeyAtom: BlockMetaKeyAtomFnType<"view" | "frame:title" | "connection">;
80+
getBlockMetaKeyAtom: MetaKeyAtomFnType<"view" | "frame:title" | "connection">;
81+
getTabMetaKeyAtom: MetaKeyAtomFnType<"tabid" | "name">;
8182
getConnConfigKeyAtom: ConnConfigKeyAtomFnType<"conn:wshenabled">;
8283

8384
// --- other atom helpers: copy verbatim ---
8485
getConnStatusAtom: WaveEnv["getConnStatusAtom"];
8586
getLocalHostDisplayNameAtom: WaveEnv["getLocalHostDisplayNameAtom"];
87+
getConfigBackgroundAtom: WaveEnv["getConfigBackgroundAtom"];
8688
}>;
8789
```
8890

@@ -104,7 +106,8 @@ Every `WaveEnvSubset<T>` automatically includes the mock fields — you never ne
104106
| `wos` | `wos: WaveEnv["wos"]` | Take the whole `wos` object (no sub-typing needed), but **only add it if `wos` is actually used**. |
105107
| `services` | `services: { svc: WaveEnv["services"]["svc"]; }` | List each service used; take the whole service object (no method-level narrowing). |
106108
| `getSettingsKeyAtom` | `SettingsKeyAtomFnType<"key1" \| "key2">` | Union all settings keys accessed. |
107-
| `getBlockMetaKeyAtom` | `BlockMetaKeyAtomFnType<"key1" \| "key2">` | Union all block meta keys accessed. |
109+
| `getBlockMetaKeyAtom` | `MetaKeyAtomFnType<"key1" \| "key2">` | Union all block meta keys accessed. |
110+
| `getTabMetaKeyAtom` | `MetaKeyAtomFnType<"key1" \| "key2">` | Union all tab meta keys accessed. |
108111
| `getConnConfigKeyAtom` | `ConnConfigKeyAtomFnType<"key1">` | Union all conn config keys accessed. |
109112
| All other `WaveEnv` fields | `WaveEnv["fieldName"]` | Copy type verbatim. |
110113

cmd/generateschema/main-generateschema.go

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const WaveSchemaSettingsFileName = "schema/settings.json"
2020
const WaveSchemaConnectionsFileName = "schema/connections.json"
2121
const WaveSchemaAiPresetsFileName = "schema/aipresets.json"
2222
const WaveSchemaWidgetsFileName = "schema/widgets.json"
23-
const WaveSchemaBgPresetsFileName = "schema/bgpresets.json"
23+
const WaveSchemaBackgroundsFileName = "schema/backgrounds.json"
2424
const WaveSchemaWaveAIFileName = "schema/waveai.json"
2525

2626
// ViewNameType is a string type whose JSON Schema offers enum suggestions for the most
@@ -105,8 +105,26 @@ type WidgetsMetaSchemaHints struct {
105105
TermDurable *bool `json:"term:durable,omitempty"`
106106
}
107107

108-
func generateSchema(template any, dir string) error {
108+
// allowNullValues wraps the top-level additionalProperties of a map schema with
109+
// anyOf: [originalSchema, {type: "null"}] so that setting a key to null is valid
110+
// (e.g. "bg@foo": null to remove a default entry).
111+
func allowNullValues(schema *jsonschema.Schema) {
112+
if schema.AdditionalProperties != nil && schema.AdditionalProperties != jsonschema.TrueSchema && schema.AdditionalProperties != jsonschema.FalseSchema {
113+
original := schema.AdditionalProperties
114+
schema.AdditionalProperties = &jsonschema.Schema{
115+
AnyOf: []*jsonschema.Schema{
116+
original,
117+
{Type: "null"},
118+
},
119+
}
120+
}
121+
}
122+
123+
func generateSchema(template any, dir string, allowNull bool) error {
109124
settingsSchema := jsonschema.Reflect(template)
125+
if allowNull {
126+
allowNullValues(settingsSchema)
127+
}
110128

111129
jsonSettingsSchema, err := json.MarshalIndent(settingsSchema, "", " ")
112130
if err != nil {
@@ -147,6 +165,7 @@ func generateWidgetsSchema(dir string) error {
147165

148166
widgetsTemplate := make(map[string]wconfig.WidgetConfigType)
149167
widgetsSchema := r.Reflect(&widgetsTemplate)
168+
allowNullValues(widgetsSchema)
150169

151170
jsonWidgetsSchema, err := json.MarshalIndent(widgetsSchema, "", " ")
152171
if err != nil {
@@ -163,19 +182,19 @@ func generateWidgetsSchema(dir string) error {
163182
}
164183

165184
func main() {
166-
err := generateSchema(&wconfig.SettingsType{}, WaveSchemaSettingsFileName)
185+
err := generateSchema(&wconfig.SettingsType{}, WaveSchemaSettingsFileName, false)
167186
if err != nil {
168187
log.Fatalf("settings schema error: %v", err)
169188
}
170189

171190
connectionTemplate := make(map[string]wconfig.ConnKeywords)
172-
err = generateSchema(&connectionTemplate, WaveSchemaConnectionsFileName)
191+
err = generateSchema(&connectionTemplate, WaveSchemaConnectionsFileName, false)
173192
if err != nil {
174193
log.Fatalf("connections schema error: %v", err)
175194
}
176195

177196
aiPresetsTemplate := make(map[string]wconfig.AiSettingsType)
178-
err = generateSchema(&aiPresetsTemplate, WaveSchemaAiPresetsFileName)
197+
err = generateSchema(&aiPresetsTemplate, WaveSchemaAiPresetsFileName, false)
179198
if err != nil {
180199
log.Fatalf("ai presets schema error: %v", err)
181200
}
@@ -185,14 +204,14 @@ func main() {
185204
log.Fatalf("widgets schema error: %v", err)
186205
}
187206

188-
bgPresetsTemplate := make(map[string]wconfig.BgPresetsType)
189-
err = generateSchema(&bgPresetsTemplate, WaveSchemaBgPresetsFileName)
207+
backgroundsTemplate := make(map[string]wconfig.BackgroundConfigType)
208+
err = generateSchema(&backgroundsTemplate, WaveSchemaBackgroundsFileName, true)
190209
if err != nil {
191-
log.Fatalf("bg presets schema error: %v", err)
210+
log.Fatalf("backgrounds schema error: %v", err)
192211
}
193212

194213
waveAITemplate := make(map[string]wconfig.AIModeConfigType)
195-
err = generateSchema(&waveAITemplate, WaveSchemaWaveAIFileName)
214+
err = generateSchema(&waveAITemplate, WaveSchemaWaveAIFileName, false)
196215
if err != nil {
197216
log.Fatalf("waveai schema error: %v", err)
198217
}

cmd/server/main-server.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,6 @@ func shutdownActivityUpdate() {
384384

385385
func createMainWshClient() {
386386
rpc := wshserver.GetMainRpcClient()
387-
wshfs.RpcClient = rpc
388387
wshutil.DefaultRouter.RegisterTrustedLeaf(rpc, wshutil.DefaultRoute)
389388
wps.Broker.SetClient(wshutil.DefaultRouter)
390389
localInitialEnv := envutil.PruneInitialEnv(envutil.SliceToMap(os.Environ()))
@@ -393,6 +392,8 @@ func createMainWshClient() {
393392
localConnWsh := wshutil.MakeWshRpc(wshrpc.RpcContext{Conn: wshrpc.LocalConnName}, remoteImpl, "conn:local")
394393
go wshremote.RunSysInfoLoop(localConnWsh, wshrpc.LocalConnName)
395394
wshutil.DefaultRouter.RegisterTrustedLeaf(localConnWsh, wshutil.MakeConnectionRouteId(wshrpc.LocalConnName))
395+
wshfs.RpcClient = localConnWsh
396+
wshfs.RpcClientRouteId = wshutil.MakeConnectionRouteId(wshrpc.LocalConnName)
396397
}
397398

398399
func grabAndRemoveEnvVars() error {
@@ -560,6 +561,7 @@ func main() {
560561
createMainWshClient()
561562
sigutil.InstallShutdownSignalHandlers(doShutdown)
562563
sigutil.InstallSIGUSR1Handler()
564+
wconfig.MigratePresetsBackgrounds()
563565
startConfigWatcher()
564566
aiusechat.InitAIModeConfigWatcher()
565567
maybeStartPprofServer()

cmd/wsh/cmd/wshcmd-connserver.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ func runListener(listener net.Listener, router *wshutil.WshRouter) {
183183
}
184184
}
185185

186-
func setupConnServerRpcClientWithRouter(router *wshutil.WshRouter, sockName string) (*wshutil.WshRpc, error) {
186+
func setupConnServerRpcClientWithRouter(router *wshutil.WshRouter, sockName string) (*wshutil.WshRpc, string, error) {
187187
routeId := wshutil.MakeConnectionRouteId(connServerConnName)
188188
rpcCtx := wshrpc.RpcContext{
189189
RouteId: routeId,
@@ -196,7 +196,7 @@ func setupConnServerRpcClientWithRouter(router *wshutil.WshRouter, sockName stri
196196

197197
connServerClient := wshutil.MakeWshRpc(rpcCtx, wshremote.MakeRemoteRpcServerImpl(os.Stdout, router, bareClient, false, connServerInitialEnv, sockName), routeId)
198198
router.RegisterTrustedLeaf(connServerClient, routeId)
199-
return connServerClient, nil
199+
return connServerClient, routeId, nil
200200
}
201201

202202
func serverRunRouter() error {
@@ -236,11 +236,12 @@ func serverRunRouter() error {
236236
sockName := getRemoteDomainSocketName()
237237

238238
// setup the connserver rpc client first
239-
client, err := setupConnServerRpcClientWithRouter(router, sockName)
239+
client, bareRouteId, err := setupConnServerRpcClientWithRouter(router, sockName)
240240
if err != nil {
241241
return fmt.Errorf("error setting up connserver rpc client: %v", err)
242242
}
243243
wshfs.RpcClient = client
244+
wshfs.RpcClientRouteId = bareRouteId
244245

245246
log.Printf("trying to get JWT public key")
246247

@@ -360,11 +361,12 @@ func serverRunRouterDomainSocket(jwtToken string) error {
360361
log.Printf("got JWT public key")
361362

362363
// now setup the connserver rpc client
363-
client, err := setupConnServerRpcClientWithRouter(router, sockName)
364+
client, bareRouteId, err := setupConnServerRpcClientWithRouter(router, sockName)
364365
if err != nil {
365366
return fmt.Errorf("error setting up connserver rpc client: %v", err)
366367
}
367368
wshfs.RpcClient = client
369+
wshfs.RpcClientRouteId = bareRouteId
368370

369371
// set up the local domain socket listener for local wsh commands
370372
unixListener, err := MakeRemoteUnixListener()
@@ -402,6 +404,7 @@ func serverRunNormal(jwtToken string) error {
402404
return err
403405
}
404406
wshfs.RpcClient = RpcClient
407+
wshfs.RpcClientRouteId = RpcClientRouteId
405408
WriteStdout("running wsh connserver (%s)\n", RpcContext.Conn)
406409
go func() {
407410
defer func() {

cmd/wsh/cmd/wshcmd-file-util.go

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2025, Command Line Inc.
1+
// Copyright 2026, Command Line Inc.
22
// SPDX-License-Identifier: Apache-2.0
33

44
package cmd
@@ -12,10 +12,10 @@ import (
1212
"strings"
1313

1414
"github.com/wavetermdev/waveterm/pkg/remote/connparse"
15-
"github.com/wavetermdev/waveterm/pkg/remote/fileshare/fsutil"
1615
"github.com/wavetermdev/waveterm/pkg/util/fileutil"
1716
"github.com/wavetermdev/waveterm/pkg/wshrpc"
1817
"github.com/wavetermdev/waveterm/pkg/wshrpc/wshclient"
18+
"github.com/wavetermdev/waveterm/pkg/wshutil"
1919
)
2020

2121
func convertNotFoundErr(err error) error {
@@ -91,8 +91,38 @@ func streamWriteToFile(fileData wshrpc.FileData, reader io.Reader) error {
9191
}
9292

9393
func streamReadFromFile(ctx context.Context, fileData wshrpc.FileData, writer io.Writer) error {
94-
ch := wshclient.FileReadStreamCommand(RpcClient, fileData, &wshrpc.RpcOpts{Timeout: fileTimeout})
95-
return fsutil.ReadFileStreamToWriter(ctx, ch, writer)
94+
broker := RpcClient.StreamBroker
95+
if broker == nil {
96+
return fmt.Errorf("stream broker not available")
97+
}
98+
if fileData.Info == nil {
99+
return fmt.Errorf("file info is required")
100+
}
101+
readerRouteId := RpcClientRouteId
102+
if readerRouteId == "" {
103+
return fmt.Errorf("no route id available")
104+
}
105+
conn, err := connparse.ParseURI(fileData.Info.Path)
106+
if err != nil {
107+
return fmt.Errorf("parsing file path: %w", err)
108+
}
109+
writerRouteId := wshutil.MakeConnectionRouteId(conn.Host)
110+
reader, streamMeta := broker.CreateStreamReader(readerRouteId, writerRouteId, 256*1024)
111+
defer reader.Close()
112+
go func() {
113+
<-ctx.Done()
114+
reader.Close()
115+
}()
116+
data := wshrpc.CommandFileStreamData{
117+
Info: fileData.Info,
118+
StreamMeta: *streamMeta,
119+
}
120+
_, err = wshclient.FileStreamCommand(RpcClient, data, nil)
121+
if err != nil {
122+
return fmt.Errorf("starting file stream: %w", err)
123+
}
124+
_, err = io.Copy(writer, reader)
125+
return err
96126
}
97127

98128
func fixRelativePaths(path string) (string, error) {

cmd/wsh/cmd/wshcmd-file.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,6 @@ func fileCatRun(cmd *cobra.Command, args []string) error {
172172
return err
173173
}
174174

175-
_, err = checkFileSize(path, MaxFileSize)
176-
if err != nil {
177-
return err
178-
}
179-
180175
fileData := wshrpc.FileData{
181176
Info: &wshrpc.FileInfo{
182177
Path: path}}

cmd/wsh/cmd/wshcmd-root.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ var WrappedStdout io.Writer = &WrappedWriter{dest: os.Stdout}
3131
var WrappedStderr io.Writer = &WrappedWriter{dest: os.Stderr}
3232
var RpcClient *wshutil.WshRpc
3333
var RpcContext wshrpc.RpcContext
34+
var RpcClientRouteId string
3435
var UsingTermWshMode bool
3536
var blockArg string
3637
var WshExitCode int
@@ -140,7 +141,12 @@ func setupRpcClientWithToken(swapTokenStr string) (wshrpc.CommandAuthenticateRtn
140141
if err != nil {
141142
return rtn, fmt.Errorf("error setting up domain socket rpc client: %w", err)
142143
}
143-
return wshclient.AuthenticateTokenCommand(RpcClient, wshrpc.CommandAuthenticateTokenData{Token: token.Token}, &wshrpc.RpcOpts{Route: wshutil.ControlRoute})
144+
rtn, err = wshclient.AuthenticateTokenCommand(RpcClient, wshrpc.CommandAuthenticateTokenData{Token: token.Token}, &wshrpc.RpcOpts{Route: wshutil.ControlRoute})
145+
if err != nil {
146+
return rtn, err
147+
}
148+
RpcClientRouteId = rtn.RouteId
149+
return rtn, nil
144150
}
145151

146152
// returns the wrapped stdin and a new rpc client (that wraps the stdin input and stdout output)
@@ -158,10 +164,11 @@ func setupRpcClient(serverImpl wshutil.ServerImpl, jwtToken string) error {
158164
if err != nil {
159165
return fmt.Errorf("error setting up domain socket rpc client: %v", err)
160166
}
161-
_, err = wshclient.AuthenticateCommand(RpcClient, jwtToken, &wshrpc.RpcOpts{Route: wshutil.ControlRoute})
167+
authRtn, err := wshclient.AuthenticateCommand(RpcClient, jwtToken, &wshrpc.RpcOpts{Route: wshutil.ControlRoute})
162168
if err != nil {
163169
return fmt.Errorf("error authenticating: %v", err)
164170
}
171+
RpcClientRouteId = authRtn.RouteId
165172
blockId := os.Getenv("WAVETERM_BLOCKID")
166173
if blockId != "" {
167174
peerInfo := fmt.Sprintf("domain:block:%s", blockId)

0 commit comments

Comments
 (0)