adamdotdevin 7 месяцев назад
Родитель
Сommit
9609c1803e

+ 1 - 1
package.json

@@ -7,7 +7,7 @@
   "scripts": {
     "dev": "bun run --conditions=development packages/opencode/src/index.ts",
     "typecheck": "bun run --filter='*' typecheck",
-    "stainless": "./scripts/stainless",
+    "generate": "(cd packages/sdk && ./js/script/generate.ts) && (cd packages/sdk/stainless && ./generate.ts)",
     "postinstall": "./script/hooks"
   },
   "workspaces": {

+ 26 - 0
packages/opencode/src/server/server.ts

@@ -1120,6 +1120,32 @@ export namespace Server {
         ),
         async (c) => c.json(await callTui(c)),
       )
+      .post(
+        "/tui/show-toast",
+        describeRoute({
+          description: "Show a toast notification in the TUI",
+          operationId: "tui.showToast",
+          responses: {
+            200: {
+              description: "Toast notification shown successfully",
+              content: {
+                "application/json": {
+                  schema: resolver(z.boolean()),
+                },
+              },
+            },
+          },
+        }),
+        zValidator(
+          "json",
+          z.object({
+            title: z.string().optional(),
+            message: z.string(),
+            variant: z.enum(["info", "success", "warning", "error"]),
+          }),
+        ),
+        async (c) => c.json(await callTui(c)),
+      )
       .route("/tui/control", TuiRoute)
       .put(
         "/auth/:id",

+ 4 - 4
packages/sdk/go/.stats.yml

@@ -1,4 +1,4 @@
-configured_endpoints: 36
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-d0eaf92deaa53a25bbbc3181540ad73ed5a4aec6381ac08d8122e24318e5e455.yml
-openapi_spec_hash: 22196d859c0711e564b9538d988abda6
-config_hash: 8d85a768523cff92b85ef06c443d49fa
+configured_endpoints: 37
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-e438f89b86b573c957866c9b057efacc912e3946be03390e4027e8dfab5b83ba.yml
+openapi_spec_hash: 4e9d257b86172e266dc9d72fb04548bc
+config_hash: a78225c7474eb9ab8745e72a0cfe6f96

+ 1 - 0
packages/sdk/go/api.md

@@ -147,4 +147,5 @@ Methods:
 - <code title="post /tui/open-models">client.Tui.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#TuiService.OpenModels">OpenModels</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>) (<a href="https://pkg.go.dev/builtin#bool">bool</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
 - <code title="post /tui/open-sessions">client.Tui.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#TuiService.OpenSessions">OpenSessions</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>) (<a href="https://pkg.go.dev/builtin#bool">bool</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
 - <code title="post /tui/open-themes">client.Tui.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#TuiService.OpenThemes">OpenThemes</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>) (<a href="https://pkg.go.dev/builtin#bool">bool</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
+- <code title="post /tui/show-toast">client.Tui.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#TuiService.ShowToast">ShowToast</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, body <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#TuiShowToastParams">TuiShowToastParams</a>) (<a href="https://pkg.go.dev/builtin#bool">bool</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
 - <code title="post /tui/submit-prompt">client.Tui.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#TuiService.SubmitPrompt">SubmitPrompt</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>) (<a href="https://pkg.go.dev/builtin#bool">bool</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>

+ 35 - 0
packages/sdk/go/tui.go

@@ -87,6 +87,14 @@ func (r *TuiService) OpenThemes(ctx context.Context, opts ...option.RequestOptio
 	return
 }
 
+// Show a toast notification in the TUI
+func (r *TuiService) ShowToast(ctx context.Context, body TuiShowToastParams, opts ...option.RequestOption) (res *bool, err error) {
+	opts = append(r.Options[:], opts...)
+	path := "tui/show-toast"
+	err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...)
+	return
+}
+
 // Submit the prompt
 func (r *TuiService) SubmitPrompt(ctx context.Context, opts ...option.RequestOption) (res *bool, err error) {
 	opts = append(r.Options[:], opts...)
@@ -110,3 +118,30 @@ type TuiExecuteCommandParams struct {
 func (r TuiExecuteCommandParams) MarshalJSON() (data []byte, err error) {
 	return apijson.MarshalRoot(r)
 }
+
+type TuiShowToastParams struct {
+	Message param.Field[string]                    `json:"message,required"`
+	Variant param.Field[TuiShowToastParamsVariant] `json:"variant,required"`
+	Title   param.Field[string]                    `json:"title"`
+}
+
+func (r TuiShowToastParams) MarshalJSON() (data []byte, err error) {
+	return apijson.MarshalRoot(r)
+}
+
+type TuiShowToastParamsVariant string
+
+const (
+	TuiShowToastParamsVariantInfo    TuiShowToastParamsVariant = "info"
+	TuiShowToastParamsVariantSuccess TuiShowToastParamsVariant = "success"
+	TuiShowToastParamsVariantWarning TuiShowToastParamsVariant = "warning"
+	TuiShowToastParamsVariantError   TuiShowToastParamsVariant = "error"
+)
+
+func (r TuiShowToastParamsVariant) IsKnown() bool {
+	switch r {
+	case TuiShowToastParamsVariantInfo, TuiShowToastParamsVariantSuccess, TuiShowToastParamsVariantWarning, TuiShowToastParamsVariantError:
+		return true
+	}
+	return false
+}

+ 26 - 0
packages/sdk/go/tui_test.go

@@ -171,6 +171,32 @@ func TestTuiOpenThemes(t *testing.T) {
 	}
 }
 
+func TestTuiShowToastWithOptionalParams(t *testing.T) {
+	t.Skip("skipped: tests are disabled for the time being")
+	baseURL := "http://localhost:4010"
+	if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok {
+		baseURL = envURL
+	}
+	if !testutil.CheckTestServer(t, baseURL) {
+		return
+	}
+	client := opencode.NewClient(
+		option.WithBaseURL(baseURL),
+	)
+	_, err := client.Tui.ShowToast(context.TODO(), opencode.TuiShowToastParams{
+		Message: opencode.F("message"),
+		Variant: opencode.F(opencode.TuiShowToastParamsVariantInfo),
+		Title:   opencode.F("title"),
+	})
+	if err != nil {
+		var apierr *opencode.Error
+		if errors.As(err, &apierr) {
+			t.Log(string(apierr.DumpRequest(true)))
+		}
+		t.Fatalf("err should be nil: %s", err.Error())
+	}
+}
+
 func TestTuiSubmitPrompt(t *testing.T) {
 	t.Skip("skipped: tests are disabled for the time being")
 	baseURL := "http://localhost:4010"

+ 16 - 0
packages/sdk/js/src/gen/sdk.gen.ts

@@ -77,6 +77,8 @@ import type {
   TuiClearPromptResponses,
   TuiExecuteCommandData,
   TuiExecuteCommandResponses,
+  TuiShowToastData,
+  TuiShowToastResponses,
   AuthSetData,
   AuthSetResponses,
   AuthSetErrors,
@@ -518,6 +520,20 @@ class Tui extends _HeyApiClient {
       },
     })
   }
