Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
"files.associations": {
"*.css": "tailwindcss"
},
"go.lintTool": "staticcheck",
"gopls": {
"analyses": {
"QF1003": false
Expand Down
6 changes: 1 addition & 5 deletions cmd/wsh/cmd/wshcmd-connserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,14 +191,10 @@ func serverRunRouter(jwtToken string) error {

func checkForUpdate() error {
remoteInfo := wshutil.GetInfo()
needsRestartRaw, err := RpcClient.SendRpcRequest(wshrpc.Command_ConnUpdateWsh, remoteInfo, &wshrpc.RpcOpts{Timeout: 60000})
needsRestart, err := wshclient.ConnUpdateWshCommand(RpcClient, remoteInfo, &wshrpc.RpcOpts{Timeout: 60000})
if err != nil {
return fmt.Errorf("could not update: %w", err)
}
needsRestart, ok := needsRestartRaw.(bool)
if !ok {
return fmt.Errorf("wrong return type from update")
}
if needsRestart {
// run the restart command here
// how to get the correct path?
Expand Down
3 changes: 2 additions & 1 deletion cmd/wsh/cmd/wshcmd-deleteblock.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/spf13/cobra"
"github.com/wavetermdev/waveterm/pkg/wshrpc"
"github.com/wavetermdev/waveterm/pkg/wshrpc/wshclient"
)

var deleteBlockCmd = &cobra.Command{
Expand Down Expand Up @@ -35,7 +36,7 @@ func deleteBlockRun(cmd *cobra.Command, args []string) (rtnErr error) {
deleteBlockData := &wshrpc.CommandDeleteBlockData{
BlockId: fullORef.OID,
}
_, err = RpcClient.SendRpcRequest(wshrpc.Command_DeleteBlock, deleteBlockData, &wshrpc.RpcOpts{Timeout: 2000})
err = wshclient.DeleteBlockCommand(RpcClient, *deleteBlockData, &wshrpc.RpcOpts{Timeout: 2000})
if err != nil {
return fmt.Errorf("delete block failed: %v", err)
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/wsh/cmd/wshcmd-editconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/spf13/cobra"
"github.com/wavetermdev/waveterm/pkg/waveobj"
"github.com/wavetermdev/waveterm/pkg/wshrpc"
"github.com/wavetermdev/waveterm/pkg/wshrpc/wshclient"
)

var editConfigMagnified bool
Expand Down Expand Up @@ -48,7 +49,7 @@ func editConfigRun(cmd *cobra.Command, args []string) (rtnErr error) {
Focused: true,
}

_, err := RpcClient.SendRpcRequest(wshrpc.Command_CreateBlock, wshCmd, &wshrpc.RpcOpts{Timeout: 2000})
_, err := wshclient.CreateBlockCommand(RpcClient, *wshCmd, &wshrpc.RpcOpts{Timeout: 2000})
if err != nil {
return fmt.Errorf("opening config file: %w", err)
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/wsh/cmd/wshcmd-notify.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/spf13/cobra"
"github.com/wavetermdev/waveterm/pkg/wshrpc"
"github.com/wavetermdev/waveterm/pkg/wshrpc/wshclient"
"github.com/wavetermdev/waveterm/pkg/wshutil"
)

Expand Down Expand Up @@ -38,7 +39,7 @@ func notifyRun(cmd *cobra.Command, args []string) (rtnErr error) {
Body: message,
Silent: notifySilent,
}
_, err := RpcClient.SendRpcRequest(wshrpc.Command_Notify, notificationOptions, &wshrpc.RpcOpts{Timeout: 2000, Route: wshutil.ElectronRoute})
err := wshclient.NotifyCommand(RpcClient, *notificationOptions, &wshrpc.RpcOpts{Timeout: 2000, Route: wshutil.ElectronRoute})
if err != nil {
return fmt.Errorf("sending notification: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/wsh/cmd/wshcmd-secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func secretUiRun(cmd *cobra.Command, args []string) (rtnErr error) {
Focused: true,
}

_, err := RpcClient.SendRpcRequest(wshrpc.Command_CreateBlock, wshCmd, &wshrpc.RpcOpts{Timeout: 2000})
_, err := wshclient.CreateBlockCommand(RpcClient, *wshCmd, &wshrpc.RpcOpts{Timeout: 2000})
if err != nil {
return fmt.Errorf("opening secrets UI: %w", err)
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/wsh/cmd/wshcmd-setmeta.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/spf13/cobra"
"github.com/wavetermdev/waveterm/pkg/wshrpc"
"github.com/wavetermdev/waveterm/pkg/wshrpc/wshclient"
)

var setMetaCmd = &cobra.Command{
Expand Down Expand Up @@ -192,7 +193,7 @@ func setMetaRun(cmd *cobra.Command, args []string) (rtnErr error) {
ORef: *fullORef,
Meta: fullMeta,
}
_, err = RpcClient.SendRpcRequest(wshrpc.Command_SetMeta, setMetaWshCmd, &wshrpc.RpcOpts{Timeout: 2000})
err = wshclient.SetMetaCommand(RpcClient, *setMetaWshCmd, &wshrpc.RpcOpts{Timeout: 2000})
if err != nil {
return fmt.Errorf("setting metadata: %v", err)
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/wsh/cmd/wshcmd-view.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/spf13/cobra"
"github.com/wavetermdev/waveterm/pkg/waveobj"
"github.com/wavetermdev/waveterm/pkg/wshrpc"
"github.com/wavetermdev/waveterm/pkg/wshrpc/wshclient"
)

var viewMagnified bool
Expand Down Expand Up @@ -99,7 +100,7 @@ func viewRun(cmd *cobra.Command, args []string) (rtnErr error) {
wshCmd.BlockDef.Meta[waveobj.MetaKey_Connection] = conn
}
}
_, err := RpcClient.SendRpcRequest(wshrpc.Command_CreateBlock, wshCmd, &wshrpc.RpcOpts{Timeout: 2000})
_, err := wshclient.CreateBlockCommand(RpcClient, *wshCmd, &wshrpc.RpcOpts{Timeout: 2000})
if err != nil {
return fmt.Errorf("running view command: %w", err)
}
Expand Down
27 changes: 0 additions & 27 deletions frontend/app/aipanel/aitooluse.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,26 +146,11 @@ interface AIToolUseBatchProps {

const AIToolUseBatch = memo(({ parts, isStreaming }: AIToolUseBatchProps) => {
const [userApprovalOverride, setUserApprovalOverride] = useState<string | null>(null);
const partsRef = useRef(parts);
partsRef.current = parts;

// All parts in a batch have the same approval status (enforced by grouping logic in AIToolUseGroup)
const firstTool = parts[0].data;
const baseApproval = userApprovalOverride || firstTool.approval;
const effectiveApproval = getEffectiveApprovalStatus(baseApproval, isStreaming);

useEffect(() => {
if (!isStreaming || effectiveApproval !== "needs-approval") return;

const interval = setInterval(() => {
partsRef.current.forEach((part) => {
WaveAIModel.getInstance().toolUseKeepalive(part.data.toolcallid);
});
}, 4000);

return () => clearInterval(interval);
}, [isStreaming, effectiveApproval]);

const handleApprove = () => {
setUserApprovalOverride("user-approved");
parts.forEach((part) => {
Expand Down Expand Up @@ -212,8 +197,6 @@ const AIToolUse = memo(({ part, isStreaming }: AIToolUseProps) => {
const showRestoreModal = restoreModalToolCallId === toolData.toolcallid;
const highlightTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const highlightedBlockIdRef = useRef<string | null>(null);
const toolCallIdRef = useRef(toolData.toolcallid);
toolCallIdRef.current = toolData.toolcallid;

const statusIcon = toolData.status === "completed" ? "✓" : toolData.status === "error" ? "✗" : "•";
const statusColor =
Expand All @@ -224,16 +207,6 @@ const AIToolUse = memo(({ part, isStreaming }: AIToolUseProps) => {

const isFileWriteTool = toolData.toolname === "write_text_file" || toolData.toolname === "edit_text_file";

useEffect(() => {
if (!isStreaming || effectiveApproval !== "needs-approval") return;

const interval = setInterval(() => {
WaveAIModel.getInstance().toolUseKeepalive(toolCallIdRef.current);
}, 4000);

return () => clearInterval(interval);
}, [isStreaming, effectiveApproval]);

useEffect(() => {
return () => {
if (highlightTimeoutRef.current) {
Expand Down
11 changes: 11 additions & 0 deletions frontend/app/store/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,16 @@ function useBlockDataLoaded(blockId: string): boolean {
return useAtomValue(loadedAtom);
}

/**
* Safely read an atom value, returning null if the atom is null.
*/
function readAtom<T>(atom: Atom<T>): T {
if (atom == null) {
return null;
}
return globalStore.get(atom);
}

/**
* Get the preload api.
*/
Expand Down Expand Up @@ -863,6 +873,7 @@ export {
getUserName,
globalPrimaryTabStartup,
globalStore,
readAtom,
initGlobal,
initGlobalWaveEventSubs,
isDev,
Expand Down
10 changes: 10 additions & 0 deletions frontend/app/view/term/term-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
getOverrideConfigAtom,
getSettingsKeyAtom,
globalStore,
readAtom,
recordTEvent,
useBlockAtom,
WOS,
} from "@/store/global";
Expand Down Expand Up @@ -478,6 +480,14 @@ export class TermViewModel implements ViewModel {
}

keyDownHandler(waveEvent: WaveKeyboardEvent): boolean {
if (keyutil.checkKeyPressed(waveEvent, "Ctrl:r")) {
const shellIntegrationStatus = readAtom(this.termRef?.current?.shellIntegrationStatusAtom);
if (shellIntegrationStatus === "ready") {
recordTEvent("action:term", { "action:type": "term:ctrlr" });
}
// just for telemetry, we allow this keybinding through, back to the terminal
return false;
}
if (keyutil.checkKeyPressed(waveEvent, "Cmd:Escape")) {
const blockAtom = WOS.getWaveObjectAtom<Block>(`block:${this.blockId}`);
const blockData = globalStore.get(blockAtom);
Expand Down
27 changes: 24 additions & 3 deletions frontend/app/view/term/termwrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,29 @@ function addTestMarkerDecoration(terminal: Terminal, marker: TermTypes.IMarker,
});
}

function checkCommandForTelemetry(decodedCmd: string) {
if (!decodedCmd) {
return;
}

if (decodedCmd.startsWith("ssh ")) {
recordTEvent("conn:connect", { "conn:conntype": "ssh-manual" });
return;
}

const editorsRegex = /^(vim|vi|nano|nvim)\b/;
if (editorsRegex.test(decodedCmd)) {
recordTEvent("action:term", { "action:type": "cli-edit" });
return;
}

const tailFollowRegex = /(^|\|\s*)tail\s+-[fF]\b/;
if (tailFollowRegex.test(decodedCmd)) {
recordTEvent("action:term", { "action:type": "cli-tailf" });
return;
}
}

// OSC 16162 - Shell Integration Commands
// See aiprompts/wave-osc-16162.md for full documentation
type ShellIntegrationStatus = "ready" | "running-command";
Expand Down Expand Up @@ -274,9 +297,7 @@ function handleOsc16162Command(data: string, blockId: string, loaded: boolean, t
const decodedCmd = base64ToString(cmd.data.cmd64);
rtInfo["shell:lastcmd"] = decodedCmd;
globalStore.set(termWrap.lastCommandAtom, decodedCmd);
if (decodedCmd?.startsWith("ssh ")) {
recordTEvent("conn:connect", { "conn:conntype": "ssh-manual" });
}
checkCommandForTelemetry(decodedCmd);
} catch (e) {
console.error("Error decoding cmd64:", e);
rtInfo["shell:lastcmd"] = null;
Expand Down
2 changes: 1 addition & 1 deletion frontend/types/gotypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,6 @@ declare global {
// wshrpc.CommandWaveAIToolApproveData
type CommandWaveAIToolApproveData = {
toolcallid: string;
keepalive?: boolean;
approval?: string;
};

Expand Down Expand Up @@ -1235,6 +1234,7 @@ declare global {
"action:type"?: string;
"debug:panictype"?: string;
"block:view"?: string;
"block:controller"?: string;
"ai:backendtype"?: string;
"ai:local"?: boolean;
"wsh:cmd"?: string;
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading