@@ -18,6 +18,7 @@ import (
1818 "encoding/json"
1919 "errors"
2020 "fmt"
21+ "io"
2122 "io/fs"
2223 "os"
2324 "path/filepath"
@@ -67,6 +68,7 @@ type InstallCmd struct {
6768 Driver string `arg:"positional,required" help:"Driver to install, optionally with a version constraint (for example: mysql, mysql=0.1.0, mysql>=1,<2)"`
6869 Level config.ConfigLevel `arg:"-l" help:"Config level to install to (user, system)"`
6970 Json bool `arg:"--json" help:"Print output as JSON instead of plaintext"`
71+ JsonStreamProgress bool `arg:"--json-stream-progress" help:"Stream progress events as JSON lines (implies --json)"`
7072 NoVerify bool `arg:"--no-verify" help:"Allow installation of drivers without a signature file"`
7173 Pre bool `arg:"--pre" help:"Allow implicit installation of pre-release versions"`
7274 InsecureNoChecksum bool `arg:"--insecure-no-checksum" help:"Skip sha256 checksum recording (not recommended)"`
@@ -89,7 +91,8 @@ func (c InstallCmd) GetModelCustom(baseModel baseModel) tea.Model {
8991 return progressiveInstallModel {
9092 Driver : c .Driver ,
9193 NoVerify : c .NoVerify ,
92- jsonOutput : c .Json ,
94+ jsonOutput : c .Json || c .JsonStreamProgress ,
95+ jsonStreamProgress : c .JsonStreamProgress ,
9396 Pre : c .Pre ,
9497 insecureNoChecksum : c .InsecureNoChecksum ,
9598 spinner : s ,
@@ -181,8 +184,21 @@ func (progressiveInstallModel) NeedsRenderer() {}
181184
182185func (m progressiveInstallModel ) IsJSONMode () bool { return m .jsonOutput }
183186
187+ func (m progressiveInstallModel ) WithJSONWriter (w io.Writer ) tea.Model {
188+ m .jsonOut = w
189+ return m
190+ }
191+
192+ func (m progressiveInstallModel ) emitJSON (kind string , payload any ) {
193+ out := m .jsonOut
194+ if out == nil {
195+ out = os .Stdout
196+ }
197+ fmt .Fprintln (out , marshalEnvelope (kind , payload ))
198+ }
199+
184200func (m progressiveInstallModel ) addEvent (event string , extra ... func (* jsonschema.InstallProgressEvent )) progressiveInstallModel {
185- if ! m .jsonOutput {
201+ if ! m .jsonStreamProgress {
186202 return m
187203 }
188204 evt := jsonschema.InstallProgressEvent {
@@ -192,19 +208,20 @@ func (m progressiveInstallModel) addEvent(event string, extra ...func(*jsonschem
192208 for _ , fn := range extra {
193209 fn (& evt )
194210 }
195- m .jsonEvents = append ( m . jsonEvents , marshalEnvelope ( "install.progress" , evt ) )
211+ m .emitJSON ( "install.progress" , evt )
196212 return m
197213}
198214
199215type progressiveInstallModel struct {
200216 baseModel
201217
202- Driver string
203- VersionInput * semver.Version
204- NoVerify bool
205- jsonOutput bool
206- Pre bool
207- cfg config.Config
218+ Driver string
219+ VersionInput * semver.Version
220+ NoVerify bool
221+ jsonOutput bool
222+ jsonStreamProgress bool
223+ Pre bool
224+ cfg config.Config
208225
209226 insecureNoChecksum bool
210227 installedDriverInfo config.DriverInfo
@@ -222,8 +239,8 @@ type progressiveInstallModel struct {
222239 localPackagePath string
223240
224241 registryErrors error
225- jsonEvents []string
226242 alreadyInstalledChecksum string
243+ jsonOut io.Writer
227244 jsonErrorOutput string // JSON error envelope to emit via FinalOutput
228245}
229246
@@ -353,31 +370,15 @@ func (m progressiveInstallModel) FinalOutput() string {
353370
354371 if m .jsonOutput {
355372 if installStatus .Checksum != "" {
356- m = m .addEvent ("verify.checksum.ok" , func (e * jsonschema.InstallProgressEvent ) {
373+ m .addEvent ("verify.checksum.ok" , func (e * jsonschema.InstallProgressEvent ) {
357374 e .Checksum = installStatus .Checksum
358375 })
359376 }
360- payloadBytes , err := json .Marshal (installStatus )
361- if err != nil {
362- return fmt .Sprintf (`{"schema_version":1,"kind":"error","payload":{"code":"marshal_error","message":"%s"}}` , err .Error ())
363- }
364- env := jsonschema.Envelope {
365- SchemaVersion : jsonschema .SchemaVersion ,
366- Kind : "install.status" ,
367- Payload : json .RawMessage (payloadBytes ),
368- }
369- jsonOutput , err := json .Marshal (env )
370- if err != nil {
371- return fmt .Sprintf (`{"schema_version":1,"kind":"error","payload":{"code":"marshal_error","message":"%s"}}` , err .Error ())
372- }
373- completeLine := marshalEnvelope ("install.progress" , jsonschema.InstallProgressEvent {
377+ m .emitJSON ("install.progress" , jsonschema.InstallProgressEvent {
374378 Event : "install.complete" ,
375379 Driver : m .Driver ,
376380 })
377- allLines := make ([]string , 0 , len (m .jsonEvents )+ 2 )
378- allLines = append (allLines , m .jsonEvents ... )
379- allLines = append (allLines , completeLine , string (jsonOutput ))
380- return strings .Join (allLines , "\n " )
381+ return marshalEnvelope ("install.status" , installStatus )
381382 }
382383
383384 if installStatus .Conflict != "" {
0 commit comments