返回 DeepSeek-Reasonix
session_export_diagnostics.go
根目录 / desktop / session_export_diagnostics.go
1 package main
2
3 import (
4 "encoding/json"
5 "errors"
6 "io"
7 "net/http"
8 "os"
9
10 "reasonix/desktop/internal/hostrpc"
11 "reasonix/internal/control"
12 "reasonix/internal/secrets"
13 "reasonix/internal/servecontract"
14 "reasonix/internal/session"
15 )
16
17 func (a *App) writeSessionDiagnosticExport(job *sessionExportJob) error {
18 browserDiagnostics := a.browserDiagnosticExport(job)
19 extra := map[string]any{"sessionIdentity": map[string]any{"session": job.handle.Snapshot.Ref, "source": "local", "workspaceRoot": job.workspaceRoot, "storageGeneration": job.handle.Snapshot.StorageGeneration}, "exportSnapshot": job.handle.Snapshot, "frontendObservation": job.observation}
20 var frontend map[string]json.RawMessage
21 _ = json.Unmarshal(job.observation, &frontend)
22 if value := frontend["readDiagnostics"]; len(value) > 0 {
23 extra["readDiagnostics"] = value
24 }
25 if len(job.observation) == 0 {
26 extra["frontendObservation"] = nil
27 }
28 return writeGoalDiagnosticsFile(job.path, func(dst io.Writer) error {
29 if job.client != nil {
30 extra["sessionIdentity"] = map[string]any{"session": job.handle.Snapshot.Ref, "source": "remote", "connectionHostId": job.sourceHostID, "workspaceRoot": job.workspaceRoot, "storageGeneration": job.handle.Snapshot.StorageGeneration}
31 body, _ := json.Marshal(extra)
32 resp, err := serveDoForSession(job.ctx, job.client, http.MethodPost, sessionExportURL(job.base, "/session-export/diagnostic", job.handle.Snapshot.Ref.SessionID, false), body, job.route)
33 if err != nil {
34 return err
35 }
36 defer resp.Body.Close()
37 if resp.StatusCode != http.StatusOK {
38 return errors.New("remote session diagnostics are unavailable; upgrade the remote service if the session was switched or taken over")
39 }
40 if _, err = copyExportContext(job.ctx, dst, resp.Body); err != nil {
41 return err
42 }
43 encoded, err := json.Marshal(browserDiagnostics)
44 if err != nil {
45 return err
46 }
47 encoded = []byte(secrets.Redact(string(encoded)))
48 if !json.Valid(encoded) {
49 return errors.New("redacted browser diagnostics are invalid")
50 }
51 return appendBrowserDiagnosticSection(dst, encoded)
52 }
53 extra["browserDiagnostics"] = browserDiagnostics
54 metadata := control.GoalDiagnosticMetadata{ApplicationVersion: version, BuildCommit: buildCommit(), ProtocolVersion: hostrpc.ProtocolVersion, Capabilities: []string{servecontract.SessionExportV1, servecontract.GoalLifecycleV2}}
55 if a.sessionDiagnosticControllerCurrent(job.controller, job.handle.Snapshot.Ref) {
56 runtimeErr := job.controller.WriteSessionDiagnostics(job.ctx, dst, metadata, extra)
57 if a.sessionDiagnosticControllerCurrent(job.controller, job.handle.Snapshot.Ref) {
58 return runtimeErr
59 }
60 file, ok := dst.(*os.File)
61 if !ok {
62 return errors.Join(runtimeErr, errors.New("session controller changed during diagnostic capture"))
63 }
64 if resetErr := file.Truncate(0); resetErr != nil {
65 return errors.Join(runtimeErr, resetErr)
66 }
67 if _, resetErr := file.Seek(0, io.SeekStart); resetErr != nil {
68 return errors.Join(runtimeErr, resetErr)
69 }
70 }
71 return control.WriteColdSessionDiagnostics(job.ctx, dst, job.query, job.handle.Snapshot, metadata, extra)
72 })
73 }
74
75 func (a *App) sessionDiagnosticControllerCurrent(controller *control.Controller, ref session.SessionRef) bool {
76 if controller == nil {
77 return false
78 }
79 boundRef, bound := controller.SessionRef()
80 if !bound || boundRef != ref {
81 return false
82 }
83 a.mu.RLock()
84 defer a.mu.RUnlock()
85 for _, tab := range a.tabs {
86 if !tab.removed && tab.Ctrl == controller && tab.SessionID == ref.SessionID {
87 return true
88 }
89 }
90 return false
91 }
92
92 lines GO