mirror of
https://github.com/wavetermdev/backup.git
synced 2026-08-05 13:57:07 -07:00
checkpoint on domain sockets + update background colors + transparency (#160)
This commit is contained in:
@@ -8,11 +8,12 @@ import (
|
||||
"os"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/wavetermdev/thenextwave/pkg/service"
|
||||
"github.com/wavetermdev/thenextwave/pkg/tsgen"
|
||||
"github.com/wavetermdev/thenextwave/pkg/util/utilfn"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wshrpc/wshserver"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wshrpc"
|
||||
)
|
||||
|
||||
func generateTypesFile(tsTypesMap map[reflect.Type]string) error {
|
||||
@@ -43,6 +44,10 @@ func generateTypesFile(tsTypesMap map[reflect.Type]string) error {
|
||||
return iname < jname
|
||||
})
|
||||
for _, key := range keys {
|
||||
// don't output generic types
|
||||
if strings.Index(key.Name(), "[") != -1 {
|
||||
continue
|
||||
}
|
||||
tsCode := tsTypesMap[key]
|
||||
istr := utilfn.IndentString(" ", tsCode)
|
||||
fmt.Fprint(fd, istr)
|
||||
@@ -79,16 +84,17 @@ func generateWshServerFile(tsTypeMap map[reflect.Type]string) error {
|
||||
return err
|
||||
}
|
||||
defer fd.Close()
|
||||
declMap := wshrpc.GenerateWshCommandDeclMap()
|
||||
fmt.Fprintf(os.Stderr, "generating wshserver file to %s\n", fd.Name())
|
||||
fmt.Fprintf(fd, "// Copyright 2024, Command Line Inc.\n")
|
||||
fmt.Fprintf(fd, "// SPDX-License-Identifier: Apache-2.0\n\n")
|
||||
fmt.Fprintf(fd, "// generated by cmd/generate/main-generate.go\n\n")
|
||||
fmt.Fprintf(fd, "import * as WOS from \"./wos\";\n\n")
|
||||
orderedKeys := utilfn.GetOrderedMapKeys(wshserver.WshServerCommandToDeclMap)
|
||||
orderedKeys := utilfn.GetOrderedMapKeys(declMap)
|
||||
fmt.Fprintf(fd, "// WshServerCommandToDeclMap\n")
|
||||
fmt.Fprintf(fd, "class WshServerType {\n")
|
||||
for _, methodDecl := range orderedKeys {
|
||||
methodDecl := wshserver.WshServerCommandToDeclMap[methodDecl]
|
||||
methodDecl := declMap[methodDecl]
|
||||
methodStr := tsgen.GenerateWshServerMethod(methodDecl, tsTypeMap)
|
||||
fmt.Fprint(fd, methodStr)
|
||||
fmt.Fprintf(fd, "\n")
|
||||
|
||||
@@ -8,11 +8,10 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/wavetermdev/thenextwave/pkg/util/utilfn"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wshrpc/wshserver"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wshutil"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wshrpc"
|
||||
)
|
||||
|
||||
func genMethod_ResponseStream(fd *os.File, methodDecl *wshserver.WshServerMethodDecl) {
|
||||
func genMethod_ResponseStream(fd *os.File, methodDecl *wshrpc.WshRpcMethodDecl) {
|
||||
fmt.Fprintf(fd, "// command %q, wshserver.%s\n", methodDecl.Command, methodDecl.MethodName)
|
||||
var dataType string
|
||||
dataVarName := "nil"
|
||||
@@ -29,7 +28,7 @@ func genMethod_ResponseStream(fd *os.File, methodDecl *wshserver.WshServerMethod
|
||||
fmt.Fprintf(fd, "}\n\n")
|
||||
}
|
||||
|
||||
func genMethod_Call(fd *os.File, methodDecl *wshserver.WshServerMethodDecl) {
|
||||
func genMethod_Call(fd *os.File, methodDecl *wshrpc.WshRpcMethodDecl) {
|
||||
fmt.Fprintf(fd, "// command %q, wshserver.%s\n", methodDecl.Command, methodDecl.MethodName)
|
||||
var dataType string
|
||||
dataVarName := "nil"
|
||||
@@ -70,14 +69,14 @@ func main() {
|
||||
fmt.Fprintf(fd, " \"github.com/wavetermdev/thenextwave/pkg/wshutil\"\n")
|
||||
fmt.Fprintf(fd, " \"github.com/wavetermdev/thenextwave/pkg/wshrpc\"\n")
|
||||
fmt.Fprintf(fd, " \"github.com/wavetermdev/thenextwave/pkg/waveobj\"\n")
|
||||
fmt.Fprintf(fd, " \"github.com/wavetermdev/thenextwave/pkg/waveai\"\n")
|
||||
fmt.Fprintf(fd, ")\n\n")
|
||||
|
||||
for _, key := range utilfn.GetOrderedMapKeys(wshserver.WshServerCommandToDeclMap) {
|
||||
methodDecl := wshserver.WshServerCommandToDeclMap[key]
|
||||
if methodDecl.CommandType == wshutil.RpcType_ResponseStream {
|
||||
wshDeclMap := wshrpc.GenerateWshCommandDeclMap()
|
||||
for _, key := range utilfn.GetOrderedMapKeys(wshDeclMap) {
|
||||
methodDecl := wshDeclMap[key]
|
||||
if methodDecl.CommandType == wshrpc.RpcType_ResponseStream {
|
||||
genMethod_ResponseStream(fd, methodDecl)
|
||||
} else if methodDecl.CommandType == wshutil.RpcType_Call {
|
||||
} else if methodDecl.CommandType == wshrpc.RpcType_Call {
|
||||
genMethod_Call(fd, methodDecl)
|
||||
} else {
|
||||
panic("unsupported command type " + methodDecl.CommandType)
|
||||
|
||||
@@ -43,7 +43,7 @@ func runReadFile(cmd *cobra.Command, args []string) {
|
||||
fmt.Fprintf(os.Stderr, "error resolving oref: %v\r\n", err)
|
||||
return
|
||||
}
|
||||
resp64, err := wshclient.ReadFile(RpcClient, wshrpc.CommandFileData{ZoneId: fullORef.OID, FileName: args[1]}, &wshrpc.WshRpcCommandOpts{Timeout: 5000})
|
||||
resp64, err := wshclient.FileReadCommand(RpcClient, wshrpc.CommandFileData{ZoneId: fullORef.OID, FileName: args[1]}, &wshrpc.WshRpcCommandOpts{Timeout: 5000})
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error reading file: %v\r\n", err)
|
||||
return
|
||||
|
||||
+59
-26
@@ -34,6 +34,7 @@ const waveSrvReady: Promise<boolean> = new Promise((resolve, _) => {
|
||||
});
|
||||
let globalIsQuitting = false;
|
||||
let globalIsStarting = true;
|
||||
let globalIsRelaunching = false;
|
||||
|
||||
const isDev = !electronApp.isPackaged;
|
||||
const isDevVite = isDev && process.env.ELECTRON_RENDERER_URL;
|
||||
@@ -214,7 +215,8 @@ async function handleWSEvent(evtMsg: WSEventType) {
|
||||
return;
|
||||
}
|
||||
const clientData = await services.ClientService.GetClientData();
|
||||
const newWin = createBrowserWindow(clientData.oid, windowData);
|
||||
const settings = await services.FileService.GetSettingsConfig();
|
||||
const newWin = createBrowserWindow(clientData.oid, windowData, settings);
|
||||
await newWin.readyPromise;
|
||||
newWin.show();
|
||||
} else if (evtMsg.eventtype == "electron:closewindow") {
|
||||
@@ -290,7 +292,11 @@ function shFrameNavHandler(event: Electron.Event<Electron.WebContentsWillFrameNa
|
||||
|
||||
// note, this does not *show* the window.
|
||||
// to show, await win.readyPromise and then win.show()
|
||||
function createBrowserWindow(clientId: string, waveWindow: WaveWindow): WaveBrowserWindow {
|
||||
function createBrowserWindow(
|
||||
clientId: string,
|
||||
waveWindow: WaveWindow,
|
||||
settings: SettingsConfigType
|
||||
): WaveBrowserWindow {
|
||||
let winBounds = {
|
||||
x: waveWindow.pos.x,
|
||||
y: waveWindow.pos.y,
|
||||
@@ -298,7 +304,7 @@ function createBrowserWindow(clientId: string, waveWindow: WaveWindow): WaveBrow
|
||||
height: waveWindow.winsize.height,
|
||||
};
|
||||
winBounds = ensureBoundsAreVisible(winBounds);
|
||||
const bwin = new electron.BrowserWindow({
|
||||
const winOpts: Electron.BrowserWindowConstructorOptions = {
|
||||
titleBarStyle: "hiddenInset",
|
||||
x: winBounds.x,
|
||||
y: winBounds.y,
|
||||
@@ -316,8 +322,14 @@ function createBrowserWindow(clientId: string, waveWindow: WaveWindow): WaveBrow
|
||||
},
|
||||
show: false,
|
||||
autoHideMenuBar: true,
|
||||
backgroundColor: "#000000",
|
||||
});
|
||||
};
|
||||
const isTransparent = settings?.window?.transparent ?? true;
|
||||
if (isTransparent) {
|
||||
winOpts.transparent = true;
|
||||
} else {
|
||||
winOpts.backgroundColor = "#222222";
|
||||
}
|
||||
const bwin = new electron.BrowserWindow(winOpts);
|
||||
(bwin as any).waveWindowId = waveWindow.oid;
|
||||
let readyResolve: (value: void) => void;
|
||||
(bwin as any).readyPromise = new Promise((resolve, _) => {
|
||||
@@ -519,7 +531,8 @@ electron.ipcMain.on("getEnv", (event, varName) => {
|
||||
async function createNewWaveWindow() {
|
||||
const clientData = await services.ClientService.GetClientData();
|
||||
const newWindow = await services.ClientService.MakeWindow();
|
||||
const newBrowserWindow = createBrowserWindow(clientData.oid, newWindow);
|
||||
const settings = await services.FileService.GetSettingsConfig();
|
||||
const newBrowserWindow = createBrowserWindow(clientData.oid, newWindow, settings);
|
||||
newBrowserWindow.show();
|
||||
}
|
||||
|
||||
@@ -616,6 +629,12 @@ function makeAppMenu() {
|
||||
{
|
||||
role: "forceReload",
|
||||
},
|
||||
{
|
||||
label: "Relaunch All Windows",
|
||||
click: () => {
|
||||
relaunchBrowserWindows();
|
||||
},
|
||||
},
|
||||
{
|
||||
role: "toggleDevTools",
|
||||
},
|
||||
@@ -663,6 +682,9 @@ function makeAppMenu() {
|
||||
}
|
||||
|
||||
electronApp.on("window-all-closed", () => {
|
||||
if (globalIsRelaunching) {
|
||||
return;
|
||||
}
|
||||
if (unamePlatform !== "darwin") {
|
||||
electronApp.quit();
|
||||
}
|
||||
@@ -857,6 +879,36 @@ async function configureAutoUpdater() {
|
||||
}
|
||||
// ====== AUTO-UPDATER ====== //
|
||||
|
||||
async function relaunchBrowserWindows() {
|
||||
globalIsRelaunching = true;
|
||||
const windows = electron.BrowserWindow.getAllWindows();
|
||||
for (const window of windows) {
|
||||
window.removeAllListeners();
|
||||
window.close();
|
||||
}
|
||||
globalIsRelaunching = false;
|
||||
|
||||
const clientData = await services.ClientService.GetClientData();
|
||||
const settings = await services.FileService.GetSettingsConfig();
|
||||
const wins: WaveBrowserWindow[] = [];
|
||||
for (const windowId of clientData.windowids.slice().reverse()) {
|
||||
const windowData: WaveWindow = (await services.ObjectService.GetObject("window:" + windowId)) as WaveWindow;
|
||||
if (windowData == null) {
|
||||
services.WindowService.CloseWindow(windowId).catch((e) => {
|
||||
/* ignore */
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const win = createBrowserWindow(clientData.oid, windowData, settings);
|
||||
wins.push(win);
|
||||
}
|
||||
for (const win of wins) {
|
||||
await win.readyPromise;
|
||||
console.log("show", win.waveWindowId);
|
||||
win.show();
|
||||
}
|
||||
}
|
||||
|
||||
async function appMain() {
|
||||
const startTs = Date.now();
|
||||
const instanceLock = electronApp.requestSingleInstanceLock();
|
||||
@@ -877,27 +929,8 @@ async function appMain() {
|
||||
}
|
||||
const ready = await waveSrvReady;
|
||||
console.log("wavesrv ready signal received", ready, Date.now() - startTs, "ms");
|
||||
console.log("get client data");
|
||||
const clientData = await services.ClientService.GetClientData();
|
||||
console.log("client data ready");
|
||||
await electronApp.whenReady();
|
||||
const wins: WaveBrowserWindow[] = [];
|
||||
for (const windowId of clientData.windowids.slice().reverse()) {
|
||||
const windowData: WaveWindow = (await services.ObjectService.GetObject("window:" + windowId)) as WaveWindow;
|
||||
if (windowData == null) {
|
||||
services.WindowService.CloseWindow(windowId).catch((e) => {
|
||||
/* ignore */
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const win = createBrowserWindow(clientData.oid, windowData);
|
||||
wins.push(win);
|
||||
}
|
||||
for (const win of wins) {
|
||||
await win.readyPromise;
|
||||
console.log("show", win.waveWindowId);
|
||||
win.show();
|
||||
}
|
||||
relaunchBrowserWindows();
|
||||
configureAutoUpdater();
|
||||
globalIsStarting = false;
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ body {
|
||||
font: var(--base-font);
|
||||
overflow: hidden;
|
||||
-webkit-font-smoothing: auto;
|
||||
backface-visibility: hidden;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
|
||||
@@ -16,6 +16,7 @@ import { HTML5Backend } from "react-dnd-html5-backend";
|
||||
import { CenteredDiv } from "./element/quickelems";
|
||||
|
||||
import clsx from "clsx";
|
||||
import Color from "color";
|
||||
import "overlayscrollbars/overlayscrollbars.css";
|
||||
import "./app.less";
|
||||
|
||||
@@ -200,6 +201,31 @@ function switchBlock(tabId: string, offsetX: number, offsetY: number) {
|
||||
}
|
||||
}
|
||||
|
||||
function AppSettingsUpdater() {
|
||||
const settings = jotai.useAtomValue(atoms.settingsConfigAtom);
|
||||
React.useEffect(() => {
|
||||
let isTransparent = settings?.window?.transparent ?? true;
|
||||
let opacity = util.boundNumber(settings?.window?.opacity ?? 0.8, 0, 1);
|
||||
let baseBgColor = settings?.window?.bgcolor;
|
||||
console.log("window settings", settings.window);
|
||||
|
||||
if (isTransparent) {
|
||||
document.body.classList.add("is-transparent");
|
||||
const rootStyles = getComputedStyle(document.documentElement);
|
||||
if (baseBgColor == null) {
|
||||
baseBgColor = rootStyles.getPropertyValue("--main-bg-color").trim();
|
||||
}
|
||||
const color = new Color(baseBgColor);
|
||||
const rgbaColor = color.alpha(opacity).string();
|
||||
document.body.style.backgroundColor = rgbaColor;
|
||||
} else {
|
||||
document.body.classList.remove("is-transparent");
|
||||
document.body.style.opacity = null;
|
||||
}
|
||||
}, [settings?.window]);
|
||||
return null;
|
||||
}
|
||||
|
||||
const AppInner = () => {
|
||||
const client = jotai.useAtomValue(atoms.client);
|
||||
const windowData = jotai.useAtomValue(atoms.waveWindow);
|
||||
@@ -251,6 +277,7 @@ const AppInner = () => {
|
||||
const isFullScreen = jotai.useAtomValue(atoms.isFullScreen);
|
||||
return (
|
||||
<div className={clsx("mainapp", PLATFORM, { fullscreen: isFullScreen })} onContextMenu={handleContextMenu}>
|
||||
<AppSettingsUpdater />
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Workspace />
|
||||
</DndProvider>
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
padding: 2px;
|
||||
|
||||
.block-frame-default-inner {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
background-color: var(--block-bg-color);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 8px;
|
||||
|
||||
@@ -7,14 +7,19 @@ import * as WOS from "./wos";
|
||||
|
||||
// WshServerCommandToDeclMap
|
||||
class WshServerType {
|
||||
// command "controller:input" [call]
|
||||
BlockInputCommand(data: CommandBlockInputData, opts?: WshRpcCommandOpts): Promise<void> {
|
||||
return WOS.wshServerRpcHelper_call("controller:input", data, opts);
|
||||
// command "authenticate" [call]
|
||||
AuthenticateCommand(data: string, opts?: WshRpcCommandOpts): Promise<void> {
|
||||
return WOS.wshServerRpcHelper_call("authenticate", data, opts);
|
||||
}
|
||||
|
||||
// command "controller:restart" [call]
|
||||
BlockRestartCommand(data: CommandBlockRestartData, opts?: WshRpcCommandOpts): Promise<void> {
|
||||
return WOS.wshServerRpcHelper_call("controller:restart", data, opts);
|
||||
// command "controllerinput" [call]
|
||||
ControllerInputCommand(data: CommandBlockInputData, opts?: WshRpcCommandOpts): Promise<void> {
|
||||
return WOS.wshServerRpcHelper_call("controllerinput", data, opts);
|
||||
}
|
||||
|
||||
// command "controllerrestart" [call]
|
||||
ControllerRestartCommand(data: CommandBlockRestartData, opts?: WshRpcCommandOpts): Promise<void> {
|
||||
return WOS.wshServerRpcHelper_call("controllerrestart", data, opts);
|
||||
}
|
||||
|
||||
// command "createblock" [call]
|
||||
@@ -27,24 +32,49 @@ class WshServerType {
|
||||
return WOS.wshServerRpcHelper_call("deleteblock", data, opts);
|
||||
}
|
||||
|
||||
// command "file:append" [call]
|
||||
AppendFileCommand(data: CommandFileData, opts?: WshRpcCommandOpts): Promise<void> {
|
||||
return WOS.wshServerRpcHelper_call("file:append", data, opts);
|
||||
// command "eventpublish" [call]
|
||||
EventPublishCommand(data: WaveEvent, opts?: WshRpcCommandOpts): Promise<void> {
|
||||
return WOS.wshServerRpcHelper_call("eventpublish", data, opts);
|
||||
}
|
||||
|
||||
// command "file:appendijson" [call]
|
||||
AppendIJsonCommand(data: CommandAppendIJsonData, opts?: WshRpcCommandOpts): Promise<void> {
|
||||
return WOS.wshServerRpcHelper_call("file:appendijson", data, opts);
|
||||
// command "eventrecv" [call]
|
||||
EventRecvCommand(data: WaveEvent, opts?: WshRpcCommandOpts): Promise<void> {
|
||||
return WOS.wshServerRpcHelper_call("eventrecv", data, opts);
|
||||
}
|
||||
|
||||
// command "file:read" [call]
|
||||
ReadFile(data: CommandFileData, opts?: WshRpcCommandOpts): Promise<string> {
|
||||
return WOS.wshServerRpcHelper_call("file:read", data, opts);
|
||||
// command "eventsub" [call]
|
||||
EventSubCommand(data: SubscriptionRequest, opts?: WshRpcCommandOpts): Promise<void> {
|
||||
return WOS.wshServerRpcHelper_call("eventsub", data, opts);
|
||||
}
|
||||
|
||||
// command "file:write" [call]
|
||||
WriteFile(data: CommandFileData, opts?: WshRpcCommandOpts): Promise<void> {
|
||||
return WOS.wshServerRpcHelper_call("file:write", data, opts);
|
||||
// command "eventunsub" [call]
|
||||
EventUnsubCommand(data: SubscriptionRequest, opts?: WshRpcCommandOpts): Promise<void> {
|
||||
return WOS.wshServerRpcHelper_call("eventunsub", data, opts);
|
||||
}
|
||||
|
||||
// command "eventunsuball" [call]
|
||||
EventUnsubAllCommand(opts?: WshRpcCommandOpts): Promise<void> {
|
||||
return WOS.wshServerRpcHelper_call("eventunsuball", null, opts);
|
||||
}
|
||||
|
||||
// command "fileappend" [call]
|
||||
FileAppendCommand(data: CommandFileData, opts?: WshRpcCommandOpts): Promise<void> {
|
||||
return WOS.wshServerRpcHelper_call("fileappend", data, opts);
|
||||
}
|
||||
|
||||
// command "fileappendijson" [call]
|
||||
FileAppendIJsonCommand(data: CommandAppendIJsonData, opts?: WshRpcCommandOpts): Promise<void> {
|
||||
return WOS.wshServerRpcHelper_call("fileappendijson", data, opts);
|
||||
}
|
||||
|
||||
// command "fileread" [call]
|
||||
FileReadCommand(data: CommandFileData, opts?: WshRpcCommandOpts): Promise<string> {
|
||||
return WOS.wshServerRpcHelper_call("fileread", data, opts);
|
||||
}
|
||||
|
||||
// command "filewrite" [call]
|
||||
FileWriteCommand(data: CommandFileData, opts?: WshRpcCommandOpts): Promise<void> {
|
||||
return WOS.wshServerRpcHelper_call("filewrite", data, opts);
|
||||
}
|
||||
|
||||
// command "getmeta" [call]
|
||||
@@ -68,18 +98,18 @@ class WshServerType {
|
||||
}
|
||||
|
||||
// command "setview" [call]
|
||||
BlockSetViewCommand(data: CommandBlockSetViewData, opts?: WshRpcCommandOpts): Promise<void> {
|
||||
SetViewCommand(data: CommandBlockSetViewData, opts?: WshRpcCommandOpts): Promise<void> {
|
||||
return WOS.wshServerRpcHelper_call("setview", data, opts);
|
||||
}
|
||||
|
||||
// command "stream:waveai" [responsestream]
|
||||
RespStreamWaveAi(data: OpenAiStreamRequest, opts?: WshRpcCommandOpts): AsyncGenerator<OpenAIPacketType, void, boolean> {
|
||||
return WOS.wshServerRpcHelper_responsestream("stream:waveai", data, opts);
|
||||
// command "streamtest" [responsestream]
|
||||
StreamTestCommand(opts?: WshRpcCommandOpts): AsyncGenerator<number, void, boolean> {
|
||||
return WOS.wshServerRpcHelper_responsestream("streamtest", null, opts);
|
||||
}
|
||||
|
||||
// command "streamtest" [responsestream]
|
||||
RespStreamTest(opts?: WshRpcCommandOpts): AsyncGenerator<number, void, boolean> {
|
||||
return WOS.wshServerRpcHelper_responsestream("streamtest", null, opts);
|
||||
// command "streamwaveai" [responsestream]
|
||||
StreamWaveAiCommand(data: OpenAiStreamRequest, opts?: WshRpcCommandOpts): AsyncGenerator<OpenAIPacketType, void, boolean> {
|
||||
return WOS.wshServerRpcHelper_responsestream("streamwaveai", data, opts);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
--title-font-size: 18px;
|
||||
--secondary-text-color: rgb(195, 200, 194);
|
||||
--grey-text-color: #666;
|
||||
--main-bg-color: #454444;
|
||||
--main-bg-color: rgb(34, 34, 34);
|
||||
--border-color: #333333;
|
||||
--base-font: normal 14px / normal "Inter", sans-serif;
|
||||
--fixed-font: normal 12px / normal "Hack", monospace;
|
||||
@@ -19,7 +19,7 @@
|
||||
--warning-color: rgb(224, 185, 86);
|
||||
--success-color: rgb(78, 154, 6);
|
||||
--hover-bg-color: rgba(255, 255, 255, 0.1);
|
||||
--block-bg-color: rgba(255, 255, 255, 0.05);
|
||||
--block-bg-color: rgba(0, 0, 0, 0.5);
|
||||
|
||||
/* scrollbar colors */
|
||||
--scrollbar-background-color: transparent;
|
||||
|
||||
@@ -214,7 +214,7 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => {
|
||||
}
|
||||
if (shellProcStatusRef.current != "running" && keyutil.checkKeyPressed(waveEvent, "Enter")) {
|
||||
// restart
|
||||
WshServer.BlockRestartCommand({ blockid: blockId });
|
||||
WshServer.ControllerRestartCommand({ blockid: blockId });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -263,7 +263,7 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => {
|
||||
return false;
|
||||
}
|
||||
const b64data = btoa(asciiVal);
|
||||
WshServer.BlockInputCommand({ blockid: blockId, inputdata64: b64data });
|
||||
WshServer.ControllerInputCommand({ blockid: blockId, inputdata64: b64data });
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ export class TermWrap {
|
||||
|
||||
handleTermData(data: string) {
|
||||
const b64data = btoa(data);
|
||||
WshServer.BlockInputCommand({ blockid: this.blockId, inputdata64: b64data });
|
||||
WshServer.ControllerInputCommand({ blockid: this.blockId, inputdata64: b64data });
|
||||
}
|
||||
|
||||
addFocusListener(focusFn: () => void) {
|
||||
|
||||
@@ -137,7 +137,7 @@ export class WaveAiModel implements ViewModel {
|
||||
opts: opts,
|
||||
prompt: prompt,
|
||||
};
|
||||
const aiGen = WshServer.RespStreamWaveAi(beMsg);
|
||||
const aiGen = WshServer.StreamWaveAiCommand(beMsg);
|
||||
let temp = async () => {
|
||||
let fullMsg = "";
|
||||
for await (const msg of aiGen) {
|
||||
|
||||
Vendored
+28
-5
@@ -187,7 +187,7 @@ declare global {
|
||||
// waveobj.ORef
|
||||
type ORef = string;
|
||||
|
||||
// waveai.OpenAIOptsType
|
||||
// wshrpc.OpenAIOptsType
|
||||
type OpenAIOptsType = {
|
||||
model: string;
|
||||
apitoken: string;
|
||||
@@ -197,7 +197,7 @@ declare global {
|
||||
timeout?: number;
|
||||
};
|
||||
|
||||
// waveai.OpenAIPacketType
|
||||
// wshrpc.OpenAIPacketType
|
||||
type OpenAIPacketType = {
|
||||
type: string;
|
||||
model?: string;
|
||||
@@ -209,21 +209,21 @@ declare global {
|
||||
error?: string;
|
||||
};
|
||||
|
||||
// waveai.OpenAIPromptMessageType
|
||||
// wshrpc.OpenAIPromptMessageType
|
||||
type OpenAIPromptMessageType = {
|
||||
role: string;
|
||||
content: string;
|
||||
name?: string;
|
||||
};
|
||||
|
||||
// waveai.OpenAIUsageType
|
||||
// wshrpc.OpenAIUsageType
|
||||
type OpenAIUsageType = {
|
||||
prompt_tokens?: number;
|
||||
completion_tokens?: number;
|
||||
total_tokens?: number;
|
||||
};
|
||||
|
||||
// waveai.OpenAiStreamRequest
|
||||
// wshrpc.OpenAiStreamRequest
|
||||
type OpenAiStreamRequest = {
|
||||
clientid?: string;
|
||||
opts: OpenAIOptsType;
|
||||
@@ -270,6 +270,7 @@ declare global {
|
||||
blockheader: BlockHeaderOpts;
|
||||
autoupdate: AutoUpdateOpts;
|
||||
termthemes: {[key: string]: TermThemeType};
|
||||
window: WindowSettingsType;
|
||||
};
|
||||
|
||||
// wstore.StickerClickOptsType
|
||||
@@ -293,6 +294,13 @@ declare global {
|
||||
display: StickerDisplayOptsType;
|
||||
};
|
||||
|
||||
// wshrpc.SubscriptionRequest
|
||||
type SubscriptionRequest = {
|
||||
event: string;
|
||||
scopes?: string[];
|
||||
allscopes?: boolean;
|
||||
};
|
||||
|
||||
// wstore.Tab
|
||||
type Tab = WaveObj & {
|
||||
name: string;
|
||||
@@ -428,6 +436,14 @@ declare global {
|
||||
error: string;
|
||||
};
|
||||
|
||||
// wshrpc.WaveEvent
|
||||
type WaveEvent = {
|
||||
event: string;
|
||||
scopes?: string[];
|
||||
sender?: string;
|
||||
data?: any;
|
||||
};
|
||||
|
||||
// filestore.WaveFile
|
||||
type WaveFile = {
|
||||
zoneid: string;
|
||||
@@ -497,6 +513,13 @@ declare global {
|
||||
height: number;
|
||||
};
|
||||
|
||||
// wconfig.WindowSettingsType
|
||||
type WindowSettingsType = {
|
||||
transparent: boolean;
|
||||
opacity: number;
|
||||
bgcolor: string;
|
||||
};
|
||||
|
||||
// wstore.Workspace
|
||||
type Workspace = WaveObj & {
|
||||
name: string;
|
||||
|
||||
@@ -34,6 +34,10 @@ function base64ToArray(b64: string): Uint8Array {
|
||||
return rtnArr;
|
||||
}
|
||||
|
||||
function boundNumber(num: number, min: number, max: number): number {
|
||||
return Math.min(Math.max(num, min), max);
|
||||
}
|
||||
|
||||
// works for json-like objects (arrays, objects, strings, numbers, booleans)
|
||||
function jsonDeepEqual(v1: any, v2: any): boolean {
|
||||
if (v1 === v2) {
|
||||
@@ -193,6 +197,7 @@ function getCrypto() {
|
||||
export {
|
||||
base64ToArray,
|
||||
base64ToString,
|
||||
boundNumber,
|
||||
fireAndForget,
|
||||
getCrypto,
|
||||
getPromiseState,
|
||||
|
||||
@@ -27,6 +27,7 @@ loadFonts();
|
||||
(window as any).globalWS = globalWS;
|
||||
(window as any).WOS = WOS;
|
||||
(window as any).globalStore = globalStore;
|
||||
(window as any).globalAtoms = atoms;
|
||||
(window as any).WshServer = WshServer;
|
||||
(window as any).isFullScreen = false;
|
||||
|
||||
|
||||
@@ -76,11 +76,13 @@
|
||||
"@table-nav/core": "^0.0.7",
|
||||
"@table-nav/react": "^0.0.7",
|
||||
"@tanstack/react-table": "^8.19.3",
|
||||
"@types/color": "^3.0.6",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/addon-serialize": "^0.13.0",
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"clsx": "^2.1.1",
|
||||
"color": "^4.2.3",
|
||||
"dayjs": "^1.11.12",
|
||||
"electron-updater": "6.3.1",
|
||||
"html-to-image": "^1.11.11",
|
||||
|
||||
@@ -6,7 +6,9 @@ package blockcontroller
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -21,12 +23,13 @@ import (
|
||||
"github.com/wavetermdev/thenextwave/pkg/shellexec"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wavebase"
|
||||
"github.com/wavetermdev/thenextwave/pkg/waveobj"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wshrpc"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wshutil"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wstore"
|
||||
)
|
||||
|
||||
// set by main-server.go (for dependency inversion)
|
||||
var WshServerFactoryFn func(inputCh chan []byte, outputCh chan []byte, initialCtx wshutil.RpcContext) = nil
|
||||
var WshServerFactoryFn func(inputCh chan []byte, outputCh chan []byte, initialCtx wshrpc.RpcContext) = nil
|
||||
|
||||
const (
|
||||
BlockController_Shell = "shell"
|
||||
@@ -205,6 +208,46 @@ func (bc *BlockController) resetTerminalState() {
|
||||
}
|
||||
}
|
||||
|
||||
func getMetaBool(meta map[string]any, key string, def bool) bool {
|
||||
val, found := meta[key]
|
||||
if !found {
|
||||
return def
|
||||
}
|
||||
if val == nil {
|
||||
return def
|
||||
}
|
||||
if bval, ok := val.(bool); ok {
|
||||
return bval
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func getMetaStr(meta map[string]any, key string, def string) string {
|
||||
val, found := meta[key]
|
||||
if !found {
|
||||
return def
|
||||
}
|
||||
if val == nil {
|
||||
return def
|
||||
}
|
||||
if sval, ok := val.(string); ok {
|
||||
return sval
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
// every byte is 4-bits of randomness
|
||||
func randomHexString(numHexDigits int) (string, error) {
|
||||
numBytes := (numHexDigits + 1) / 2 // Calculate the number of bytes needed
|
||||
bytes := make([]byte, numBytes)
|
||||
if _, err := rand.Read(bytes); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
hexStr := hex.EncodeToString(bytes)
|
||||
return hexStr[:numHexDigits], nil // Return the exact number of hex digits
|
||||
}
|
||||
|
||||
func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts, blockMeta map[string]any) error {
|
||||
// create a circular blockfile for the output
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
@@ -232,12 +275,35 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts, blockMeta map[str
|
||||
if shellProcErr != nil {
|
||||
return shellProcErr
|
||||
}
|
||||
var remoteDomainSocketName string
|
||||
remoteName := getMetaStr(blockMeta, "connection", "")
|
||||
isRemote := remoteName != ""
|
||||
if isRemote {
|
||||
randStr, err := randomHexString(16) // 64-bits of randomness
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating random string: %w", err)
|
||||
}
|
||||
remoteDomainSocketName = fmt.Sprintf("/tmp/waveterm-%s.sock", randStr)
|
||||
}
|
||||
var cmdStr string
|
||||
cmdOpts := shellexec.CommandOptsType{
|
||||
Env: make(map[string]string),
|
||||
}
|
||||
// temporary for blockid (will switch to a JWT at some point)
|
||||
cmdOpts.Env["LC_WAVETERM_BLOCKID"] = bc.BlockId
|
||||
if !getMetaBool(blockMeta, "nowsh", false) {
|
||||
if isRemote {
|
||||
jwtStr, err := wshutil.MakeClientJWTToken(wshrpc.RpcContext{TabId: bc.TabId, BlockId: bc.BlockId}, remoteDomainSocketName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error making jwt token: %w", err)
|
||||
}
|
||||
cmdOpts.Env["WAVETERM_JWT"] = jwtStr
|
||||
} else {
|
||||
jwtStr, err := wshutil.MakeClientJWTToken(wshrpc.RpcContext{TabId: bc.TabId, BlockId: bc.BlockId}, wavebase.GetDomainSocketName())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error making jwt token: %w", err)
|
||||
}
|
||||
cmdOpts.Env["WAVETERM_JWT"] = jwtStr
|
||||
}
|
||||
}
|
||||
if bc.ControllerType == BlockController_Shell {
|
||||
cmdOpts.Interactive = true
|
||||
cmdOpts.Login = true
|
||||
@@ -284,11 +350,8 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts, blockMeta map[str
|
||||
} else {
|
||||
return fmt.Errorf("unknown controller type %q", bc.ControllerType)
|
||||
}
|
||||
// pty buffer equivalent for ssh? i think if i have the ecmd or session i can manage it with output
|
||||
// pty write needs stdin, so if i provide that, i might be able to write that way
|
||||
// need a way to handle setsize???
|
||||
var shellProc *shellexec.ShellProc
|
||||
if remoteName, ok := blockMeta["connection"].(string); ok && remoteName != "" {
|
||||
if remoteName != "" {
|
||||
shellProc, err = shellexec.StartRemoteShellProc(rc.TermSize, cmdStr, cmdOpts, remoteName)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -309,7 +372,7 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts, blockMeta map[str
|
||||
messageCh := make(chan []byte, 32)
|
||||
ptyBuffer := wshutil.MakePtyBuffer(wshutil.WaveOSCPrefix, bc.ShellProc.Pty, messageCh)
|
||||
outputCh := make(chan []byte, 32)
|
||||
WshServerFactoryFn(messageCh, outputCh, wshutil.RpcContext{BlockId: bc.BlockId, TabId: bc.TabId})
|
||||
WshServerFactoryFn(messageCh, outputCh, wshrpc.RpcContext{BlockId: bc.BlockId, TabId: bc.TabId})
|
||||
go func() {
|
||||
// handles regular output from the pty (goes to the blockfile and xterm)
|
||||
defer func() {
|
||||
|
||||
+22
-22
@@ -20,7 +20,6 @@ import (
|
||||
"github.com/wavetermdev/thenextwave/pkg/wconfig"
|
||||
"github.com/wavetermdev/thenextwave/pkg/web/webcmd"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wshrpc"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wshrpc/wshserver"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wshutil"
|
||||
"github.com/wavetermdev/thenextwave/pkg/wstore"
|
||||
)
|
||||
@@ -61,10 +60,13 @@ var uiContextRType = reflect.TypeOf((*wstore.UIContext)(nil)).Elem()
|
||||
var waveObjRType = reflect.TypeOf((*waveobj.WaveObj)(nil)).Elem()
|
||||
var updatesRtnRType = reflect.TypeOf(wstore.UpdatesRtnType{})
|
||||
var orefRType = reflect.TypeOf((*waveobj.ORef)(nil)).Elem()
|
||||
var wshRpcInterfaceRType = reflect.TypeOf((*wshrpc.WshRpcInterface)(nil)).Elem()
|
||||
|
||||
func generateTSMethodTypes(method reflect.Method, tsTypesMap map[reflect.Type]string) error {
|
||||
for idx := 1; idx < method.Type.NumIn(); idx++ {
|
||||
// skip receiver
|
||||
func generateTSMethodTypes(method reflect.Method, tsTypesMap map[reflect.Type]string, skipFirstArg bool) error {
|
||||
for idx := 0; idx < method.Type.NumIn(); idx++ {
|
||||
if skipFirstArg && idx == 0 {
|
||||
continue
|
||||
}
|
||||
inType := method.Type.In(idx)
|
||||
GenerateTSType(inType, tsTypesMap)
|
||||
}
|
||||
@@ -159,14 +161,13 @@ var tsRenameMap = map[string]string{
|
||||
|
||||
func generateTSTypeInternal(rtype reflect.Type, tsTypesMap map[reflect.Type]string) (string, []reflect.Type) {
|
||||
var buf bytes.Buffer
|
||||
waveObjType := reflect.TypeOf((*waveobj.WaveObj)(nil)).Elem()
|
||||
tsTypeName := rtype.Name()
|
||||
if tsRename, ok := tsRenameMap[tsTypeName]; ok {
|
||||
tsTypeName = tsRename
|
||||
}
|
||||
var isWaveObj bool
|
||||
buf.WriteString(fmt.Sprintf("// %s\n", rtype.String()))
|
||||
if rtype.Implements(waveObjType) || reflect.PointerTo(rtype).Implements(waveObjType) {
|
||||
if rtype.Implements(waveObjRType) || reflect.PointerTo(rtype).Implements(waveObjRType) {
|
||||
isWaveObj = true
|
||||
buf.WriteString(fmt.Sprintf("type %s = WaveObj & {\n", tsTypeName))
|
||||
} else {
|
||||
@@ -253,6 +254,9 @@ func GenerateTSType(rtype reflect.Type, tsTypesMap map[reflect.Type]string) {
|
||||
if rtype == nil {
|
||||
return
|
||||
}
|
||||
if rtype.Kind() == reflect.Chan {
|
||||
rtype = rtype.Elem()
|
||||
}
|
||||
if rtype == metaRType {
|
||||
tsTypesMap[metaRType] = GenerateMetaType()
|
||||
return
|
||||
@@ -397,17 +401,17 @@ func GenerateServiceClass(serviceName string, serviceObj any, tsTypesMap map[ref
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func GenerateWshServerMethod(methodDecl *wshserver.WshServerMethodDecl, tsTypesMap map[reflect.Type]string) string {
|
||||
if methodDecl.CommandType == wshutil.RpcType_ResponseStream {
|
||||
func GenerateWshServerMethod(methodDecl *wshrpc.WshRpcMethodDecl, tsTypesMap map[reflect.Type]string) string {
|
||||
if methodDecl.CommandType == wshrpc.RpcType_ResponseStream {
|
||||
return GenerateWshServerMethod_ResponseStream(methodDecl, tsTypesMap)
|
||||
} else if methodDecl.CommandType == wshutil.RpcType_Call {
|
||||
} else if methodDecl.CommandType == wshrpc.RpcType_Call {
|
||||
return GenerateWshServerMethod_Call(methodDecl, tsTypesMap)
|
||||
} else {
|
||||
panic(fmt.Sprintf("cannot generate wshserver commandtype %q", methodDecl.CommandType))
|
||||
}
|
||||
}
|
||||
|
||||
func GenerateWshServerMethod_ResponseStream(methodDecl *wshserver.WshServerMethodDecl, tsTypesMap map[reflect.Type]string) string {
|
||||
func GenerateWshServerMethod_ResponseStream(methodDecl *wshrpc.WshRpcMethodDecl, tsTypesMap map[reflect.Type]string) string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString(fmt.Sprintf(" // command %q [%s]\n", methodDecl.Command, methodDecl.CommandType))
|
||||
respType := "any"
|
||||
@@ -429,7 +433,7 @@ func GenerateWshServerMethod_ResponseStream(methodDecl *wshserver.WshServerMetho
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func GenerateWshServerMethod_Call(methodDecl *wshserver.WshServerMethodDecl, tsTypesMap map[reflect.Type]string) string {
|
||||
func GenerateWshServerMethod_Call(methodDecl *wshrpc.WshRpcMethodDecl, tsTypesMap map[reflect.Type]string) string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString(fmt.Sprintf(" // command %q [%s]\n", methodDecl.Command, methodDecl.CommandType))
|
||||
rtnType := "Promise<void>"
|
||||
@@ -469,7 +473,7 @@ func GenerateServiceTypes(tsTypesMap map[reflect.Type]string) error {
|
||||
serviceType := reflect.TypeOf(serviceObj)
|
||||
for midx := 0; midx < serviceType.NumMethod(); midx++ {
|
||||
method := serviceType.Method(midx)
|
||||
err := generateTSMethodTypes(method, tsTypesMap)
|
||||
err := generateTSMethodTypes(method, tsTypesMap, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating TS method types for %s.%s: %v", serviceType, method.Name, err)
|
||||
}
|
||||
@@ -480,16 +484,12 @@ func GenerateServiceTypes(tsTypesMap map[reflect.Type]string) error {
|
||||
|
||||
func GenerateWshServerTypes(tsTypesMap map[reflect.Type]string) error {
|
||||
GenerateTSType(reflect.TypeOf(wshrpc.WshRpcCommandOpts{}), tsTypesMap)
|
||||
for _, methodDecl := range wshserver.WshServerCommandToDeclMap {
|
||||
GenerateTSType(methodDecl.CommandDataType, tsTypesMap)
|
||||
if methodDecl.DefaultResponseDataType != nil {
|
||||
GenerateTSType(methodDecl.DefaultResponseDataType, tsTypesMap)
|
||||
}
|
||||
for _, rtype := range methodDecl.RequestDataTypes {
|
||||
GenerateTSType(rtype, tsTypesMap)
|
||||
}
|
||||
for _, rtype := range methodDecl.ResponseDataTypes {
|
||||
GenerateTSType(rtype, tsTypesMap)
|
||||
rtype := wshRpcInterfaceRType
|
||||
for midx := 0; midx < rtype.NumMethod(); midx++ {
|
||||
method := rtype.Method(midx)
|
||||
err := generateTSMethodTypes(method, tsTypesMap, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating TS method types for %s.%s: %v", rtype, method.Name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -800,3 +800,29 @@ func MoveSliceIdxToFront[T any](arr []T, idx int) []T {
|
||||
rtn = append(rtn, arr[idx+1:]...)
|
||||
return rtn
|
||||
}
|
||||
|
||||
// matches a delimited string with a pattern string
|
||||
// the pattern string can contain "*" to match a single part, or "**" to match the rest of the string
|
||||
// note that "**" may only appear at the end of the string
|
||||
func StarMatchString(pattern string, s string, delimiter string) bool {
|
||||
patternParts := strings.Split(pattern, delimiter)
|
||||
stringParts := strings.Split(s, delimiter)
|
||||
pLen, sLen := len(patternParts), len(stringParts)
|
||||
|
||||
for i := 0; i < pLen; i++ {
|
||||
if patternParts[i] == "**" {
|
||||
// '**' must be at the end to be valid
|
||||
return i == pLen-1
|
||||
}
|
||||
if i >= sLen {
|
||||
// If string is exhausted but pattern is not
|
||||
return false
|
||||
}
|
||||
if patternParts[i] != "*" && patternParts[i] != stringParts[i] {
|
||||
// If current parts don't match and pattern part is not '*'
|
||||
return false
|
||||
}
|
||||
}
|
||||
// Check if both pattern and string are fully matched
|
||||
return pLen == sLen
|
||||
}
|
||||
|
||||
+41
-79
@@ -23,12 +23,6 @@ const OpenAIPacketStr = "openai"
|
||||
const OpenAICloudReqStr = "openai-cloudreq"
|
||||
const PacketEOFStr = "EOF"
|
||||
|
||||
type OpenAIUsageType struct {
|
||||
PromptTokens int `json:"prompt_tokens,omitempty"`
|
||||
CompletionTokens int `json:"completion_tokens,omitempty"`
|
||||
TotalTokens int `json:"total_tokens,omitempty"`
|
||||
}
|
||||
|
||||
type OpenAICmdInfoPacketOutputType struct {
|
||||
Model string `json:"model,omitempty"`
|
||||
Created int64 `json:"created,omitempty"`
|
||||
@@ -37,19 +31,8 @@ type OpenAICmdInfoPacketOutputType struct {
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type OpenAIPacketType struct {
|
||||
Type string `json:"type"`
|
||||
Model string `json:"model,omitempty"`
|
||||
Created int64 `json:"created,omitempty"`
|
||||
FinishReason string `json:"finish_reason,omitempty"`
|
||||
Usage *OpenAIUsageType `json:"usage,omitempty"`
|
||||
Index int `json:"index,omitempty"`
|
||||
Text string `json:"text,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func MakeOpenAIPacket() *OpenAIPacketType {
|
||||
return &OpenAIPacketType{Type: OpenAIPacketStr}
|
||||
func MakeOpenAIPacket() *wshrpc.OpenAIPacketType {
|
||||
return &wshrpc.OpenAIPacketType{Type: OpenAIPacketStr}
|
||||
}
|
||||
|
||||
type OpenAICmdInfoChatMessage struct {
|
||||
@@ -60,27 +43,12 @@ type OpenAICmdInfoChatMessage struct {
|
||||
UserEngineeredQuery string `json:"userengineeredquery,omitempty"`
|
||||
}
|
||||
|
||||
type OpenAIPromptMessageType struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
type OpenAICloudReqPacketType struct {
|
||||
Type string `json:"type"`
|
||||
ClientId string `json:"clientid"`
|
||||
Prompt []OpenAIPromptMessageType `json:"prompt"`
|
||||
MaxTokens int `json:"maxtokens,omitempty"`
|
||||
MaxChoices int `json:"maxchoices,omitempty"`
|
||||
}
|
||||
|
||||
type OpenAIOptsType struct {
|
||||
Model string `json:"model"`
|
||||
APIToken string `json:"apitoken"`
|
||||
BaseURL string `json:"baseurl,omitempty"`
|
||||
MaxTokens int `json:"maxtokens,omitempty"`
|
||||
MaxChoices int `json:"maxchoices,omitempty"`
|
||||
Timeout int `json:"timeout,omitempty"`
|
||||
Type string `json:"type"`
|
||||
ClientId string `json:"clientid"`
|
||||
Prompt []wshrpc.OpenAIPromptMessageType `json:"prompt"`
|
||||
MaxTokens int `json:"maxtokens,omitempty"`
|
||||
MaxChoices int `json:"maxchoices,omitempty"`
|
||||
}
|
||||
|
||||
func MakeOpenAICloudReqPacket() *OpenAICloudReqPacketType {
|
||||
@@ -89,12 +57,6 @@ func MakeOpenAICloudReqPacket() *OpenAICloudReqPacketType {
|
||||
}
|
||||
}
|
||||
|
||||
type OpenAiStreamRequest struct {
|
||||
ClientId string `json:"clientid,omitempty"`
|
||||
Opts *OpenAIOptsType `json:"opts"`
|
||||
Prompt []OpenAIPromptMessageType `json:"prompt"`
|
||||
}
|
||||
|
||||
func GetWSEndpoint() string {
|
||||
return PCloudWSEndpoint
|
||||
if !wavebase.IsDevMode() {
|
||||
@@ -116,18 +78,18 @@ const PCloudWSEndpointVarName = "PCLOUD_WS_ENDPOINT"
|
||||
|
||||
const CloudWebsocketConnectTimeout = 1 * time.Minute
|
||||
|
||||
func convertUsage(resp openaiapi.ChatCompletionResponse) *OpenAIUsageType {
|
||||
func convertUsage(resp openaiapi.ChatCompletionResponse) *wshrpc.OpenAIUsageType {
|
||||
if resp.Usage.TotalTokens == 0 {
|
||||
return nil
|
||||
}
|
||||
return &OpenAIUsageType{
|
||||
return &wshrpc.OpenAIUsageType{
|
||||
PromptTokens: resp.Usage.PromptTokens,
|
||||
CompletionTokens: resp.Usage.CompletionTokens,
|
||||
TotalTokens: resp.Usage.TotalTokens,
|
||||
}
|
||||
}
|
||||
|
||||
func ConvertPrompt(prompt []OpenAIPromptMessageType) []openaiapi.ChatCompletionMessage {
|
||||
func ConvertPrompt(prompt []wshrpc.OpenAIPromptMessageType) []openaiapi.ChatCompletionMessage {
|
||||
var rtn []openaiapi.ChatCompletionMessage
|
||||
for _, p := range prompt {
|
||||
msg := openaiapi.ChatCompletionMessage{Role: p.Role, Content: p.Content, Name: p.Name}
|
||||
@@ -136,31 +98,31 @@ func ConvertPrompt(prompt []OpenAIPromptMessageType) []openaiapi.ChatCompletionM
|
||||
return rtn
|
||||
}
|
||||
|
||||
func RunCloudCompletionStream(ctx context.Context, request OpenAiStreamRequest) chan wshrpc.RespOrErrorUnion[OpenAIPacketType] {
|
||||
rtn := make(chan wshrpc.RespOrErrorUnion[OpenAIPacketType])
|
||||
func RunCloudCompletionStream(ctx context.Context, request wshrpc.OpenAiStreamRequest) chan wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType] {
|
||||
rtn := make(chan wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType])
|
||||
go func() {
|
||||
log.Printf("start: %v", request)
|
||||
defer close(rtn)
|
||||
if request.Opts == nil {
|
||||
rtn <- wshrpc.RespOrErrorUnion[OpenAIPacketType]{Error: fmt.Errorf("no openai opts found")}
|
||||
rtn <- wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType]{Error: fmt.Errorf("no openai opts found")}
|
||||
return
|
||||
}
|
||||
websocketContext, dialCancelFn := context.WithTimeout(context.Background(), CloudWebsocketConnectTimeout)
|
||||
defer dialCancelFn()
|
||||
conn, _, err := websocket.DefaultDialer.DialContext(websocketContext, GetWSEndpoint(), nil)
|
||||
if err == context.DeadlineExceeded {
|
||||
rtn <- wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType]{Error: fmt.Errorf("OpenAI request, timed out connecting to cloud server: %v", err)}
|
||||
return
|
||||
} else if err != nil {
|
||||
rtn <- wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType]{Error: fmt.Errorf("OpenAI request, websocket connect error: %v", err)}
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
err = conn.Close()
|
||||
if err != nil {
|
||||
rtn <- wshrpc.RespOrErrorUnion[OpenAIPacketType]{Error: fmt.Errorf("unable to close openai channel: %v", err)}
|
||||
rtn <- wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType]{Error: fmt.Errorf("unable to close openai channel: %v", err)}
|
||||
}
|
||||
}()
|
||||
if err == context.DeadlineExceeded {
|
||||
rtn <- wshrpc.RespOrErrorUnion[OpenAIPacketType]{Error: fmt.Errorf("OpenAI request, timed out connecting to cloud server: %v", err)}
|
||||
return
|
||||
} else if err != nil {
|
||||
rtn <- wshrpc.RespOrErrorUnion[OpenAIPacketType]{Error: fmt.Errorf("OpenAI request, websocket connect error: %v", err)}
|
||||
return
|
||||
}
|
||||
reqPk := MakeOpenAICloudReqPacket()
|
||||
reqPk.ClientId = request.ClientId
|
||||
reqPk.Prompt = request.Prompt
|
||||
@@ -168,12 +130,12 @@ func RunCloudCompletionStream(ctx context.Context, request OpenAiStreamRequest)
|
||||
reqPk.MaxChoices = request.Opts.MaxChoices
|
||||
configMessageBuf, err := json.Marshal(reqPk)
|
||||
if err != nil {
|
||||
rtn <- wshrpc.RespOrErrorUnion[OpenAIPacketType]{Error: fmt.Errorf("OpenAI request, packet marshal error: %v", err)}
|
||||
rtn <- wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType]{Error: fmt.Errorf("OpenAI request, packet marshal error: %v", err)}
|
||||
return
|
||||
}
|
||||
err = conn.WriteMessage(websocket.TextMessage, configMessageBuf)
|
||||
if err != nil {
|
||||
rtn <- wshrpc.RespOrErrorUnion[OpenAIPacketType]{Error: fmt.Errorf("OpenAI request, websocket write config error: %v", err)}
|
||||
rtn <- wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType]{Error: fmt.Errorf("OpenAI request, websocket write config error: %v", err)}
|
||||
return
|
||||
}
|
||||
for {
|
||||
@@ -184,14 +146,14 @@ func RunCloudCompletionStream(ctx context.Context, request OpenAiStreamRequest)
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("err received: %v", err)
|
||||
rtn <- wshrpc.RespOrErrorUnion[OpenAIPacketType]{Error: fmt.Errorf("OpenAI request, websocket error reading message: %v", err)}
|
||||
rtn <- wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType]{Error: fmt.Errorf("OpenAI request, websocket error reading message: %v", err)}
|
||||
break
|
||||
}
|
||||
var streamResp *OpenAIPacketType
|
||||
var streamResp *wshrpc.OpenAIPacketType
|
||||
err = json.Unmarshal(socketMessage, &streamResp)
|
||||
log.Printf("ai resp: %v", streamResp)
|
||||
if err != nil {
|
||||
rtn <- wshrpc.RespOrErrorUnion[OpenAIPacketType]{Error: fmt.Errorf("OpenAI request, websocket response json decode error: %v", err)}
|
||||
rtn <- wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType]{Error: fmt.Errorf("OpenAI request, websocket response json decode error: %v", err)}
|
||||
break
|
||||
}
|
||||
if streamResp.Error == PacketEOFStr {
|
||||
@@ -199,30 +161,30 @@ func RunCloudCompletionStream(ctx context.Context, request OpenAiStreamRequest)
|
||||
break
|
||||
} else if streamResp.Error != "" {
|
||||
// use error from server directly
|
||||
rtn <- wshrpc.RespOrErrorUnion[OpenAIPacketType]{Error: fmt.Errorf("%v", streamResp.Error)}
|
||||
rtn <- wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType]{Error: fmt.Errorf("%v", streamResp.Error)}
|
||||
break
|
||||
}
|
||||
rtn <- wshrpc.RespOrErrorUnion[OpenAIPacketType]{Response: *streamResp}
|
||||
rtn <- wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType]{Response: *streamResp}
|
||||
}
|
||||
}()
|
||||
return rtn
|
||||
}
|
||||
|
||||
func RunLocalCompletionStream(ctx context.Context, request OpenAiStreamRequest) chan wshrpc.RespOrErrorUnion[OpenAIPacketType] {
|
||||
rtn := make(chan wshrpc.RespOrErrorUnion[OpenAIPacketType])
|
||||
func RunLocalCompletionStream(ctx context.Context, request wshrpc.OpenAiStreamRequest) chan wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType] {
|
||||
rtn := make(chan wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType])
|
||||
go func() {
|
||||
log.Printf("start2: %v", request)
|
||||
defer close(rtn)
|
||||
if request.Opts == nil {
|
||||
rtn <- wshrpc.RespOrErrorUnion[OpenAIPacketType]{Error: fmt.Errorf("no openai opts found")}
|
||||
rtn <- wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType]{Error: fmt.Errorf("no openai opts found")}
|
||||
return
|
||||
}
|
||||
if request.Opts.Model == "" {
|
||||
rtn <- wshrpc.RespOrErrorUnion[OpenAIPacketType]{Error: fmt.Errorf("no openai model specified")}
|
||||
rtn <- wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType]{Error: fmt.Errorf("no openai model specified")}
|
||||
return
|
||||
}
|
||||
if request.Opts.BaseURL == "" && request.Opts.APIToken == "" {
|
||||
rtn <- wshrpc.RespOrErrorUnion[OpenAIPacketType]{Error: fmt.Errorf("no api token")}
|
||||
rtn <- wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType]{Error: fmt.Errorf("no api token")}
|
||||
return
|
||||
}
|
||||
clientConfig := openaiapi.DefaultConfig(request.Opts.APIToken)
|
||||
@@ -241,7 +203,7 @@ func RunLocalCompletionStream(ctx context.Context, request OpenAiStreamRequest)
|
||||
}
|
||||
apiResp, err := client.CreateChatCompletionStream(ctx, req)
|
||||
if err != nil {
|
||||
rtn <- wshrpc.RespOrErrorUnion[OpenAIPacketType]{Error: fmt.Errorf("error calling openai API: %v", err)}
|
||||
rtn <- wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType]{Error: fmt.Errorf("error calling openai API: %v", err)}
|
||||
return
|
||||
}
|
||||
sentHeader := false
|
||||
@@ -253,14 +215,14 @@ func RunLocalCompletionStream(ctx context.Context, request OpenAiStreamRequest)
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("err received2: %v", err)
|
||||
rtn <- wshrpc.RespOrErrorUnion[OpenAIPacketType]{Error: fmt.Errorf("OpenAI request, websocket error reading message: %v", err)}
|
||||
rtn <- wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType]{Error: fmt.Errorf("OpenAI request, websocket error reading message: %v", err)}
|
||||
break
|
||||
}
|
||||
if streamResp.Model != "" && !sentHeader {
|
||||
pk := MakeOpenAIPacket()
|
||||
pk.Model = streamResp.Model
|
||||
pk.Created = streamResp.Created
|
||||
rtn <- wshrpc.RespOrErrorUnion[OpenAIPacketType]{Response: *pk}
|
||||
rtn <- wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType]{Response: *pk}
|
||||
sentHeader = true
|
||||
}
|
||||
for _, choice := range streamResp.Choices {
|
||||
@@ -268,15 +230,15 @@ func RunLocalCompletionStream(ctx context.Context, request OpenAiStreamRequest)
|
||||
pk.Index = choice.Index
|
||||
pk.Text = choice.Delta.Content
|
||||
pk.FinishReason = string(choice.FinishReason)
|
||||
rtn <- wshrpc.RespOrErrorUnion[OpenAIPacketType]{Response: *pk}
|
||||
rtn <- wshrpc.RespOrErrorUnion[wshrpc.OpenAIPacketType]{Response: *pk}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return rtn
|
||||
}
|
||||
|
||||
func marshalResponse(resp openaiapi.ChatCompletionResponse) []*OpenAIPacketType {
|
||||
var rtn []*OpenAIPacketType
|
||||
func marshalResponse(resp openaiapi.ChatCompletionResponse) []*wshrpc.OpenAIPacketType {
|
||||
var rtn []*wshrpc.OpenAIPacketType
|
||||
headerPk := MakeOpenAIPacket()
|
||||
headerPk.Model = resp.Model
|
||||
headerPk.Created = resp.Created
|
||||
@@ -292,14 +254,14 @@ func marshalResponse(resp openaiapi.ChatCompletionResponse) []*OpenAIPacketType
|
||||
return rtn
|
||||
}
|
||||
|
||||
func CreateErrorPacket(errStr string) *OpenAIPacketType {
|
||||
func CreateErrorPacket(errStr string) *wshrpc.OpenAIPacketType {
|
||||
errPk := MakeOpenAIPacket()
|
||||
errPk.FinishReason = "error"
|
||||
errPk.Error = errStr
|
||||
return errPk
|
||||
}
|
||||
|
||||
func CreateTextPacket(text string) *OpenAIPacketType {
|
||||
func CreateTextPacket(text string) *wshrpc.OpenAIPacketType {
|
||||
pk := MakeOpenAIPacket()
|
||||
pk.Text = text
|
||||
return pk
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user