返回 DeepSeek-Reasonix
session_migration_input.go
根目录 / desktop / session_migration_input.go
1 package main
2
3 import (
4 "crypto/sha256"
5 "encoding/hex"
6 "encoding/json"
7 "errors"
8 "fmt"
9 "io"
10 "os"
11 "sort"
12 "strings"
13 )
14
15 // Used only for an input that needs import, and again if its cheap stat stamp
16 // changes during import. Completed unchanged receipts retain the stat-only path.
17 func desktopMigrationInputDigest(files []string) (string, error) {
18 paths := append([]string(nil), files...)
19 sort.Strings(paths)
20 h := sha256.New()
21 for _, path := range paths {
22 info, err := os.Lstat(path)
23 if os.IsNotExist(err) {
24 fmt.Fprintf(h, "%q:missing\n", path)
25 continue
26 }
27 if err != nil {
28 return "", err
29 }
30 if !info.Mode().IsRegular() {
31 return "", errors.New("migration input is not a regular file")
32 }
33 fileHash := sha256.New()
34 if strings.HasSuffix(path, ".jsonl.meta") {
35 body, err := os.ReadFile(path)
36 if err != nil {
37 return "", err
38 }
39 var fields map[string]json.RawMessage
40 if err := json.Unmarshal(body, &fields); err != nil {
41 return "", err
42 }
43 // These fields are read-model projections. Keep ancestry, workspace,
44 // selected-head semantics, configuration and every unknown field.
45 for _, key := range []string{"revision", "content_digest", "writer_id", "schema_version", "turns", "preview", "listing_revision", "listing_content_digest"} {
46 delete(fields, key)
47 }
48 body, err = json.Marshal(fields)
49 if err != nil {
50 return "", err
51 }
52 _, _ = fileHash.Write(body)
53 } else {
54 file, err := os.Open(path)
55 if err != nil {
56 return "", err
57 }
58 _, readErr := io.Copy(fileHash, file)
59 if err := errors.Join(readErr, file.Close()); err != nil {
60 return "", err
61 }
62 }
63 fmt.Fprintf(h, "%q:%x\n", path, fileHash.Sum(nil))
64 }
65 return hex.EncodeToString(h.Sum(nil)), nil
66 }
67
67 lines GO