+
+  /**
+   * Show a toast notification in the TUI
+   */
+  public showToast<ThrowOnError extends boolean = false>(options?: Options<TuiShowToastData, ThrowOnError>) {
+    return (options?.client ?? this._client).post<TuiShowToastResponses, unknown, ThrowOnError>({
+      url: "/tui/show-toast",
+      ...options,
+      headers: {
+        "Content-Type": "application/json",
+        ...options?.headers,
+      },
+    })
+  }
 }
 
 class Auth extends _HeyApiClient {

+ 20 - 0
packages/sdk/js/src/gen/types.gen.ts

@@ -1911,6 +1911,26 @@ export type TuiExecuteCommandResponses = {
 
 export type TuiExecuteCommandResponse = TuiExecuteCommandResponses[keyof TuiExecuteCommandResponses]
 
+export type TuiShowToastData = {
+  body?: {
+    title?: string
+    message: string
+    variant: "info" | "success" | "warning" | "error"
+  }
+  path?: never
+  query?: never
+  url: "/tui/show-toast"
+}
+
+export type TuiShowToastResponses = {
+  /**
+   * Toast notification shown successfully
+   */
+  200: boolean
+}
+
+export type TuiShowToastResponse = TuiShowToastResponses[keyof TuiShowToastResponses]
+
 export type AuthSetData = {
   body?: Auth
   path: {

+ 1 - 0
packages/sdk/stainless/stainless.yml

@@ -146,6 +146,7 @@ resources:
       openThemes: post /tui/open-themes
       openModels: post /tui/open-models
       executeCommand: post /tui/execute-command
+      showToast: post /tui/show-toast
 
 settings:
   disable_mock_tests: true

+ 39 - 0
packages/tui/internal/tui/tui.go

@@ -705,6 +705,45 @@ func (a Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 			updated, cmd := a.executeCommand(commands.Command(command))
 			a = updated.(Model)
 			cmds = append(cmds, cmd)
+		case "/tui/show-toast":
+			var body struct {
+				Title   string `json:"title,omitempty"`
+				Message string `json:"message"`
+				Variant string `json:"variant"`
+			}
+			json.Unmarshal((msg.Body), &body)
+
+			var toastCmd tea.Cmd
+			switch body.Variant {
+			case "info":
+				if body.Title != "" {
+					toastCmd = toast.NewInfoToast(body.Message, toast.WithTitle(body.Title))
+				} else {
+					toastCmd = toast.NewInfoToast(body.Message)
+				}
+			case "success":
+				if body.Title != "" {
+					toastCmd = toast.NewSuccessToast(body.Message, toast.WithTitle(body.Title))
+				} else {
+					toastCmd = toast.NewSuccessToast(body.Message)
+				}
+			case "warning":
+				if body.Title != "" {
+					toastCmd = toast.NewErrorToast(body.Message, toast.WithTitle(body.Title))
+				} else {
+					toastCmd = toast.NewErrorToast(body.Message)
+				}
+			case "error":
+				if body.Title != "" {
+					toastCmd = toast.NewErrorToast(body.Message, toast.WithTitle(body.Title))
+				} else {
+					toastCmd = toast.NewErrorToast(body.Message)
+				}
+			default:
+				slog.Error("Invalid toast variant", "variant", body.Variant)
+				return a, nil
+			}
+			cmds = append(cmds, toastCmd)
 
 		default:
 			break