bundle

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 16, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package bundle creates and (later) reads .codexbundle archives: a ZIP containing a manifest, a checksum map, and the copied Codex rollout files.

A .codexbundle is a portable, hosting-free way to move local Codex sessions between devices. It never includes or modifies Codex's SQLite state DB.

Index

Constants

View Source
const (
	ManifestName  = "manifest.json"
	ChecksumsName = "checksums.json"
)

Bundle file names.

View Source
const FormatVersion = "codex-sync-bundle-v1"

FormatVersion identifies the bundle layout. Bump this on breaking changes.

Variables

This section is empty.

Functions

func DirExists

func DirExists(p string) bool

DirExists reports whether p exists on the local machine and is a directory. It only reads (os.Stat); it never creates anything.

Types

type Action

type Action string

Action describes what import decided to do with one bundle entry.

const (
	ActionImport         Action = "import"           // new file; copied (unless dry-run)
	ActionSkipIdentical  Action = "skip-identical"   // target exists with same checksum
	ActionConflict       Action = "conflict"         // target exists with different checksum; not overwritten
	ActionReplace        Action = "replace"          // conflict, overwritten after backing up the local file
	ActionImportCopy     Action = "import-copy"      // conflict, imported as a brand-new session (fresh id + filename)
	ActionSkipArchived   Action = "skip-archived"    // archived_sessions entry; not imported in v0.1
	ActionSkipNonSession Action = "skip-non-session" // unexpected non-session file
	ActionSkipDeselected Action = "skip-deselected"  // not among the --session ids requested for import
)

type CWDInfo

type CWDInfo struct {
	// Path is the first-seen original spelling of the cwd (for display).
	Path string
	// Count is how many sessions recorded this cwd.
	Count int
	// ExistsLocal reports whether the directory exists on this machine. A
	// session whose cwd does not exist locally is hidden in Codex's project
	// sidebar until the folder is created or the cwd is remapped on import.
	ExistsLocal bool
}

CWDInfo describes one distinct recorded working directory across a bundle's sessions and whether that directory exists on the local machine.

type CWDMapping

type CWDMapping struct {
	Old string
	New string
}

CWDMapping rewrites a session's recorded working directory from Old to New during import. It is the only feature that mutates session content, and it touches nothing except the canonical "cwd" field of the session_meta line.

func ParseCWDMappings

func ParseCWDMappings(specs []string) ([]CWDMapping, error)

ParseCWDMappings parses and validates a list of "OLD=NEW" mapping specs.

Rules:

  • syntax must be OLD=NEW (split on the first '='),
  • OLD must not be empty,
  • NEW must not be empty and must be absolute/plausible for the target OS,
  • OLD and NEW must differ after normalization,
  • duplicate OLD paths (case-insensitive on Windows) are rejected.

type CWDSummary

type CWDSummary struct {
	// Dirs are the distinct non-empty recorded cwds, sorted by path.
	Dirs []CWDInfo
	// UnknownCWD counts sessions with no recorded cwd (e.g. compressed
	// sessions, whose cwd is unknown in v0.1).
	UnknownCWD int
	// MissingCount is how many entries in Dirs do not exist locally.
	MissingCount int
}

CWDSummary groups a bundle's sessions by their recorded working directory and reports which directories are missing on the local machine. It is the data behind the "project folders" view that helps users find the #1 portability gotcha (imported sessions hidden because their cwd does not exist here).

func SummarizeCWDs

func SummarizeCWDs(sessions []ManifestSession, exists func(string) bool) CWDSummary

SummarizeCWDs groups sessions by recorded cwd and, for each distinct directory, reports the session count and whether it exists locally. Grouping is case-insensitive on Windows (via normalizeCWD), matching how cwd is compared elsewhere; the first-seen spelling is preserved for display. The exists function is injected so the logic is testable without touching the filesystem; production callers pass DirExists.

type Checksums

type Checksums map[string]string

