diff --git a/cmd/cloud.go b/cmd/cloud.go index 8e0f0da9..00beab5d 100644 --- a/cmd/cloud.go +++ b/cmd/cloud.go @@ -198,6 +198,7 @@ Examples: description, _ := cmd.Flags().GetString("description") message, _ := cmd.Flags().GetString("message") dryRun, _ := cmd.Flags().GetString("dry-run") + noBuild, _ := cmd.Flags().GetBool("no-build") // remove duplicates and empty strings tags = util.RemoveDuplicates(tags) @@ -308,7 +309,7 @@ Examples: Config: deploymentConfig, OSEnvironment: loadOSEnv(), PromptHelpers: createPromptHelper(), - }) + }, noBuild) if err != nil { errsystem.New(errsystem.ErrDeployProject, err).ShowErrorAndExit() } @@ -1016,6 +1017,8 @@ func init() { cloudDeployCmd.Flags().MarkHidden("ci-message") cloudDeployCmd.Flags().MarkHidden("ci-git-provider") cloudDeployCmd.Flags().MarkHidden("ci-logs-url") + cloudDeployCmd.Flags().Bool("no-build", false, "Do not build the project before running it (useful for debugging)") + cloudDeployCmd.Flags().MarkHidden("no-build") cloudDeployCmd.Flags().String("format", "text", "The output format to use for results which can be either 'text' or 'json'") cloudDeployCmd.Flags().String("org-id", "", "The organization to create the project in") diff --git a/cmd/dev.go b/cmd/dev.go index 06af5e4b..de9f6d8a 100644 --- a/cmd/dev.go +++ b/cmd/dev.go @@ -40,11 +40,13 @@ Flags: Examples: agentuity dev - agentuity dev --dir /path/to/project`, + agentuity dev --dir /path/to/project + agentuity dev --no-build`, Run: func(cmd *cobra.Command, args []string) { log := env.NewLogger(cmd) logLevel := env.LogLevel(cmd) apiUrl, appUrl, transportUrl := util.GetURLs(log) + noBuild, _ := cmd.Flags().GetBool("no-build") ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGINT, syscall.SIGTERM) defer cancel() @@ -142,7 +144,13 @@ Examples: errsystem.New(errsystem.ErrInvalidConfiguration, err, errsystem.WithContextMessage("Failed to run project")).ShowErrorAndExit() } - build := func(initial bool) bool { + var build func(initial bool) bool + + build = func(initial bool) bool { + if noBuild { + log.Info("Skipping build (no-build flag set)") + return true + } started := time.Now() var ok bool tui.ShowSpinner("Building project ...", func() { @@ -190,6 +198,7 @@ Examples: } // Initial build must exit if it fails + if !build(true) { return } @@ -264,4 +273,6 @@ func init() { rootCmd.AddCommand(devCmd) devCmd.Flags().StringP("dir", "d", ".", "The directory to run the development server in") devCmd.Flags().Int("port", 0, "The port to run the development server on (uses project default if not provided)") + devCmd.Flags().Bool("no-build", false, "Do not build the project before running it (useful for debugging)") + devCmd.Flags().MarkHidden("no-build") } diff --git a/internal/bundler/bundler.go b/internal/bundler/bundler.go index 9f6b4542..15328680 100644 --- a/internal/bundler/bundler.go +++ b/internal/bundler/bundler.go @@ -296,6 +296,7 @@ func bundleJavascript(ctx BundleContext, dir string, outdir string, theproject * } if ctx.DevMode { + ctx.Logger.Debug("build failed: %v", result.Errors) return ErrBuildFailed } diff --git a/internal/bundler/node-fetch.go b/internal/bundler/node-fetch.go new file mode 100644 index 00000000..c9fce2b4 --- /dev/null +++ b/internal/bundler/node-fetch.go @@ -0,0 +1,30 @@ +package bundler + +var patchCode = ` +if (result) { return true; } +if (typeof _args[0] === 'object') { + return true; +} +` + +func init() { + patches["node-fetch-2.7.0"] = patchModule{ + Module: "node-fetch", + Filename: "lib/index", + Functions: map[string]patchAction{ + "isAbortSignal": { + After: patchCode, + }, + }, + } + patches["node-fetch-3.3.2"] = patchModule{ + Module: "node-fetch", + Filename: "src/utils/is", + Functions: map[string]patchAction{ + "isAbortSignal": { + After: patchCode, + }, + }, + } + +} diff --git a/internal/bundler/patch.go b/internal/bundler/patch.go index d5386d7c..10f6a7e6 100644 --- a/internal/bundler/patch.go +++ b/internal/bundler/patch.go @@ -101,11 +101,18 @@ func createPlugin(logger logger.Logger, dir string, shimSourceMap bool) api.Plug } contents := string(buf) var suffix strings.Builder + isJS := strings.HasSuffix(args.Path, ".js") for fn, mod := range mod.Functions { fnname := "function " + fn index := strings.Index(contents, fnname) + var isConstVariable bool if index == -1 { - continue + fnname = "const " + fn + " = " + index = strings.Index(contents, fnname) + isConstVariable = true + if index == -1 { + continue + } } eol := searchBackwards(contents, index, '\n') if eol < 0 { @@ -113,20 +120,42 @@ func createPlugin(logger logger.Logger, dir string, shimSourceMap bool) api.Plug } prefix := strings.TrimSpace(contents[eol+1 : index]) isAsync := strings.Contains(prefix, "async") + isExport := strings.Contains(prefix, "export") newname := "__agentuity_" + fn - newfnname := "function " + newname + var newfnname string + if isConstVariable { + newfnname = "const " + newname + " = " + } else { + newfnname = "function " + newname + } var fnprefix string if isAsync { fnprefix = "async " } + if isExport { + fnprefix += "export " + fnprefix + } contents = strings.Replace(contents, fnname, newfnname, 1) - suffix.WriteString(fnprefix + fnname + "(...args) {\n") + if isJS { + suffix.WriteString(fnprefix + "function " + fn + "() {\n") + suffix.WriteString("let args = arguments;\n") + } else { + suffix.WriteString(fnprefix + fnname + "(...args) {\n") + } suffix.WriteString("\tlet _args = args;\n") if mod.Before != "" { suffix.WriteString(mod.Before) suffix.WriteString("\n") } - suffix.WriteString("\tlet result = " + newname + "(..._args);\n") + + if isJS { + // For JS: use .apply to preserve 'this' context + suffix.WriteString("\tlet result = " + newname + ".apply(this, _args);\n") + } else { + // For TS: use spread operator + suffix.WriteString("\tlet result = " + newname + "(..._args);\n") + } + if isAsync { suffix.WriteString("\tif (result instanceof Promise) {\n") suffix.WriteString("\t\tresult = await result;\n") @@ -150,7 +179,7 @@ func createPlugin(logger logger.Logger, dir string, shimSourceMap bool) api.Plug } } loader := api.LoaderJS - if strings.HasSuffix(args.Path, ".ts") { + if !isJS { loader = api.LoaderTS } return api.OnLoadResult{ diff --git a/internal/bundler/shim.go b/internal/bundler/shim.go index 78b1fd68..33ed8dc6 100644 --- a/internal/bundler/shim.go +++ b/internal/bundler/shim.go @@ -10,7 +10,9 @@ import { dirname as __agentuity_dirname } from 'path'; import { readFileSync as __agentuity_readFileSync, existsSync as __agentuity_existsSync } from 'fs'; const __filename = __agentuity_fileURLToPath(import.meta.url); -const __dirname = __agentuity_dirname(__filename); +if (!('__dirname' in globalThis)) { + globalThis.__dirname = __agentuity_dirname(__filename); +} // List of Node.js built-in modules that might be dynamically required const nodeBuiltins = [ diff --git a/internal/deployer/deployer.go b/internal/deployer/deployer.go index ef6c7dc1..1f71d655 100644 --- a/internal/deployer/deployer.go +++ b/internal/deployer/deployer.go @@ -68,7 +68,7 @@ type DeployPreflightCheckData struct { OSEnvironment map[string]string } -func PreflightCheck(ctx context.Context, logger logger.Logger, data DeployPreflightCheckData) (util.ZipDirCallbackMutator, error) { +func PreflightCheck(ctx context.Context, logger logger.Logger, data DeployPreflightCheckData, noBuild bool) (util.ZipDirCallbackMutator, error) { started := time.Now() bundleCtx := bundler.BundleContext{ Context: context.Background(), @@ -78,8 +78,10 @@ func PreflightCheck(ctx context.Context, logger logger.Logger, data DeployPrefli Project: data.Project, Writer: os.Stderr, } - if err := bundler.Bundle(bundleCtx); err != nil { - return nil, err + if !noBuild { + if err := bundler.Bundle(bundleCtx); err != nil { + return nil, err + } } logger.Debug("bundled in %s", time.Since(started)) return bundler.CreateDeploymentMutator(bundleCtx), nil