mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
Add wsh wavepath
command for getting Wave paths (#1545)
This commit is contained in:
parent
799aecd501
commit
0a3dadb628
135
cmd/wsh/cmd/wshcmd-wavepath.go
Normal file
135
cmd/wsh/cmd/wshcmd-wavepath.go
Normal file
@ -0,0 +1,135 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/wavetermdev/waveterm/pkg/wshrpc"
|
||||
"github.com/wavetermdev/waveterm/pkg/wshrpc/wshclient"
|
||||
)
|
||||
|
||||
var wavepathCmd = &cobra.Command{
|
||||
Use: "wavepath {config|data|log}",
|
||||
Short: "Get paths to various waveterm files and directories",
|
||||
RunE: wavepathRun,
|
||||
PreRunE: preRunSetupRpcClient,
|
||||
}
|
||||
|
||||
func init() {
|
||||
wavepathCmd.Flags().BoolP("open", "o", false, "Open the path in a new block")
|
||||
wavepathCmd.Flags().BoolP("open-external", "O", false, "Open the path in the default external application")
|
||||
wavepathCmd.Flags().BoolP("tail", "t", false, "Tail the last 100 lines of the log")
|
||||
rootCmd.AddCommand(wavepathCmd)
|
||||
}
|
||||
|
||||
func wavepathRun(cmd *cobra.Command, args []string) (rtnErr error) {
|
||||
defer func() {
|
||||
sendActivity("wavepath", rtnErr == nil)
|
||||
}()
|
||||
|
||||
if len(args) == 0 {
|
||||
OutputHelpMessage(cmd)
|
||||
return fmt.Errorf("no arguments. wsh wavepath requires a type argument (config, data, or log)")
|
||||
}
|
||||
if len(args) > 1 {
|
||||
OutputHelpMessage(cmd)
|
||||
return fmt.Errorf("too many arguments. wsh wavepath requires exactly one argument")
|
||||
}
|
||||
|
||||
pathType := args[0]
|
||||
if pathType != "config" && pathType != "data" && pathType != "log" {
|
||||
OutputHelpMessage(cmd)
|
||||
return fmt.Errorf("invalid path type %q. must be one of: config, data, log", pathType)
|
||||
}
|
||||
|
||||
tail, _ := cmd.Flags().GetBool("tail")
|
||||
if tail && pathType != "log" {
|
||||
return fmt.Errorf("--tail can only be used with the log path type")
|
||||
}
|
||||
|
||||
open, _ := cmd.Flags().GetBool("open")
|
||||
openExternal, _ := cmd.Flags().GetBool("open-external")
|
||||
|
||||
path, err := wshclient.PathCommand(RpcClient, wshrpc.PathCommandData{
|
||||
PathType: pathType,
|
||||
Open: open,
|
||||
OpenExternal: openExternal,
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting path: %w", err)
|
||||
}
|
||||
|
||||
if tail && pathType == "log" {
|
||||
err = tailLogFile(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("tailing log file: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
WriteStdout("%s\n", path)
|
||||
return nil
|
||||
}
|
||||
|
||||
func tailLogFile(path string) error {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("opening log file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// Get file size
|
||||
stat, err := file.Stat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting file stats: %w", err)
|
||||
}
|
||||
|
||||
// Read last 16KB or whole file if smaller
|
||||
readSize := int64(16 * 1024)
|
||||
var offset int64
|
||||
if stat.Size() > readSize {
|
||||
offset = stat.Size() - readSize
|
||||
}
|
||||
|
||||
_, err = file.Seek(offset, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("seeking file: %w", err)
|
||||
}
|
||||
|
||||
buf := make([]byte, readSize)
|
||||
n, err := file.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
return fmt.Errorf("reading file: %w", err)
|
||||
}
|
||||
buf = buf[:n]
|
||||
|
||||
// Skip partial line at start if we're not at beginning of file
|
||||
if offset > 0 {
|
||||
idx := bytes.IndexByte(buf, '\n')
|
||||
if idx >= 0 {
|
||||
buf = buf[idx+1:]
|
||||
}
|
||||
}
|
||||
|
||||
// Split into lines
|
||||
lines := bytes.Split(buf, []byte{'\n'})
|
||||
|
||||
// Take last 100 lines if we have more
|
||||
startIdx := 0
|
||||
if len(lines) > 100 {
|
||||
startIdx = len(lines) - 100
|
||||
}
|
||||
|
||||
// Print lines
|
||||
for _, line := range lines[startIdx:] {
|
||||
WriteStdout("%s\n", string(line))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -693,4 +693,52 @@ wsh setvar -b client MYVAR=value
|
||||
|
||||
Variables set with these commands persist across sessions and can be used to store configuration values, secrets, or any other string data that needs to be accessible across blocks or tabs.
|
||||
|
||||
## wavepath
|
||||
|
||||
The `wavepath` command lets you get the paths to various Wave Terminal directories and files, including configuration, data storage, and logs.
|
||||
|
||||
```bash
|
||||
wsh wavepath {config|data|log}
|
||||
```
|
||||
|
||||
This command returns the full path to the requested Wave Terminal system directory or file. It's useful for accessing Wave's configuration files, data storage, or checking logs.
|
||||
|
||||
Flags:
|
||||
|
||||
- `-o, --open` - open the path in a new block
|
||||
- `-O, --open-external` - open the path in the default external application
|
||||
- `-t, --tail` - show the last ~100 lines of the log file (only valid for log path)
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
# Get path to config directory
|
||||
wsh wavepath config
|
||||
|
||||
# Get path to data directory
|
||||
wsh wavepath data
|
||||
|
||||
# Get path to log file
|
||||
wsh wavepath log
|
||||
|
||||
# Open log file in a new block
|
||||
wsh wavepath -o log
|
||||
|
||||
# Open config directory in system file explorer
|
||||
wsh wavepath -O config
|
||||
|
||||
# View recent log entries
|
||||
wsh wavepath -t log
|
||||
```
|
||||
|
||||
The command will show you the full path to:
|
||||
|
||||
- `config` - Where Wave Terminal stores its configuration files
|
||||
- `data` - Where Wave Terminal stores its persistent data
|
||||
- `log` - The main Wave Terminal log file
|
||||
|
||||
:::tip
|
||||
Use the `-t` flag with the log path to quickly view recent log entries without having to open the full file. This is particularly useful for troubleshooting.
|
||||
:::
|
||||
|
||||
</PlatformProvider>
|
||||
|
@ -202,6 +202,11 @@ class RpcApiType {
|
||||
return client.wshRpcCall("notify", data, opts);
|
||||
}
|
||||
|
||||
// command "path" [call]
|
||||
PathCommand(client: WshClient, data: PathCommandData, opts?: RpcOpts): Promise<string> {
|
||||
return client.wshRpcCall("path", data, opts);
|
||||
}
|
||||
|
||||
// command "remotefiledelete" [call]
|
||||
RemoteFileDeleteCommand(client: WshClient, data: string, opts?: RpcOpts): Promise<void> {
|
||||
return client.wshRpcCall("remotefiledelete", data, opts);
|
||||
|
@ -412,6 +412,11 @@ export class LayoutModel {
|
||||
for (const action of actions) {
|
||||
switch (action.actiontype) {
|
||||
case LayoutTreeActionType.InsertNode: {
|
||||
if (action.ephemeral) {
|
||||
this.newEphemeralNode(action.blockid);
|
||||
break;
|
||||
}
|
||||
|
||||
const insertNodeAction: LayoutTreeInsertNodeAction = {
|
||||
type: LayoutTreeActionType.InsertNode,
|
||||
node: newLayoutNode(undefined, undefined, undefined, {
|
||||
|
10
frontend/types/gotypes.d.ts
vendored
10
frontend/types/gotypes.d.ts
vendored
@ -139,6 +139,7 @@ declare global {
|
||||
blockdef: BlockDef;
|
||||
rtopts?: RuntimeOpts;
|
||||
magnified?: boolean;
|
||||
ephemeral?: boolean;
|
||||
};
|
||||
|
||||
// wshrpc.CommandCreateSubBlockData
|
||||
@ -400,6 +401,7 @@ declare global {
|
||||
indexarr?: number[];
|
||||
focused: boolean;
|
||||
magnified: boolean;
|
||||
ephemeral: boolean;
|
||||
};
|
||||
|
||||
// waveobj.LayoutState
|
||||
@ -562,6 +564,14 @@ declare global {
|
||||
prompt: OpenAIPromptMessageType[];
|
||||
};
|
||||
|
||||
// wshrpc.PathCommandData
|
||||
type PathCommandData = {
|
||||
pathtype: string;
|
||||
open: boolean;
|
||||
openexternal: boolean;
|
||||
tabid: string;
|
||||
};
|
||||
|
||||
// waveobj.Point
|
||||
type Point = {
|
||||
x: number;
|
||||
|
1
go.mod
1
go.mod
@ -20,6 +20,7 @@ require (
|
||||
github.com/sawka/txwrap v0.2.0
|
||||
github.com/shirou/gopsutil/v4 v4.24.11
|
||||
github.com/skeema/knownhosts v1.3.0
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/ubuntu/gowsl v0.0.0-20240906163211-049fd49bd93b
|
||||
github.com/wavetermdev/htmltoken v0.2.0
|
||||
|
2
go.sum
2
go.sum
@ -68,6 +68,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
|
||||
github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
|
@ -208,6 +208,7 @@ type LayoutActionData struct {
|
||||
IndexArr *[]int `json:"indexarr,omitempty"`
|
||||
Focused bool `json:"focused"`
|
||||
Magnified bool `json:"magnified"`
|
||||
Ephemeral bool `json:"ephemeral"`
|
||||
}
|
||||
|
||||
type LeafOrderEntry struct {
|
||||
|
@ -247,6 +247,12 @@ func NotifyCommand(w *wshutil.WshRpc, data wshrpc.WaveNotificationOptions, opts
|
||||
return err
|
||||
}
|
||||
|
||||
// command "path", wshserver.PathCommand
|
||||
func PathCommand(w *wshutil.WshRpc, data wshrpc.PathCommandData, opts *wshrpc.RpcOpts) (string, error) {
|
||||
resp, err := sendRpcRequestCallHelper[string](w, "path", data, opts)
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// command "remotefiledelete", wshserver.RemoteFileDeleteCommand
|
||||
func RemoteFileDeleteCommand(w *wshutil.WshRpc, data string, opts *wshrpc.RpcOpts) error {
|
||||
_, err := sendRpcRequestCallHelper[any](w, "remotefiledelete", data, opts)
|
||||
|
@ -149,6 +149,7 @@ type WshRpcInterface interface {
|
||||
ActivityCommand(ctx context.Context, data ActivityUpdate) error
|
||||
GetVarCommand(ctx context.Context, data CommandVarData) (*CommandVarResponseData, error)
|
||||
SetVarCommand(ctx context.Context, data CommandVarData) error
|
||||
PathCommand(ctx context.Context, data PathCommandData) (string, error)
|
||||
|
||||
// connection functions
|
||||
ConnStatusCommand(ctx context.Context) ([]ConnStatus, error)
|
||||
@ -290,6 +291,7 @@ type CommandCreateBlockData struct {
|
||||
BlockDef *waveobj.BlockDef `json:"blockdef"`
|
||||
RtOpts *waveobj.RuntimeOpts `json:"rtopts,omitempty"`
|
||||
Magnified bool `json:"magnified,omitempty"`
|
||||
Ephemeral bool `json:"ephemeral,omitempty"`
|
||||
}
|
||||
|
||||
type CommandCreateSubBlockData struct {
|
||||
@ -604,6 +606,13 @@ type CommandVarResponseData struct {
|
||||
Exists bool `json:"exists"`
|
||||
}
|
||||
|
||||
type PathCommandData struct {
|
||||
PathType string `json:"pathtype"`
|
||||
Open bool `json:"open"`
|
||||
OpenExternal bool `json:"openexternal"`
|
||||
TabId string `json:"tabid" wshcontext:"TabId"`
|
||||
}
|
||||
|
||||
type ActivityDisplayType struct {
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
|
@ -12,10 +12,12 @@ import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/skratchdot/open-golang/open"
|
||||
"github.com/wavetermdev/waveterm/pkg/blockcontroller"
|
||||
"github.com/wavetermdev/waveterm/pkg/filestore"
|
||||
"github.com/wavetermdev/waveterm/pkg/panichandler"
|
||||
@ -183,6 +185,7 @@ func (ws *WshServer) CreateBlockCommand(ctx context.Context, data wshrpc.Command
|
||||
ActionType: wcore.LayoutActionDataType_Insert,
|
||||
BlockId: blockData.OID,
|
||||
Magnified: data.Magnified,
|
||||
Ephemeral: data.Ephemeral,
|
||||
Focused: true,
|
||||
})
|
||||
if err != nil {
|
||||
@ -829,3 +832,39 @@ func (ws *WshServer) SetVarCommand(ctx context.Context, data wshrpc.CommandVarDa
|
||||
envStr := envutil.MapToEnv(envMap)
|
||||
return filestore.WFS.WriteFile(ctx, data.ZoneId, data.FileName, []byte(envStr))
|
||||
}
|
||||
|
||||
func (ws *WshServer) PathCommand(ctx context.Context, data wshrpc.PathCommandData) (string, error) {
|
||||
pathType := data.PathType
|
||||
openInternal := data.Open
|
||||
openExternal := data.OpenExternal
|
||||
var path string
|
||||
switch pathType {
|
||||
case "config":
|
||||
path = wavebase.GetWaveConfigDir()
|
||||
case "data":
|
||||
path = wavebase.GetWaveDataDir()
|
||||
case "log":
|
||||
path = filepath.Join(wavebase.GetWaveDataDir(), "waveapp.log")
|
||||
}
|
||||
|
||||
if openInternal && openExternal {
|
||||
return "", fmt.Errorf("open and openExternal cannot both be true")
|
||||
}
|
||||
|
||||
if openInternal {
|
||||
_, err := ws.CreateBlockCommand(ctx, wshrpc.CommandCreateBlockData{BlockDef: &waveobj.BlockDef{Meta: map[string]any{
|
||||
waveobj.MetaKey_View: "preview",
|
||||
waveobj.MetaKey_File: path,
|
||||
}}, Ephemeral: true, TabId: data.TabId})
|
||||
|
||||
if err != nil {
|
||||
return path, fmt.Errorf("error opening path: %w", err)
|
||||
}
|
||||
} else if openExternal {
|
||||
err := open.Run(path)
|
||||
if err != nil {
|
||||
return path, fmt.Errorf("error opening path: %w", err)
|
||||
}
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user