Checksums maps each bundle-relative file path (forward slashes) to its SHA-256 hex digest. It is written as checksums.json at the root of the ZIP and covers every file in the bundle except checksums.json itself.

type ExportOptions

type ExportOptions struct {
	// ProjectPath, when non-empty, restricts the export to sessions whose
	// SessionMeta cwd matches this (already absolute) path. Leave empty to
	// export every session regardless of cwd (the `--all` behavior).
	ProjectPath string
	// OutputPath is the .codexbundle file to write.
	OutputPath string
	// IncludeArchived also considers archived sessions.
	IncludeArchived bool
	// Since, when non-zero, restricts the export to sessions whose file
	// modification time is at or after this instant.
	Since time.Time
	// SessionID, when non-empty, exports exactly the single session whose
	// thread id equals (or uniquely begins with) this value, regardless of
	// cwd. It is mutually exclusive with project/all filtering.
	SessionID string
	// WithGit forces capture of the project's git metadata (remote, branch,
	// commit, dirty/unpushed) into the manifest even when ProjectPath is empty
	// (e.g. with --all or --session). When ProjectPath is set, git metadata is
	// always captured regardless of this flag.
	WithGit bool
}

ExportOptions configures an export.

type ExportResult

type ExportResult struct {
	BundlePath        string
	Manifest          Manifest
	IncludedCount     int
	TotalScanned      int
	CompressedSkipped int // compressed sessions skipped by cwd filter (cwd unknown)
	Warnings          []string
}

ExportResult summarizes what was exported.

func Export

func Export(home codexhome.Home, opts ExportOptions) (ExportResult, error)

