mirror of
https://github.com/wavetermdev/waveterm
synced 2026-04-21 14:37:16 +00:00
fix tsunami scaffold in build (#2564)
This commit is contained in:
parent
1a190da638
commit
3b97084471
20 changed files with 145 additions and 129 deletions
|
|
@ -536,6 +536,7 @@ tasks:
|
|||
- mkdir -p scaffold
|
||||
- cp ../templates/package.json.tmpl scaffold/package.json
|
||||
- cd scaffold && npm install
|
||||
- mv scaffold/node_modules scaffold/nm
|
||||
- cp -r dist scaffold/
|
||||
- mkdir -p scaffold/dist/tw
|
||||
- cp ../templates/app-main.go.tmpl scaffold/app-main.go
|
||||
|
|
@ -556,6 +557,7 @@ tasks:
|
|||
- powershell New-Item -ItemType Directory -Force -Path scaffold
|
||||
- powershell Copy-Item -Path ../templates/package.json.tmpl -Destination scaffold/package.json
|
||||
- powershell -Command "Set-Location scaffold; npm install"
|
||||
- powershell Move-Item -Path scaffold/node_modules -Destination scaffold/nm
|
||||
- powershell Copy-Item -Recurse -Force -Path dist -Destination scaffold/
|
||||
- powershell New-Item -ItemType Directory -Force -Path scaffold/dist/tw
|
||||
- powershell Copy-Item -Path ../templates/app-main.go.tmpl -Destination scaffold/app-main.go
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ Patch release with Wave AI model upgrade, new secret management features, and im
|
|||
|
||||
**Wave AI Updates:**
|
||||
- **GPT-5.1 Model** - Upgraded to use OpenAI's GPT-5.1 model for improved responses
|
||||
- **Thinking Mode Toggle** - New dropdown to select between Quick, Balanced, and Deep thinking modes for optimal response quality vs speed
|
||||
- [bugfix] Fixed path mismatch issue when restoring AI write file backups
|
||||
|
||||
**New Features:**
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const config = {
|
|||
{
|
||||
from: "./dist",
|
||||
to: "./dist",
|
||||
filter: ["**/*", "!bin/*", "bin/wavesrv.${arch}*", "bin/wsh*"],
|
||||
filter: ["**/*", "!bin/*", "bin/wavesrv.${arch}*", "bin/wsh*", "!tsunamiscaffold/**/*"],
|
||||
},
|
||||
{
|
||||
from: ".",
|
||||
|
|
@ -31,13 +31,18 @@ const config = {
|
|||
},
|
||||
"!node_modules", // We don't need electron-builder to package in Node modules as Vite has already bundled any code that our program is using.
|
||||
],
|
||||
extraResources: [
|
||||
{
|
||||
from: "dist/tsunamiscaffold",
|
||||
to: "tsunamiscaffold",
|
||||
},
|
||||
],
|
||||
directories: {
|
||||
output: "make",
|
||||
},
|
||||
asarUnpack: [
|
||||
"dist/bin/**/*", // wavesrv and wsh binaries
|
||||
"dist/schema/**/*", // schema files for Monaco editor
|
||||
"dist/tsunamiscaffold/**/*", // tsunami scaffold files
|
||||
],
|
||||
mac: {
|
||||
target: [
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ function makeEditMenu(): Electron.MenuItemConstructorOptions[] {
|
|||
];
|
||||
}
|
||||
|
||||
function makeFileMenu(numWaveWindows: number, callbacks: AppMenuCallbacks): Electron.MenuItemConstructorOptions[] {
|
||||
function makeFileMenu(numWaveWindows: number, callbacks: AppMenuCallbacks, fullConfig: FullConfigType): Electron.MenuItemConstructorOptions[] {
|
||||
const fileMenu: Electron.MenuItemConstructorOptions[] = [
|
||||
{
|
||||
label: "New Window",
|
||||
|
|
@ -125,7 +125,8 @@ function makeFileMenu(numWaveWindows: number, callbacks: AppMenuCallbacks): Elec
|
|||
},
|
||||
},
|
||||
];
|
||||
if (isDev) {
|
||||
const featureWaveAppBuilder = fullConfig?.settings?.["feature:waveappbuilder"];
|
||||
if (isDev || featureWaveAppBuilder) {
|
||||
fileMenu.splice(1, 0, {
|
||||
label: "New WaveApp Builder Window",
|
||||
accelerator: unamePlatform === "darwin" ? "Command+Shift+B" : "Alt+Shift+B",
|
||||
|
|
@ -310,18 +311,19 @@ function makeViewMenu(
|
|||
async function makeFullAppMenu(callbacks: AppMenuCallbacks, workspaceOrBuilderId?: string): Promise<Electron.Menu> {
|
||||
const numWaveWindows = getAllWaveWindows().length;
|
||||
const webContents = workspaceOrBuilderId && getWebContentsByWorkspaceOrBuilderId(workspaceOrBuilderId);
|
||||
const fileMenu = makeFileMenu(numWaveWindows, callbacks);
|
||||
const appMenuItems = makeAppMenuItems(webContents);
|
||||
const editMenu = makeEditMenu();
|
||||
|
||||
const isBuilderWindowFocused = focusedBuilderWindow != null;
|
||||
let fullscreenOnLaunch = false;
|
||||
let fullConfig: FullConfigType = null;
|
||||
try {
|
||||
const fullConfig = await RpcApi.GetFullConfigCommand(ElectronWshClient);
|
||||
fullConfig = await RpcApi.GetFullConfigCommand(ElectronWshClient);
|
||||
fullscreenOnLaunch = fullConfig?.settings["window:fullscreenonlaunch"];
|
||||
} catch (e) {
|
||||
console.error("Error fetching fullscreen launch config:", e);
|
||||
console.error("Error fetching config:", e);
|
||||
}
|
||||
const fileMenu = makeFileMenu(numWaveWindows, callbacks, fullConfig);
|
||||
const viewMenu = makeViewMenu(webContents, callbacks, isBuilderWindowFocused, fullscreenOnLaunch);
|
||||
let workspaceMenu: Electron.MenuItemConstructorOptions[] = null;
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@ function getWaveDataDir(): string {
|
|||
}
|
||||
|
||||
function getElectronAppBasePath(): string {
|
||||
// import.meta.dirname in dev points to waveterm/dist/main
|
||||
return path.dirname(import.meta.dirname);
|
||||
}
|
||||
|
||||
|
|
@ -156,6 +157,14 @@ function getElectronAppUnpackedBasePath(): string {
|
|||
return getElectronAppBasePath().replace("app.asar", "app.asar.unpacked");
|
||||
}
|
||||
|
||||
function getElectronAppResourcesPath(): string {
|
||||
if (isDev) {
|
||||
// import.meta.dirname in dev points to waveterm/dist/main
|
||||
return path.dirname(import.meta.dirname);
|
||||
}
|
||||
return process.resourcesPath;
|
||||
}
|
||||
|
||||
const wavesrvBinName = `wavesrv.${unameArch}`;
|
||||
|
||||
function getWaveSrvPath(): string {
|
||||
|
|
@ -261,6 +270,7 @@ export {
|
|||
callWithOriginalXdgCurrentDesktop,
|
||||
callWithOriginalXdgCurrentDesktopAsync,
|
||||
getElectronAppBasePath,
|
||||
getElectronAppResourcesPath,
|
||||
getElectronAppUnpackedBasePath,
|
||||
getWaveConfigDir,
|
||||
getWaveDataDir,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import * as electron from "electron";
|
|||
import { getWebServerEndpoint } from "../frontend/util/endpoints";
|
||||
|
||||
export const WaveAppPathVarName = "WAVETERM_APP_PATH";
|
||||
export const WaveAppResourcesPathVarName = "WAVETERM_RESOURCES_PATH";
|
||||
export const WaveAppElectronExecPath = "WAVETERM_ELECTRONEXECPATH";
|
||||
|
||||
export function getElectronExecPath(): string {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { WebServerEndpointVarName, WSServerEndpointVarName } from "../frontend/u
|
|||
import { AuthKey, WaveAuthKeyEnv } from "./authkey";
|
||||
import { setForceQuit } from "./emain-activity";
|
||||
import {
|
||||
getElectronAppResourcesPath,
|
||||
getElectronAppUnpackedBasePath,
|
||||
getWaveConfigDir,
|
||||
getWaveDataDir,
|
||||
|
|
@ -17,7 +18,12 @@ import {
|
|||
WaveConfigHomeVarName,
|
||||
WaveDataHomeVarName,
|
||||
} from "./emain-platform";
|
||||
import { getElectronExecPath, WaveAppElectronExecPath, WaveAppPathVarName } from "./emain-util";
|
||||
import {
|
||||
getElectronExecPath,
|
||||
WaveAppElectronExecPath,
|
||||
WaveAppPathVarName,
|
||||
WaveAppResourcesPathVarName,
|
||||
} from "./emain-util";
|
||||
import { updater } from "./updater";
|
||||
|
||||
let isWaveSrvDead = false;
|
||||
|
|
@ -59,6 +65,7 @@ export function runWaveSrv(handleWSEvent: (evtMsg: WSEventType) => void): Promis
|
|||
envCopy["XDG_CURRENT_DESKTOP"] = xdgCurrentDesktop;
|
||||
}
|
||||
envCopy[WaveAppPathVarName] = getElectronAppUnpackedBasePath();
|
||||
envCopy[WaveAppResourcesPathVarName] = getElectronAppResourcesPath();
|
||||
envCopy[WaveAppElectronExecPath] = getElectronExecPath();
|
||||
envCopy[WaveAuthKeyEnv] = AuthKey;
|
||||
envCopy[WaveDataHomeVarName] = getWaveDataDir();
|
||||
|
|
|
|||
|
|
@ -62,14 +62,8 @@ class TsunamiViewModel extends WebViewModel {
|
|||
oref: WOS.makeORef("block", blockId),
|
||||
});
|
||||
initialRTInfo.then((rtInfo) => {
|
||||
if (rtInfo) {
|
||||
const meta: AppMeta = {
|
||||
title: rtInfo["tsunami:title"],
|
||||
shortdesc: rtInfo["tsunami:shortdesc"],
|
||||
icon: rtInfo["tsunami:icon"],
|
||||
iconcolor: rtInfo["tsunami:iconcolor"],
|
||||
};
|
||||
globalStore.set(this.appMeta, meta);
|
||||
if (rtInfo && rtInfo["tsunami:appmeta"]) {
|
||||
globalStore.set(this.appMeta, rtInfo["tsunami:appmeta"]);
|
||||
}
|
||||
});
|
||||
this.appMetaUnsubFn = waveEventSubscribe({
|
||||
|
|
|
|||
|
|
@ -229,6 +229,7 @@ const Widgets = memo(() => {
|
|||
magnified: true,
|
||||
};
|
||||
const showHelp = fullConfig?.settings?.["widget:showhelp"] ?? true;
|
||||
const featureWaveAppBuilder = fullConfig?.settings?.["feature:waveappbuilder"] ?? false;
|
||||
const widgetsMap = fullConfig?.widgets ?? {};
|
||||
const filteredWidgets = hasCustomAIPresets
|
||||
? widgetsMap
|
||||
|
|
@ -342,9 +343,9 @@ const Widgets = memo(() => {
|
|||
))}
|
||||
</div>
|
||||
<div className="flex-grow" />
|
||||
{isDev() || showHelp ? (
|
||||
{isDev() || featureWaveAppBuilder || showHelp ? (
|
||||
<div className="grid grid-cols-2 gap-0 w-full">
|
||||
{isDev() ? (
|
||||
{isDev() || featureWaveAppBuilder ? (
|
||||
<div
|
||||
ref={appsButtonRef}
|
||||
className="flex flex-col justify-center items-center w-full py-1.5 pr-0.5 text-secondary text-sm overflow-hidden rounded-sm hover:bg-hoverbg hover:text-white cursor-pointer"
|
||||
|
|
@ -372,7 +373,7 @@ const Widgets = memo(() => {
|
|||
<Widget key={`widget-${idx}`} widget={data} mode={mode} />
|
||||
))}
|
||||
<div className="flex-grow" />
|
||||
{isDev() ? (
|
||||
{isDev() || featureWaveAppBuilder ? (
|
||||
<div
|
||||
ref={appsButtonRef}
|
||||
className="flex flex-col justify-center items-center w-full py-1.5 pr-0.5 text-secondary text-lg overflow-hidden rounded-sm hover:bg-hoverbg hover:text-white cursor-pointer"
|
||||
|
|
@ -407,7 +408,7 @@ const Widgets = memo(() => {
|
|||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
{isDev() && appsButtonRef.current && (
|
||||
{(isDev() || featureWaveAppBuilder) && appsButtonRef.current && (
|
||||
<AppsFloatingWindow
|
||||
isOpen={isAppsOpen}
|
||||
onClose={() => setIsAppsOpen(false)}
|
||||
|
|
|
|||
6
frontend/types/gotypes.d.ts
vendored
6
frontend/types/gotypes.d.ts
vendored
|
|
@ -914,10 +914,7 @@ declare global {
|
|||
|
||||
// waveobj.ObjRTInfo
|
||||
type ObjRTInfo = {
|
||||
"tsunami:title"?: string;
|
||||
"tsunami:shortdesc"?: string;
|
||||
"tsunami:icon"?: string;
|
||||
"tsunami:iconcolor"?: string;
|
||||
"tsunami:appmeta"?: AppMeta;
|
||||
"tsunami:schemas"?: any;
|
||||
"shell:hascurcwd"?: boolean;
|
||||
"shell:state"?: string;
|
||||
|
|
@ -1030,6 +1027,7 @@ declare global {
|
|||
"app:dismissarchitecturewarning"?: boolean;
|
||||
"app:defaultnewblock"?: string;
|
||||
"app:showoverlayblocknums"?: boolean;
|
||||
"feature:waveappbuilder"?: boolean;
|
||||
"ai:*"?: boolean;
|
||||
"ai:preset"?: string;
|
||||
"ai:apitype"?: string;
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "waveterm",
|
||||
"version": "0.12.2",
|
||||
"version": "0.12.3-beta.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "waveterm",
|
||||
"version": "0.12.2",
|
||||
"version": "0.12.3-beta.1",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"workspaces": [
|
||||
|
|
|
|||
|
|
@ -13,10 +13,23 @@ import (
|
|||
|
||||
"github.com/wavetermdev/waveterm/pkg/aiusechat/uctypes"
|
||||
"github.com/wavetermdev/waveterm/pkg/blockcontroller"
|
||||
"github.com/wavetermdev/waveterm/pkg/util/utilfn"
|
||||
"github.com/wavetermdev/waveterm/pkg/waveobj"
|
||||
"github.com/wavetermdev/waveterm/pkg/wshrpc"
|
||||
"github.com/wavetermdev/waveterm/pkg/wstore"
|
||||
)
|
||||
|
||||
func getTsunamiShortDesc(rtInfo *waveobj.ObjRTInfo) string {
|
||||
if rtInfo == nil || rtInfo.TsunamiAppMeta == nil {
|
||||
return ""
|
||||
}
|
||||
var appMeta wshrpc.AppMeta
|
||||
if err := utilfn.ReUnmarshal(&appMeta, rtInfo.TsunamiAppMeta); err == nil && appMeta.ShortDesc != "" {
|
||||
return appMeta.ShortDesc
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func handleTsunamiBlockDesc(block *waveobj.Block) string {
|
||||
status := blockcontroller.GetBlockControllerRuntimeStatus(block.OID)
|
||||
if status == nil || status.ShellProcStatus != blockcontroller.Status_Running {
|
||||
|
|
@ -25,8 +38,8 @@ func handleTsunamiBlockDesc(block *waveobj.Block) string {
|
|||
|
||||
blockORef := waveobj.MakeORef(waveobj.OType_Block, block.OID)
|
||||
rtInfo := wstore.GetRTInfo(blockORef)
|
||||
if rtInfo != nil && rtInfo.TsunamiShortDesc != "" {
|
||||
return fmt.Sprintf("tsunami widget - %s", rtInfo.TsunamiShortDesc)
|
||||
if shortDesc := getTsunamiShortDesc(rtInfo); shortDesc != "" {
|
||||
return fmt.Sprintf("tsunami widget - %s", shortDesc)
|
||||
}
|
||||
return "tsunami widget - unknown description"
|
||||
}
|
||||
|
|
@ -111,8 +124,8 @@ func GetTsunamiGetDataToolDefinition(block *waveobj.Block, rtInfo *waveobj.ObjRT
|
|||
toolName := fmt.Sprintf("tsunami_getdata_%s", blockIdPrefix)
|
||||
|
||||
desc := "tsunami widget"
|
||||
if rtInfo != nil && rtInfo.TsunamiShortDesc != "" {
|
||||
desc = rtInfo.TsunamiShortDesc
|
||||
if shortDesc := getTsunamiShortDesc(rtInfo); shortDesc != "" {
|
||||
desc = shortDesc
|
||||
}
|
||||
|
||||
return &uctypes.ToolDefinition{
|
||||
|
|
@ -136,8 +149,8 @@ func GetTsunamiGetConfigToolDefinition(block *waveobj.Block, rtInfo *waveobj.Obj
|
|||
toolName := fmt.Sprintf("tsunami_getconfig_%s", blockIdPrefix)
|
||||
|
||||
desc := "tsunami widget"
|
||||
if rtInfo != nil && rtInfo.TsunamiShortDesc != "" {
|
||||
desc = rtInfo.TsunamiShortDesc
|
||||
if shortDesc := getTsunamiShortDesc(rtInfo); shortDesc != "" {
|
||||
desc = shortDesc
|
||||
}
|
||||
|
||||
return &uctypes.ToolDefinition{
|
||||
|
|
@ -174,8 +187,8 @@ func GetTsunamiSetConfigToolDefinition(block *waveobj.Block, rtInfo *waveobj.Obj
|
|||
}
|
||||
|
||||
desc := "tsunami widget"
|
||||
if rtInfo != nil && rtInfo.TsunamiShortDesc != "" {
|
||||
desc = rtInfo.TsunamiShortDesc
|
||||
if shortDesc := getTsunamiShortDesc(rtInfo); shortDesc != "" {
|
||||
desc = shortDesc
|
||||
}
|
||||
|
||||
return &uctypes.ToolDefinition{
|
||||
|
|
|
|||
|
|
@ -5,18 +5,15 @@ package blockcontroller
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/wavetermdev/waveterm/pkg/tsunamiutil"
|
||||
"github.com/wavetermdev/waveterm/pkg/utilds"
|
||||
|
|
@ -51,37 +48,31 @@ type TsunamiController struct {
|
|||
port int
|
||||
}
|
||||
|
||||
|
||||
func (c *TsunamiController) fetchAndSetSchemas(port int) {
|
||||
url := fmt.Sprintf("http://localhost:%d/api/schemas", port)
|
||||
client := &http.Client{
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
resp, err := client.Get(url)
|
||||
func (c *TsunamiController) setManifestMetadata(appId string) {
|
||||
manifest, err := waveappstore.ReadAppManifest(appId)
|
||||
if err != nil {
|
||||
log.Printf("TsunamiController: failed to fetch schemas from %s: %v", url, err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
log.Printf("TsunamiController: received non-200 status %d from %s", resp.StatusCode, url)
|
||||
return
|
||||
}
|
||||
|
||||
var schemas any
|
||||
if err := json.NewDecoder(resp.Body).Decode(&schemas); err != nil {
|
||||
log.Printf("TsunamiController: failed to decode schemas response: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
blockRef := waveobj.MakeORef(waveobj.OType_Block, c.blockId)
|
||||
wstore.SetRTInfo(blockRef, map[string]any{
|
||||
"tsunami:schemas": schemas,
|
||||
rtInfo := make(map[string]any)
|
||||
rtInfo["tsunami:appmeta"] = manifest.AppMeta
|
||||
if manifest.ConfigSchema != nil || manifest.DataSchema != nil {
|
||||
schemas := make(map[string]any)
|
||||
if manifest.ConfigSchema != nil {
|
||||
schemas["config"] = manifest.ConfigSchema
|
||||
}
|
||||
if manifest.DataSchema != nil {
|
||||
schemas["data"] = manifest.DataSchema
|
||||
}
|
||||
rtInfo["tsunami:schemas"] = schemas
|
||||
}
|
||||
wstore.SetRTInfo(blockRef, rtInfo)
|
||||
wps.Broker.Publish(wps.WaveEvent{
|
||||
Event: wps.Event_TsunamiUpdateMeta,
|
||||
Scopes: []string{waveobj.MakeORef(waveobj.OType_Block, c.blockId).String()},
|
||||
Data: manifest.AppMeta,
|
||||
})
|
||||
|
||||
log.Printf("TsunamiController: successfully fetched and cached schemas for block %s", c.blockId)
|
||||
}
|
||||
|
||||
func (c *TsunamiController) clearSchemas() {
|
||||
|
|
@ -92,7 +83,6 @@ func (c *TsunamiController) clearSchemas() {
|
|||
log.Printf("TsunamiController: cleared schemas for block %s", c.blockId)
|
||||
}
|
||||
|
||||
|
||||
func isBuildCacheUpToDate(appPath string) (bool, error) {
|
||||
appName := build.GetAppName(appPath)
|
||||
|
||||
|
|
@ -136,7 +126,7 @@ func (c *TsunamiController) Start(ctx context.Context, blockMeta waveobj.MetaMap
|
|||
|
||||
appPath := blockMeta.GetString(waveobj.MetaKey_TsunamiAppPath, "")
|
||||
appId := blockMeta.GetString(waveobj.MetaKey_TsunamiAppId, "")
|
||||
|
||||
|
||||
if appPath == "" {
|
||||
if appId == "" {
|
||||
return fmt.Errorf("tsunami:apppath or tsunami:appid is required")
|
||||
|
|
@ -157,32 +147,8 @@ func (c *TsunamiController) Start(ctx context.Context, blockMeta waveobj.MetaMap
|
|||
}
|
||||
}
|
||||
|
||||
// Read and set app metadata from manifest if appId is available
|
||||
if appId != "" {
|
||||
if manifest, err := waveappstore.ReadAppManifest(appId); err == nil {
|
||||
blockRef := waveobj.MakeORef(waveobj.OType_Block, c.blockId)
|
||||
rtInfo := make(map[string]any)
|
||||
if manifest.AppMeta.Title != "" {
|
||||
rtInfo["tsunami:title"] = manifest.AppMeta.Title
|
||||
}
|
||||
if manifest.AppMeta.ShortDesc != "" {
|
||||
rtInfo["tsunami:shortdesc"] = manifest.AppMeta.ShortDesc
|
||||
}
|
||||
if manifest.AppMeta.Icon != "" {
|
||||
rtInfo["tsunami:icon"] = manifest.AppMeta.Icon
|
||||
}
|
||||
if manifest.AppMeta.IconColor != "" {
|
||||
rtInfo["tsunami:iconcolor"] = manifest.AppMeta.IconColor
|
||||
}
|
||||
if len(rtInfo) > 0 {
|
||||
wstore.SetRTInfo(blockRef, rtInfo)
|
||||
wps.Broker.Publish(wps.WaveEvent{
|
||||
Event: wps.Event_TsunamiUpdateMeta,
|
||||
Scopes: []string{waveobj.MakeORef(waveobj.OType_Block, c.blockId).String()},
|
||||
Data: manifest.AppMeta,
|
||||
})
|
||||
}
|
||||
}
|
||||
c.setManifestMetadata(appId)
|
||||
}
|
||||
|
||||
appName := build.GetAppName(appPath)
|
||||
|
|
@ -220,6 +186,7 @@ func (c *TsunamiController) Start(ctx context.Context, blockMeta waveobj.MetaMap
|
|||
err = build.TsunamiBuild(opts)
|
||||
if err != nil {
|
||||
log.Printf("TsunamiController build error for block %s: %v", c.blockId, err)
|
||||
log.Printf("BuildOpts %#v\n", opts)
|
||||
return fmt.Errorf("failed to build tsunami app: %w", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -248,11 +215,6 @@ func (c *TsunamiController) Start(ctx context.Context, blockMeta waveobj.MetaMap
|
|||
})
|
||||
go c.sendStatusUpdate()
|
||||
|
||||
// Asynchronously fetch schemas after port is detected
|
||||
go func() {
|
||||
c.fetchAndSetSchemas(tsunamiProc.Port)
|
||||
}()
|
||||
|
||||
// Monitor process completion
|
||||
go func() {
|
||||
<-tsunamiProc.WaitCh
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ func GetTsunamiScaffoldPath() string {
|
|||
settings := wconfig.GetWatcher().GetFullConfig().Settings
|
||||
scaffoldPath := settings.TsunamiScaffoldPath
|
||||
if scaffoldPath == "" {
|
||||
scaffoldPath = filepath.Join(wavebase.GetWaveAppPath(), "tsunamiscaffold")
|
||||
scaffoldPath = filepath.Join(wavebase.GetWaveAppResourcesPath(), "tsunamiscaffold")
|
||||
}
|
||||
return scaffoldPath
|
||||
}
|
||||
|
|
@ -76,4 +76,4 @@ func FormatGoCode(contents []byte) []byte {
|
|||
}
|
||||
|
||||
return formattedOutput
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ const (
|
|||
WaveConfigHomeEnvVar = "WAVETERM_CONFIG_HOME"
|
||||
WaveDataHomeEnvVar = "WAVETERM_DATA_HOME"
|
||||
WaveAppPathVarName = "WAVETERM_APP_PATH"
|
||||
WaveAppResourcesPathVarName = "WAVETERM_RESOURCES_PATH"
|
||||
WaveAppElectronExecPathVarName = "WAVETERM_ELECTRONEXECPATH"
|
||||
WaveDevVarName = "WAVETERM_DEV"
|
||||
WaveDevViteVarName = "WAVETERM_DEV_VITE"
|
||||
|
|
@ -50,6 +51,7 @@ const NeedJwtConst = "NEED-JWT"
|
|||
var ConfigHome_VarCache string // caches WAVETERM_CONFIG_HOME
|
||||
var DataHome_VarCache string // caches WAVETERM_DATA_HOME
|
||||
var AppPath_VarCache string // caches WAVETERM_APP_PATH
|
||||
var AppResourcesPath_VarCache string // caches WAVETERM_RESOURCES_PATH
|
||||
var AppElectronExecPath_VarCache string // caches WAVETERM_ELECTRONEXECPATH
|
||||
var Dev_VarCache string // caches WAVETERM_DEV
|
||||
|
||||
|
|
@ -98,6 +100,8 @@ func CacheAndRemoveEnvVars() error {
|
|||
os.Unsetenv(WaveDataHomeEnvVar)
|
||||
AppPath_VarCache = os.Getenv(WaveAppPathVarName)
|
||||
os.Unsetenv(WaveAppPathVarName)
|
||||
AppResourcesPath_VarCache = os.Getenv(WaveAppResourcesPathVarName)
|
||||
os.Unsetenv(WaveAppResourcesPathVarName)
|
||||
AppElectronExecPath_VarCache = os.Getenv(WaveAppElectronExecPathVarName)
|
||||
os.Unsetenv(WaveAppElectronExecPathVarName)
|
||||
Dev_VarCache = os.Getenv(WaveDevVarName)
|
||||
|
|
@ -114,6 +118,10 @@ func GetWaveAppPath() string {
|
|||
return AppPath_VarCache
|
||||
}
|
||||
|
||||
func GetWaveAppResourcesPath() string {
|
||||
return AppResourcesPath_VarCache
|
||||
}
|
||||
|
||||
func GetWaveDataDir() string {
|
||||
return DataHome_VarCache
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,8 @@
|
|||
package waveobj
|
||||
|
||||
type ObjRTInfo struct {
|
||||
TsunamiTitle string `json:"tsunami:title,omitempty"`
|
||||
TsunamiShortDesc string `json:"tsunami:shortdesc,omitempty"`
|
||||
TsunamiIcon string `json:"tsunami:icon,omitempty"`
|
||||
TsunamiIconColor string `json:"tsunami:iconcolor,omitempty"`
|
||||
TsunamiSchemas any `json:"tsunami:schemas,omitempty"`
|
||||
TsunamiAppMeta any `json:"tsunami:appmeta,omitempty" tstype:"AppMeta"`
|
||||
TsunamiSchemas any `json:"tsunami:schemas,omitempty"`
|
||||
|
||||
ShellHasCurCwd bool `json:"shell:hascurcwd,omitempty"`
|
||||
ShellState string `json:"shell:state,omitempty"`
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ const (
|
|||
ConfigKey_AppDefaultNewBlock = "app:defaultnewblock"
|
||||
ConfigKey_AppShowOverlayBlockNums = "app:showoverlayblocknums"
|
||||
|
||||
ConfigKey_FeatureWaveAppBuilder = "feature:waveappbuilder"
|
||||
|
||||
ConfigKey_AiClear = "ai:*"
|
||||
ConfigKey_AiPreset = "ai:preset"
|
||||
ConfigKey_AiApiType = "ai:apitype"
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ type SettingsType struct {
|
|||
AppDefaultNewBlock string `json:"app:defaultnewblock,omitempty"`
|
||||
AppShowOverlayBlockNums *bool `json:"app:showoverlayblocknums,omitempty"`
|
||||
|
||||
FeatureWaveAppBuilder bool `json:"feature:waveappbuilder,omitempty"`
|
||||
|
||||
AiClear bool `json:"ai:*,omitempty"`
|
||||
AiPreset string `json:"ai:preset,omitempty"`
|
||||
AiApiType string `json:"ai:apitype,omitempty"`
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@
|
|||
"app:showoverlayblocknums": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"feature:waveappbuilder": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"ai:*": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ func GetAppName(appPath string) string {
|
|||
|
||||
type BuildEnv struct {
|
||||
GoVersion string
|
||||
GoPath string
|
||||
TempDir string
|
||||
cleanupOnce *sync.Once
|
||||
}
|
||||
|
|
@ -329,11 +330,12 @@ func verifyEnvironment(verbose bool, opts BuildOpts) (*BuildEnv, error) {
|
|||
|
||||
return &BuildEnv{
|
||||
GoVersion: goVersion,
|
||||
GoPath: result.GoPath,
|
||||
cleanupOnce: &sync.Once{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createGoMod(tempDir, appNS, appName, goVersion string, opts BuildOpts, verbose bool) error {
|
||||
func createGoMod(tempDir, appNS, appName string, buildEnv *BuildEnv, opts BuildOpts, verbose bool) error {
|
||||
oc := opts.OutputCapture
|
||||
if appNS == "" {
|
||||
appNS = "app"
|
||||
|
|
@ -372,7 +374,7 @@ func createGoMod(tempDir, appNS, appName, goVersion string, opts BuildOpts, verb
|
|||
return fmt.Errorf("failed to add module statement: %w", err)
|
||||
}
|
||||
|
||||
if err := modFile.AddGoStmt(goVersion); err != nil {
|
||||
if err := modFile.AddGoStmt(buildEnv.GoVersion); err != nil {
|
||||
return fmt.Errorf("failed to add go version: %w", err)
|
||||
}
|
||||
|
||||
|
|
@ -412,7 +414,7 @@ func createGoMod(tempDir, appNS, appName, goVersion string, opts BuildOpts, verb
|
|||
}
|
||||
|
||||
// Run go mod tidy to clean up dependencies
|
||||
tidyCmd := exec.Command("go", "mod", "tidy")
|
||||
tidyCmd := exec.Command(buildEnv.GoPath, "mod", "tidy")
|
||||
tidyCmd.Dir = tempDir
|
||||
|
||||
if verbose {
|
||||
|
|
@ -428,6 +430,7 @@ func createGoMod(tempDir, appNS, appName, goVersion string, opts BuildOpts, verb
|
|||
}
|
||||
|
||||
if err := tidyCmd.Run(); err != nil {
|
||||
oc.Flush()
|
||||
return fmt.Errorf("go mod tidy failed (see output for errors)")
|
||||
}
|
||||
|
||||
|
|
@ -498,13 +501,13 @@ func verifyScaffoldFs(fsys fs.FS) error {
|
|||
return fmt.Errorf("package.json check failed: %w", err)
|
||||
}
|
||||
|
||||
// Check for node_modules directory
|
||||
if err := isDirOrNotFoundFS(fsys, "node_modules"); err != nil {
|
||||
return fmt.Errorf("node_modules directory check failed: %w", err)
|
||||
// Check for nm directory
|
||||
if err := isDirOrNotFoundFS(fsys, "nm"); err != nil {
|
||||
return fmt.Errorf("nm (node_modules) directory check failed: %w", err)
|
||||
}
|
||||
info, err = fs.Stat(fsys, "node_modules")
|
||||
info, err = fs.Stat(fsys, "nm")
|
||||
if err != nil || !info.IsDir() {
|
||||
return fmt.Errorf("node_modules directory must exist in scaffold")
|
||||
return fmt.Errorf("nm (node_modules) directory must exist in scaffold")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -621,6 +624,7 @@ func TsunamiBuildInternal(opts BuildOpts) (*BuildEnv, error) {
|
|||
buildEnv.TempDir = tempDir
|
||||
|
||||
oc.Printf("Building tsunami app from %s", opts.AppPath)
|
||||
oc.Printf("[debug] using scaffold path %s", opts.ScaffoldPath)
|
||||
|
||||
if opts.Verbose || opts.KeepTemp {
|
||||
oc.Printf("[debug] Temp dir: %s", tempDir)
|
||||
|
|
@ -652,7 +656,7 @@ func TsunamiBuildInternal(opts BuildOpts) (*BuildEnv, error) {
|
|||
|
||||
// Create go.mod file
|
||||
appName := GetAppName(opts.AppPath)
|
||||
if err := createGoMod(tempDir, opts.AppNS, appName, buildEnv.GoVersion, opts, opts.Verbose); err != nil {
|
||||
if err := createGoMod(tempDir, opts.AppNS, appName, buildEnv, opts, opts.Verbose); err != nil {
|
||||
return buildEnv, err
|
||||
}
|
||||
|
||||
|
|
@ -662,7 +666,7 @@ func TsunamiBuildInternal(opts BuildOpts) (*BuildEnv, error) {
|
|||
}
|
||||
|
||||
// Build the Go application
|
||||
outputPath, err := runGoBuild(tempDir, opts)
|
||||
outputPath, err := runGoBuild(tempDir, buildEnv, opts)
|
||||
if err != nil {
|
||||
return buildEnv, err
|
||||
}
|
||||
|
|
@ -743,7 +747,7 @@ func moveFilesBack(tempDir, originalDir string, verbose bool, oc *OutputCapture)
|
|||
return nil
|
||||
}
|
||||
|
||||
func runGoBuild(tempDir string, opts BuildOpts) (string, error) {
|
||||
func runGoBuild(tempDir string, buildEnv *BuildEnv, opts BuildOpts) (string, error) {
|
||||
oc := opts.OutputCapture
|
||||
var outputPath string
|
||||
var absOutputPath string
|
||||
|
|
@ -775,7 +779,7 @@ func runGoBuild(tempDir string, opts BuildOpts) (string, error) {
|
|||
|
||||
// Build command with explicit go files
|
||||
args := append([]string{"build", "-o", outputPath}, ".")
|
||||
buildCmd := exec.Command("go", args...)
|
||||
buildCmd := exec.Command(buildEnv.GoPath, args...)
|
||||
buildCmd.Dir = tempDir
|
||||
|
||||
if oc != nil || opts.Verbose {
|
||||
|
|
@ -838,7 +842,8 @@ func generateAppTailwindCss(tempDir string, verbose bool, opts BuildOpts) error
|
|||
oc := opts.OutputCapture
|
||||
// tailwind.css is already in tempDir from scaffold copy
|
||||
tailwindOutput := filepath.Join(tempDir, "static", "tw.css")
|
||||
tailwindCmd := exec.Command(opts.getNodePath(), "node_modules/@tailwindcss/cli/dist/index.mjs",
|
||||
tailwindCmd := exec.Command(opts.getNodePath(), "--preserve-symlinks-main", "--preserve-symlinks",
|
||||
"node_modules/@tailwindcss/cli/dist/index.mjs",
|
||||
"-i", "./tailwind.css",
|
||||
"-o", tailwindOutput)
|
||||
tailwindCmd.Dir = tempDir
|
||||
|
|
@ -1074,33 +1079,36 @@ func ParseTsunamiPort(line string) int {
|
|||
func copyScaffoldFS(scaffoldFS fs.FS, destDir string, verbose bool, oc *OutputCapture) (int, error) {
|
||||
fileCount := 0
|
||||
|
||||
// Handle node_modules directory - prefer symlink if possible, otherwise copy
|
||||
if _, err := fs.Stat(scaffoldFS, "node_modules"); err == nil {
|
||||
// Handle nm (node_modules) directory - prefer symlink if possible, otherwise copy
|
||||
if _, err := fs.Stat(scaffoldFS, "nm"); err == nil {
|
||||
destPath := filepath.Join(destDir, "node_modules")
|
||||
|
||||
// Try to create symlink if we have DirFS
|
||||
symlinked := false
|
||||
if dirFS, ok := scaffoldFS.(DirFS); ok {
|
||||
srcPath := dirFS.JoinOS("node_modules")
|
||||
if err := os.Symlink(srcPath, destPath); err != nil {
|
||||
return 0, fmt.Errorf("failed to create symlink for node_modules: %w", err)
|
||||
srcPath := dirFS.JoinOS("nm")
|
||||
if err := os.Symlink(srcPath, destPath); err == nil {
|
||||
if verbose {
|
||||
oc.Printf("[debug] Symlinked nm to node_modules directory")
|
||||
}
|
||||
fileCount++
|
||||
symlinked = true
|
||||
}
|
||||
if verbose {
|
||||
oc.Printf("[debug] Symlinked node_modules directory")
|
||||
}
|
||||
fileCount++
|
||||
} else {
|
||||
// Fallback to recursive copy
|
||||
dirCount, err := copyDirFromFS(scaffoldFS, "node_modules", destPath, false)
|
||||
}
|
||||
|
||||
// Fallback to recursive copy if symlink failed or not attempted
|
||||
if !symlinked {
|
||||
dirCount, err := copyDirFromFS(scaffoldFS, "nm", destPath, false)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to copy node_modules directory: %w", err)
|
||||
return 0, fmt.Errorf("failed to copy nm (node_modules) directory: %w", err)
|
||||
}
|
||||
if verbose {
|
||||
oc.Printf("Copied node_modules directory (%d files)", dirCount)
|
||||
oc.Printf("Copied nm to node_modules directory (%d files)", dirCount)
|
||||
}
|
||||
fileCount += dirCount
|
||||
}
|
||||
} else if !os.IsNotExist(err) {
|
||||
return 0, fmt.Errorf("error checking node_modules: %w", err)
|
||||
return 0, fmt.Errorf("error checking nm (node_modules): %w", err)
|
||||
}
|
||||
|
||||
// Copy package files instead of symlinking
|
||||
|
|
|
|||
Loading…
Reference in a new issue