Files
wehub-resource-sync f99010fae1
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Windows) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

183 lines
3.8 KiB
Go

package main
import (
"context"
"fmt"
"log"
"os"
"path/filepath"
"strings"
"go.kenn.io/agentsview/internal/config"
"go.kenn.io/agentsview/internal/db"
"go.kenn.io/agentsview/internal/importer"
)
type ImportConfig struct {
Type string
Path string
}
func runImport(cfg ImportConfig) {
appCfg, err := config.LoadMinimal()
if err != nil {
log.Fatalf("loading config: %v", err)
}
database, writeLock, err := openWriteDB(context.Background(), appCfg)
if err != nil {
log.Fatalf("Error opening database: %v", err)
}
defer closeWriteDB(database, writeLock)
ctx := context.Background()
// Handle zip files.
dir, cleanup, err := resolveImportSource(cfg.Path)
if err != nil {
log.Fatalf("Error: %v", err)
}
if cleanup != nil {
defer cleanup()
}
var stats importer.ImportStats
switch cfg.Type {
case "claude-ai":
stats, err = runClaudeAIImport(
ctx, database, dir, appCfg.LocalMachineName,
)
case "chatgpt":
assetsDir := filepath.Join(appCfg.DataDir, "assets")
stats, err = runChatGPTImport(
ctx, database, dir, assetsDir, appCfg.LocalMachineName,
)
default:
log.Fatalf(
"Unknown import type: %s (use claude-ai or chatgpt)",
cfg.Type,
)
}
if err != nil {
fmt.Fprintln(os.Stderr)
log.Fatalf("Import failed: %v", err)
}
printImportSummary(stats)
if stats.Errors > 0 {
os.Exit(1)
}
}
func runClaudeAIImport(
ctx context.Context, database *db.DB, path, machine string,
) (importer.ImportStats, error) {
jsonPath := path
info, err := os.Stat(path)
if err != nil {
return importer.ImportStats{}, err
}
if info.IsDir() {
jsonPath = filepath.Join(path, "conversations.json")
}
f, err := os.Open(jsonPath)
if err != nil {
return importer.ImportStats{},
fmt.Errorf("opening %s: %w", jsonPath, err)
}
defer f.Close()
return importer.ImportClaudeAI(
ctx, database, f, &importer.ImportCallbacks{
OnProgress: func(s importer.ImportStats) {
n := s.Imported + s.Updated + s.Skipped
fmt.Fprintf(
os.Stderr,
"\r%d conversations processed...", n,
)
},
OnIndexing: func() {
fmt.Fprintf(
os.Stderr,
"\rRebuilding search index... ",
)
},
}, machine,
)
}
func runChatGPTImport(
ctx context.Context, database *db.DB,
dir, assetsDir, machine string,
) (importer.ImportStats, error) {
return importer.ImportChatGPT(
ctx, database, dir, assetsDir,
&importer.ImportCallbacks{
OnProgress: func(s importer.ImportStats) {
n := s.Imported + s.Skipped
fmt.Fprintf(
os.Stderr,
"\r%d conversations processed...", n,
)
},
OnIndexing: func() {
fmt.Fprintf(
os.Stderr,
"\rRebuilding search index... ",
)
},
}, machine,
)
}
func printImportSummary(stats importer.ImportStats) {
total := stats.Imported + stats.Updated + stats.Skipped
fmt.Fprintf(os.Stderr, "\rDone: %d processed", total)
var parts []string
if stats.Imported > 0 {
parts = append(
parts, fmt.Sprintf("%d new", stats.Imported),
)
}
if stats.Updated > 0 {
parts = append(
parts, fmt.Sprintf("%d updated", stats.Updated),
)
}
if stats.Skipped > 0 {
parts = append(
parts, fmt.Sprintf("%d skipped", stats.Skipped),
)
}
if len(parts) > 0 {
fmt.Fprintf(
os.Stderr, " (%s)", strings.Join(parts, ", "),
)
}
fmt.Fprintln(os.Stderr)
if stats.Errors > 0 {
fmt.Fprintf(os.Stderr, " %d errors\n", stats.Errors)
}
}
// resolveImportSource handles zip extraction. If the path is
// a .zip file, it extracts to a temp dir and returns the dir
// path with a cleanup function. Otherwise returns the original
// path with nil cleanup.
func resolveImportSource(
path string,
) (string, func(), error) {
if strings.HasSuffix(strings.ToLower(path), ".zip") {
return importer.ExtractZip(path)
}
if _, err := os.Stat(path); err != nil {
return "", nil,
fmt.Errorf("cannot access %s: %w", path, err)
}
return path, nil, nil
}