Export scans the Codex home, selects sessions (optionally filtered to a project's cwd), and writes a .codexbundle ZIP containing the rollout files, a manifest, and a checksum map. It never reads or writes Codex's SQLite.

type ImportItem

type ImportItem struct {
	BundlePath  string
	DestPath    string
	Action      Action
	OriginalCWD string
	CWDMismatch bool
	// Mapped reports whether --map-cwd rewrote this session's cwd.
	Mapped bool
	// Copied reports whether this entry was imported as a brand-new session
	// (ActionImportCopy, --import-as-copy) with NewThreadID as its fresh id.
	Copied bool
	// NewThreadID is the freshly assigned session id when Copied is true.
	NewThreadID string
	// BackupPath, when non-empty, is where the pre-existing local file was
	// backed up before being replaced (ActionReplace, --replace-with-backup).
	BackupPath string
	// contains filtered or unexported fields
}

ImportItem is the plan/outcome for a single bundle entry.

type ImportOptions

type ImportOptions struct {
	BundlePath string
	DryRun     bool
	// ProjectPath, when non-empty, enables per-session cwd-mismatch checks
	// against this (already absolute) path.
	ProjectPath string
	// MapCWD, when set, rewrites matching sessions' recorded cwd on import.
	// Only plain .jsonl files are rewritten; .jsonl.zst files that match a
	// mapping are copied byte-for-byte and reported as unmappable.
	MapCWD []CWDMapping
	// ReplaceWithBackup turns conflicts (a local file with different content
	// for the same session) into a replace: the local file is backed up next
	// to itself and then overwritten with the bundle's version. Without it,
	// conflicts are reported and skipped (the default, never-overwrite behavior).
	ReplaceWithBackup bool
	// SessionIDs, when non-empty, restricts the import to the bundle sessions
	// whose thread id equals (or uniquely begins with) one of these values; every
	// other session in the bundle is skipped (ActionSkipDeselected). An id that
	// matches no session in the bundle is an error (nothing is written).
	SessionIDs []string
	// ImportAsCopy turns conflicts into a brand-new session: the bundle's
	// version is assigned a fresh session id and written under a new rollout
	// filename, so it coexists with the diverged local session rather than being
	// skipped or replacing it. Only plain .jsonl files can be copied; a
	// compressed (.jsonl.zst) conflict, or one without a session_meta id, stays a
	// skipped conflict. Mutually exclusive with ReplaceWithBackup at the CLI.
	ImportAsCopy bool
}

ImportOptions configures an import.

type ImportResult

type ImportResult struct {
	Manifest         Manifest
	Items            []ImportItem
	Imported         int
	SkippedIdentical int
	Conflicts        int
	SkippedOther     int
	// SkippedDeselected counts bundle sessions excluded by a --session filter.
	SkippedDeselected int
	CWDMismatchCount  int
	// Replaced counts conflicting sessions that were overwritten after the local
	// file was backed up (--replace-with-backup).
	Replaced int
	// ImportedCopies counts conflicting sessions imported as brand-new sessions
	// (--import-as-copy).
	ImportedCopies int
	// Mapped counts imported sessions whose cwd was rewritten by --map-cwd.
	Mapped int
	// MappedCompressedSkipped counts compressed sessions that matched a
	// mapping but could not be rewritten in v0.1 (copied byte-for-byte).
	MappedCompressedSkipped int
	ProjectProvided         bool
	DryRun                  bool
	Warnings                []string
}

ImportResult summarizes an import.

func Import

func Import(home codexhome.Home, opts ImportOptions) (ImportResult, error)

Import validates a bundle end-to-end and, unless DryRun is set, copies its rollout files into the Codex home, preserving the sessions/YYYY/MM/DD layout.

Safety guarantees:

  • The whole bundle's checksums are verified BEFORE anything is written.
  • Unsafe entry paths (absolute, drive-letter, zip-slip) abort the import.
  • Only sessions/YYYY/MM/DD rollout files are imported.
  • Existing files are never overwritten: identical files are skipped, differing files are reported as conflicts and skipped.
  • Writes are atomic (temp file + rename). SQLite is never touched.
  • .jsonl.zst files are copied byte-for-byte; never parsed or decompressed.

type InspectResult

type InspectResult struct {
	Manifest  Manifest
	Checksums Checksums
	Entries   []string
}

InspectResult is the read-only view of a bundle: its manifest, its checksum map, and the names of the files it contains. Inspecting never extracts or writes anything.

func Inspect

func Inspect(bundlePath string) (InspectResult, error)

Inspect opens a .codexbundle and reads manifest.json and checksums.json without extracting any session files. It validates that the bundle has the expected format version.

type Manifest

type Manifest struct {
	FormatVersion     string            `json:"format_version"`
	CreatedAt         string            `json:"created_at"`
	CreatedByDevice   string            `json:"created_by_device,omitempty"`
	SourceOS          string            `json:"source_os"`
	SourceCodexHome   string            `json:"source_codex_home"`
	SourceProjectPath string            `json:"source_project_path,omitempty"`
	Git               *git.Info         `json:"git,omitempty"`
	CodexVersion      string            `json:"codex_version,omitempty"`
	Sessions          []ManifestSession `json:"sessions"`
}

Manifest describes a bundle and everything inside it. It is written as manifest.json at the root of the ZIP.

type ManifestSession

type ManifestSession struct {
	ThreadID         string `json:"thread_id,omitempty"`
	OriginalPath     string `json:"original_path"`
	BundlePath       string `json:"bundle_path"`
	OriginalCWD      string `json:"original_cwd,omitempty"`
	Preview          string `json:"preview,omitempty"`
	FirstUserMessage string `json:"first_user_message,omitempty"`
	CreatedAt        string `json:"created_at,omitempty"`
	UpdatedAt        string `json:"updated_at,omitempty"`
	Source           string `json:"source,omitempty"`
	ModelProvider    string `json:"model_provider,omitempty"`
	GitBranch        string `json:"git_branch,omitempty"`
	GitSHA           string `json:"git_sha,omitempty"`
	Archived         bool   `json:"archived"`
	Compressed       bool   `json:"compressed"`
	SizeBytes        int64  `json:"size_bytes"`
	SHA256           string `json:"sha256"`
}

ManifestSession is one rollout file recorded in the manifest.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL