返回 DeepSeek-Reasonix
session_locator.go
根目录 / desktop / session_locator.go
1 package main
2
3 import (
4 "fmt"
5 "path/filepath"
6 "runtime"
7 "strings"
8
9 "reasonix/internal/session"
10 "reasonix/internal/store"
11 )
12
13 type sessionLocatorKind uint8
14
15 const (
16 sessionLocatorEmpty sessionLocatorKind = iota
17 sessionLocatorCanonical
18 sessionLocatorLegacy
19 sessionLocatorInvalid
20 )
21
22 type legacySessionPath string
23
24 type sessionLocator struct {
25 kind sessionLocatorKind
26 ref session.SessionRef
27 legacyPath legacySessionPath
28 reason string
29 }
30
31 func isLegacySessionTranscriptName(name string) bool {
32 if runtime.GOOS == "windows" {
33 name = strings.ToLower(name)
34 }
35 return store.IsSessionTranscriptName(name)
36 }
37
38 func validatedLegacySessionPathForRead(raw string) (legacySessionPath, bool) {
39 locator := classifySessionLocator(raw)
40 if locator.kind != sessionLocatorLegacy {
41 return "", false
42 }
43 path := string(locator.legacyPath)
44 if !filepath.IsAbs(path) {
45 return "", false
46 }
47 validated, err := resolveLegacySessionPath(path, filepath.Dir(path))
48 return validated, err == nil
49 }
50
51 func legacySessionPathForFileAccess(raw string) (legacySessionPath, bool, error) {
52 locator := classifySessionLocator(raw)
53 switch locator.kind {
54 case sessionLocatorEmpty, sessionLocatorCanonical:
55 return "", false, nil
56 case sessionLocatorInvalid:
57 return "", false, &sessionLocatorError{reason: locator.reason}
58 }
59 path := string(locator.legacyPath)
60 if !filepath.IsAbs(path) {
61 return "", false, &sessionLocatorError{reason: "legacy_path_not_absolute"}
62 }
63 validated, err := resolveLegacySessionPath(path, filepath.Dir(path))
64 if err != nil {
65 return "", false, err
66 }
67 return validated, true, nil
68 }
69
70 type sessionLocatorError struct {
71 reason string
72 }
73
74 func (e *sessionLocatorError) Error() string {
75 if e == nil || e.reason == "" {
76 return "invalid session identity"
77 }
78 return fmt.Sprintf("invalid session identity: %s", e.reason)
79 }
80
81 // classifySessionLocator is deliberately path-I/O free. A value with the
82 // canonical route prefix is always a route candidate: malformed IDs never
83 // fall through to the legacy path branch.
84 func classifySessionLocator(raw string) sessionLocator {
85 value := strings.TrimSpace(raw)
86 if value == "" {
87 return sessionLocator{kind: sessionLocatorEmpty}
88 }
89 if id, route := strings.CutPrefix(value, remoteSessionIDRoutePrefix); route {
90 id = strings.TrimSpace(id)
91 if err := session.ValidateSessionID(id); err != nil {
92 return sessionLocator{kind: sessionLocatorInvalid, reason: "invalid_canonical_route"}
93 }
94 return sessionLocator{
95 kind: sessionLocatorCanonical,
96 ref: session.SessionRef{HostID: localDesktopHostID, SessionID: id},
97 }
98 }
99 return sessionLocator{kind: sessionLocatorLegacy, legacyPath: legacySessionPath(value)}
100 }
101
102 func resolveLegacySessionPath(raw string, dirs ...string) (legacySessionPath, error) {
103 locator := classifySessionLocator(raw)
104 switch locator.kind {
105 case sessionLocatorEmpty:
106 return "", &sessionLocatorError{reason: "empty"}
107 case sessionLocatorCanonical:
108 return "", &sessionLocatorError{reason: "canonical_route_is_not_a_path"}
109 case sessionLocatorInvalid:
110 return "", &sessionLocatorError{reason: locator.reason}
111 }
112 for _, dir := range dirs {
113 if path, ok := pinnedTabSessionPath(dir, string(locator.legacyPath)); ok {
114 return legacySessionPath(path), nil
115 }
116 }
117 return "", &sessionLocatorError{reason: "invalid_legacy_path"}
118 }
119
119 lines GO