diff --git a/code/server.go b/code/server.go index a9ca84ea..4111b390 100644 --- a/code/server.go +++ b/code/server.go @@ -15,6 +15,7 @@ import ( "path/filepath" "strconv" "strings" + "sync" "github.com/codefly-dev/core/failures" basev0 "github.com/codefly-dev/core/generated/go/codefly/base/v0" @@ -75,6 +76,12 @@ func WithContentCache(budgetBytes int64) ServerOption { } } +// WithWorkspaceCheckpointStore sets the agent-owned directory used for +// durable mutable-workspace checkpoints. +func WithWorkspaceCheckpointStore(dir string) ServerOption { + return func(s *DefaultCodeServer) { s.workspaceCheckpointStore = dir } +} + // WriteListener is called after every successful file mutation so that // external indexes or subscribers can stay aligned with the VFS state. // @@ -94,18 +101,20 @@ type WriteListener func(ctx context.Context, kind, path, prevPath string, conten type DefaultCodeServer struct { codev0.UnimplementedCodeServer - SourceDir string - FS VFS - overrides map[string]OperationHandler - wantCachedFS bool - wantTrigramIndex bool - contentCacheBudget int64 // 0 = no content cache - cachedFS *CachedVFS // non-nil when CachedVFS is active - trigramIdx *TrigramIndex // non-nil when trigram indexing is active - nativeGit *NativeGit // lazily opened go-git repo - writeListener WriteListener // optional post-mutation hook - sourceFixer SourceFixer // optional language-aware in-memory fixer - semantic SemanticAnalyzer // optional tree-sitter source analysis + SourceDir string + FS VFS + overrides map[string]OperationHandler + wantCachedFS bool + wantTrigramIndex bool + contentCacheBudget int64 // 0 = no content cache + cachedFS *CachedVFS // non-nil when CachedVFS is active + trigramIdx *TrigramIndex // non-nil when trigram indexing is active + nativeGit *NativeGit // lazily opened go-git repo + writeListener WriteListener // optional post-mutation hook + sourceFixer SourceFixer // optional language-aware in-memory fixer + semantic SemanticAnalyzer // optional tree-sitter source analysis + workspaceCheckpointMu sync.RWMutex + workspaceCheckpointStore string } // NewDefaultCodeServer creates a server rooted at sourceDir. @@ -221,8 +230,26 @@ func (s *DefaultCodeServer) Execute(ctx context.Context, req *codev0.CodeRequest return nil, failures.GRPC(failures.New(basev0.FailureCode_FAILURE_CODE_INVALID_ARGUMENT, "code.execute", "empty CodeRequest: no operation set")) } if h, ok := s.overrides[opName]; ok { + switch req.Operation.(type) { + case *codev0.CodeRequest_CreateWorkspaceCheckpoint, + *codev0.CodeRequest_RestoreWorkspaceCheckpoint, + *codev0.CodeRequest_ReleaseWorkspaceCheckpoint: + s.workspaceCheckpointMu.Lock() + defer s.workspaceCheckpointMu.Unlock() + default: + s.workspaceCheckpointMu.RLock() + defer s.workspaceCheckpointMu.RUnlock() + } return h(ctx, req) } + switch req.Operation.(type) { + case *codev0.CodeRequest_CreateWorkspaceCheckpoint, + *codev0.CodeRequest_RestoreWorkspaceCheckpoint, + *codev0.CodeRequest_ReleaseWorkspaceCheckpoint: + default: + s.workspaceCheckpointMu.RLock() + defer s.workspaceCheckpointMu.RUnlock() + } return s.dispatch(ctx, req) } @@ -308,6 +335,12 @@ func (s *DefaultCodeServer) dispatch(ctx context.Context, req *codev0.CodeReques case *codev0.CodeRequest_ShellExec: return s.shellExec(ctx, op.ShellExec) + case *codev0.CodeRequest_CreateWorkspaceCheckpoint: + return s.createWorkspaceCheckpoint(ctx, op.CreateWorkspaceCheckpoint) + case *codev0.CodeRequest_RestoreWorkspaceCheckpoint: + return s.restoreWorkspaceCheckpoint(ctx, op.RestoreWorkspaceCheckpoint) + case *codev0.CodeRequest_ReleaseWorkspaceCheckpoint: + return s.releaseWorkspaceCheckpoint(ctx, op.ReleaseWorkspaceCheckpoint) default: return nil, failures.GRPC(failures.New(basev0.FailureCode_FAILURE_CODE_UNSUPPORTED_OPERATION, "code.execute", fmt.Sprintf("unknown operation: %T", req.Operation))) @@ -1089,6 +1122,12 @@ func OperationName(req *codev0.CodeRequest) string { // Shell execution (THE sanctioned path for running commands) case *codev0.CodeRequest_ShellExec: return "shell_exec" + case *codev0.CodeRequest_CreateWorkspaceCheckpoint: + return "create_workspace_checkpoint" + case *codev0.CodeRequest_RestoreWorkspaceCheckpoint: + return "restore_workspace_checkpoint" + case *codev0.CodeRequest_ReleaseWorkspaceCheckpoint: + return "release_workspace_checkpoint" default: return "" } diff --git a/code/server_test.go b/code/server_test.go index 02afa8b0..bb5bb09a 100644 --- a/code/server_test.go +++ b/code/server_test.go @@ -175,6 +175,9 @@ func TestOperationName(t *testing.T) { {&codev0.CodeRequest{Operation: &codev0.CodeRequest_GetInstructionIndex{}}, "get_instruction_index"}, {&codev0.CodeRequest{Operation: &codev0.CodeRequest_GetSourceManifest{}}, "get_source_manifest"}, {&codev0.CodeRequest{Operation: &codev0.CodeRequest_ListDependencies{}}, "list_dependencies"}, + {&codev0.CodeRequest{Operation: &codev0.CodeRequest_CreateWorkspaceCheckpoint{}}, "create_workspace_checkpoint"}, + {&codev0.CodeRequest{Operation: &codev0.CodeRequest_RestoreWorkspaceCheckpoint{}}, "restore_workspace_checkpoint"}, + {&codev0.CodeRequest{Operation: &codev0.CodeRequest_ReleaseWorkspaceCheckpoint{}}, "release_workspace_checkpoint"}, {&codev0.CodeRequest{}, ""}, } for _, tt := range tests { diff --git a/code/workspace_checkpoint.go b/code/workspace_checkpoint.go new file mode 100644 index 00000000..e670baf1 --- /dev/null +++ b/code/workspace_checkpoint.go @@ -0,0 +1,1148 @@ +package code + +import ( + "archive/tar" + "context" + "crypto/rand" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "io/fs" + "math" + "os" + "os/exec" + "path/filepath" + "regexp" + "sort" + "strings" + "time" + + "github.com/codefly-dev/core/failures" + basev0 "github.com/codefly-dev/core/generated/go/codefly/base/v0" + codev0 "github.com/codefly-dev/core/generated/go/codefly/services/code/v0" + "github.com/codefly-dev/core/resources" + "google.golang.org/protobuf/types/known/timestamppb" +) + +const workspaceCheckpointSchemaVersion = 1 + +var workspaceCheckpointIDPattern = regexp.MustCompile(`^wcp_[0-9a-f]{64}$`) + +type workspaceCheckpointFile struct { + Path string `json:"path"` + Mode uint32 `json:"mode"` + Kind string `json:"kind"` + Digest string `json:"digest"` + Size int64 `json:"size"` + Link string `json:"link,omitempty"` +} + +type workspaceCheckpointCapacity struct { + AuthorityID string `json:"authority_id"` + TotalBytes uint64 `json:"total_bytes"` + AvailableBytes uint64 `json:"available_bytes"` + RequiredBytes uint64 `json:"required_bytes"` + ProjectedBytes uint64 `json:"projected_available_bytes"` + ShortfallBytes uint64 `json:"shortfall_bytes"` + Admitted bool `json:"admitted"` + AllocationUnitBytes uint64 `json:"allocation_unit_bytes"` +} + +type workspaceCheckpointRestore struct { + AuthorizationID string `json:"authorization_id"` + IdempotencyKey string `json:"idempotency_key"` + ExpectedVersion string `json:"expected_version"` + PriorVersion string `json:"prior_version"` + PriorDigest string `json:"prior_digest"` + RestoredAt time.Time `json:"restored_at,omitempty"` +} + +type workspaceCheckpointMetadata struct { + SchemaVersion int `json:"schema_version"` + CheckpointID string `json:"checkpoint_id"` + Nonce string `json:"nonce"` + WorkspaceID string `json:"workspace_id"` + LeaseID string `json:"lease_id"` + SourceVersion string `json:"source_version"` + SourceDigest string `json:"source_digest"` + CallerID string `json:"caller_id"` + IdempotencyKey string `json:"idempotency_key"` + Retention basev0.WorkspaceCheckpointRetentionIntent `json:"retention"` + CreatedAt time.Time `json:"created_at"` + ArchiveSHA256 string `json:"archive_sha256"` + Files []workspaceCheckpointFile `json:"files"` + Capacity workspaceCheckpointCapacity `json:"capacity"` + Restores []workspaceCheckpointRestore `json:"restores,omitempty"` + ReleasedAt time.Time `json:"released_at,omitempty"` +} + +func (s *DefaultCodeServer) createWorkspaceCheckpoint(ctx context.Context, req *basev0.CreateWorkspaceCheckpointRequest) (*codev0.CodeResponse, error) { + s.workspaceCheckpointMu.Lock() + defer s.workspaceCheckpointMu.Unlock() + if failure := validateCreateWorkspaceCheckpoint(ctx, req); failure != nil { + return createWorkspaceCheckpointResult(nil, failure), nil + } + store, failure := s.workspaceCheckpointStoreDir() + if failure != nil { + return createWorkspaceCheckpointResult(nil, failure), nil + } + if err := os.MkdirAll(store, 0o700); err != nil { + return createWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.create-workspace-checkpoint", err)), nil + } + metadata, err := findWorkspaceCheckpointByIdempotency(store, req.GetLease(), req.GetCallerId(), req.GetIdempotencyKey()) + if err != nil { + return createWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.create-workspace-checkpoint", err)), nil + } + if metadata != nil { + if metadata.SourceVersion != req.GetExpectedWorkspaceVersion() || metadata.Retention != req.GetRetentionIntent() { + return createWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_CONFLICT, "code.create-workspace-checkpoint", errors.New("idempotency key was already used for another checkpoint request"))), nil + } + return createWorkspaceCheckpointResult(workspaceCheckpointProto(metadata), nil), nil + } + files, state, err := s.currentWorkspaceCheckpointState(ctx) + if err != nil { + return createWorkspaceCheckpointResult(nil, checkpointOperationFailure("code.create-workspace-checkpoint", err)), nil + } + if state.GetVersion() != req.GetExpectedWorkspaceVersion() { + return createWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_PRECONDITION_FAILED, "code.create-workspace-checkpoint", errors.New("workspace version is stale"))), nil + } + id, nonce, err := newWorkspaceCheckpointID(req.GetLease(), state.GetDigest()) + if err != nil { + return createWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_INTERNAL, "code.create-workspace-checkpoint", err)), nil + } + metadata = &workspaceCheckpointMetadata{ + SchemaVersion: workspaceCheckpointSchemaVersion, + CheckpointID: id, Nonce: nonce, WorkspaceID: req.GetLease().GetWorkspaceId(), LeaseID: req.GetLease().GetLeaseId(), + SourceVersion: state.GetVersion(), SourceDigest: state.GetDigest(), CallerID: req.GetCallerId(), + IdempotencyKey: req.GetIdempotencyKey(), Retention: req.GetRetentionIntent(), CreatedAt: time.Now().UTC(), Files: files, + } + filesystem, err := resources.InspectStorageFilesystem(store) + if err != nil { + return createWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.create-workspace-checkpoint", err)), nil + } + required, err := workspaceCheckpointRequiredBytes(files, metadata, filesystem.AllocationUnitBytes) + if err != nil { + return createWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_RESOURCE_EXHAUSTED, "code.create-workspace-checkpoint", err)), nil + } + metadata.Capacity = workspaceCheckpointCapacity{ + AuthorityID: filesystem.AuthorityID, TotalBytes: filesystem.TotalBytes, AvailableBytes: filesystem.AvailableBytes, + RequiredBytes: required, AllocationUnitBytes: filesystem.AllocationUnitBytes, + } + if filesystem.AvailableBytes >= required { + metadata.Capacity.Admitted = true + metadata.Capacity.ProjectedBytes = filesystem.AvailableBytes - required + } else { + metadata.Capacity.ShortfallBytes = required - filesystem.AvailableBytes + return createWorkspaceCheckpointResult(workspaceCheckpointProto(metadata), checkpointFailure(basev0.FailureCode_FAILURE_CODE_RESOURCE_EXHAUSTED, "code.create-workspace-checkpoint", errors.New("checkpoint storage capacity was not admitted"))), nil + } + archivePath := workspaceCheckpointArchivePath(store, id) + archiveTemp, err := os.CreateTemp(store, ".checkpoint-*.tar") + if err != nil { + return createWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.create-workspace-checkpoint", err)), nil + } + archiveTempPath := archiveTemp.Name() + archiveCommitted := false + defer func() { + _ = archiveTemp.Close() + _ = os.Remove(archiveTempPath) + if !archiveCommitted { + _ = os.Remove(archivePath) + } + }() + hasher := sha256.New() + if err := s.writeWorkspaceCheckpointArchive(ctx, io.MultiWriter(archiveTemp, hasher), files); err != nil { + return createWorkspaceCheckpointResult(nil, checkpointOperationFailure("code.create-workspace-checkpoint", err)), nil + } + if err := archiveTemp.Sync(); err != nil { + return createWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.create-workspace-checkpoint", err)), nil + } + if err := archiveTemp.Close(); err != nil { + return createWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.create-workspace-checkpoint", err)), nil + } + metadata.ArchiveSHA256 = hex.EncodeToString(hasher.Sum(nil)) + if err := os.Rename(archiveTempPath, archivePath); err != nil { + return createWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.create-workspace-checkpoint", err)), nil + } + if err := validateWorkspaceCheckpointArchiveContents(ctx, archivePath, files); err != nil { + return createWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_PRECONDITION_FAILED, "code.create-workspace-checkpoint", fmt.Errorf("workspace changed while checkpointing: %w", err))), nil + } + if err := writeWorkspaceCheckpointMetadata(store, metadata); err != nil { + return createWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.create-workspace-checkpoint", err)), nil + } + archiveCommitted = true + return createWorkspaceCheckpointResult(workspaceCheckpointProto(metadata), nil), nil +} + +func (s *DefaultCodeServer) restoreWorkspaceCheckpoint(ctx context.Context, req *basev0.RestoreWorkspaceCheckpointRequest) (*codev0.CodeResponse, error) { + s.workspaceCheckpointMu.Lock() + defer s.workspaceCheckpointMu.Unlock() + if failure := validateRestoreWorkspaceCheckpoint(ctx, req); failure != nil { + return restoreWorkspaceCheckpointResult(nil, failure), nil + } + store, failure := s.workspaceCheckpointStoreDir() + if failure != nil { + return restoreWorkspaceCheckpointResult(nil, failure), nil + } + metadata, err := readWorkspaceCheckpointMetadata(store, req.GetCheckpointId()) + if err != nil { + return restoreWorkspaceCheckpointResult(nil, checkpointReadFailure("code.restore-workspace-checkpoint", err)), nil + } + if !sameWorkspaceCheckpointLease(metadata, req.GetLease()) { + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_PERMISSION_DENIED, "code.restore-workspace-checkpoint", errors.New("checkpoint belongs to another workspace lease"))), nil + } + if !metadata.ReleasedAt.IsZero() { + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_PRECONDITION_FAILED, "code.restore-workspace-checkpoint", errors.New("checkpoint has been released"))), nil + } + for index := range metadata.Restores { + restored := &metadata.Restores[index] + if restored.AuthorizationID != req.GetAuthorizationId() || restored.IdempotencyKey != req.GetIdempotencyKey() { + continue + } + if restored.ExpectedVersion != req.GetExpectedWorkspaceVersion() { + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_CONFLICT, "code.restore-workspace-checkpoint", errors.New("idempotency key was already used for another restore request"))), nil + } + if !restored.RestoredAt.IsZero() { + return restoreWorkspaceCheckpointResult(workspaceCheckpointRestoreResponse(metadata, restored), nil), nil + } + _, current, stateErr := s.currentWorkspaceCheckpointState(ctx) + if stateErr != nil { + return restoreWorkspaceCheckpointResult(nil, checkpointOperationFailure("code.restore-workspace-checkpoint", stateErr)), nil + } + if current.GetVersion() == metadata.SourceVersion { + restored.RestoredAt = time.Now().UTC() + if err := writeWorkspaceCheckpointMetadata(store, metadata); err != nil { + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.restore-workspace-checkpoint", err)), nil + } + return restoreWorkspaceCheckpointResult(workspaceCheckpointRestoreResponse(metadata, restored), nil), nil + } + if current.GetVersion() != restored.PriorVersion { + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_CONFLICT, "code.restore-workspace-checkpoint", errors.New("workspace diverged while recovering an interrupted restore"))), nil + } + return s.completeWorkspaceCheckpointRestore(ctx, store, metadata, restored) + } + _, current, err := s.currentWorkspaceCheckpointState(ctx) + if err != nil { + return restoreWorkspaceCheckpointResult(nil, checkpointOperationFailure("code.restore-workspace-checkpoint", err)), nil + } + if current.GetVersion() != req.GetExpectedWorkspaceVersion() { + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_PRECONDITION_FAILED, "code.restore-workspace-checkpoint", errors.New("workspace version is stale"))), nil + } + metadata.Restores = append(metadata.Restores, workspaceCheckpointRestore{ + AuthorizationID: req.GetAuthorizationId(), IdempotencyKey: req.GetIdempotencyKey(), ExpectedVersion: req.GetExpectedWorkspaceVersion(), + PriorVersion: current.GetVersion(), PriorDigest: current.GetDigest(), + }) + restored := &metadata.Restores[len(metadata.Restores)-1] + if err := writeWorkspaceCheckpointMetadata(store, metadata); err != nil { + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.restore-workspace-checkpoint", err)), nil + } + return s.completeWorkspaceCheckpointRestore(ctx, store, metadata, restored) +} + +func (s *DefaultCodeServer) completeWorkspaceCheckpointRestore(ctx context.Context, store string, metadata *workspaceCheckpointMetadata, restored *workspaceCheckpointRestore) (*codev0.CodeResponse, error) { + archivePath := workspaceCheckpointArchivePath(store, metadata.CheckpointID) + if err := verifyWorkspaceCheckpointArchive(archivePath, metadata.ArchiveSHA256); err != nil { + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_INTEGRITY_FAILED, "code.restore-workspace-checkpoint", err)), nil + } + parent := filepath.Dir(s.SourceDir) + stage, err := os.MkdirTemp(parent, ".codefly-workspace-restore-*") + if err != nil { + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.restore-workspace-checkpoint", err)), nil + } + stageOwned := true + defer func() { + if stageOwned { + _ = os.RemoveAll(stage) + } + }() + if err := extractWorkspaceCheckpointArchive(ctx, archivePath, stage, metadata.Files); err != nil { + return restoreWorkspaceCheckpointResult(nil, checkpointOperationFailure("code.restore-workspace-checkpoint", err)), nil + } + if err := verifyWorkspaceCheckpointFiles(ctx, stage, metadata.Files); err != nil { + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_INTEGRITY_FAILED, "code.restore-workspace-checkpoint", err)), nil + } + rootInfo, err := os.Stat(s.SourceDir) + if err != nil { + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.restore-workspace-checkpoint", err)), nil + } + if err := os.Chmod(stage, rootInfo.Mode().Perm()); err != nil { + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.restore-workspace-checkpoint", err)), nil + } + currentFiles, current, err := s.currentWorkspaceCheckpointState(ctx) + if err != nil { + return restoreWorkspaceCheckpointResult(nil, checkpointOperationFailure("code.restore-workspace-checkpoint", err)), nil + } + if current.GetVersion() != restored.PriorVersion { + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_PRECONDITION_FAILED, "code.restore-workspace-checkpoint", errors.New("workspace changed during restore preparation"))), nil + } + priorGitlinks := make(map[string]string) + for _, file := range currentFiles { + if file.Kind == "gitlink" { + priorGitlinks[file.Path] = file.Digest + } + } + backup := filepath.Join(parent, ".codefly-workspace-backup-"+metadata.CheckpointID[4:20]) + if _, err := os.Lstat(backup); !errors.Is(err, os.ErrNotExist) { + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_CONFLICT, "code.restore-workspace-checkpoint", errors.New("workspace restore backup already exists"))), nil + } + if err := os.Rename(s.SourceDir, backup); err != nil { + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.restore-workspace-checkpoint", err)), nil + } + if err := os.Rename(stage, s.SourceDir); err != nil { + if rollbackErr := os.Rename(backup, s.SourceDir); rollbackErr != nil { + err = errors.Join(err, fmt.Errorf("restore workspace rollback: %w", rollbackErr)) + } + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.restore-workspace-checkpoint", err)), nil + } + stageOwned = false + movedMetadata := make([]string, 0, 4) + preserved := []string{".git", ".jj", ".hg", ".svn"} + for _, file := range metadata.Files { + if file.Kind == "gitlink" { + preserved = append(preserved, filepath.FromSlash(file.Path)) + } + } + for _, name := range preserved { + oldPath := filepath.Join(backup, name) + if _, err := os.Lstat(oldPath); errors.Is(err, os.ErrNotExist) { + continue + } else if err != nil { + err = errors.Join(err, s.rollbackWorkspaceCheckpointRestore(backup, movedMetadata, priorGitlinks)) + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.restore-workspace-checkpoint", err)), nil + } + newPath := filepath.Join(s.SourceDir, name) + if err := os.MkdirAll(filepath.Dir(newPath), 0o755); err != nil { + err = errors.Join(err, s.rollbackWorkspaceCheckpointRestore(backup, movedMetadata, priorGitlinks)) + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.restore-workspace-checkpoint", err)), nil + } + if err := os.Rename(oldPath, newPath); err != nil { + err = errors.Join(err, s.rollbackWorkspaceCheckpointRestore(backup, movedMetadata, priorGitlinks)) + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.restore-workspace-checkpoint", err)), nil + } + movedMetadata = append(movedMetadata, name) + } + for _, file := range metadata.Files { + if file.Kind != "gitlink" { + continue + } + command := exec.Command("git", "checkout", "--detach", "--force", file.Digest) + command.Dir = filepath.Join(s.SourceDir, filepath.FromSlash(file.Path)) + if output, err := command.CombinedOutput(); err != nil { + restoreErr := fmt.Errorf("restore gitlink %s: %w: %s", file.Path, err, strings.TrimSpace(string(output))) + restoreErr = errors.Join(restoreErr, s.rollbackWorkspaceCheckpointRestore(backup, movedMetadata, priorGitlinks)) + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.restore-workspace-checkpoint", restoreErr)), nil + } + } + _, finalState, err := s.currentWorkspaceCheckpointState(context.Background()) + if err != nil || finalState.GetVersion() != metadata.SourceVersion { + if err == nil { + err = errors.New("restored workspace does not match checkpoint source identity") + } + err = errors.Join(err, s.rollbackWorkspaceCheckpointRestore(backup, movedMetadata, priorGitlinks)) + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_INTEGRITY_FAILED, "code.restore-workspace-checkpoint", err)), nil + } + if err := os.RemoveAll(backup); err != nil { + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.restore-workspace-checkpoint", err)), nil + } + restored.RestoredAt = time.Now().UTC() + if err := writeWorkspaceCheckpointMetadata(store, metadata); err != nil { + return restoreWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.restore-workspace-checkpoint", err)), nil + } + return restoreWorkspaceCheckpointResult(workspaceCheckpointRestoreResponse(metadata, restored), nil), nil +} + +func (s *DefaultCodeServer) rollbackWorkspaceCheckpointRestore(backup string, moved []string, priorGitlinks map[string]string) error { + var rollbackErrors []error + for index := len(moved) - 1; index >= 0; index-- { + if err := os.Rename(filepath.Join(s.SourceDir, moved[index]), filepath.Join(backup, moved[index])); err != nil { + rollbackErrors = append(rollbackErrors, err) + } + } + marker, err := os.CreateTemp(filepath.Dir(backup), ".codefly-workspace-failed-*") + if err != nil { + return errors.Join(append(rollbackErrors, err)...) + } + failed := marker.Name() + if err := marker.Close(); err != nil { + rollbackErrors = append(rollbackErrors, err) + } + if err := os.Remove(failed); err != nil { + rollbackErrors = append(rollbackErrors, err) + return errors.Join(rollbackErrors...) + } + if err := os.Rename(s.SourceDir, failed); err != nil { + rollbackErrors = append(rollbackErrors, err) + return errors.Join(rollbackErrors...) + } + if err := os.Rename(backup, s.SourceDir); err != nil { + rollbackErrors = append(rollbackErrors, err) + return errors.Join(rollbackErrors...) + } + for path, digest := range priorGitlinks { + command := exec.Command("git", "checkout", "--detach", "--force", digest) + command.Dir = filepath.Join(s.SourceDir, filepath.FromSlash(path)) + if output, err := command.CombinedOutput(); err != nil { + rollbackErrors = append(rollbackErrors, fmt.Errorf("restore prior gitlink %s: %w: %s", path, err, strings.TrimSpace(string(output)))) + } + } + if err := os.RemoveAll(failed); err != nil { + rollbackErrors = append(rollbackErrors, err) + } + return errors.Join(rollbackErrors...) +} + +func (s *DefaultCodeServer) releaseWorkspaceCheckpoint(ctx context.Context, req *basev0.ReleaseWorkspaceCheckpointRequest) (*codev0.CodeResponse, error) { + s.workspaceCheckpointMu.Lock() + defer s.workspaceCheckpointMu.Unlock() + if failure := validateReleaseWorkspaceCheckpoint(ctx, req); failure != nil { + return releaseWorkspaceCheckpointResult(nil, failure), nil + } + store, failure := s.workspaceCheckpointStoreDir() + if failure != nil { + return releaseWorkspaceCheckpointResult(nil, failure), nil + } + metadata, err := readWorkspaceCheckpointMetadata(store, req.GetCheckpointId()) + if err != nil { + return releaseWorkspaceCheckpointResult(nil, checkpointReadFailure("code.release-workspace-checkpoint", err)), nil + } + if !sameWorkspaceCheckpointLease(metadata, req.GetLease()) { + return releaseWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_PERMISSION_DENIED, "code.release-workspace-checkpoint", errors.New("checkpoint belongs to another workspace lease"))), nil + } + if metadata.ReleasedAt.IsZero() { + metadata.ReleasedAt = time.Now().UTC() + if err := writeWorkspaceCheckpointMetadata(store, metadata); err != nil { + return releaseWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.release-workspace-checkpoint", err)), nil + } + } + if err := os.Remove(workspaceCheckpointArchivePath(store, metadata.CheckpointID)); err != nil && !errors.Is(err, os.ErrNotExist) { + return releaseWorkspaceCheckpointResult(nil, checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, "code.release-workspace-checkpoint", err)), nil + } + receipt := &basev0.WorkspaceCheckpointReleaseReceipt{ + CheckpointId: metadata.CheckpointID, Lease: workspaceCheckpointLeaseProto(metadata), ReleasedAt: timestamppb.New(metadata.ReleasedAt), + } + return releaseWorkspaceCheckpointResult(receipt, nil), nil +} + +func (s *DefaultCodeServer) currentWorkspaceCheckpointState(ctx context.Context) ([]workspaceCheckpointFile, *basev0.WorkspaceStateIdentity, error) { + if err := ctx.Err(); err != nil { + return nil, nil, err + } + files, err := s.workspaceCheckpointFiles(ctx) + if err != nil { + return nil, nil, err + } + state, err := workspaceCheckpointStateForFiles(files) + if err != nil { + return nil, nil, err + } + return files, state, nil +} + +func (s *DefaultCodeServer) workspaceCheckpointFiles(ctx context.Context) ([]workspaceCheckpointFile, error) { + tracked, trackedDirectories, gitlinks, err := s.workspaceCheckpointGitState(ctx) + if err != nil { + return nil, err + } + files := make([]workspaceCheckpointFile, 0, len(tracked)+128) + for _, gitlink := range gitlinks { + files = append(files, gitlink) + } + err = filepath.WalkDir(s.SourceDir, func(path string, entry fs.DirEntry, walkErr error) error { + if walkErr != nil { + return walkErr + } + if err := ctx.Err(); err != nil { + return err + } + relative, err := filepath.Rel(s.SourceDir, path) + if err != nil { + return err + } + relative = filepath.ToSlash(relative) + if relative == "." { + return nil + } + if _, gitlink := gitlinks[relative]; gitlink { + if entry.IsDir() { + return fs.SkipDir + } + return nil + } + if entry.IsDir() { + if workspaceCheckpointAlwaysExcluded(relative) { + return fs.SkipDir + } + if isExcludedSourceDirectory(entry.Name()) { + if _, ok := trackedDirectories[relative]; !ok { + return fs.SkipDir + } + } + return nil + } + if workspaceCheckpointAlwaysExcluded(relative) { + return nil + } + if workspaceCheckpointGeneratedPath(relative) { + if _, ok := tracked[relative]; !ok { + return nil + } + } + info, err := os.Lstat(path) + if err != nil { + return err + } + record := workspaceCheckpointFile{Path: relative} + switch { + case info.Mode()&os.ModeSymlink != 0: + record.Mode, record.Kind = 0o120000, "symlink" + record.Link, err = os.Readlink(path) + if err != nil { + return err + } + record.Size = int64(len(record.Link)) + sum := sha256.Sum256([]byte(record.Link)) + record.Digest = hex.EncodeToString(sum[:]) + case info.Mode().IsRegular(): + record.Mode, record.Kind = 0o100644, "file" + if info.Mode().Perm()&0o111 != 0 { + record.Mode = 0o100755 + } + body, err := os.ReadFile(path) + if err != nil { + return err + } + record.Size = int64(len(body)) + sum := sha256.Sum256(body) + record.Digest = hex.EncodeToString(sum[:]) + default: + return fmt.Errorf("checkpoint path %q has unsupported mode %s", relative, info.Mode()) + } + files = append(files, record) + return nil + }) + if err != nil { + return nil, fmt.Errorf("inspect workspace checkpoint state: %w", err) + } + sort.Slice(files, func(i, j int) bool { return files[i].Path < files[j].Path }) + return files, nil +} + +func (s *DefaultCodeServer) workspaceCheckpointGitState(ctx context.Context) (map[string]struct{}, map[string]struct{}, map[string]workspaceCheckpointFile, error) { + tracked := map[string]struct{}{} + directories := map[string]struct{}{} + gitlinks := map[string]workspaceCheckpointFile{} + inside, err := s.runGit(ctx, "rev-parse", "--is-inside-work-tree") + if err != nil || strings.TrimSpace(inside) != "true" { + return tracked, directories, gitlinks, nil + } + output, err := s.runGit(ctx, "ls-files", "--stage", "-z", "--", ".") + if err != nil { + return nil, nil, nil, fmt.Errorf("inspect Git index: %w", err) + } + for _, item := range strings.Split(output, "\x00") { + if item == "" { + continue + } + header, name, ok := strings.Cut(item, "\t") + fields := strings.Fields(header) + name = filepath.ToSlash(name) + if !ok || len(fields) != 3 || fields[2] != "0" || name == "" || strings.HasPrefix(name, "../") { + return nil, nil, nil, fmt.Errorf("invalid Git index entry %q", item) + } + tracked[name] = struct{}{} + for directory := filepath.ToSlash(filepath.Dir(name)); directory != "." && directory != "/"; directory = filepath.ToSlash(filepath.Dir(directory)) { + directories[directory] = struct{}{} + } + if fields[0] != "160000" { + continue + } + resolved, err := s.runGit(ctx, "-C", name, "rev-parse", "--verify", "HEAD^{commit}") + if err != nil { + return nil, nil, nil, fmt.Errorf("resolve gitlink %s: %w", name, err) + } + dirty, err := s.runGit(ctx, "-C", name, "status", "--porcelain=v1", "--untracked-files=normal") + if err != nil || strings.TrimSpace(dirty) != "" { + return nil, nil, nil, fmt.Errorf("gitlink %s must be initialized and clean", name) + } + digest := strings.TrimSpace(resolved) + gitlinks[name] = workspaceCheckpointFile{Path: name, Mode: 0o160000, Kind: "gitlink", Digest: digest, Size: -1} + } + return tracked, directories, gitlinks, nil +} + +func (s *DefaultCodeServer) writeWorkspaceCheckpointArchive(ctx context.Context, destination io.Writer, files []workspaceCheckpointFile) error { + archive := tar.NewWriter(destination) + for _, file := range files { + if err := ctx.Err(); err != nil { + _ = archive.Close() + return err + } + if file.Kind == "gitlink" { + continue + } + header := &tar.Header{Name: file.Path, Mode: int64(file.Mode & 0o777), ModTime: time.Unix(0, 0), AccessTime: time.Unix(0, 0), ChangeTime: time.Unix(0, 0)} + switch file.Kind { + case "file": + header.Typeflag, header.Size = tar.TypeReg, file.Size + case "symlink": + header.Typeflag, header.Linkname = tar.TypeSymlink, file.Link + default: + _ = archive.Close() + return fmt.Errorf("unsupported checkpoint entry kind %q", file.Kind) + } + if err := archive.WriteHeader(header); err != nil { + return err + } + if file.Kind == "file" { + source, err := os.Open(filepath.Join(s.SourceDir, filepath.FromSlash(file.Path))) + if err != nil { + return err + } + written, copyErr := io.Copy(archive, source) + closeErr := source.Close() + if copyErr != nil { + return copyErr + } + if closeErr != nil { + return closeErr + } + if written != file.Size { + return fmt.Errorf("checkpoint source %q changed while archiving", file.Path) + } + } + } + return archive.Close() +} + +func extractWorkspaceCheckpointArchive(ctx context.Context, archivePath, destination string, files []workspaceCheckpointFile) error { + source, err := os.Open(archivePath) + if err != nil { + return err + } + defer source.Close() + expected := make(map[string]workspaceCheckpointFile, len(files)) + for _, file := range files { + if file.Kind != "gitlink" { + expected[file.Path] = file + } + } + seen := make(map[string]struct{}, len(expected)) + reader := tar.NewReader(source) + for { + if err := ctx.Err(); err != nil { + return err + } + header, err := reader.Next() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return err + } + record, ok := expected[header.Name] + if !ok { + return fmt.Errorf("archive contains unexpected path %q", header.Name) + } + if _, duplicate := seen[header.Name]; duplicate { + return fmt.Errorf("archive repeats path %q", header.Name) + } + seen[header.Name] = struct{}{} + target, err := resolvePath(destination, filepath.FromSlash(header.Name)) + if err != nil { + return err + } + if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil { + return err + } + switch record.Kind { + case "file": + if header.Typeflag != tar.TypeReg || header.Size != record.Size { + return fmt.Errorf("archive metadata for %q does not match checkpoint", header.Name) + } + file, err := os.OpenFile(target, os.O_CREATE|os.O_EXCL|os.O_WRONLY, fs.FileMode(record.Mode&0o777)) + if err != nil { + return err + } + _, copyErr := io.CopyN(file, reader, record.Size) + closeErr := file.Close() + if copyErr != nil { + return copyErr + } + if closeErr != nil { + return closeErr + } + case "symlink": + if header.Typeflag != tar.TypeSymlink || header.Linkname != record.Link { + return fmt.Errorf("archive symlink %q does not match checkpoint", header.Name) + } + if err := os.Symlink(record.Link, target); err != nil { + return err + } + default: + return fmt.Errorf("unsupported checkpoint entry kind %q", record.Kind) + } + } + if len(seen) != len(expected) { + return errors.New("archive does not contain every checkpoint path") + } + return nil +} + +func verifyWorkspaceCheckpointFiles(ctx context.Context, root string, files []workspaceCheckpointFile) error { + for _, file := range files { + if err := ctx.Err(); err != nil { + return err + } + if file.Kind == "gitlink" { + continue + } + path := filepath.Join(root, filepath.FromSlash(file.Path)) + info, err := os.Lstat(path) + if err != nil { + return err + } + var body []byte + if file.Kind == "symlink" { + target, err := os.Readlink(path) + if err != nil { + return err + } + body = []byte(target) + } else { + body, err = os.ReadFile(path) + if err != nil { + return err + } + if uint32(info.Mode().Perm()) != file.Mode&0o777 { + return fmt.Errorf("checkpoint mode mismatch for %q", file.Path) + } + } + digest := sha256.Sum256(body) + if int64(len(body)) != file.Size || hex.EncodeToString(digest[:]) != file.Digest { + return fmt.Errorf("checkpoint content mismatch for %q", file.Path) + } + } + return nil +} + +func verifyWorkspaceCheckpointArchive(path, expected string) error { + file, err := os.Open(path) + if err != nil { + return err + } + defer file.Close() + hasher := sha256.New() + if _, err := io.Copy(hasher, file); err != nil { + return err + } + if hex.EncodeToString(hasher.Sum(nil)) != expected { + return errors.New("checkpoint archive digest mismatch") + } + return nil +} + +func validateWorkspaceCheckpointArchiveContents(ctx context.Context, path string, files []workspaceCheckpointFile) error { + source, err := os.Open(path) + if err != nil { + return err + } + defer source.Close() + expected := make(map[string]workspaceCheckpointFile, len(files)) + for _, file := range files { + if file.Kind != "gitlink" { + expected[file.Path] = file + } + } + seen := make(map[string]struct{}, len(expected)) + reader := tar.NewReader(source) + for { + if err := ctx.Err(); err != nil { + return err + } + header, err := reader.Next() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return err + } + record, ok := expected[header.Name] + if !ok { + return fmt.Errorf("archive contains unexpected path %q", header.Name) + } + if _, duplicate := seen[header.Name]; duplicate { + return fmt.Errorf("archive repeats path %q", header.Name) + } + seen[header.Name] = struct{}{} + switch record.Kind { + case "file": + if header.Typeflag != tar.TypeReg || header.Size != record.Size { + return fmt.Errorf("archive metadata for %q does not match", header.Name) + } + hasher := sha256.New() + written, err := io.CopyN(hasher, reader, record.Size) + if err != nil || written != record.Size || hex.EncodeToString(hasher.Sum(nil)) != record.Digest { + return fmt.Errorf("archive content for %q does not match", header.Name) + } + case "symlink": + if header.Typeflag != tar.TypeSymlink || header.Linkname != record.Link { + return fmt.Errorf("archive symlink %q does not match", header.Name) + } + default: + return fmt.Errorf("unsupported checkpoint entry kind %q", record.Kind) + } + } + if len(seen) != len(expected) { + return errors.New("archive does not contain every checkpoint path") + } + return nil +} + +func (s *DefaultCodeServer) workspaceCheckpointStoreDir() (string, *basev0.Failure) { + switch s.FS.(type) { + case LocalVFS, *LocalVFS: + default: + return "", checkpointFailure(basev0.FailureCode_FAILURE_CODE_UNSUPPORTED_OPERATION, "code.workspace-checkpoint", errors.New("workspace checkpoints require the local filesystem boundary")) + } + if strings.TrimSpace(s.workspaceCheckpointStore) != "" { + absolute, err := filepath.Abs(filepath.Clean(s.workspaceCheckpointStore)) + if err != nil { + return "", checkpointFailure(basev0.FailureCode_FAILURE_CODE_INVALID_CONFIGURATION, "code.workspace-checkpoint", err) + } + if relative, err := filepath.Rel(s.SourceDir, absolute); err == nil && !filepath.IsAbs(relative) && relative != ".." && !strings.HasPrefix(relative, ".."+string(filepath.Separator)) { + if relative == "." { + return "", checkpointFailure(basev0.FailureCode_FAILURE_CODE_INVALID_CONFIGURATION, "code.workspace-checkpoint", errors.New("workspace checkpoint store must be outside project bytes")) + } + first, _, _ := strings.Cut(filepath.ToSlash(relative), "/") + switch first { + case ".git", ".jj", ".hg", ".svn": + default: + return "", checkpointFailure(basev0.FailureCode_FAILURE_CODE_INVALID_CONFIGURATION, "code.workspace-checkpoint", errors.New("workspace checkpoint store must be outside project bytes")) + } + } + return absolute, nil + } + command := exec.Command("git", "rev-parse", "--path-format=absolute", "--git-common-dir") + command.Dir = s.SourceDir + if output, err := command.Output(); err == nil { + root := strings.TrimSpace(string(output)) + if root != "" { + return filepath.Join(root, "codefly", "workspace-checkpoints"), nil + } + } + cache, err := os.UserCacheDir() + if err != nil { + return "", checkpointFailure(basev0.FailureCode_FAILURE_CODE_INVALID_CONFIGURATION, "code.workspace-checkpoint", err) + } + absolute, err := filepath.Abs(s.SourceDir) + if err != nil { + return "", checkpointFailure(basev0.FailureCode_FAILURE_CODE_INVALID_CONFIGURATION, "code.workspace-checkpoint", err) + } + digest := sha256.Sum256([]byte(absolute)) + return filepath.Join(cache, "codefly", "workspace-checkpoints", hex.EncodeToString(digest[:16])), nil +} + +func workspaceCheckpointRequiredBytes(files []workspaceCheckpointFile, metadata *workspaceCheckpointMetadata, allocationUnit uint64) (uint64, error) { + if allocationUnit == 0 { + return 0, errors.New("checkpoint storage allocation unit is zero") + } + required := allocationUnit * 4 + for _, file := range files { + entryBytes := uint64(0) + if file.Size > 0 { + entryBytes = uint64(file.Size) + } + allocated, ok := checkpointRoundBytes(entryBytes+allocationUnit, allocationUnit) + if !ok || math.MaxUint64-required < allocated { + return 0, errors.New("checkpoint storage requirement overflows uint64") + } + required += allocated + } + encoded, err := json.Marshal(metadata) + if err != nil { + return 0, err + } + metadataBytes, ok := checkpointRoundBytes(uint64(len(encoded))+allocationUnit, allocationUnit) + if !ok || math.MaxUint64-required < metadataBytes { + return 0, errors.New("checkpoint metadata requirement overflows uint64") + } + return required + metadataBytes, nil +} + +func checkpointRoundBytes(size, unit uint64) (uint64, bool) { + remainder := size % unit + if remainder == 0 { + return size, true + } + addition := unit - remainder + if size > math.MaxUint64-addition { + return 0, false + } + return size + addition, true +} + +func writeWorkspaceCheckpointMetadata(store string, metadata *workspaceCheckpointMetadata) error { + encoded, err := json.Marshal(metadata) + if err != nil { + return err + } + temporary, err := os.CreateTemp(store, ".metadata-*.json") + if err != nil { + return err + } + temporaryPath := temporary.Name() + defer os.Remove(temporaryPath) + if err := temporary.Chmod(0o600); err != nil { + _ = temporary.Close() + return err + } + if _, err := temporary.Write(encoded); err != nil { + _ = temporary.Close() + return err + } + if err := temporary.Sync(); err != nil { + _ = temporary.Close() + return err + } + if err := temporary.Close(); err != nil { + return err + } + if err := os.Rename(temporaryPath, workspaceCheckpointMetadataPath(store, metadata.CheckpointID)); err != nil { + return err + } + directory, err := os.Open(store) + if err != nil { + return err + } + defer directory.Close() + return directory.Sync() +} + +func readWorkspaceCheckpointMetadata(store, id string) (*workspaceCheckpointMetadata, error) { + if !workspaceCheckpointIDPattern.MatchString(id) { + return nil, errors.New("invalid checkpoint ID") + } + encoded, err := os.ReadFile(workspaceCheckpointMetadataPath(store, id)) + if err != nil { + return nil, err + } + var metadata workspaceCheckpointMetadata + if err := json.Unmarshal(encoded, &metadata); err != nil { + return nil, err + } + if metadata.SchemaVersion != workspaceCheckpointSchemaVersion || metadata.CheckpointID != id { + return nil, errors.New("checkpoint metadata is invalid") + } + state, err := workspaceCheckpointStateForFiles(metadata.Files) + if err != nil || state.GetVersion() != metadata.SourceVersion || state.GetDigest() != metadata.SourceDigest { + return nil, errors.New("checkpoint source identity is invalid") + } + nonce, err := hex.DecodeString(metadata.Nonce) + if err != nil || len(nonce) != 32 || workspaceCheckpointID(metadata.WorkspaceID, metadata.LeaseID, metadata.SourceDigest, nonce) != id { + return nil, errors.New("checkpoint identity binding is invalid") + } + return &metadata, nil +} + +func findWorkspaceCheckpointByIdempotency(store string, lease *basev0.WorkspaceLeaseIdentity, callerID, key string) (*workspaceCheckpointMetadata, error) { + entries, err := os.ReadDir(store) + if err != nil { + return nil, err + } + for _, entry := range entries { + name := entry.Name() + if entry.IsDir() || !strings.HasSuffix(name, ".json") { + continue + } + id := strings.TrimSuffix(name, ".json") + if !workspaceCheckpointIDPattern.MatchString(id) { + continue + } + metadata, err := readWorkspaceCheckpointMetadata(store, id) + if err != nil { + return nil, err + } + if sameWorkspaceCheckpointLease(metadata, lease) && metadata.CallerID == callerID && metadata.IdempotencyKey == key { + return metadata, nil + } + } + return nil, nil +} + +func newWorkspaceCheckpointID(lease *basev0.WorkspaceLeaseIdentity, sourceDigest string) (string, string, error) { + random := make([]byte, 32) + if _, err := rand.Read(random); err != nil { + return "", "", err + } + return workspaceCheckpointID(lease.GetWorkspaceId(), lease.GetLeaseId(), sourceDigest, random), hex.EncodeToString(random), nil +} + +func workspaceCheckpointID(workspaceID, leaseID, sourceDigest string, nonce []byte) string { + hasher := sha256.New() + _, _ = io.WriteString(hasher, workspaceID) + _, _ = io.WriteString(hasher, "\x00") + _, _ = io.WriteString(hasher, leaseID) + _, _ = io.WriteString(hasher, "\x00") + _, _ = io.WriteString(hasher, sourceDigest) + _, _ = hasher.Write(nonce) + return "wcp_" + hex.EncodeToString(hasher.Sum(nil)) +} + +func workspaceCheckpointStateForFiles(files []workspaceCheckpointFile) (*basev0.WorkspaceStateIdentity, error) { + encoded, err := json.Marshal(files) + if err != nil { + return nil, fmt.Errorf("encode workspace identity: %w", err) + } + digest := sha256.Sum256(encoded) + value := "sha256:" + hex.EncodeToString(digest[:]) + return &basev0.WorkspaceStateIdentity{Version: value, Digest: value}, nil +} + +func workspaceCheckpointMetadataPath(store, id string) string { + return filepath.Join(store, id+".json") +} +func workspaceCheckpointArchivePath(store, id string) string { return filepath.Join(store, id+".tar") } + +func workspaceCheckpointAlwaysExcluded(path string) bool { + for _, component := range strings.Split(filepath.ToSlash(path), "/") { + switch component { + case ".git", ".jj", ".hg", ".svn": + return true + } + } + return false +} + +func workspaceCheckpointGeneratedPath(path string) bool { + for _, component := range strings.Split(filepath.ToSlash(path), "/") { + if isExcludedSourceDirectory(component) { + return true + } + } + return false +} + +func validateCreateWorkspaceCheckpoint(ctx context.Context, req *basev0.CreateWorkspaceCheckpointRequest) *basev0.Failure { + if err := ctx.Err(); err != nil { + return checkpointOperationFailure("code.create-workspace-checkpoint", err) + } + if req == nil || !validWorkspaceCheckpointLease(req.GetLease()) || !validCheckpointValue(req.GetCallerId()) || !validCheckpointValue(req.GetIdempotencyKey()) || !validWorkspaceVersion(req.GetExpectedWorkspaceVersion()) { + return checkpointFailure(basev0.FailureCode_FAILURE_CODE_INVALID_ARGUMENT, "code.create-workspace-checkpoint", errors.New("lease, expected_workspace_version, caller_id, and idempotency_key are required and must be canonical")) + } + if req.GetRetentionIntent() != basev0.WorkspaceCheckpointRetentionIntent_WORKSPACE_CHECKPOINT_RETENTION_INTENT_UNTIL_RELEASE_OR_LEASE_END { + return checkpointFailure(basev0.FailureCode_FAILURE_CODE_INVALID_ARGUMENT, "code.create-workspace-checkpoint", errors.New("retention_intent must be UNTIL_RELEASE_OR_LEASE_END")) + } + return nil +} + +func validateRestoreWorkspaceCheckpoint(ctx context.Context, req *basev0.RestoreWorkspaceCheckpointRequest) *basev0.Failure { + if err := ctx.Err(); err != nil { + return checkpointOperationFailure("code.restore-workspace-checkpoint", err) + } + if req == nil || !workspaceCheckpointIDPattern.MatchString(req.GetCheckpointId()) || !validWorkspaceCheckpointLease(req.GetLease()) || !validWorkspaceVersion(req.GetExpectedWorkspaceVersion()) || !validCheckpointValue(req.GetAuthorizationId()) || !validCheckpointValue(req.GetIdempotencyKey()) { + return checkpointFailure(basev0.FailureCode_FAILURE_CODE_INVALID_ARGUMENT, "code.restore-workspace-checkpoint", errors.New("checkpoint_id, lease, expected_workspace_version, authorization_id, and idempotency_key are required and must be canonical")) + } + return nil +} + +func validateReleaseWorkspaceCheckpoint(ctx context.Context, req *basev0.ReleaseWorkspaceCheckpointRequest) *basev0.Failure { + if err := ctx.Err(); err != nil { + return checkpointOperationFailure("code.release-workspace-checkpoint", err) + } + if req == nil || !workspaceCheckpointIDPattern.MatchString(req.GetCheckpointId()) || !validWorkspaceCheckpointLease(req.GetLease()) || !validCheckpointValue(req.GetCallerId()) || !validCheckpointValue(req.GetIdempotencyKey()) { + return checkpointFailure(basev0.FailureCode_FAILURE_CODE_INVALID_ARGUMENT, "code.release-workspace-checkpoint", errors.New("checkpoint_id, lease, caller_id, and idempotency_key are required and must be canonical")) + } + return nil +} + +func validWorkspaceCheckpointLease(lease *basev0.WorkspaceLeaseIdentity) bool { + return lease != nil && validCheckpointValue(lease.GetWorkspaceId()) && validCheckpointValue(lease.GetLeaseId()) +} + +func validCheckpointValue(value string) bool { + return value != "" && len(value) <= 512 && strings.TrimSpace(value) == value +} + +func validWorkspaceVersion(value string) bool { + if !strings.HasPrefix(value, "sha256:") || len(value) != len("sha256:")+64 { + return false + } + _, err := hex.DecodeString(strings.TrimPrefix(value, "sha256:")) + return err == nil +} + +func sameWorkspaceCheckpointLease(metadata *workspaceCheckpointMetadata, lease *basev0.WorkspaceLeaseIdentity) bool { + return metadata.WorkspaceID == lease.GetWorkspaceId() && metadata.LeaseID == lease.GetLeaseId() +} + +func checkpointFailure(code basev0.FailureCode, operation string, err error) *basev0.Failure { + return failures.New(code, operation, err.Error()) +} + +func checkpointOperationFailure(operation string, err error) *basev0.Failure { + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + return failures.FromError(operation, err) + } + return checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, operation, err) +} + +func checkpointReadFailure(operation string, err error) *basev0.Failure { + if errors.Is(err, os.ErrNotExist) { + return checkpointFailure(basev0.FailureCode_FAILURE_CODE_NOT_FOUND, operation, errors.New("workspace checkpoint not found")) + } + return checkpointFailure(basev0.FailureCode_FAILURE_CODE_IO_FAILED, operation, err) +} + +func createWorkspaceCheckpointResult(checkpoint *basev0.WorkspaceCheckpoint, failure *basev0.Failure) *codev0.CodeResponse { + nested := &basev0.CreateWorkspaceCheckpointResponse{Checkpoint: checkpoint, Failure: failure} + return &codev0.CodeResponse{Failure: failure, Result: &codev0.CodeResponse_CreateWorkspaceCheckpoint{CreateWorkspaceCheckpoint: nested}} +} + +func restoreWorkspaceCheckpointResult(response *basev0.RestoreWorkspaceCheckpointResponse, failure *basev0.Failure) *codev0.CodeResponse { + if response == nil { + response = &basev0.RestoreWorkspaceCheckpointResponse{} + } + response.Failure = failure + return &codev0.CodeResponse{Failure: failure, Result: &codev0.CodeResponse_RestoreWorkspaceCheckpoint{RestoreWorkspaceCheckpoint: response}} +} + +func releaseWorkspaceCheckpointResult(receipt *basev0.WorkspaceCheckpointReleaseReceipt, failure *basev0.Failure) *codev0.CodeResponse { + nested := &basev0.ReleaseWorkspaceCheckpointResponse{Receipt: receipt, Failure: failure} + return &codev0.CodeResponse{Failure: failure, Result: &codev0.CodeResponse_ReleaseWorkspaceCheckpoint{ReleaseWorkspaceCheckpoint: nested}} +} + +func workspaceCheckpointProto(metadata *workspaceCheckpointMetadata) *basev0.WorkspaceCheckpoint { + requirement := &basev0.StorageCapacityRequirement{ + Component: "workspace_checkpoint", Bytes: metadata.Capacity.RequiredBytes, + AuthorityKind: basev0.StorageAuthorityKind_STORAGE_AUTHORITY_KIND_GATEWAY_ROOT, + } + admission := &basev0.StorageCapacityAdmission{ + AuthorityId: metadata.Capacity.AuthorityID, + AuthorityKinds: []basev0.StorageAuthorityKind{basev0.StorageAuthorityKind_STORAGE_AUTHORITY_KIND_GATEWAY_ROOT}, + TotalBytes: metadata.Capacity.TotalBytes, AvailableBytes: metadata.Capacity.AvailableBytes, + Requirements: []*basev0.StorageCapacityRequirement{requirement}, RequiredBytes: metadata.Capacity.RequiredBytes, + ProjectedAvailableBytes: metadata.Capacity.ProjectedBytes, ShortfallBytes: metadata.Capacity.ShortfallBytes, Admitted: metadata.Capacity.Admitted, + } + return &basev0.WorkspaceCheckpoint{ + CheckpointId: metadata.CheckpointID, Lease: workspaceCheckpointLeaseProto(metadata), + Source: &basev0.WorkspaceStateIdentity{Version: metadata.SourceVersion, Digest: metadata.SourceDigest}, + StorageRequirement: requirement, StorageAdmission: admission, RetentionIntent: metadata.Retention, + CreatedAt: timestamppb.New(metadata.CreatedAt), + } +} + +func workspaceCheckpointLeaseProto(metadata *workspaceCheckpointMetadata) *basev0.WorkspaceLeaseIdentity { + return &basev0.WorkspaceLeaseIdentity{WorkspaceId: metadata.WorkspaceID, LeaseId: metadata.LeaseID} +} + +func workspaceCheckpointRestoreResponse(metadata *workspaceCheckpointMetadata, restored *workspaceCheckpointRestore) *basev0.RestoreWorkspaceCheckpointResponse { + state := &basev0.WorkspaceStateIdentity{Version: metadata.SourceVersion, Digest: metadata.SourceDigest} + receipt := &basev0.WorkspaceCheckpointRestoreReceipt{ + CheckpointId: metadata.CheckpointID, Lease: workspaceCheckpointLeaseProto(metadata), + Prior: &basev0.WorkspaceStateIdentity{Version: restored.PriorVersion, Digest: restored.PriorDigest}, Restored: state, + AuthorizationId: restored.AuthorizationID, IdempotencyKey: restored.IdempotencyKey, RestoredAt: timestamppb.New(restored.RestoredAt), + } + return &basev0.RestoreWorkspaceCheckpointResponse{Workspace: state, Receipt: receipt} +} diff --git a/code/workspace_checkpoint_test.go b/code/workspace_checkpoint_test.go new file mode 100644 index 00000000..096d7f28 --- /dev/null +++ b/code/workspace_checkpoint_test.go @@ -0,0 +1,194 @@ +package code + +import ( + "context" + "errors" + "net" + "os" + "path/filepath" + "testing" + + basev0 "github.com/codefly-dev/core/generated/go/codefly/base/v0" + codev0 "github.com/codefly-dev/core/generated/go/codefly/services/code/v0" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/test/bufconn" + "google.golang.org/protobuf/proto" +) + +func TestWorkspaceCheckpointAgentIntegration(t *testing.T) { + root := t.TempDir() + writeSourceManifestFile(t, root, "go.mod", "module checkpoint.test\n\ngo 1.25\n", 0o644) + writeSourceManifestFile(t, root, "candidate.go", "package candidate\n\nfunc Value() int { return 0 }\n", 0o644) + gitSourceManifest(t, root, "init", "-b", "main") + gitSourceManifest(t, root, "config", "commit.gpgsign", "false") + gitSourceManifest(t, root, "add", "go.mod", "candidate.go") + gitSourceManifest(t, root, "commit", "-m", "baseline") + + writeSourceManifestFile(t, root, "candidate.go", "package candidate\n\nfunc Value() int { return 1 }\n", 0o644) + writeSourceManifestFile(t, root, "candidate_test.go", "package candidate\n\nimport \"testing\"\n\nfunc TestValue(t *testing.T) { if Value() != 1 { t.Fatalf(\"Value() = %d\", Value()) } }\n", 0o644) + writeSourceManifestFile(t, root, ".pytest_cache/state", "candidate-cache\n", 0o644) + + server := NewDefaultCodeServer(root) + _, candidateState, err := server.currentWorkspaceCheckpointState(t.Context()) + if err != nil { + t.Fatalf("candidate workspace state: %v", err) + } + client, closeClient := startWorkspaceCheckpointCodeClient(t, server) + lease := &basev0.WorkspaceLeaseIdentity{WorkspaceId: "workspace-319", LeaseId: "lease-primary"} + createRequest := &codev0.CodeRequest{Operation: &codev0.CodeRequest_CreateWorkspaceCheckpoint{CreateWorkspaceCheckpoint: &basev0.CreateWorkspaceCheckpointRequest{ + Lease: lease, ExpectedWorkspaceVersion: candidateState.GetVersion(), CallerId: "mind-attempt-1", IdempotencyKey: "create-candidate-1", + RetentionIntent: basev0.WorkspaceCheckpointRetentionIntent_WORKSPACE_CHECKPOINT_RETENTION_INTENT_UNTIL_RELEASE_OR_LEASE_END, + }}} + createdEnvelope, err := client.Execute(t.Context(), createRequest) + if err != nil { + t.Fatalf("CreateWorkspaceCheckpoint: %v", err) + } + created := createdEnvelope.GetCreateWorkspaceCheckpoint() + if created.GetFailure() != nil || created.GetCheckpoint() == nil { + t.Fatalf("CreateWorkspaceCheckpoint response = %+v", created) + } + checkpoint := created.GetCheckpoint() + if checkpoint.GetSource().GetVersion() != candidateState.GetVersion() || checkpoint.GetSource().GetDigest() != candidateState.GetDigest() { + t.Fatalf("checkpoint source = %+v, want %+v", checkpoint.GetSource(), candidateState) + } + if checkpoint.GetStorageRequirement().GetBytes() == 0 || !checkpoint.GetStorageAdmission().GetAdmitted() || checkpoint.GetCreatedAt() == nil { + t.Fatalf("checkpoint receipt is incomplete: %+v", checkpoint) + } + retriedEnvelope, err := client.Execute(t.Context(), createRequest) + if err != nil || retriedEnvelope.GetFailure() != nil { + t.Fatalf("idempotent CreateWorkspaceCheckpoint: response=%+v err=%v", retriedEnvelope, err) + } + if !proto.Equal(checkpoint, retriedEnvelope.GetCreateWorkspaceCheckpoint().GetCheckpoint()) { + t.Fatalf("idempotent create returned another receipt") + } + closeClient() + + server = NewDefaultCodeServer(root) + client, closeClient = startWorkspaceCheckpointCodeClient(t, server) + defer closeClient() + writeResponse, err := client.Execute(t.Context(), &codev0.CodeRequest{Operation: &codev0.CodeRequest_WriteFile{WriteFile: &codev0.WriteFileRequest{ + Path: "candidate.go", Content: "package candidate\n\nfunc Value() int { return 2 }\n", + }}}) + if err != nil || !writeResponse.GetWriteFile().GetSuccess() { + t.Fatalf("mutate candidate: response=%+v err=%v", writeResponse, err) + } + writeSourceManifestFile(t, root, "regression.txt", "later mutation\n", 0o644) + writeSourceManifestFile(t, root, ".pytest_cache/state", "later-cache\n", 0o644) + _, regressedState, err := server.currentWorkspaceCheckpointState(t.Context()) + if err != nil { + t.Fatalf("regressed workspace state: %v", err) + } + + stale := restoreWorkspaceCheckpointRequest(checkpoint.GetCheckpointId(), lease, candidateState.GetVersion(), "restore-stale") + staleEnvelope, err := client.Execute(t.Context(), stale) + if err != nil || staleEnvelope.GetFailure().GetCode() != basev0.FailureCode_FAILURE_CODE_PRECONDITION_FAILED { + t.Fatalf("stale restore = response:%+v err:%v", staleEnvelope, err) + } + if body, err := os.ReadFile(filepath.Join(root, "candidate.go")); err != nil || string(body) != "package candidate\n\nfunc Value() int { return 2 }\n" { + t.Fatalf("stale restore changed candidate: body=%q err=%v", body, err) + } + + crossLease := restoreWorkspaceCheckpointRequest(checkpoint.GetCheckpointId(), &basev0.WorkspaceLeaseIdentity{WorkspaceId: lease.GetWorkspaceId(), LeaseId: "lease-other"}, regressedState.GetVersion(), "restore-cross-lease") + crossLeaseEnvelope, err := client.Execute(t.Context(), crossLease) + if err != nil || crossLeaseEnvelope.GetFailure().GetCode() != basev0.FailureCode_FAILURE_CODE_PERMISSION_DENIED { + t.Fatalf("cross-lease restore = response:%+v err:%v", crossLeaseEnvelope, err) + } + + cancelledContext, cancel := context.WithCancel(t.Context()) + cancel() + cancelled, err := server.Execute(cancelledContext, &codev0.CodeRequest{Operation: &codev0.CodeRequest_CreateWorkspaceCheckpoint{CreateWorkspaceCheckpoint: &basev0.CreateWorkspaceCheckpointRequest{ + Lease: lease, ExpectedWorkspaceVersion: regressedState.GetVersion(), CallerId: "mind-attempt-1", IdempotencyKey: "cancelled-create", + RetentionIntent: basev0.WorkspaceCheckpointRetentionIntent_WORKSPACE_CHECKPOINT_RETENTION_INTENT_UNTIL_RELEASE_OR_LEASE_END, + }}}) + if err != nil || cancelled.GetFailure().GetCode() != basev0.FailureCode_FAILURE_CODE_CANCELLED { + t.Fatalf("cancelled create = response:%+v err:%v", cancelled, err) + } + cancelledRestore, err := server.Execute(cancelledContext, restoreWorkspaceCheckpointRequest(checkpoint.GetCheckpointId(), lease, regressedState.GetVersion(), "cancelled-restore")) + if err != nil || cancelledRestore.GetFailure().GetCode() != basev0.FailureCode_FAILURE_CODE_CANCELLED { + t.Fatalf("cancelled restore = response:%+v err:%v", cancelledRestore, err) + } + if body, err := os.ReadFile(filepath.Join(root, "candidate.go")); err != nil || string(body) != "package candidate\n\nfunc Value() int { return 2 }\n" { + t.Fatalf("cancelled restore changed candidate: body=%q err=%v", body, err) + } + + restoreRequest := restoreWorkspaceCheckpointRequest(checkpoint.GetCheckpointId(), lease, regressedState.GetVersion(), "restore-candidate") + restoredEnvelope, err := client.Execute(t.Context(), restoreRequest) + if err != nil { + t.Fatalf("RestoreWorkspaceCheckpoint: %v", err) + } + restored := restoredEnvelope.GetRestoreWorkspaceCheckpoint() + if restored.GetFailure() != nil || restored.GetWorkspace().GetVersion() != candidateState.GetVersion() { + t.Fatalf("RestoreWorkspaceCheckpoint response = %+v", restored) + } + if restored.GetReceipt().GetPrior().GetVersion() != regressedState.GetVersion() || restored.GetReceipt().GetRestoredAt() == nil { + t.Fatalf("restore receipt = %+v", restored.GetReceipt()) + } + verified, err := client.Execute(t.Context(), &codev0.CodeRequest{Operation: &codev0.CodeRequest_ShellExec{ShellExec: &codev0.ShellExecRequest{ + Args: []string{"go", "test", "./..."}, TimeoutSeconds: 30, + }}}) + if err != nil || verified.GetFailure() != nil || verified.GetShellExec().GetExitCode() != 0 { + t.Fatalf("runtime verification after restore = response:%+v err:%v", verified, err) + } + if _, err := os.Stat(filepath.Join(root, "regression.txt")); !errors.Is(err, os.ErrNotExist) { + t.Fatalf("later untracked file survived restore: %v", err) + } + if _, err := os.Stat(filepath.Join(root, ".pytest_cache")); !errors.Is(err, os.ErrNotExist) { + t.Fatalf("runtime cache survived restore: %v", err) + } + gitSourceManifest(t, root, "status", "--short") + + retriedRestore, err := client.Execute(t.Context(), restoreRequest) + if err != nil || retriedRestore.GetFailure() != nil || !proto.Equal(restored.GetReceipt(), retriedRestore.GetRestoreWorkspaceCheckpoint().GetReceipt()) { + t.Fatalf("idempotent restore = response:%+v err:%v", retriedRestore, err) + } + + releaseRequest := &codev0.CodeRequest{Operation: &codev0.CodeRequest_ReleaseWorkspaceCheckpoint{ReleaseWorkspaceCheckpoint: &basev0.ReleaseWorkspaceCheckpointRequest{ + CheckpointId: checkpoint.GetCheckpointId(), Lease: lease, CallerId: "mind-attempt-1", IdempotencyKey: "release-candidate", + }}} + releasedEnvelope, err := client.Execute(t.Context(), releaseRequest) + if err != nil || releasedEnvelope.GetFailure() != nil || releasedEnvelope.GetReleaseWorkspaceCheckpoint().GetReceipt().GetReleasedAt() == nil { + t.Fatalf("ReleaseWorkspaceCheckpoint = response:%+v err:%v", releasedEnvelope, err) + } + retriedRelease, err := client.Execute(t.Context(), releaseRequest) + if err != nil || retriedRelease.GetFailure() != nil || !proto.Equal(releasedEnvelope.GetReleaseWorkspaceCheckpoint().GetReceipt(), retriedRelease.GetReleaseWorkspaceCheckpoint().GetReceipt()) { + t.Fatalf("idempotent release = response:%+v err:%v", retriedRelease, err) + } + store, failure := server.workspaceCheckpointStoreDir() + if failure != nil { + t.Fatalf("checkpoint store: %v", failure) + } + if _, err := os.Stat(workspaceCheckpointArchivePath(store, checkpoint.GetCheckpointId())); !errors.Is(err, os.ErrNotExist) { + t.Fatalf("released archive still exists: %v", err) + } +} + +func restoreWorkspaceCheckpointRequest(checkpointID string, lease *basev0.WorkspaceLeaseIdentity, expectedVersion, idempotencyKey string) *codev0.CodeRequest { + return &codev0.CodeRequest{Operation: &codev0.CodeRequest_RestoreWorkspaceCheckpoint{RestoreWorkspaceCheckpoint: &basev0.RestoreWorkspaceCheckpointRequest{ + CheckpointId: checkpointID, Lease: lease, ExpectedWorkspaceVersion: expectedVersion, + AuthorizationId: "authorization-319", IdempotencyKey: idempotencyKey, + }}} +} + +func startWorkspaceCheckpointCodeClient(t *testing.T, server codev0.CodeServer) (codev0.CodeClient, func()) { + t.Helper() + listener := bufconn.Listen(1 << 20) + grpcServer := grpc.NewServer() + codev0.RegisterCodeServer(grpcServer, server) + go func() { + _ = grpcServer.Serve(listener) + }() + connection, err := grpc.NewClient("passthrough:///workspace-checkpoint", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + return listener.Dial() + })) + if err != nil { + grpcServer.Stop() + _ = listener.Close() + t.Fatalf("connect Code client: %v", err) + } + return codev0.NewCodeClient(connection), func() { + _ = connection.Close() + grpcServer.Stop() + _ = listener.Close() + } +} diff --git a/generated/go/codefly/base/v0/workspace_checkpoint.pb.go b/generated/go/codefly/base/v0/workspace_checkpoint.pb.go new file mode 100644 index 00000000..e2b50eeb --- /dev/null +++ b/generated/go/codefly/base/v0/workspace_checkpoint.pb.go @@ -0,0 +1,1021 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc (unknown) +// source: codefly/base/v0/workspace_checkpoint.proto + +package v0 + +import ( + reflect "reflect" + sync "sync" + unsafe "unsafe" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// WorkspaceCheckpointRetentionIntent selects the maximum retention lifetime. +type WorkspaceCheckpointRetentionIntent int32 + +const ( + // UNSPECIFIED is rejected by checkpoint creation. + WorkspaceCheckpointRetentionIntent_WORKSPACE_CHECKPOINT_RETENTION_INTENT_UNSPECIFIED WorkspaceCheckpointRetentionIntent = 0 + // UNTIL_RELEASE_OR_LEASE_END retains the checkpoint across run-control + // segments until explicit release or termination of its owning lease. + WorkspaceCheckpointRetentionIntent_WORKSPACE_CHECKPOINT_RETENTION_INTENT_UNTIL_RELEASE_OR_LEASE_END WorkspaceCheckpointRetentionIntent = 1 +) + +// Enum value maps for WorkspaceCheckpointRetentionIntent. +var ( + WorkspaceCheckpointRetentionIntent_name = map[int32]string{ + 0: "WORKSPACE_CHECKPOINT_RETENTION_INTENT_UNSPECIFIED", + 1: "WORKSPACE_CHECKPOINT_RETENTION_INTENT_UNTIL_RELEASE_OR_LEASE_END", + } + WorkspaceCheckpointRetentionIntent_value = map[string]int32{ + "WORKSPACE_CHECKPOINT_RETENTION_INTENT_UNSPECIFIED": 0, + "WORKSPACE_CHECKPOINT_RETENTION_INTENT_UNTIL_RELEASE_OR_LEASE_END": 1, + } +) + +func (x WorkspaceCheckpointRetentionIntent) Enum() *WorkspaceCheckpointRetentionIntent { + p := new(WorkspaceCheckpointRetentionIntent) + *p = x + return p +} + +func (x WorkspaceCheckpointRetentionIntent) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (WorkspaceCheckpointRetentionIntent) Descriptor() protoreflect.EnumDescriptor { + return file_codefly_base_v0_workspace_checkpoint_proto_enumTypes[0].Descriptor() +} + +func (WorkspaceCheckpointRetentionIntent) Type() protoreflect.EnumType { + return &file_codefly_base_v0_workspace_checkpoint_proto_enumTypes[0] +} + +func (x WorkspaceCheckpointRetentionIntent) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use WorkspaceCheckpointRetentionIntent.Descriptor instead. +func (WorkspaceCheckpointRetentionIntent) EnumDescriptor() ([]byte, []int) { + return file_codefly_base_v0_workspace_checkpoint_proto_rawDescGZIP(), []int{0} +} + +// WorkspaceLeaseIdentity binds mutable state to one active workspace lease. +type WorkspaceLeaseIdentity struct { + state protoimpl.MessageState `protogen:"open.v1"` + // workspace_id identifies the stable mutable workspace. + WorkspaceId string `protobuf:"bytes,1,opt,name=workspace_id,json=workspaceId,proto3" json:"workspace_id,omitempty"` + // lease_id identifies the active authority lifetime for the workspace. + LeaseId string `protobuf:"bytes,2,opt,name=lease_id,json=leaseId,proto3" json:"lease_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WorkspaceLeaseIdentity) Reset() { + *x = WorkspaceLeaseIdentity{} + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WorkspaceLeaseIdentity) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WorkspaceLeaseIdentity) ProtoMessage() {} + +func (x *WorkspaceLeaseIdentity) ProtoReflect() protoreflect.Message { + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WorkspaceLeaseIdentity.ProtoReflect.Descriptor instead. +func (*WorkspaceLeaseIdentity) Descriptor() ([]byte, []int) { + return file_codefly_base_v0_workspace_checkpoint_proto_rawDescGZIP(), []int{0} +} + +func (x *WorkspaceLeaseIdentity) GetWorkspaceId() string { + if x != nil { + return x.WorkspaceId + } + return "" +} + +func (x *WorkspaceLeaseIdentity) GetLeaseId() string { + if x != nil { + return x.LeaseId + } + return "" +} + +// WorkspaceStateIdentity is Codefly's body-free identity for exact project +// bytes. version is the canonical compare-and-swap token; digest is retained +// separately so journals never need to interpret the version encoding. +type WorkspaceStateIdentity struct { + state protoimpl.MessageState `protogen:"open.v1"` + // version is the canonical token used for compare-and-swap operations. + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` + // digest is the SHA-256 identity of the exact project state. + Digest string `protobuf:"bytes,2,opt,name=digest,proto3" json:"digest,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WorkspaceStateIdentity) Reset() { + *x = WorkspaceStateIdentity{} + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WorkspaceStateIdentity) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WorkspaceStateIdentity) ProtoMessage() {} + +func (x *WorkspaceStateIdentity) ProtoReflect() protoreflect.Message { + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WorkspaceStateIdentity.ProtoReflect.Descriptor instead. +func (*WorkspaceStateIdentity) Descriptor() ([]byte, []int) { + return file_codefly_base_v0_workspace_checkpoint_proto_rawDescGZIP(), []int{1} +} + +func (x *WorkspaceStateIdentity) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *WorkspaceStateIdentity) GetDigest() string { + if x != nil { + return x.Digest + } + return "" +} + +// WorkspaceCheckpoint is the source-free receipt for one durable dirty +// workspace candidate. checkpoint_id is opaque and never a filesystem path. +type WorkspaceCheckpoint struct { + state protoimpl.MessageState `protogen:"open.v1"` + // checkpoint_id is the opaque, lease- and content-bound lookup identity. + CheckpointId string `protobuf:"bytes,1,opt,name=checkpoint_id,json=checkpointId,proto3" json:"checkpoint_id,omitempty"` + // lease is the only workspace lease authorized to restore or release it. + Lease *WorkspaceLeaseIdentity `protobuf:"bytes,2,opt,name=lease,proto3" json:"lease,omitempty"` + // source identifies the exact project state stored by the checkpoint. + Source *WorkspaceStateIdentity `protobuf:"bytes,3,opt,name=source,proto3" json:"source,omitempty"` + // storage_requirement is the pre-admitted conservative storage demand. + StorageRequirement *StorageCapacityRequirement `protobuf:"bytes,4,opt,name=storage_requirement,json=storageRequirement,proto3" json:"storage_requirement,omitempty"` + // storage_admission records the capacity authority's create-time decision. + StorageAdmission *StorageCapacityAdmission `protobuf:"bytes,5,opt,name=storage_admission,json=storageAdmission,proto3" json:"storage_admission,omitempty"` + // retention_intent records the caller's admitted retention lifetime. + RetentionIntent WorkspaceCheckpointRetentionIntent `protobuf:"varint,6,opt,name=retention_intent,json=retentionIntent,proto3,enum=codefly.base.v0.WorkspaceCheckpointRetentionIntent" json:"retention_intent,omitempty"` + // created_at is when the checkpoint became durable. + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WorkspaceCheckpoint) Reset() { + *x = WorkspaceCheckpoint{} + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WorkspaceCheckpoint) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WorkspaceCheckpoint) ProtoMessage() {} + +func (x *WorkspaceCheckpoint) ProtoReflect() protoreflect.Message { + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WorkspaceCheckpoint.ProtoReflect.Descriptor instead. +func (*WorkspaceCheckpoint) Descriptor() ([]byte, []int) { + return file_codefly_base_v0_workspace_checkpoint_proto_rawDescGZIP(), []int{2} +} + +func (x *WorkspaceCheckpoint) GetCheckpointId() string { + if x != nil { + return x.CheckpointId + } + return "" +} + +func (x *WorkspaceCheckpoint) GetLease() *WorkspaceLeaseIdentity { + if x != nil { + return x.Lease + } + return nil +} + +func (x *WorkspaceCheckpoint) GetSource() *WorkspaceStateIdentity { + if x != nil { + return x.Source + } + return nil +} + +func (x *WorkspaceCheckpoint) GetStorageRequirement() *StorageCapacityRequirement { + if x != nil { + return x.StorageRequirement + } + return nil +} + +func (x *WorkspaceCheckpoint) GetStorageAdmission() *StorageCapacityAdmission { + if x != nil { + return x.StorageAdmission + } + return nil +} + +func (x *WorkspaceCheckpoint) GetRetentionIntent() WorkspaceCheckpointRetentionIntent { + if x != nil { + return x.RetentionIntent + } + return WorkspaceCheckpointRetentionIntent_WORKSPACE_CHECKPOINT_RETENTION_INTENT_UNSPECIFIED +} + +func (x *WorkspaceCheckpoint) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +// CreateWorkspaceCheckpointRequest preserves an exact live workspace state. +type CreateWorkspaceCheckpointRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // lease binds the checkpoint to one active workspace authority lifetime. + Lease *WorkspaceLeaseIdentity `protobuf:"bytes,1,opt,name=lease,proto3" json:"lease,omitempty"` + // expected_workspace_version is compared with authoritative project state. + ExpectedWorkspaceVersion string `protobuf:"bytes,2,opt,name=expected_workspace_version,json=expectedWorkspaceVersion,proto3" json:"expected_workspace_version,omitempty"` + // caller_id identifies the actor preserving the candidate. + CallerId string `protobuf:"bytes,3,opt,name=caller_id,json=callerId,proto3" json:"caller_id,omitempty"` + // idempotency_key makes retries return the original checkpoint receipt. + IdempotencyKey string `protobuf:"bytes,4,opt,name=idempotency_key,json=idempotencyKey,proto3" json:"idempotency_key,omitempty"` + // retention_intent requests the supported checkpoint lifetime. + RetentionIntent WorkspaceCheckpointRetentionIntent `protobuf:"varint,5,opt,name=retention_intent,json=retentionIntent,proto3,enum=codefly.base.v0.WorkspaceCheckpointRetentionIntent" json:"retention_intent,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateWorkspaceCheckpointRequest) Reset() { + *x = CreateWorkspaceCheckpointRequest{} + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateWorkspaceCheckpointRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateWorkspaceCheckpointRequest) ProtoMessage() {} + +func (x *CreateWorkspaceCheckpointRequest) ProtoReflect() protoreflect.Message { + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateWorkspaceCheckpointRequest.ProtoReflect.Descriptor instead. +func (*CreateWorkspaceCheckpointRequest) Descriptor() ([]byte, []int) { + return file_codefly_base_v0_workspace_checkpoint_proto_rawDescGZIP(), []int{3} +} + +func (x *CreateWorkspaceCheckpointRequest) GetLease() *WorkspaceLeaseIdentity { + if x != nil { + return x.Lease + } + return nil +} + +func (x *CreateWorkspaceCheckpointRequest) GetExpectedWorkspaceVersion() string { + if x != nil { + return x.ExpectedWorkspaceVersion + } + return "" +} + +func (x *CreateWorkspaceCheckpointRequest) GetCallerId() string { + if x != nil { + return x.CallerId + } + return "" +} + +func (x *CreateWorkspaceCheckpointRequest) GetIdempotencyKey() string { + if x != nil { + return x.IdempotencyKey + } + return "" +} + +func (x *CreateWorkspaceCheckpointRequest) GetRetentionIntent() WorkspaceCheckpointRetentionIntent { + if x != nil { + return x.RetentionIntent + } + return WorkspaceCheckpointRetentionIntent_WORKSPACE_CHECKPOINT_RETENTION_INTENT_UNSPECIFIED +} + +// CreateWorkspaceCheckpointResponse returns a durable receipt or typed failure. +type CreateWorkspaceCheckpointResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // checkpoint is present when creation succeeded. + Checkpoint *WorkspaceCheckpoint `protobuf:"bytes,1,opt,name=checkpoint,proto3" json:"checkpoint,omitempty"` + // failure is the structured reason creation did not complete. + Failure *Failure `protobuf:"bytes,2,opt,name=failure,proto3" json:"failure,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateWorkspaceCheckpointResponse) Reset() { + *x = CreateWorkspaceCheckpointResponse{} + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateWorkspaceCheckpointResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateWorkspaceCheckpointResponse) ProtoMessage() {} + +func (x *CreateWorkspaceCheckpointResponse) ProtoReflect() protoreflect.Message { + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateWorkspaceCheckpointResponse.ProtoReflect.Descriptor instead. +func (*CreateWorkspaceCheckpointResponse) Descriptor() ([]byte, []int) { + return file_codefly_base_v0_workspace_checkpoint_proto_rawDescGZIP(), []int{4} +} + +func (x *CreateWorkspaceCheckpointResponse) GetCheckpoint() *WorkspaceCheckpoint { + if x != nil { + return x.Checkpoint + } + return nil +} + +func (x *CreateWorkspaceCheckpointResponse) GetFailure() *Failure { + if x != nil { + return x.Failure + } + return nil +} + +// WorkspaceCheckpointRestoreReceipt records the exact compare-and-swap +// transition without exposing files, patches, Git objects, or host paths. +type WorkspaceCheckpointRestoreReceipt struct { + state protoimpl.MessageState `protogen:"open.v1"` + // checkpoint_id is the opaque checkpoint that supplied restored state. + CheckpointId string `protobuf:"bytes,1,opt,name=checkpoint_id,json=checkpointId,proto3" json:"checkpoint_id,omitempty"` + // lease identifies the workspace authority that admitted the transition. + Lease *WorkspaceLeaseIdentity `protobuf:"bytes,2,opt,name=lease,proto3" json:"lease,omitempty"` + // prior identifies project state immediately before restore. + Prior *WorkspaceStateIdentity `protobuf:"bytes,3,opt,name=prior,proto3" json:"prior,omitempty"` + // restored identifies authoritative project state after restore. + Restored *WorkspaceStateIdentity `protobuf:"bytes,4,opt,name=restored,proto3" json:"restored,omitempty"` + // authorization_id identifies the authority decision admitting restore. + AuthorizationId string `protobuf:"bytes,5,opt,name=authorization_id,json=authorizationId,proto3" json:"authorization_id,omitempty"` + // idempotency_key binds retries to this exact transition receipt. + IdempotencyKey string `protobuf:"bytes,6,opt,name=idempotency_key,json=idempotencyKey,proto3" json:"idempotency_key,omitempty"` + // restored_at is when the restored state became authoritative. + RestoredAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=restored_at,json=restoredAt,proto3" json:"restored_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WorkspaceCheckpointRestoreReceipt) Reset() { + *x = WorkspaceCheckpointRestoreReceipt{} + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WorkspaceCheckpointRestoreReceipt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WorkspaceCheckpointRestoreReceipt) ProtoMessage() {} + +func (x *WorkspaceCheckpointRestoreReceipt) ProtoReflect() protoreflect.Message { + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WorkspaceCheckpointRestoreReceipt.ProtoReflect.Descriptor instead. +func (*WorkspaceCheckpointRestoreReceipt) Descriptor() ([]byte, []int) { + return file_codefly_base_v0_workspace_checkpoint_proto_rawDescGZIP(), []int{5} +} + +func (x *WorkspaceCheckpointRestoreReceipt) GetCheckpointId() string { + if x != nil { + return x.CheckpointId + } + return "" +} + +func (x *WorkspaceCheckpointRestoreReceipt) GetLease() *WorkspaceLeaseIdentity { + if x != nil { + return x.Lease + } + return nil +} + +func (x *WorkspaceCheckpointRestoreReceipt) GetPrior() *WorkspaceStateIdentity { + if x != nil { + return x.Prior + } + return nil +} + +func (x *WorkspaceCheckpointRestoreReceipt) GetRestored() *WorkspaceStateIdentity { + if x != nil { + return x.Restored + } + return nil +} + +func (x *WorkspaceCheckpointRestoreReceipt) GetAuthorizationId() string { + if x != nil { + return x.AuthorizationId + } + return "" +} + +func (x *WorkspaceCheckpointRestoreReceipt) GetIdempotencyKey() string { + if x != nil { + return x.IdempotencyKey + } + return "" +} + +func (x *WorkspaceCheckpointRestoreReceipt) GetRestoredAt() *timestamppb.Timestamp { + if x != nil { + return x.RestoredAt + } + return nil +} + +// RestoreWorkspaceCheckpointRequest applies one lease-bound candidate by CAS. +type RestoreWorkspaceCheckpointRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // checkpoint_id is the opaque checkpoint selected by the caller. + CheckpointId string `protobuf:"bytes,1,opt,name=checkpoint_id,json=checkpointId,proto3" json:"checkpoint_id,omitempty"` + // lease must exactly match the checkpoint's owning lease. + Lease *WorkspaceLeaseIdentity `protobuf:"bytes,2,opt,name=lease,proto3" json:"lease,omitempty"` + // expected_workspace_version is compared before any project mutation. + ExpectedWorkspaceVersion string `protobuf:"bytes,3,opt,name=expected_workspace_version,json=expectedWorkspaceVersion,proto3" json:"expected_workspace_version,omitempty"` + // authorization_id identifies the restore authorization decision. + AuthorizationId string `protobuf:"bytes,4,opt,name=authorization_id,json=authorizationId,proto3" json:"authorization_id,omitempty"` + // idempotency_key makes retries return the original transition receipt. + IdempotencyKey string `protobuf:"bytes,5,opt,name=idempotency_key,json=idempotencyKey,proto3" json:"idempotency_key,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RestoreWorkspaceCheckpointRequest) Reset() { + *x = RestoreWorkspaceCheckpointRequest{} + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RestoreWorkspaceCheckpointRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RestoreWorkspaceCheckpointRequest) ProtoMessage() {} + +func (x *RestoreWorkspaceCheckpointRequest) ProtoReflect() protoreflect.Message { + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RestoreWorkspaceCheckpointRequest.ProtoReflect.Descriptor instead. +func (*RestoreWorkspaceCheckpointRequest) Descriptor() ([]byte, []int) { + return file_codefly_base_v0_workspace_checkpoint_proto_rawDescGZIP(), []int{6} +} + +func (x *RestoreWorkspaceCheckpointRequest) GetCheckpointId() string { + if x != nil { + return x.CheckpointId + } + return "" +} + +func (x *RestoreWorkspaceCheckpointRequest) GetLease() *WorkspaceLeaseIdentity { + if x != nil { + return x.Lease + } + return nil +} + +func (x *RestoreWorkspaceCheckpointRequest) GetExpectedWorkspaceVersion() string { + if x != nil { + return x.ExpectedWorkspaceVersion + } + return "" +} + +func (x *RestoreWorkspaceCheckpointRequest) GetAuthorizationId() string { + if x != nil { + return x.AuthorizationId + } + return "" +} + +func (x *RestoreWorkspaceCheckpointRequest) GetIdempotencyKey() string { + if x != nil { + return x.IdempotencyKey + } + return "" +} + +// RestoreWorkspaceCheckpointResponse reports authoritative restored state. +type RestoreWorkspaceCheckpointResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // workspace identifies the new authoritative project state. + Workspace *WorkspaceStateIdentity `protobuf:"bytes,1,opt,name=workspace,proto3" json:"workspace,omitempty"` + // receipt records the exact source-free restore transition. + Receipt *WorkspaceCheckpointRestoreReceipt `protobuf:"bytes,2,opt,name=receipt,proto3" json:"receipt,omitempty"` + // failure is the structured reason restore did not complete. + Failure *Failure `protobuf:"bytes,3,opt,name=failure,proto3" json:"failure,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RestoreWorkspaceCheckpointResponse) Reset() { + *x = RestoreWorkspaceCheckpointResponse{} + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RestoreWorkspaceCheckpointResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RestoreWorkspaceCheckpointResponse) ProtoMessage() {} + +func (x *RestoreWorkspaceCheckpointResponse) ProtoReflect() protoreflect.Message { + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RestoreWorkspaceCheckpointResponse.ProtoReflect.Descriptor instead. +func (*RestoreWorkspaceCheckpointResponse) Descriptor() ([]byte, []int) { + return file_codefly_base_v0_workspace_checkpoint_proto_rawDescGZIP(), []int{7} +} + +func (x *RestoreWorkspaceCheckpointResponse) GetWorkspace() *WorkspaceStateIdentity { + if x != nil { + return x.Workspace + } + return nil +} + +func (x *RestoreWorkspaceCheckpointResponse) GetReceipt() *WorkspaceCheckpointRestoreReceipt { + if x != nil { + return x.Receipt + } + return nil +} + +func (x *RestoreWorkspaceCheckpointResponse) GetFailure() *Failure { + if x != nil { + return x.Failure + } + return nil +} + +// WorkspaceCheckpointReleaseReceipt records durable checkpoint cleanup. +type WorkspaceCheckpointReleaseReceipt struct { + state protoimpl.MessageState `protogen:"open.v1"` + // checkpoint_id is the opaque checkpoint that was released. + CheckpointId string `protobuf:"bytes,1,opt,name=checkpoint_id,json=checkpointId,proto3" json:"checkpoint_id,omitempty"` + // lease identifies the workspace authority that owned the checkpoint. + Lease *WorkspaceLeaseIdentity `protobuf:"bytes,2,opt,name=lease,proto3" json:"lease,omitempty"` + // released_at is stable across idempotent release retries. + ReleasedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=released_at,json=releasedAt,proto3" json:"released_at,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WorkspaceCheckpointReleaseReceipt) Reset() { + *x = WorkspaceCheckpointReleaseReceipt{} + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WorkspaceCheckpointReleaseReceipt) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WorkspaceCheckpointReleaseReceipt) ProtoMessage() {} + +func (x *WorkspaceCheckpointReleaseReceipt) ProtoReflect() protoreflect.Message { + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WorkspaceCheckpointReleaseReceipt.ProtoReflect.Descriptor instead. +func (*WorkspaceCheckpointReleaseReceipt) Descriptor() ([]byte, []int) { + return file_codefly_base_v0_workspace_checkpoint_proto_rawDescGZIP(), []int{8} +} + +func (x *WorkspaceCheckpointReleaseReceipt) GetCheckpointId() string { + if x != nil { + return x.CheckpointId + } + return "" +} + +func (x *WorkspaceCheckpointReleaseReceipt) GetLease() *WorkspaceLeaseIdentity { + if x != nil { + return x.Lease + } + return nil +} + +func (x *WorkspaceCheckpointReleaseReceipt) GetReleasedAt() *timestamppb.Timestamp { + if x != nil { + return x.ReleasedAt + } + return nil +} + +// ReleaseWorkspaceCheckpointRequest releases one lease-bound checkpoint. +type ReleaseWorkspaceCheckpointRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // checkpoint_id is the opaque checkpoint selected for cleanup. + CheckpointId string `protobuf:"bytes,1,opt,name=checkpoint_id,json=checkpointId,proto3" json:"checkpoint_id,omitempty"` + // lease must exactly match the checkpoint's owning lease. + Lease *WorkspaceLeaseIdentity `protobuf:"bytes,2,opt,name=lease,proto3" json:"lease,omitempty"` + // caller_id identifies the actor requesting cleanup. + CallerId string `protobuf:"bytes,3,opt,name=caller_id,json=callerId,proto3" json:"caller_id,omitempty"` + // idempotency_key identifies this cleanup request. + IdempotencyKey string `protobuf:"bytes,4,opt,name=idempotency_key,json=idempotencyKey,proto3" json:"idempotency_key,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReleaseWorkspaceCheckpointRequest) Reset() { + *x = ReleaseWorkspaceCheckpointRequest{} + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReleaseWorkspaceCheckpointRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReleaseWorkspaceCheckpointRequest) ProtoMessage() {} + +func (x *ReleaseWorkspaceCheckpointRequest) ProtoReflect() protoreflect.Message { + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReleaseWorkspaceCheckpointRequest.ProtoReflect.Descriptor instead. +func (*ReleaseWorkspaceCheckpointRequest) Descriptor() ([]byte, []int) { + return file_codefly_base_v0_workspace_checkpoint_proto_rawDescGZIP(), []int{9} +} + +func (x *ReleaseWorkspaceCheckpointRequest) GetCheckpointId() string { + if x != nil { + return x.CheckpointId + } + return "" +} + +func (x *ReleaseWorkspaceCheckpointRequest) GetLease() *WorkspaceLeaseIdentity { + if x != nil { + return x.Lease + } + return nil +} + +func (x *ReleaseWorkspaceCheckpointRequest) GetCallerId() string { + if x != nil { + return x.CallerId + } + return "" +} + +func (x *ReleaseWorkspaceCheckpointRequest) GetIdempotencyKey() string { + if x != nil { + return x.IdempotencyKey + } + return "" +} + +// ReleaseWorkspaceCheckpointResponse returns a stable cleanup receipt. +type ReleaseWorkspaceCheckpointResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // receipt records when the checkpoint was released. + Receipt *WorkspaceCheckpointReleaseReceipt `protobuf:"bytes,1,opt,name=receipt,proto3" json:"receipt,omitempty"` + // failure is the structured reason release did not complete. + Failure *Failure `protobuf:"bytes,2,opt,name=failure,proto3" json:"failure,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReleaseWorkspaceCheckpointResponse) Reset() { + *x = ReleaseWorkspaceCheckpointResponse{} + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReleaseWorkspaceCheckpointResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReleaseWorkspaceCheckpointResponse) ProtoMessage() {} + +func (x *ReleaseWorkspaceCheckpointResponse) ProtoReflect() protoreflect.Message { + mi := &file_codefly_base_v0_workspace_checkpoint_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReleaseWorkspaceCheckpointResponse.ProtoReflect.Descriptor instead. +func (*ReleaseWorkspaceCheckpointResponse) Descriptor() ([]byte, []int) { + return file_codefly_base_v0_workspace_checkpoint_proto_rawDescGZIP(), []int{10} +} + +func (x *ReleaseWorkspaceCheckpointResponse) GetReceipt() *WorkspaceCheckpointReleaseReceipt { + if x != nil { + return x.Receipt + } + return nil +} + +func (x *ReleaseWorkspaceCheckpointResponse) GetFailure() *Failure { + if x != nil { + return x.Failure + } + return nil +} + +var File_codefly_base_v0_workspace_checkpoint_proto protoreflect.FileDescriptor + +const file_codefly_base_v0_workspace_checkpoint_proto_rawDesc = "" + + "\n" + + "*codefly/base/v0/workspace_checkpoint.proto\x12\x0fcodefly.base.v0\x1a\x1dcodefly/base/v0/failure.proto\x1a\x1dcodefly/base/v0/storage.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"V\n" + + "\x16WorkspaceLeaseIdentity\x12!\n" + + "\fworkspace_id\x18\x01 \x01(\tR\vworkspaceId\x12\x19\n" + + "\blease_id\x18\x02 \x01(\tR\aleaseId\"J\n" + + "\x16WorkspaceStateIdentity\x12\x18\n" + + "\aversion\x18\x01 \x01(\tR\aversion\x12\x16\n" + + "\x06digest\x18\x02 \x01(\tR\x06digest\"\x8b\x04\n" + + "\x13WorkspaceCheckpoint\x12#\n" + + "\rcheckpoint_id\x18\x01 \x01(\tR\fcheckpointId\x12=\n" + + "\x05lease\x18\x02 \x01(\v2'.codefly.base.v0.WorkspaceLeaseIdentityR\x05lease\x12?\n" + + "\x06source\x18\x03 \x01(\v2'.codefly.base.v0.WorkspaceStateIdentityR\x06source\x12\\\n" + + "\x13storage_requirement\x18\x04 \x01(\v2+.codefly.base.v0.StorageCapacityRequirementR\x12storageRequirement\x12V\n" + + "\x11storage_admission\x18\x05 \x01(\v2).codefly.base.v0.StorageCapacityAdmissionR\x10storageAdmission\x12^\n" + + "\x10retention_intent\x18\x06 \x01(\x0e23.codefly.base.v0.WorkspaceCheckpointRetentionIntentR\x0fretentionIntent\x129\n" + + "\n" + + "created_at\x18\a \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt\"\xc5\x02\n" + + " CreateWorkspaceCheckpointRequest\x12=\n" + + "\x05lease\x18\x01 \x01(\v2'.codefly.base.v0.WorkspaceLeaseIdentityR\x05lease\x12<\n" + + "\x1aexpected_workspace_version\x18\x02 \x01(\tR\x18expectedWorkspaceVersion\x12\x1b\n" + + "\tcaller_id\x18\x03 \x01(\tR\bcallerId\x12'\n" + + "\x0fidempotency_key\x18\x04 \x01(\tR\x0eidempotencyKey\x12^\n" + + "\x10retention_intent\x18\x05 \x01(\x0e23.codefly.base.v0.WorkspaceCheckpointRetentionIntentR\x0fretentionIntent\"\x9d\x01\n" + + "!CreateWorkspaceCheckpointResponse\x12D\n" + + "\n" + + "checkpoint\x18\x01 \x01(\v2$.codefly.base.v0.WorkspaceCheckpointR\n" + + "checkpoint\x122\n" + + "\afailure\x18\x02 \x01(\v2\x18.codefly.base.v0.FailureR\afailure\"\x9c\x03\n" + + "!WorkspaceCheckpointRestoreReceipt\x12#\n" + + "\rcheckpoint_id\x18\x01 \x01(\tR\fcheckpointId\x12=\n" + + "\x05lease\x18\x02 \x01(\v2'.codefly.base.v0.WorkspaceLeaseIdentityR\x05lease\x12=\n" + + "\x05prior\x18\x03 \x01(\v2'.codefly.base.v0.WorkspaceStateIdentityR\x05prior\x12C\n" + + "\brestored\x18\x04 \x01(\v2'.codefly.base.v0.WorkspaceStateIdentityR\brestored\x12)\n" + + "\x10authorization_id\x18\x05 \x01(\tR\x0fauthorizationId\x12'\n" + + "\x0fidempotency_key\x18\x06 \x01(\tR\x0eidempotencyKey\x12;\n" + + "\vrestored_at\x18\a \x01(\v2\x1a.google.protobuf.TimestampR\n" + + "restoredAt\"\x99\x02\n" + + "!RestoreWorkspaceCheckpointRequest\x12#\n" + + "\rcheckpoint_id\x18\x01 \x01(\tR\fcheckpointId\x12=\n" + + "\x05lease\x18\x02 \x01(\v2'.codefly.base.v0.WorkspaceLeaseIdentityR\x05lease\x12<\n" + + "\x1aexpected_workspace_version\x18\x03 \x01(\tR\x18expectedWorkspaceVersion\x12)\n" + + "\x10authorization_id\x18\x04 \x01(\tR\x0fauthorizationId\x12'\n" + + "\x0fidempotency_key\x18\x05 \x01(\tR\x0eidempotencyKey\"\xed\x01\n" + + "\"RestoreWorkspaceCheckpointResponse\x12E\n" + + "\tworkspace\x18\x01 \x01(\v2'.codefly.base.v0.WorkspaceStateIdentityR\tworkspace\x12L\n" + + "\areceipt\x18\x02 \x01(\v22.codefly.base.v0.WorkspaceCheckpointRestoreReceiptR\areceipt\x122\n" + + "\afailure\x18\x03 \x01(\v2\x18.codefly.base.v0.FailureR\afailure\"\xc4\x01\n" + + "!WorkspaceCheckpointReleaseReceipt\x12#\n" + + "\rcheckpoint_id\x18\x01 \x01(\tR\fcheckpointId\x12=\n" + + "\x05lease\x18\x02 \x01(\v2'.codefly.base.v0.WorkspaceLeaseIdentityR\x05lease\x12;\n" + + "\vreleased_at\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\n" + + "releasedAt\"\xcd\x01\n" + + "!ReleaseWorkspaceCheckpointRequest\x12#\n" + + "\rcheckpoint_id\x18\x01 \x01(\tR\fcheckpointId\x12=\n" + + "\x05lease\x18\x02 \x01(\v2'.codefly.base.v0.WorkspaceLeaseIdentityR\x05lease\x12\x1b\n" + + "\tcaller_id\x18\x03 \x01(\tR\bcallerId\x12'\n" + + "\x0fidempotency_key\x18\x04 \x01(\tR\x0eidempotencyKey\"\xa6\x01\n" + + "\"ReleaseWorkspaceCheckpointResponse\x12L\n" + + "\areceipt\x18\x01 \x01(\v22.codefly.base.v0.WorkspaceCheckpointReleaseReceiptR\areceipt\x122\n" + + "\afailure\x18\x02 \x01(\v2\x18.codefly.base.v0.FailureR\afailure*\xa1\x01\n" + + "\"WorkspaceCheckpointRetentionIntent\x125\n" + + "1WORKSPACE_CHECKPOINT_RETENTION_INTENT_UNSPECIFIED\x10\x00\x12D\n" + + "@WORKSPACE_CHECKPOINT_RETENTION_INTENT_UNTIL_RELEASE_OR_LEASE_END\x10\x01B\xc7\x01\n" + + "\x13com.codefly.base.v0B\x18WorkspaceCheckpointProtoP\x01Z8github.com/codefly-dev/core/generated/go/codefly/base/v0\xa2\x02\x03CBV\xaa\x02\x0fCodefly.Base.V0\xca\x02\x0fCodefly\\Base\\V0\xe2\x02\x1bCodefly\\Base\\V0\\GPBMetadata\xea\x02\x11Codefly::Base::V0b\x06proto3" + +var ( + file_codefly_base_v0_workspace_checkpoint_proto_rawDescOnce sync.Once + file_codefly_base_v0_workspace_checkpoint_proto_rawDescData []byte +) + +func file_codefly_base_v0_workspace_checkpoint_proto_rawDescGZIP() []byte { + file_codefly_base_v0_workspace_checkpoint_proto_rawDescOnce.Do(func() { + file_codefly_base_v0_workspace_checkpoint_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_codefly_base_v0_workspace_checkpoint_proto_rawDesc), len(file_codefly_base_v0_workspace_checkpoint_proto_rawDesc))) + }) + return file_codefly_base_v0_workspace_checkpoint_proto_rawDescData +} + +var file_codefly_base_v0_workspace_checkpoint_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_codefly_base_v0_workspace_checkpoint_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +var file_codefly_base_v0_workspace_checkpoint_proto_goTypes = []any{ + (WorkspaceCheckpointRetentionIntent)(0), // 0: codefly.base.v0.WorkspaceCheckpointRetentionIntent + (*WorkspaceLeaseIdentity)(nil), // 1: codefly.base.v0.WorkspaceLeaseIdentity + (*WorkspaceStateIdentity)(nil), // 2: codefly.base.v0.WorkspaceStateIdentity + (*WorkspaceCheckpoint)(nil), // 3: codefly.base.v0.WorkspaceCheckpoint + (*CreateWorkspaceCheckpointRequest)(nil), // 4: codefly.base.v0.CreateWorkspaceCheckpointRequest + (*CreateWorkspaceCheckpointResponse)(nil), // 5: codefly.base.v0.CreateWorkspaceCheckpointResponse + (*WorkspaceCheckpointRestoreReceipt)(nil), // 6: codefly.base.v0.WorkspaceCheckpointRestoreReceipt + (*RestoreWorkspaceCheckpointRequest)(nil), // 7: codefly.base.v0.RestoreWorkspaceCheckpointRequest + (*RestoreWorkspaceCheckpointResponse)(nil), // 8: codefly.base.v0.RestoreWorkspaceCheckpointResponse + (*WorkspaceCheckpointReleaseReceipt)(nil), // 9: codefly.base.v0.WorkspaceCheckpointReleaseReceipt + (*ReleaseWorkspaceCheckpointRequest)(nil), // 10: codefly.base.v0.ReleaseWorkspaceCheckpointRequest + (*ReleaseWorkspaceCheckpointResponse)(nil), // 11: codefly.base.v0.ReleaseWorkspaceCheckpointResponse + (*StorageCapacityRequirement)(nil), // 12: codefly.base.v0.StorageCapacityRequirement + (*StorageCapacityAdmission)(nil), // 13: codefly.base.v0.StorageCapacityAdmission + (*timestamppb.Timestamp)(nil), // 14: google.protobuf.Timestamp + (*Failure)(nil), // 15: codefly.base.v0.Failure +} +var file_codefly_base_v0_workspace_checkpoint_proto_depIdxs = []int32{ + 1, // 0: codefly.base.v0.WorkspaceCheckpoint.lease:type_name -> codefly.base.v0.WorkspaceLeaseIdentity + 2, // 1: codefly.base.v0.WorkspaceCheckpoint.source:type_name -> codefly.base.v0.WorkspaceStateIdentity + 12, // 2: codefly.base.v0.WorkspaceCheckpoint.storage_requirement:type_name -> codefly.base.v0.StorageCapacityRequirement + 13, // 3: codefly.base.v0.WorkspaceCheckpoint.storage_admission:type_name -> codefly.base.v0.StorageCapacityAdmission + 0, // 4: codefly.base.v0.WorkspaceCheckpoint.retention_intent:type_name -> codefly.base.v0.WorkspaceCheckpointRetentionIntent + 14, // 5: codefly.base.v0.WorkspaceCheckpoint.created_at:type_name -> google.protobuf.Timestamp + 1, // 6: codefly.base.v0.CreateWorkspaceCheckpointRequest.lease:type_name -> codefly.base.v0.WorkspaceLeaseIdentity + 0, // 7: codefly.base.v0.CreateWorkspaceCheckpointRequest.retention_intent:type_name -> codefly.base.v0.WorkspaceCheckpointRetentionIntent + 3, // 8: codefly.base.v0.CreateWorkspaceCheckpointResponse.checkpoint:type_name -> codefly.base.v0.WorkspaceCheckpoint + 15, // 9: codefly.base.v0.CreateWorkspaceCheckpointResponse.failure:type_name -> codefly.base.v0.Failure + 1, // 10: codefly.base.v0.WorkspaceCheckpointRestoreReceipt.lease:type_name -> codefly.base.v0.WorkspaceLeaseIdentity + 2, // 11: codefly.base.v0.WorkspaceCheckpointRestoreReceipt.prior:type_name -> codefly.base.v0.WorkspaceStateIdentity + 2, // 12: codefly.base.v0.WorkspaceCheckpointRestoreReceipt.restored:type_name -> codefly.base.v0.WorkspaceStateIdentity + 14, // 13: codefly.base.v0.WorkspaceCheckpointRestoreReceipt.restored_at:type_name -> google.protobuf.Timestamp + 1, // 14: codefly.base.v0.RestoreWorkspaceCheckpointRequest.lease:type_name -> codefly.base.v0.WorkspaceLeaseIdentity + 2, // 15: codefly.base.v0.RestoreWorkspaceCheckpointResponse.workspace:type_name -> codefly.base.v0.WorkspaceStateIdentity + 6, // 16: codefly.base.v0.RestoreWorkspaceCheckpointResponse.receipt:type_name -> codefly.base.v0.WorkspaceCheckpointRestoreReceipt + 15, // 17: codefly.base.v0.RestoreWorkspaceCheckpointResponse.failure:type_name -> codefly.base.v0.Failure + 1, // 18: codefly.base.v0.WorkspaceCheckpointReleaseReceipt.lease:type_name -> codefly.base.v0.WorkspaceLeaseIdentity + 14, // 19: codefly.base.v0.WorkspaceCheckpointReleaseReceipt.released_at:type_name -> google.protobuf.Timestamp + 1, // 20: codefly.base.v0.ReleaseWorkspaceCheckpointRequest.lease:type_name -> codefly.base.v0.WorkspaceLeaseIdentity + 9, // 21: codefly.base.v0.ReleaseWorkspaceCheckpointResponse.receipt:type_name -> codefly.base.v0.WorkspaceCheckpointReleaseReceipt + 15, // 22: codefly.base.v0.ReleaseWorkspaceCheckpointResponse.failure:type_name -> codefly.base.v0.Failure + 23, // [23:23] is the sub-list for method output_type + 23, // [23:23] is the sub-list for method input_type + 23, // [23:23] is the sub-list for extension type_name + 23, // [23:23] is the sub-list for extension extendee + 0, // [0:23] is the sub-list for field type_name +} + +func init() { file_codefly_base_v0_workspace_checkpoint_proto_init() } +func file_codefly_base_v0_workspace_checkpoint_proto_init() { + if File_codefly_base_v0_workspace_checkpoint_proto != nil { + return + } + file_codefly_base_v0_failure_proto_init() + file_codefly_base_v0_storage_proto_init() + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_codefly_base_v0_workspace_checkpoint_proto_rawDesc), len(file_codefly_base_v0_workspace_checkpoint_proto_rawDesc)), + NumEnums: 1, + NumMessages: 11, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_codefly_base_v0_workspace_checkpoint_proto_goTypes, + DependencyIndexes: file_codefly_base_v0_workspace_checkpoint_proto_depIdxs, + EnumInfos: file_codefly_base_v0_workspace_checkpoint_proto_enumTypes, + MessageInfos: file_codefly_base_v0_workspace_checkpoint_proto_msgTypes, + }.Build() + File_codefly_base_v0_workspace_checkpoint_proto = out.File + file_codefly_base_v0_workspace_checkpoint_proto_goTypes = nil + file_codefly_base_v0_workspace_checkpoint_proto_depIdxs = nil +} diff --git a/generated/go/codefly/services/code/v0/code.pb.go b/generated/go/codefly/services/code/v0/code.pb.go index d4f0e05a..df8b8fac 100644 --- a/generated/go/codefly/services/code/v0/code.pb.go +++ b/generated/go/codefly/services/code/v0/code.pb.go @@ -3586,6 +3586,9 @@ type CodeRequest struct { // *CodeRequest_GitBlame // *CodeRequest_GitDiff // *CodeRequest_ShellExec + // *CodeRequest_CreateWorkspaceCheckpoint + // *CodeRequest_RestoreWorkspaceCheckpoint + // *CodeRequest_ReleaseWorkspaceCheckpoint Operation isCodeRequest_Operation `protobuf_oneof:"operation"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -3835,6 +3838,33 @@ func (x *CodeRequest) GetShellExec() *ShellExecRequest { return nil } +func (x *CodeRequest) GetCreateWorkspaceCheckpoint() *v0.CreateWorkspaceCheckpointRequest { + if x != nil { + if x, ok := x.Operation.(*CodeRequest_CreateWorkspaceCheckpoint); ok { + return x.CreateWorkspaceCheckpoint + } + } + return nil +} + +func (x *CodeRequest) GetRestoreWorkspaceCheckpoint() *v0.RestoreWorkspaceCheckpointRequest { + if x != nil { + if x, ok := x.Operation.(*CodeRequest_RestoreWorkspaceCheckpoint); ok { + return x.RestoreWorkspaceCheckpoint + } + } + return nil +} + +func (x *CodeRequest) GetReleaseWorkspaceCheckpoint() *v0.ReleaseWorkspaceCheckpointRequest { + if x != nil { + if x, ok := x.Operation.(*CodeRequest_ReleaseWorkspaceCheckpoint); ok { + return x.ReleaseWorkspaceCheckpoint + } + } + return nil +} + type isCodeRequest_Operation interface { isCodeRequest_Operation() } @@ -3962,6 +3992,20 @@ type CodeRequest_ShellExec struct { ShellExec *ShellExecRequest `protobuf:"bytes,37,opt,name=shell_exec,json=shellExec,proto3,oneof"` } +type CodeRequest_CreateWorkspaceCheckpoint struct { + // Workspace checkpoint operations keep dirty project bytes and storage + // paths inside the owning Codefly agent. + CreateWorkspaceCheckpoint *v0.CreateWorkspaceCheckpointRequest `protobuf:"bytes,38,opt,name=create_workspace_checkpoint,json=createWorkspaceCheckpoint,proto3,oneof"` +} + +type CodeRequest_RestoreWorkspaceCheckpoint struct { + RestoreWorkspaceCheckpoint *v0.RestoreWorkspaceCheckpointRequest `protobuf:"bytes,39,opt,name=restore_workspace_checkpoint,json=restoreWorkspaceCheckpoint,proto3,oneof"` +} + +type CodeRequest_ReleaseWorkspaceCheckpoint struct { + ReleaseWorkspaceCheckpoint *v0.ReleaseWorkspaceCheckpointRequest `protobuf:"bytes,40,opt,name=release_workspace_checkpoint,json=releaseWorkspaceCheckpoint,proto3,oneof"` +} + func (*CodeRequest_Fix) isCodeRequest_Operation() {} func (*CodeRequest_ApplyEdit) isCodeRequest_Operation() {} @@ -4008,6 +4052,12 @@ func (*CodeRequest_GitDiff) isCodeRequest_Operation() {} func (*CodeRequest_ShellExec) isCodeRequest_Operation() {} +func (*CodeRequest_CreateWorkspaceCheckpoint) isCodeRequest_Operation() {} + +func (*CodeRequest_RestoreWorkspaceCheckpoint) isCodeRequest_Operation() {} + +func (*CodeRequest_ReleaseWorkspaceCheckpoint) isCodeRequest_Operation() {} + // CodeResponse wraps all code operation results. type CodeResponse struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -4040,6 +4090,9 @@ type CodeResponse struct { // *CodeResponse_GitBlame // *CodeResponse_GitDiff // *CodeResponse_ShellExec + // *CodeResponse_CreateWorkspaceCheckpoint + // *CodeResponse_RestoreWorkspaceCheckpoint + // *CodeResponse_ReleaseWorkspaceCheckpoint Result isCodeResponse_Result `protobuf_oneof:"result"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -4296,6 +4349,33 @@ func (x *CodeResponse) GetShellExec() *ShellExecResponse { return nil } +func (x *CodeResponse) GetCreateWorkspaceCheckpoint() *v0.CreateWorkspaceCheckpointResponse { + if x != nil { + if x, ok := x.Result.(*CodeResponse_CreateWorkspaceCheckpoint); ok { + return x.CreateWorkspaceCheckpoint + } + } + return nil +} + +func (x *CodeResponse) GetRestoreWorkspaceCheckpoint() *v0.RestoreWorkspaceCheckpointResponse { + if x != nil { + if x, ok := x.Result.(*CodeResponse_RestoreWorkspaceCheckpoint); ok { + return x.RestoreWorkspaceCheckpoint + } + } + return nil +} + +func (x *CodeResponse) GetReleaseWorkspaceCheckpoint() *v0.ReleaseWorkspaceCheckpointResponse { + if x != nil { + if x, ok := x.Result.(*CodeResponse_ReleaseWorkspaceCheckpoint); ok { + return x.ReleaseWorkspaceCheckpoint + } + } + return nil +} + type isCodeResponse_Result interface { isCodeResponse_Result() } @@ -4416,6 +4496,18 @@ type CodeResponse_ShellExec struct { ShellExec *ShellExecResponse `protobuf:"bytes,37,opt,name=shell_exec,json=shellExec,proto3,oneof"` } +type CodeResponse_CreateWorkspaceCheckpoint struct { + CreateWorkspaceCheckpoint *v0.CreateWorkspaceCheckpointResponse `protobuf:"bytes,38,opt,name=create_workspace_checkpoint,json=createWorkspaceCheckpoint,proto3,oneof"` +} + +type CodeResponse_RestoreWorkspaceCheckpoint struct { + RestoreWorkspaceCheckpoint *v0.RestoreWorkspaceCheckpointResponse `protobuf:"bytes,39,opt,name=restore_workspace_checkpoint,json=restoreWorkspaceCheckpoint,proto3,oneof"` +} + +type CodeResponse_ReleaseWorkspaceCheckpoint struct { + ReleaseWorkspaceCheckpoint *v0.ReleaseWorkspaceCheckpointResponse `protobuf:"bytes,40,opt,name=release_workspace_checkpoint,json=releaseWorkspaceCheckpoint,proto3,oneof"` +} + func (*CodeResponse_Fix) isCodeResponse_Result() {} func (*CodeResponse_ApplyEdit) isCodeResponse_Result() {} @@ -4462,11 +4554,17 @@ func (*CodeResponse_GitDiff) isCodeResponse_Result() {} func (*CodeResponse_ShellExec) isCodeResponse_Result() {} +func (*CodeResponse_CreateWorkspaceCheckpoint) isCodeResponse_Result() {} + +func (*CodeResponse_RestoreWorkspaceCheckpoint) isCodeResponse_Result() {} + +func (*CodeResponse_ReleaseWorkspaceCheckpoint) isCodeResponse_Result() {} + var File_codefly_services_code_v0_code_proto protoreflect.FileDescriptor const file_codefly_services_code_v0_code_proto_rawDesc = "" + "\n" + - "#codefly/services/code/v0/code.proto\x12\x18codefly.services.code.v0\x1a\x1dcodefly/base/v0/failure.proto\x1a!codefly/base/v0/instruction.proto\x1a\x1ecodefly/base/v0/semantic.proto\x1a\x1ccodefly/base/v0/source.proto\"%\n" + + "#codefly/services/code/v0/code.proto\x12\x18codefly.services.code.v0\x1a\x1dcodefly/base/v0/failure.proto\x1a!codefly/base/v0/instruction.proto\x1a\x1ecodefly/base/v0/semantic.proto\x1a\x1ccodefly/base/v0/source.proto\x1a*codefly/base/v0/workspace_checkpoint.proto\"%\n" + "\x0fReadFileRequest\x12\x12\n" + "\x04path\x18\x01 \x01(\tR\x04path\"D\n" + "\x10ReadFileResponse\x12\x18\n" + @@ -4710,7 +4808,7 @@ const file_codefly_services_code_v0_code_proto_rawDesc = "" + "\texit_code\x18\x01 \x01(\x05R\bexitCode\x12\x16\n" + "\x06stdout\x18\x02 \x01(\tR\x06stdout\x12\x16\n" + "\x06stderr\x18\x03 \x01(\tR\x06stderr\x12\x1b\n" + - "\ttimed_out\x18\x04 \x01(\bR\btimedOutJ\x04\b\x05\x10\x06J\x04\b\x06\x10\aR\x05errorR\afailure\"\x93\x0f\n" + + "\ttimed_out\x18\x04 \x01(\bR\btimedOutJ\x04\b\x05\x10\x06J\x04\b\x06\x10\aR\x05errorR\afailure\"\xf8\x11\n" + "\vCodeRequest\x128\n" + "\x03fix\x18\r \x01(\v2$.codefly.services.code.v0.FixRequestH\x00R\x03fix\x12K\n" + "\n" + @@ -4740,8 +4838,11 @@ const file_codefly_services_code_v0_code_proto_rawDesc = "" + "\tgit_blame\x18# \x01(\v2).codefly.services.code.v0.GitBlameRequestH\x00R\bgitBlame\x12E\n" + "\bgit_diff\x18$ \x01(\v2(.codefly.services.code.v0.GitDiffRequestH\x00R\agitDiff\x12K\n" + "\n" + - "shell_exec\x18% \x01(\v2*.codefly.services.code.v0.ShellExecRequestH\x00R\tshellExecB\v\n" + - "\toperation\"\xa0\x0f\n" + + "shell_exec\x18% \x01(\v2*.codefly.services.code.v0.ShellExecRequestH\x00R\tshellExec\x12s\n" + + "\x1bcreate_workspace_checkpoint\x18& \x01(\v21.codefly.base.v0.CreateWorkspaceCheckpointRequestH\x00R\x19createWorkspaceCheckpoint\x12v\n" + + "\x1crestore_workspace_checkpoint\x18' \x01(\v22.codefly.base.v0.RestoreWorkspaceCheckpointRequestH\x00R\x1arestoreWorkspaceCheckpoint\x12v\n" + + "\x1crelease_workspace_checkpoint\x18( \x01(\v22.codefly.base.v0.ReleaseWorkspaceCheckpointRequestH\x00R\x1areleaseWorkspaceCheckpointB\v\n" + + "\toperation\"\x88\x12\n" + "\fCodeResponse\x122\n" + "\afailure\x18\x01 \x01(\v2\x18.codefly.base.v0.FailureR\afailure\x129\n" + "\x03fix\x18\r \x01(\v2%.codefly.services.code.v0.FixResponseH\x00R\x03fix\x12L\n" + @@ -4772,7 +4873,10 @@ const file_codefly_services_code_v0_code_proto_rawDesc = "" + "\tgit_blame\x18# \x01(\v2*.codefly.services.code.v0.GitBlameResponseH\x00R\bgitBlame\x12F\n" + "\bgit_diff\x18$ \x01(\v2).codefly.services.code.v0.GitDiffResponseH\x00R\agitDiff\x12L\n" + "\n" + - "shell_exec\x18% \x01(\v2+.codefly.services.code.v0.ShellExecResponseH\x00R\tshellExecB\b\n" + + "shell_exec\x18% \x01(\v2+.codefly.services.code.v0.ShellExecResponseH\x00R\tshellExec\x12t\n" + + "\x1bcreate_workspace_checkpoint\x18& \x01(\v22.codefly.base.v0.CreateWorkspaceCheckpointResponseH\x00R\x19createWorkspaceCheckpoint\x12w\n" + + "\x1crestore_workspace_checkpoint\x18' \x01(\v23.codefly.base.v0.RestoreWorkspaceCheckpointResponseH\x00R\x1arestoreWorkspaceCheckpoint\x12w\n" + + "\x1crelease_workspace_checkpoint\x18( \x01(\v23.codefly.base.v0.ReleaseWorkspaceCheckpointResponseH\x00R\x1areleaseWorkspaceCheckpointB\b\n" + "\x06result*\x94\x01\n" + "\x16SearchTruncationReason\x12(\n" + "$SEARCH_TRUNCATION_REASON_UNSPECIFIED\x10\x00\x12(\n" + @@ -4797,69 +4901,75 @@ func file_codefly_services_code_v0_code_proto_rawDescGZIP() []byte { var file_codefly_services_code_v0_code_proto_enumTypes = make([]protoimpl.EnumInfo, 1) var file_codefly_services_code_v0_code_proto_msgTypes = make([]protoimpl.MessageInfo, 55) var file_codefly_services_code_v0_code_proto_goTypes = []any{ - (SearchTruncationReason)(0), // 0: codefly.services.code.v0.SearchTruncationReason - (*ReadFileRequest)(nil), // 1: codefly.services.code.v0.ReadFileRequest - (*ReadFileResponse)(nil), // 2: codefly.services.code.v0.ReadFileResponse - (*WriteFileRequest)(nil), // 3: codefly.services.code.v0.WriteFileRequest - (*WriteFileResponse)(nil), // 4: codefly.services.code.v0.WriteFileResponse - (*ListFilesRequest)(nil), // 5: codefly.services.code.v0.ListFilesRequest - (*FileInfo)(nil), // 6: codefly.services.code.v0.FileInfo - (*ListFilesResponse)(nil), // 7: codefly.services.code.v0.ListFilesResponse - (*FixRequest)(nil), // 8: codefly.services.code.v0.FixRequest - (*FixResponse)(nil), // 9: codefly.services.code.v0.FixResponse - (*ApplyEditRequest)(nil), // 10: codefly.services.code.v0.ApplyEditRequest - (*ApplyEditResponse)(nil), // 11: codefly.services.code.v0.ApplyEditResponse - (*ApplySymbolPatchRequest)(nil), // 12: codefly.services.code.v0.ApplySymbolPatchRequest - (*ApplySymbolPatchResponse)(nil), // 13: codefly.services.code.v0.ApplySymbolPatchResponse - (*SearchRequest)(nil), // 14: codefly.services.code.v0.SearchRequest - (*SearchMatch)(nil), // 15: codefly.services.code.v0.SearchMatch - (*SearchResponse)(nil), // 16: codefly.services.code.v0.SearchResponse - (*DeleteFileRequest)(nil), // 17: codefly.services.code.v0.DeleteFileRequest - (*DeleteFileResponse)(nil), // 18: codefly.services.code.v0.DeleteFileResponse - (*MoveFileRequest)(nil), // 19: codefly.services.code.v0.MoveFileRequest - (*MoveFileResponse)(nil), // 20: codefly.services.code.v0.MoveFileResponse - (*CreateFileRequest)(nil), // 21: codefly.services.code.v0.CreateFileRequest - (*CreateFileResponse)(nil), // 22: codefly.services.code.v0.CreateFileResponse - (*Dependency)(nil), // 23: codefly.services.code.v0.Dependency - (*ListDependenciesRequest)(nil), // 24: codefly.services.code.v0.ListDependenciesRequest - (*ListDependenciesResponse)(nil), // 25: codefly.services.code.v0.ListDependenciesResponse - (*AddDependencyRequest)(nil), // 26: codefly.services.code.v0.AddDependencyRequest - (*AddDependencyResponse)(nil), // 27: codefly.services.code.v0.AddDependencyResponse - (*RemoveDependencyRequest)(nil), // 28: codefly.services.code.v0.RemoveDependencyRequest - (*RemoveDependencyResponse)(nil), // 29: codefly.services.code.v0.RemoveDependencyResponse - (*PackageInfo)(nil), // 30: codefly.services.code.v0.PackageInfo - (*SourceFileInfo)(nil), // 31: codefly.services.code.v0.SourceFileInfo - (*GetProjectInfoRequest)(nil), // 32: codefly.services.code.v0.GetProjectInfoRequest - (*GetProjectInfoResponse)(nil), // 33: codefly.services.code.v0.GetProjectInfoResponse - (*GetSemanticIndexRequest)(nil), // 34: codefly.services.code.v0.GetSemanticIndexRequest - (*GetInstructionIndexRequest)(nil), // 35: codefly.services.code.v0.GetInstructionIndexRequest - (*GetSourceManifestRequest)(nil), // 36: codefly.services.code.v0.GetSourceManifestRequest - (*DiscoverCodeUnitsRequest)(nil), // 37: codefly.services.code.v0.DiscoverCodeUnitsRequest - (*CodeUnitInfo)(nil), // 38: codefly.services.code.v0.CodeUnitInfo - (*DiscoverCodeUnitsResponse)(nil), // 39: codefly.services.code.v0.DiscoverCodeUnitsResponse - (*GitLogRequest)(nil), // 40: codefly.services.code.v0.GitLogRequest - (*GitCommit)(nil), // 41: codefly.services.code.v0.GitCommit - (*GitLogResponse)(nil), // 42: codefly.services.code.v0.GitLogResponse - (*GitDiffRequest)(nil), // 43: codefly.services.code.v0.GitDiffRequest - (*GitDiffFile)(nil), // 44: codefly.services.code.v0.GitDiffFile - (*GitDiffResponse)(nil), // 45: codefly.services.code.v0.GitDiffResponse - (*GitShowRequest)(nil), // 46: codefly.services.code.v0.GitShowRequest - (*GitShowResponse)(nil), // 47: codefly.services.code.v0.GitShowResponse - (*GitBlameRequest)(nil), // 48: codefly.services.code.v0.GitBlameRequest - (*GitBlameLine)(nil), // 49: codefly.services.code.v0.GitBlameLine - (*GitBlameResponse)(nil), // 50: codefly.services.code.v0.GitBlameResponse - (*ShellExecRequest)(nil), // 51: codefly.services.code.v0.ShellExecRequest - (*ShellExecResponse)(nil), // 52: codefly.services.code.v0.ShellExecResponse - (*CodeRequest)(nil), // 53: codefly.services.code.v0.CodeRequest - (*CodeResponse)(nil), // 54: codefly.services.code.v0.CodeResponse - nil, // 55: codefly.services.code.v0.GetProjectInfoResponse.FileHashesEntry - (v0.FixMode)(0), // 56: codefly.base.v0.FixMode - (v0.SymbolPatchFailureReason)(0), // 57: codefly.base.v0.SymbolPatchFailureReason - (v0.SourceManifestIdentityMode)(0), // 58: codefly.base.v0.SourceManifestIdentityMode - (*v0.Failure)(nil), // 59: codefly.base.v0.Failure - (*v0.SemanticIndex)(nil), // 60: codefly.base.v0.SemanticIndex - (*v0.SourceManifest)(nil), // 61: codefly.base.v0.SourceManifest - (*v0.InstructionIndex)(nil), // 62: codefly.base.v0.InstructionIndex + (SearchTruncationReason)(0), // 0: codefly.services.code.v0.SearchTruncationReason + (*ReadFileRequest)(nil), // 1: codefly.services.code.v0.ReadFileRequest + (*ReadFileResponse)(nil), // 2: codefly.services.code.v0.ReadFileResponse + (*WriteFileRequest)(nil), // 3: codefly.services.code.v0.WriteFileRequest + (*WriteFileResponse)(nil), // 4: codefly.services.code.v0.WriteFileResponse + (*ListFilesRequest)(nil), // 5: codefly.services.code.v0.ListFilesRequest + (*FileInfo)(nil), // 6: codefly.services.code.v0.FileInfo + (*ListFilesResponse)(nil), // 7: codefly.services.code.v0.ListFilesResponse + (*FixRequest)(nil), // 8: codefly.services.code.v0.FixRequest + (*FixResponse)(nil), // 9: codefly.services.code.v0.FixResponse + (*ApplyEditRequest)(nil), // 10: codefly.services.code.v0.ApplyEditRequest + (*ApplyEditResponse)(nil), // 11: codefly.services.code.v0.ApplyEditResponse + (*ApplySymbolPatchRequest)(nil), // 12: codefly.services.code.v0.ApplySymbolPatchRequest + (*ApplySymbolPatchResponse)(nil), // 13: codefly.services.code.v0.ApplySymbolPatchResponse + (*SearchRequest)(nil), // 14: codefly.services.code.v0.SearchRequest + (*SearchMatch)(nil), // 15: codefly.services.code.v0.SearchMatch + (*SearchResponse)(nil), // 16: codefly.services.code.v0.SearchResponse + (*DeleteFileRequest)(nil), // 17: codefly.services.code.v0.DeleteFileRequest + (*DeleteFileResponse)(nil), // 18: codefly.services.code.v0.DeleteFileResponse + (*MoveFileRequest)(nil), // 19: codefly.services.code.v0.MoveFileRequest + (*MoveFileResponse)(nil), // 20: codefly.services.code.v0.MoveFileResponse + (*CreateFileRequest)(nil), // 21: codefly.services.code.v0.CreateFileRequest + (*CreateFileResponse)(nil), // 22: codefly.services.code.v0.CreateFileResponse + (*Dependency)(nil), // 23: codefly.services.code.v0.Dependency + (*ListDependenciesRequest)(nil), // 24: codefly.services.code.v0.ListDependenciesRequest + (*ListDependenciesResponse)(nil), // 25: codefly.services.code.v0.ListDependenciesResponse + (*AddDependencyRequest)(nil), // 26: codefly.services.code.v0.AddDependencyRequest + (*AddDependencyResponse)(nil), // 27: codefly.services.code.v0.AddDependencyResponse + (*RemoveDependencyRequest)(nil), // 28: codefly.services.code.v0.RemoveDependencyRequest + (*RemoveDependencyResponse)(nil), // 29: codefly.services.code.v0.RemoveDependencyResponse + (*PackageInfo)(nil), // 30: codefly.services.code.v0.PackageInfo + (*SourceFileInfo)(nil), // 31: codefly.services.code.v0.SourceFileInfo + (*GetProjectInfoRequest)(nil), // 32: codefly.services.code.v0.GetProjectInfoRequest + (*GetProjectInfoResponse)(nil), // 33: codefly.services.code.v0.GetProjectInfoResponse + (*GetSemanticIndexRequest)(nil), // 34: codefly.services.code.v0.GetSemanticIndexRequest + (*GetInstructionIndexRequest)(nil), // 35: codefly.services.code.v0.GetInstructionIndexRequest + (*GetSourceManifestRequest)(nil), // 36: codefly.services.code.v0.GetSourceManifestRequest + (*DiscoverCodeUnitsRequest)(nil), // 37: codefly.services.code.v0.DiscoverCodeUnitsRequest + (*CodeUnitInfo)(nil), // 38: codefly.services.code.v0.CodeUnitInfo + (*DiscoverCodeUnitsResponse)(nil), // 39: codefly.services.code.v0.DiscoverCodeUnitsResponse + (*GitLogRequest)(nil), // 40: codefly.services.code.v0.GitLogRequest + (*GitCommit)(nil), // 41: codefly.services.code.v0.GitCommit + (*GitLogResponse)(nil), // 42: codefly.services.code.v0.GitLogResponse + (*GitDiffRequest)(nil), // 43: codefly.services.code.v0.GitDiffRequest + (*GitDiffFile)(nil), // 44: codefly.services.code.v0.GitDiffFile + (*GitDiffResponse)(nil), // 45: codefly.services.code.v0.GitDiffResponse + (*GitShowRequest)(nil), // 46: codefly.services.code.v0.GitShowRequest + (*GitShowResponse)(nil), // 47: codefly.services.code.v0.GitShowResponse + (*GitBlameRequest)(nil), // 48: codefly.services.code.v0.GitBlameRequest + (*GitBlameLine)(nil), // 49: codefly.services.code.v0.GitBlameLine + (*GitBlameResponse)(nil), // 50: codefly.services.code.v0.GitBlameResponse + (*ShellExecRequest)(nil), // 51: codefly.services.code.v0.ShellExecRequest + (*ShellExecResponse)(nil), // 52: codefly.services.code.v0.ShellExecResponse + (*CodeRequest)(nil), // 53: codefly.services.code.v0.CodeRequest + (*CodeResponse)(nil), // 54: codefly.services.code.v0.CodeResponse + nil, // 55: codefly.services.code.v0.GetProjectInfoResponse.FileHashesEntry + (v0.FixMode)(0), // 56: codefly.base.v0.FixMode + (v0.SymbolPatchFailureReason)(0), // 57: codefly.base.v0.SymbolPatchFailureReason + (v0.SourceManifestIdentityMode)(0), // 58: codefly.base.v0.SourceManifestIdentityMode + (*v0.CreateWorkspaceCheckpointRequest)(nil), // 59: codefly.base.v0.CreateWorkspaceCheckpointRequest + (*v0.RestoreWorkspaceCheckpointRequest)(nil), // 60: codefly.base.v0.RestoreWorkspaceCheckpointRequest + (*v0.ReleaseWorkspaceCheckpointRequest)(nil), // 61: codefly.base.v0.ReleaseWorkspaceCheckpointRequest + (*v0.Failure)(nil), // 62: codefly.base.v0.Failure + (*v0.SemanticIndex)(nil), // 63: codefly.base.v0.SemanticIndex + (*v0.SourceManifest)(nil), // 64: codefly.base.v0.SourceManifest + (*v0.InstructionIndex)(nil), // 65: codefly.base.v0.InstructionIndex + (*v0.CreateWorkspaceCheckpointResponse)(nil), // 66: codefly.base.v0.CreateWorkspaceCheckpointResponse + (*v0.RestoreWorkspaceCheckpointResponse)(nil), // 67: codefly.base.v0.RestoreWorkspaceCheckpointResponse + (*v0.ReleaseWorkspaceCheckpointResponse)(nil), // 68: codefly.base.v0.ReleaseWorkspaceCheckpointResponse } var file_codefly_services_code_v0_code_proto_depIdxs = []int32{ 6, // 0: codefly.services.code.v0.ListFilesResponse.files:type_name -> codefly.services.code.v0.FileInfo @@ -4902,37 +5012,43 @@ var file_codefly_services_code_v0_code_proto_depIdxs = []int32{ 48, // 37: codefly.services.code.v0.CodeRequest.git_blame:type_name -> codefly.services.code.v0.GitBlameRequest 43, // 38: codefly.services.code.v0.CodeRequest.git_diff:type_name -> codefly.services.code.v0.GitDiffRequest 51, // 39: codefly.services.code.v0.CodeRequest.shell_exec:type_name -> codefly.services.code.v0.ShellExecRequest - 59, // 40: codefly.services.code.v0.CodeResponse.failure:type_name -> codefly.base.v0.Failure - 9, // 41: codefly.services.code.v0.CodeResponse.fix:type_name -> codefly.services.code.v0.FixResponse - 11, // 42: codefly.services.code.v0.CodeResponse.apply_edit:type_name -> codefly.services.code.v0.ApplyEditResponse - 13, // 43: codefly.services.code.v0.CodeResponse.apply_symbol_patch:type_name -> codefly.services.code.v0.ApplySymbolPatchResponse - 25, // 44: codefly.services.code.v0.CodeResponse.list_dependencies:type_name -> codefly.services.code.v0.ListDependenciesResponse - 27, // 45: codefly.services.code.v0.CodeResponse.add_dependency:type_name -> codefly.services.code.v0.AddDependencyResponse - 29, // 46: codefly.services.code.v0.CodeResponse.remove_dependency:type_name -> codefly.services.code.v0.RemoveDependencyResponse - 33, // 47: codefly.services.code.v0.CodeResponse.get_project_info:type_name -> codefly.services.code.v0.GetProjectInfoResponse - 39, // 48: codefly.services.code.v0.CodeResponse.discover_code_units:type_name -> codefly.services.code.v0.DiscoverCodeUnitsResponse - 60, // 49: codefly.services.code.v0.CodeResponse.get_semantic_index:type_name -> codefly.base.v0.SemanticIndex - 61, // 50: codefly.services.code.v0.CodeResponse.get_source_manifest:type_name -> codefly.base.v0.SourceManifest - 62, // 51: codefly.services.code.v0.CodeResponse.get_instruction_index:type_name -> codefly.base.v0.InstructionIndex - 2, // 52: codefly.services.code.v0.CodeResponse.read_file:type_name -> codefly.services.code.v0.ReadFileResponse - 4, // 53: codefly.services.code.v0.CodeResponse.write_file:type_name -> codefly.services.code.v0.WriteFileResponse - 22, // 54: codefly.services.code.v0.CodeResponse.create_file:type_name -> codefly.services.code.v0.CreateFileResponse - 18, // 55: codefly.services.code.v0.CodeResponse.delete_file:type_name -> codefly.services.code.v0.DeleteFileResponse - 20, // 56: codefly.services.code.v0.CodeResponse.move_file:type_name -> codefly.services.code.v0.MoveFileResponse - 7, // 57: codefly.services.code.v0.CodeResponse.list_files:type_name -> codefly.services.code.v0.ListFilesResponse - 16, // 58: codefly.services.code.v0.CodeResponse.search:type_name -> codefly.services.code.v0.SearchResponse - 42, // 59: codefly.services.code.v0.CodeResponse.git_log:type_name -> codefly.services.code.v0.GitLogResponse - 47, // 60: codefly.services.code.v0.CodeResponse.git_show:type_name -> codefly.services.code.v0.GitShowResponse - 50, // 61: codefly.services.code.v0.CodeResponse.git_blame:type_name -> codefly.services.code.v0.GitBlameResponse - 45, // 62: codefly.services.code.v0.CodeResponse.git_diff:type_name -> codefly.services.code.v0.GitDiffResponse - 52, // 63: codefly.services.code.v0.CodeResponse.shell_exec:type_name -> codefly.services.code.v0.ShellExecResponse - 53, // 64: codefly.services.code.v0.Code.Execute:input_type -> codefly.services.code.v0.CodeRequest - 54, // 65: codefly.services.code.v0.Code.Execute:output_type -> codefly.services.code.v0.CodeResponse - 65, // [65:66] is the sub-list for method output_type - 64, // [64:65] is the sub-list for method input_type - 64, // [64:64] is the sub-list for extension type_name - 64, // [64:64] is the sub-list for extension extendee - 0, // [0:64] is the sub-list for field type_name + 59, // 40: codefly.services.code.v0.CodeRequest.create_workspace_checkpoint:type_name -> codefly.base.v0.CreateWorkspaceCheckpointRequest + 60, // 41: codefly.services.code.v0.CodeRequest.restore_workspace_checkpoint:type_name -> codefly.base.v0.RestoreWorkspaceCheckpointRequest + 61, // 42: codefly.services.code.v0.CodeRequest.release_workspace_checkpoint:type_name -> codefly.base.v0.ReleaseWorkspaceCheckpointRequest + 62, // 43: codefly.services.code.v0.CodeResponse.failure:type_name -> codefly.base.v0.Failure + 9, // 44: codefly.services.code.v0.CodeResponse.fix:type_name -> codefly.services.code.v0.FixResponse + 11, // 45: codefly.services.code.v0.CodeResponse.apply_edit:type_name -> codefly.services.code.v0.ApplyEditResponse + 13, // 46: codefly.services.code.v0.CodeResponse.apply_symbol_patch:type_name -> codefly.services.code.v0.ApplySymbolPatchResponse + 25, // 47: codefly.services.code.v0.CodeResponse.list_dependencies:type_name -> codefly.services.code.v0.ListDependenciesResponse + 27, // 48: codefly.services.code.v0.CodeResponse.add_dependency:type_name -> codefly.services.code.v0.AddDependencyResponse + 29, // 49: codefly.services.code.v0.CodeResponse.remove_dependency:type_name -> codefly.services.code.v0.RemoveDependencyResponse + 33, // 50: codefly.services.code.v0.CodeResponse.get_project_info:type_name -> codefly.services.code.v0.GetProjectInfoResponse + 39, // 51: codefly.services.code.v0.CodeResponse.discover_code_units:type_name -> codefly.services.code.v0.DiscoverCodeUnitsResponse + 63, // 52: codefly.services.code.v0.CodeResponse.get_semantic_index:type_name -> codefly.base.v0.SemanticIndex + 64, // 53: codefly.services.code.v0.CodeResponse.get_source_manifest:type_name -> codefly.base.v0.SourceManifest + 65, // 54: codefly.services.code.v0.CodeResponse.get_instruction_index:type_name -> codefly.base.v0.InstructionIndex + 2, // 55: codefly.services.code.v0.CodeResponse.read_file:type_name -> codefly.services.code.v0.ReadFileResponse + 4, // 56: codefly.services.code.v0.CodeResponse.write_file:type_name -> codefly.services.code.v0.WriteFileResponse + 22, // 57: codefly.services.code.v0.CodeResponse.create_file:type_name -> codefly.services.code.v0.CreateFileResponse + 18, // 58: codefly.services.code.v0.CodeResponse.delete_file:type_name -> codefly.services.code.v0.DeleteFileResponse + 20, // 59: codefly.services.code.v0.CodeResponse.move_file:type_name -> codefly.services.code.v0.MoveFileResponse + 7, // 60: codefly.services.code.v0.CodeResponse.list_files:type_name -> codefly.services.code.v0.ListFilesResponse + 16, // 61: codefly.services.code.v0.CodeResponse.search:type_name -> codefly.services.code.v0.SearchResponse + 42, // 62: codefly.services.code.v0.CodeResponse.git_log:type_name -> codefly.services.code.v0.GitLogResponse + 47, // 63: codefly.services.code.v0.CodeResponse.git_show:type_name -> codefly.services.code.v0.GitShowResponse + 50, // 64: codefly.services.code.v0.CodeResponse.git_blame:type_name -> codefly.services.code.v0.GitBlameResponse + 45, // 65: codefly.services.code.v0.CodeResponse.git_diff:type_name -> codefly.services.code.v0.GitDiffResponse + 52, // 66: codefly.services.code.v0.CodeResponse.shell_exec:type_name -> codefly.services.code.v0.ShellExecResponse + 66, // 67: codefly.services.code.v0.CodeResponse.create_workspace_checkpoint:type_name -> codefly.base.v0.CreateWorkspaceCheckpointResponse + 67, // 68: codefly.services.code.v0.CodeResponse.restore_workspace_checkpoint:type_name -> codefly.base.v0.RestoreWorkspaceCheckpointResponse + 68, // 69: codefly.services.code.v0.CodeResponse.release_workspace_checkpoint:type_name -> codefly.base.v0.ReleaseWorkspaceCheckpointResponse + 53, // 70: codefly.services.code.v0.Code.Execute:input_type -> codefly.services.code.v0.CodeRequest + 54, // 71: codefly.services.code.v0.Code.Execute:output_type -> codefly.services.code.v0.CodeResponse + 71, // [71:72] is the sub-list for method output_type + 70, // [70:71] is the sub-list for method input_type + 70, // [70:70] is the sub-list for extension type_name + 70, // [70:70] is the sub-list for extension extendee + 0, // [0:70] is the sub-list for field type_name } func init() { file_codefly_services_code_v0_code_proto_init() } @@ -4964,6 +5080,9 @@ func file_codefly_services_code_v0_code_proto_init() { (*CodeRequest_GitBlame)(nil), (*CodeRequest_GitDiff)(nil), (*CodeRequest_ShellExec)(nil), + (*CodeRequest_CreateWorkspaceCheckpoint)(nil), + (*CodeRequest_RestoreWorkspaceCheckpoint)(nil), + (*CodeRequest_ReleaseWorkspaceCheckpoint)(nil), } file_codefly_services_code_v0_code_proto_msgTypes[53].OneofWrappers = []any{ (*CodeResponse_Fix)(nil), @@ -4989,6 +5108,9 @@ func file_codefly_services_code_v0_code_proto_init() { (*CodeResponse_GitBlame)(nil), (*CodeResponse_GitDiff)(nil), (*CodeResponse_ShellExec)(nil), + (*CodeResponse_CreateWorkspaceCheckpoint)(nil), + (*CodeResponse_RestoreWorkspaceCheckpoint)(nil), + (*CodeResponse_ReleaseWorkspaceCheckpoint)(nil), } type x struct{} out := protoimpl.TypeBuilder{ diff --git a/generated/go/mind/gateway/v1/gateway.pb.go b/generated/go/mind/gateway/v1/gateway.pb.go index 947ff9d2..bce7eb12 100644 --- a/generated/go/mind/gateway/v1/gateway.pb.go +++ b/generated/go/mind/gateway/v1/gateway.pb.go @@ -11242,7 +11242,7 @@ var File_mind_gateway_v1_gateway_proto protoreflect.FileDescriptor const file_mind_gateway_v1_gateway_proto_rawDesc = "" + "\n" + - "\x1dmind/gateway/v1/gateway.proto\x12\x0fmind.gateway.v1\x1a\x1ccodefly/base/v0/source.proto\x1a\x1dcodefly/base/v0/failure.proto\x1a!codefly/base/v0/instruction.proto\x1a\x1ecodefly/base/v0/semantic.proto\x1a\x1dcodefly/base/v0/storage.proto\x1a)codefly/services/builder/v0/builder.proto\x1a)codefly/services/runtime/v0/runtime.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x15\n" + + "\x1dmind/gateway/v1/gateway.proto\x12\x0fmind.gateway.v1\x1a\x1ccodefly/base/v0/source.proto\x1a\x1dcodefly/base/v0/failure.proto\x1a!codefly/base/v0/instruction.proto\x1a\x1ecodefly/base/v0/semantic.proto\x1a\x1dcodefly/base/v0/storage.proto\x1a*codefly/base/v0/workspace_checkpoint.proto\x1a)codefly/services/builder/v0/builder.proto\x1a)codefly/services/runtime/v0/runtime.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\x15\n" + "\x13ListServicesRequest\"e\n" + "\vServiceInfo\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x1a\n" + @@ -12091,10 +12091,13 @@ const file_mind_gateway_v1_gateway_proto_rawDesc = "" + "\x1cFORGE_EVENT_KIND_UNSPECIFIED\x10\x00\x12!\n" + "\x1dFORGE_EVENT_KIND_PULL_REQUEST\x10\x01\x12\x1a\n" + "\x16FORGE_EVENT_KIND_CHECK\x10\x02\x12\x1b\n" + - "\x17FORGE_EVENT_KIND_REVIEW\x10\x032\xe8*\n" + + "\x17FORGE_EVENT_KIND_REVIEW\x10\x032\xfd-\n" + "\aGateway\x12[\n" + "\fListServices\x12$.mind.gateway.v1.ListServicesRequest\x1a%.mind.gateway.v1.ListServicesResponse\x12|\n" + - "\x17EvaluateStorageCapacity\x12/.mind.gateway.v1.EvaluateStorageCapacityRequest\x1a0.mind.gateway.v1.EvaluateStorageCapacityResponse\x12O\n" + + "\x17EvaluateStorageCapacity\x12/.mind.gateway.v1.EvaluateStorageCapacityRequest\x1a0.mind.gateway.v1.EvaluateStorageCapacityResponse\x12\x82\x01\n" + + "\x19CreateWorkspaceCheckpoint\x121.codefly.base.v0.CreateWorkspaceCheckpointRequest\x1a2.codefly.base.v0.CreateWorkspaceCheckpointResponse\x12\x85\x01\n" + + "\x1aRestoreWorkspaceCheckpoint\x122.codefly.base.v0.RestoreWorkspaceCheckpointRequest\x1a3.codefly.base.v0.RestoreWorkspaceCheckpointResponse\x12\x85\x01\n" + + "\x1aReleaseWorkspaceCheckpoint\x122.codefly.base.v0.ReleaseWorkspaceCheckpointRequest\x1a3.codefly.base.v0.ReleaseWorkspaceCheckpointResponse\x12O\n" + "\bReadFile\x12 .mind.gateway.v1.ReadFileRequest\x1a!.mind.gateway.v1.ReadFileResponse\x12R\n" + "\tWriteFile\x12!.mind.gateway.v1.WriteFileRequest\x1a\".mind.gateway.v1.WriteFileResponse\x12R\n" + "\tListFiles\x12!.mind.gateway.v1.ListFilesRequest\x1a\".mind.gateway.v1.ListFilesResponse\x12w\n" + @@ -12345,6 +12348,12 @@ var file_mind_gateway_v1_gateway_proto_goTypes = []any{ (v0.SourceManifestIdentityMode)(0), // 171: codefly.base.v0.SourceManifestIdentityMode (*v0.SourceManifest)(nil), // 172: codefly.base.v0.SourceManifest (*v0.StorageCapacityAdmission)(nil), // 173: codefly.base.v0.StorageCapacityAdmission + (*v0.CreateWorkspaceCheckpointRequest)(nil), // 174: codefly.base.v0.CreateWorkspaceCheckpointRequest + (*v0.RestoreWorkspaceCheckpointRequest)(nil), // 175: codefly.base.v0.RestoreWorkspaceCheckpointRequest + (*v0.ReleaseWorkspaceCheckpointRequest)(nil), // 176: codefly.base.v0.ReleaseWorkspaceCheckpointRequest + (*v0.CreateWorkspaceCheckpointResponse)(nil), // 177: codefly.base.v0.CreateWorkspaceCheckpointResponse + (*v0.RestoreWorkspaceCheckpointResponse)(nil), // 178: codefly.base.v0.RestoreWorkspaceCheckpointResponse + (*v0.ReleaseWorkspaceCheckpointResponse)(nil), // 179: codefly.base.v0.ReleaseWorkspaceCheckpointResponse } var file_mind_gateway_v1_gateway_proto_depIdxs = []int32{ 11, // 0: mind.gateway.v1.ListServicesResponse.services:type_name -> mind.gateway.v1.ServiceInfo @@ -12456,120 +12465,126 @@ var file_mind_gateway_v1_gateway_proto_depIdxs = []int32{ 162, // 106: mind.gateway.v1.EvaluateStorageCapacityResponse.failure:type_name -> codefly.base.v0.Failure 10, // 107: mind.gateway.v1.Gateway.ListServices:input_type -> mind.gateway.v1.ListServicesRequest 157, // 108: mind.gateway.v1.Gateway.EvaluateStorageCapacity:input_type -> mind.gateway.v1.EvaluateStorageCapacityRequest - 13, // 109: mind.gateway.v1.Gateway.ReadFile:input_type -> mind.gateway.v1.ReadFileRequest - 15, // 110: mind.gateway.v1.Gateway.WriteFile:input_type -> mind.gateway.v1.WriteFileRequest - 17, // 111: mind.gateway.v1.Gateway.ListFiles:input_type -> mind.gateway.v1.ListFilesRequest - 21, // 112: mind.gateway.v1.Gateway.SubscribeWorkspaceChanges:input_type -> mind.gateway.v1.SubscribeWorkspaceChangesRequest - 24, // 113: mind.gateway.v1.Gateway.DeleteFile:input_type -> mind.gateway.v1.DeleteFileRequest - 26, // 114: mind.gateway.v1.Gateway.MoveFile:input_type -> mind.gateway.v1.MoveFileRequest - 28, // 115: mind.gateway.v1.Gateway.CreateFile:input_type -> mind.gateway.v1.CreateFileRequest - 30, // 116: mind.gateway.v1.Gateway.Fix:input_type -> mind.gateway.v1.FixRequest - 32, // 117: mind.gateway.v1.Gateway.ApplyEdit:input_type -> mind.gateway.v1.ApplyEditRequest - 34, // 118: mind.gateway.v1.Gateway.ApplySymbolPatch:input_type -> mind.gateway.v1.ApplySymbolPatchRequest - 36, // 119: mind.gateway.v1.Gateway.BatchApplyEdits:input_type -> mind.gateway.v1.BatchApplyEditsRequest - 39, // 120: mind.gateway.v1.Gateway.ConfigureMutationAuthority:input_type -> mind.gateway.v1.ConfigureMutationAuthorityRequest - 45, // 121: mind.gateway.v1.Gateway.PrepareMutation:input_type -> mind.gateway.v1.PrepareMutationRequest - 47, // 122: mind.gateway.v1.Gateway.ApplyPreparedMutation:input_type -> mind.gateway.v1.ApplyPreparedMutationRequest - 50, // 123: mind.gateway.v1.Gateway.Search:input_type -> mind.gateway.v1.SearchRequest - 53, // 124: mind.gateway.v1.Gateway.Build:input_type -> mind.gateway.v1.BuildRequest - 56, // 125: mind.gateway.v1.Gateway.Lint:input_type -> mind.gateway.v1.LintRequest - 59, // 126: mind.gateway.v1.Gateway.Test:input_type -> mind.gateway.v1.TestRequest - 61, // 127: mind.gateway.v1.Gateway.ConfigureService:input_type -> mind.gateway.v1.ConfigureServiceRequest - 63, // 128: mind.gateway.v1.Gateway.Format:input_type -> mind.gateway.v1.FormatRequest - 66, // 129: mind.gateway.v1.Gateway.RunCommand:input_type -> mind.gateway.v1.RunCommandRequest - 144, // 130: mind.gateway.v1.Gateway.ListAllCommands:input_type -> mind.gateway.v1.ListAllCommandsRequest - 68, // 131: mind.gateway.v1.Gateway.RunChecks:input_type -> mind.gateway.v1.RunChecksRequest - 75, // 132: mind.gateway.v1.Gateway.GitStatus:input_type -> mind.gateway.v1.GitStatusRequest - 78, // 133: mind.gateway.v1.Gateway.GitDiff:input_type -> mind.gateway.v1.GitDiffRequest - 80, // 134: mind.gateway.v1.Gateway.ApplyPatch:input_type -> mind.gateway.v1.ApplyPatchRequest - 82, // 135: mind.gateway.v1.Gateway.GitLog:input_type -> mind.gateway.v1.GitLogRequest - 85, // 136: mind.gateway.v1.Gateway.GitCommit:input_type -> mind.gateway.v1.GitCommitRequest - 88, // 137: mind.gateway.v1.Gateway.GitBranch:input_type -> mind.gateway.v1.GitBranchRequest - 90, // 138: mind.gateway.v1.Gateway.GitCheckout:input_type -> mind.gateway.v1.GitCheckoutRequest - 92, // 139: mind.gateway.v1.Gateway.GitPush:input_type -> mind.gateway.v1.GitPushRequest - 94, // 140: mind.gateway.v1.Gateway.GitTag:input_type -> mind.gateway.v1.GitTagRequest - 96, // 141: mind.gateway.v1.Gateway.GitMerge:input_type -> mind.gateway.v1.GitMergeRequest - 98, // 142: mind.gateway.v1.Gateway.GitRevert:input_type -> mind.gateway.v1.GitRevertRequest - 100, // 143: mind.gateway.v1.Gateway.MaterializeRepositorySnapshot:input_type -> mind.gateway.v1.MaterializeRepositorySnapshotRequest - 102, // 144: mind.gateway.v1.Gateway.PrepareRepositoryCheckout:input_type -> mind.gateway.v1.PrepareRepositoryCheckoutRequest - 104, // 145: mind.gateway.v1.Gateway.ReleaseRepositorySnapshot:input_type -> mind.gateway.v1.ReleaseRepositorySnapshotRequest - 108, // 146: mind.gateway.v1.Gateway.Release:input_type -> mind.gateway.v1.ReleaseRequest - 114, // 147: mind.gateway.v1.Gateway.ForgePullRequestStatus:input_type -> mind.gateway.v1.ForgePullRequestStatusRequest - 116, // 148: mind.gateway.v1.Gateway.ForgeMergePullRequest:input_type -> mind.gateway.v1.ForgeMergePullRequestRequest - 118, // 149: mind.gateway.v1.Gateway.ForgeRequestReview:input_type -> mind.gateway.v1.ForgeRequestReviewRequest - 121, // 150: mind.gateway.v1.Gateway.ForgeNormalizeWebhook:input_type -> mind.gateway.v1.ForgeNormalizeWebhookRequest - 124, // 151: mind.gateway.v1.Gateway.ListDependencies:input_type -> mind.gateway.v1.ListDependenciesRequest - 126, // 152: mind.gateway.v1.Gateway.AddDependency:input_type -> mind.gateway.v1.AddDependencyRequest - 128, // 153: mind.gateway.v1.Gateway.RemoveDependency:input_type -> mind.gateway.v1.RemoveDependencyRequest - 132, // 154: mind.gateway.v1.Gateway.GetProjectInfo:input_type -> mind.gateway.v1.GetProjectInfoRequest - 134, // 155: mind.gateway.v1.Gateway.GetSemanticIndex:input_type -> mind.gateway.v1.GetSemanticIndexRequest - 136, // 156: mind.gateway.v1.Gateway.GetInstructionIndex:input_type -> mind.gateway.v1.GetInstructionIndexRequest - 138, // 157: mind.gateway.v1.Gateway.GetSourceManifest:input_type -> mind.gateway.v1.GetSourceManifestRequest - 140, // 158: mind.gateway.v1.Gateway.DiscoverCodeUnits:input_type -> mind.gateway.v1.DiscoverCodeUnitsRequest - 146, // 159: mind.gateway.v1.Gateway.OpenTerminal:input_type -> mind.gateway.v1.OpenTerminalRequest - 148, // 160: mind.gateway.v1.Gateway.AttachTerminal:input_type -> mind.gateway.v1.TerminalInput - 150, // 161: mind.gateway.v1.Gateway.ResizeTerminal:input_type -> mind.gateway.v1.ResizeTerminalRequest - 152, // 162: mind.gateway.v1.Gateway.CloseTerminal:input_type -> mind.gateway.v1.CloseTerminalRequest - 154, // 163: mind.gateway.v1.Gateway.ListTerminals:input_type -> mind.gateway.v1.ListTerminalsRequest - 12, // 164: mind.gateway.v1.Gateway.ListServices:output_type -> mind.gateway.v1.ListServicesResponse - 158, // 165: mind.gateway.v1.Gateway.EvaluateStorageCapacity:output_type -> mind.gateway.v1.EvaluateStorageCapacityResponse - 14, // 166: mind.gateway.v1.Gateway.ReadFile:output_type -> mind.gateway.v1.ReadFileResponse - 16, // 167: mind.gateway.v1.Gateway.WriteFile:output_type -> mind.gateway.v1.WriteFileResponse - 19, // 168: mind.gateway.v1.Gateway.ListFiles:output_type -> mind.gateway.v1.ListFilesResponse - 23, // 169: mind.gateway.v1.Gateway.SubscribeWorkspaceChanges:output_type -> mind.gateway.v1.WorkspaceChangeEvent - 25, // 170: mind.gateway.v1.Gateway.DeleteFile:output_type -> mind.gateway.v1.DeleteFileResponse - 27, // 171: mind.gateway.v1.Gateway.MoveFile:output_type -> mind.gateway.v1.MoveFileResponse - 29, // 172: mind.gateway.v1.Gateway.CreateFile:output_type -> mind.gateway.v1.CreateFileResponse - 31, // 173: mind.gateway.v1.Gateway.Fix:output_type -> mind.gateway.v1.FixResponse - 33, // 174: mind.gateway.v1.Gateway.ApplyEdit:output_type -> mind.gateway.v1.ApplyEditResponse - 35, // 175: mind.gateway.v1.Gateway.ApplySymbolPatch:output_type -> mind.gateway.v1.ApplySymbolPatchResponse - 38, // 176: mind.gateway.v1.Gateway.BatchApplyEdits:output_type -> mind.gateway.v1.BatchApplyEditsResponse - 40, // 177: mind.gateway.v1.Gateway.ConfigureMutationAuthority:output_type -> mind.gateway.v1.ConfigureMutationAuthorityResponse - 46, // 178: mind.gateway.v1.Gateway.PrepareMutation:output_type -> mind.gateway.v1.PrepareMutationResponse - 49, // 179: mind.gateway.v1.Gateway.ApplyPreparedMutation:output_type -> mind.gateway.v1.ApplyPreparedMutationResponse - 52, // 180: mind.gateway.v1.Gateway.Search:output_type -> mind.gateway.v1.SearchResponse - 55, // 181: mind.gateway.v1.Gateway.Build:output_type -> mind.gateway.v1.BuildResponse - 57, // 182: mind.gateway.v1.Gateway.Lint:output_type -> mind.gateway.v1.LintResponse - 60, // 183: mind.gateway.v1.Gateway.Test:output_type -> mind.gateway.v1.TestResponse - 62, // 184: mind.gateway.v1.Gateway.ConfigureService:output_type -> mind.gateway.v1.ConfigureServiceResponse - 64, // 185: mind.gateway.v1.Gateway.Format:output_type -> mind.gateway.v1.FormatResponse - 67, // 186: mind.gateway.v1.Gateway.RunCommand:output_type -> mind.gateway.v1.RunCommandResponse - 145, // 187: mind.gateway.v1.Gateway.ListAllCommands:output_type -> mind.gateway.v1.ListAllCommandsResponse - 74, // 188: mind.gateway.v1.Gateway.RunChecks:output_type -> mind.gateway.v1.RunChecksResponse - 77, // 189: mind.gateway.v1.Gateway.GitStatus:output_type -> mind.gateway.v1.GitStatusResponse - 79, // 190: mind.gateway.v1.Gateway.GitDiff:output_type -> mind.gateway.v1.GitDiffResponse - 81, // 191: mind.gateway.v1.Gateway.ApplyPatch:output_type -> mind.gateway.v1.ApplyPatchResponse - 84, // 192: mind.gateway.v1.Gateway.GitLog:output_type -> mind.gateway.v1.GitLogResponse - 86, // 193: mind.gateway.v1.Gateway.GitCommit:output_type -> mind.gateway.v1.GitCommitResponse - 89, // 194: mind.gateway.v1.Gateway.GitBranch:output_type -> mind.gateway.v1.GitBranchResponse - 91, // 195: mind.gateway.v1.Gateway.GitCheckout:output_type -> mind.gateway.v1.GitCheckoutResponse - 93, // 196: mind.gateway.v1.Gateway.GitPush:output_type -> mind.gateway.v1.GitPushResponse - 95, // 197: mind.gateway.v1.Gateway.GitTag:output_type -> mind.gateway.v1.GitTagResponse - 97, // 198: mind.gateway.v1.Gateway.GitMerge:output_type -> mind.gateway.v1.GitMergeResponse - 99, // 199: mind.gateway.v1.Gateway.GitRevert:output_type -> mind.gateway.v1.GitRevertResponse - 101, // 200: mind.gateway.v1.Gateway.MaterializeRepositorySnapshot:output_type -> mind.gateway.v1.MaterializeRepositorySnapshotResponse - 103, // 201: mind.gateway.v1.Gateway.PrepareRepositoryCheckout:output_type -> mind.gateway.v1.PrepareRepositoryCheckoutResponse - 105, // 202: mind.gateway.v1.Gateway.ReleaseRepositorySnapshot:output_type -> mind.gateway.v1.ReleaseRepositorySnapshotResponse - 109, // 203: mind.gateway.v1.Gateway.Release:output_type -> mind.gateway.v1.ReleaseResponse - 115, // 204: mind.gateway.v1.Gateway.ForgePullRequestStatus:output_type -> mind.gateway.v1.ForgePullRequestStatusResponse - 117, // 205: mind.gateway.v1.Gateway.ForgeMergePullRequest:output_type -> mind.gateway.v1.ForgeMergePullRequestResponse - 119, // 206: mind.gateway.v1.Gateway.ForgeRequestReview:output_type -> mind.gateway.v1.ForgeRequestReviewResponse - 122, // 207: mind.gateway.v1.Gateway.ForgeNormalizeWebhook:output_type -> mind.gateway.v1.ForgeNormalizeWebhookResponse - 125, // 208: mind.gateway.v1.Gateway.ListDependencies:output_type -> mind.gateway.v1.ListDependenciesResponse - 127, // 209: mind.gateway.v1.Gateway.AddDependency:output_type -> mind.gateway.v1.AddDependencyResponse - 129, // 210: mind.gateway.v1.Gateway.RemoveDependency:output_type -> mind.gateway.v1.RemoveDependencyResponse - 133, // 211: mind.gateway.v1.Gateway.GetProjectInfo:output_type -> mind.gateway.v1.GetProjectInfoResponse - 135, // 212: mind.gateway.v1.Gateway.GetSemanticIndex:output_type -> mind.gateway.v1.GetSemanticIndexResponse - 137, // 213: mind.gateway.v1.Gateway.GetInstructionIndex:output_type -> mind.gateway.v1.GetInstructionIndexResponse - 139, // 214: mind.gateway.v1.Gateway.GetSourceManifest:output_type -> mind.gateway.v1.GetSourceManifestResponse - 142, // 215: mind.gateway.v1.Gateway.DiscoverCodeUnits:output_type -> mind.gateway.v1.DiscoverCodeUnitsResponse - 147, // 216: mind.gateway.v1.Gateway.OpenTerminal:output_type -> mind.gateway.v1.OpenTerminalResponse - 149, // 217: mind.gateway.v1.Gateway.AttachTerminal:output_type -> mind.gateway.v1.TerminalOutput - 151, // 218: mind.gateway.v1.Gateway.ResizeTerminal:output_type -> mind.gateway.v1.ResizeTerminalResponse - 153, // 219: mind.gateway.v1.Gateway.CloseTerminal:output_type -> mind.gateway.v1.CloseTerminalResponse - 156, // 220: mind.gateway.v1.Gateway.ListTerminals:output_type -> mind.gateway.v1.ListTerminalsResponse - 164, // [164:221] is the sub-list for method output_type - 107, // [107:164] is the sub-list for method input_type + 174, // 109: mind.gateway.v1.Gateway.CreateWorkspaceCheckpoint:input_type -> codefly.base.v0.CreateWorkspaceCheckpointRequest + 175, // 110: mind.gateway.v1.Gateway.RestoreWorkspaceCheckpoint:input_type -> codefly.base.v0.RestoreWorkspaceCheckpointRequest + 176, // 111: mind.gateway.v1.Gateway.ReleaseWorkspaceCheckpoint:input_type -> codefly.base.v0.ReleaseWorkspaceCheckpointRequest + 13, // 112: mind.gateway.v1.Gateway.ReadFile:input_type -> mind.gateway.v1.ReadFileRequest + 15, // 113: mind.gateway.v1.Gateway.WriteFile:input_type -> mind.gateway.v1.WriteFileRequest + 17, // 114: mind.gateway.v1.Gateway.ListFiles:input_type -> mind.gateway.v1.ListFilesRequest + 21, // 115: mind.gateway.v1.Gateway.SubscribeWorkspaceChanges:input_type -> mind.gateway.v1.SubscribeWorkspaceChangesRequest + 24, // 116: mind.gateway.v1.Gateway.DeleteFile:input_type -> mind.gateway.v1.DeleteFileRequest + 26, // 117: mind.gateway.v1.Gateway.MoveFile:input_type -> mind.gateway.v1.MoveFileRequest + 28, // 118: mind.gateway.v1.Gateway.CreateFile:input_type -> mind.gateway.v1.CreateFileRequest + 30, // 119: mind.gateway.v1.Gateway.Fix:input_type -> mind.gateway.v1.FixRequest + 32, // 120: mind.gateway.v1.Gateway.ApplyEdit:input_type -> mind.gateway.v1.ApplyEditRequest + 34, // 121: mind.gateway.v1.Gateway.ApplySymbolPatch:input_type -> mind.gateway.v1.ApplySymbolPatchRequest + 36, // 122: mind.gateway.v1.Gateway.BatchApplyEdits:input_type -> mind.gateway.v1.BatchApplyEditsRequest + 39, // 123: mind.gateway.v1.Gateway.ConfigureMutationAuthority:input_type -> mind.gateway.v1.ConfigureMutationAuthorityRequest + 45, // 124: mind.gateway.v1.Gateway.PrepareMutation:input_type -> mind.gateway.v1.PrepareMutationRequest + 47, // 125: mind.gateway.v1.Gateway.ApplyPreparedMutation:input_type -> mind.gateway.v1.ApplyPreparedMutationRequest + 50, // 126: mind.gateway.v1.Gateway.Search:input_type -> mind.gateway.v1.SearchRequest + 53, // 127: mind.gateway.v1.Gateway.Build:input_type -> mind.gateway.v1.BuildRequest + 56, // 128: mind.gateway.v1.Gateway.Lint:input_type -> mind.gateway.v1.LintRequest + 59, // 129: mind.gateway.v1.Gateway.Test:input_type -> mind.gateway.v1.TestRequest + 61, // 130: mind.gateway.v1.Gateway.ConfigureService:input_type -> mind.gateway.v1.ConfigureServiceRequest + 63, // 131: mind.gateway.v1.Gateway.Format:input_type -> mind.gateway.v1.FormatRequest + 66, // 132: mind.gateway.v1.Gateway.RunCommand:input_type -> mind.gateway.v1.RunCommandRequest + 144, // 133: mind.gateway.v1.Gateway.ListAllCommands:input_type -> mind.gateway.v1.ListAllCommandsRequest + 68, // 134: mind.gateway.v1.Gateway.RunChecks:input_type -> mind.gateway.v1.RunChecksRequest + 75, // 135: mind.gateway.v1.Gateway.GitStatus:input_type -> mind.gateway.v1.GitStatusRequest + 78, // 136: mind.gateway.v1.Gateway.GitDiff:input_type -> mind.gateway.v1.GitDiffRequest + 80, // 137: mind.gateway.v1.Gateway.ApplyPatch:input_type -> mind.gateway.v1.ApplyPatchRequest + 82, // 138: mind.gateway.v1.Gateway.GitLog:input_type -> mind.gateway.v1.GitLogRequest + 85, // 139: mind.gateway.v1.Gateway.GitCommit:input_type -> mind.gateway.v1.GitCommitRequest + 88, // 140: mind.gateway.v1.Gateway.GitBranch:input_type -> mind.gateway.v1.GitBranchRequest + 90, // 141: mind.gateway.v1.Gateway.GitCheckout:input_type -> mind.gateway.v1.GitCheckoutRequest + 92, // 142: mind.gateway.v1.Gateway.GitPush:input_type -> mind.gateway.v1.GitPushRequest + 94, // 143: mind.gateway.v1.Gateway.GitTag:input_type -> mind.gateway.v1.GitTagRequest + 96, // 144: mind.gateway.v1.Gateway.GitMerge:input_type -> mind.gateway.v1.GitMergeRequest + 98, // 145: mind.gateway.v1.Gateway.GitRevert:input_type -> mind.gateway.v1.GitRevertRequest + 100, // 146: mind.gateway.v1.Gateway.MaterializeRepositorySnapshot:input_type -> mind.gateway.v1.MaterializeRepositorySnapshotRequest + 102, // 147: mind.gateway.v1.Gateway.PrepareRepositoryCheckout:input_type -> mind.gateway.v1.PrepareRepositoryCheckoutRequest + 104, // 148: mind.gateway.v1.Gateway.ReleaseRepositorySnapshot:input_type -> mind.gateway.v1.ReleaseRepositorySnapshotRequest + 108, // 149: mind.gateway.v1.Gateway.Release:input_type -> mind.gateway.v1.ReleaseRequest + 114, // 150: mind.gateway.v1.Gateway.ForgePullRequestStatus:input_type -> mind.gateway.v1.ForgePullRequestStatusRequest + 116, // 151: mind.gateway.v1.Gateway.ForgeMergePullRequest:input_type -> mind.gateway.v1.ForgeMergePullRequestRequest + 118, // 152: mind.gateway.v1.Gateway.ForgeRequestReview:input_type -> mind.gateway.v1.ForgeRequestReviewRequest + 121, // 153: mind.gateway.v1.Gateway.ForgeNormalizeWebhook:input_type -> mind.gateway.v1.ForgeNormalizeWebhookRequest + 124, // 154: mind.gateway.v1.Gateway.ListDependencies:input_type -> mind.gateway.v1.ListDependenciesRequest + 126, // 155: mind.gateway.v1.Gateway.AddDependency:input_type -> mind.gateway.v1.AddDependencyRequest + 128, // 156: mind.gateway.v1.Gateway.RemoveDependency:input_type -> mind.gateway.v1.RemoveDependencyRequest + 132, // 157: mind.gateway.v1.Gateway.GetProjectInfo:input_type -> mind.gateway.v1.GetProjectInfoRequest + 134, // 158: mind.gateway.v1.Gateway.GetSemanticIndex:input_type -> mind.gateway.v1.GetSemanticIndexRequest + 136, // 159: mind.gateway.v1.Gateway.GetInstructionIndex:input_type -> mind.gateway.v1.GetInstructionIndexRequest + 138, // 160: mind.gateway.v1.Gateway.GetSourceManifest:input_type -> mind.gateway.v1.GetSourceManifestRequest + 140, // 161: mind.gateway.v1.Gateway.DiscoverCodeUnits:input_type -> mind.gateway.v1.DiscoverCodeUnitsRequest + 146, // 162: mind.gateway.v1.Gateway.OpenTerminal:input_type -> mind.gateway.v1.OpenTerminalRequest + 148, // 163: mind.gateway.v1.Gateway.AttachTerminal:input_type -> mind.gateway.v1.TerminalInput + 150, // 164: mind.gateway.v1.Gateway.ResizeTerminal:input_type -> mind.gateway.v1.ResizeTerminalRequest + 152, // 165: mind.gateway.v1.Gateway.CloseTerminal:input_type -> mind.gateway.v1.CloseTerminalRequest + 154, // 166: mind.gateway.v1.Gateway.ListTerminals:input_type -> mind.gateway.v1.ListTerminalsRequest + 12, // 167: mind.gateway.v1.Gateway.ListServices:output_type -> mind.gateway.v1.ListServicesResponse + 158, // 168: mind.gateway.v1.Gateway.EvaluateStorageCapacity:output_type -> mind.gateway.v1.EvaluateStorageCapacityResponse + 177, // 169: mind.gateway.v1.Gateway.CreateWorkspaceCheckpoint:output_type -> codefly.base.v0.CreateWorkspaceCheckpointResponse + 178, // 170: mind.gateway.v1.Gateway.RestoreWorkspaceCheckpoint:output_type -> codefly.base.v0.RestoreWorkspaceCheckpointResponse + 179, // 171: mind.gateway.v1.Gateway.ReleaseWorkspaceCheckpoint:output_type -> codefly.base.v0.ReleaseWorkspaceCheckpointResponse + 14, // 172: mind.gateway.v1.Gateway.ReadFile:output_type -> mind.gateway.v1.ReadFileResponse + 16, // 173: mind.gateway.v1.Gateway.WriteFile:output_type -> mind.gateway.v1.WriteFileResponse + 19, // 174: mind.gateway.v1.Gateway.ListFiles:output_type -> mind.gateway.v1.ListFilesResponse + 23, // 175: mind.gateway.v1.Gateway.SubscribeWorkspaceChanges:output_type -> mind.gateway.v1.WorkspaceChangeEvent + 25, // 176: mind.gateway.v1.Gateway.DeleteFile:output_type -> mind.gateway.v1.DeleteFileResponse + 27, // 177: mind.gateway.v1.Gateway.MoveFile:output_type -> mind.gateway.v1.MoveFileResponse + 29, // 178: mind.gateway.v1.Gateway.CreateFile:output_type -> mind.gateway.v1.CreateFileResponse + 31, // 179: mind.gateway.v1.Gateway.Fix:output_type -> mind.gateway.v1.FixResponse + 33, // 180: mind.gateway.v1.Gateway.ApplyEdit:output_type -> mind.gateway.v1.ApplyEditResponse + 35, // 181: mind.gateway.v1.Gateway.ApplySymbolPatch:output_type -> mind.gateway.v1.ApplySymbolPatchResponse + 38, // 182: mind.gateway.v1.Gateway.BatchApplyEdits:output_type -> mind.gateway.v1.BatchApplyEditsResponse + 40, // 183: mind.gateway.v1.Gateway.ConfigureMutationAuthority:output_type -> mind.gateway.v1.ConfigureMutationAuthorityResponse + 46, // 184: mind.gateway.v1.Gateway.PrepareMutation:output_type -> mind.gateway.v1.PrepareMutationResponse + 49, // 185: mind.gateway.v1.Gateway.ApplyPreparedMutation:output_type -> mind.gateway.v1.ApplyPreparedMutationResponse + 52, // 186: mind.gateway.v1.Gateway.Search:output_type -> mind.gateway.v1.SearchResponse + 55, // 187: mind.gateway.v1.Gateway.Build:output_type -> mind.gateway.v1.BuildResponse + 57, // 188: mind.gateway.v1.Gateway.Lint:output_type -> mind.gateway.v1.LintResponse + 60, // 189: mind.gateway.v1.Gateway.Test:output_type -> mind.gateway.v1.TestResponse + 62, // 190: mind.gateway.v1.Gateway.ConfigureService:output_type -> mind.gateway.v1.ConfigureServiceResponse + 64, // 191: mind.gateway.v1.Gateway.Format:output_type -> mind.gateway.v1.FormatResponse + 67, // 192: mind.gateway.v1.Gateway.RunCommand:output_type -> mind.gateway.v1.RunCommandResponse + 145, // 193: mind.gateway.v1.Gateway.ListAllCommands:output_type -> mind.gateway.v1.ListAllCommandsResponse + 74, // 194: mind.gateway.v1.Gateway.RunChecks:output_type -> mind.gateway.v1.RunChecksResponse + 77, // 195: mind.gateway.v1.Gateway.GitStatus:output_type -> mind.gateway.v1.GitStatusResponse + 79, // 196: mind.gateway.v1.Gateway.GitDiff:output_type -> mind.gateway.v1.GitDiffResponse + 81, // 197: mind.gateway.v1.Gateway.ApplyPatch:output_type -> mind.gateway.v1.ApplyPatchResponse + 84, // 198: mind.gateway.v1.Gateway.GitLog:output_type -> mind.gateway.v1.GitLogResponse + 86, // 199: mind.gateway.v1.Gateway.GitCommit:output_type -> mind.gateway.v1.GitCommitResponse + 89, // 200: mind.gateway.v1.Gateway.GitBranch:output_type -> mind.gateway.v1.GitBranchResponse + 91, // 201: mind.gateway.v1.Gateway.GitCheckout:output_type -> mind.gateway.v1.GitCheckoutResponse + 93, // 202: mind.gateway.v1.Gateway.GitPush:output_type -> mind.gateway.v1.GitPushResponse + 95, // 203: mind.gateway.v1.Gateway.GitTag:output_type -> mind.gateway.v1.GitTagResponse + 97, // 204: mind.gateway.v1.Gateway.GitMerge:output_type -> mind.gateway.v1.GitMergeResponse + 99, // 205: mind.gateway.v1.Gateway.GitRevert:output_type -> mind.gateway.v1.GitRevertResponse + 101, // 206: mind.gateway.v1.Gateway.MaterializeRepositorySnapshot:output_type -> mind.gateway.v1.MaterializeRepositorySnapshotResponse + 103, // 207: mind.gateway.v1.Gateway.PrepareRepositoryCheckout:output_type -> mind.gateway.v1.PrepareRepositoryCheckoutResponse + 105, // 208: mind.gateway.v1.Gateway.ReleaseRepositorySnapshot:output_type -> mind.gateway.v1.ReleaseRepositorySnapshotResponse + 109, // 209: mind.gateway.v1.Gateway.Release:output_type -> mind.gateway.v1.ReleaseResponse + 115, // 210: mind.gateway.v1.Gateway.ForgePullRequestStatus:output_type -> mind.gateway.v1.ForgePullRequestStatusResponse + 117, // 211: mind.gateway.v1.Gateway.ForgeMergePullRequest:output_type -> mind.gateway.v1.ForgeMergePullRequestResponse + 119, // 212: mind.gateway.v1.Gateway.ForgeRequestReview:output_type -> mind.gateway.v1.ForgeRequestReviewResponse + 122, // 213: mind.gateway.v1.Gateway.ForgeNormalizeWebhook:output_type -> mind.gateway.v1.ForgeNormalizeWebhookResponse + 125, // 214: mind.gateway.v1.Gateway.ListDependencies:output_type -> mind.gateway.v1.ListDependenciesResponse + 127, // 215: mind.gateway.v1.Gateway.AddDependency:output_type -> mind.gateway.v1.AddDependencyResponse + 129, // 216: mind.gateway.v1.Gateway.RemoveDependency:output_type -> mind.gateway.v1.RemoveDependencyResponse + 133, // 217: mind.gateway.v1.Gateway.GetProjectInfo:output_type -> mind.gateway.v1.GetProjectInfoResponse + 135, // 218: mind.gateway.v1.Gateway.GetSemanticIndex:output_type -> mind.gateway.v1.GetSemanticIndexResponse + 137, // 219: mind.gateway.v1.Gateway.GetInstructionIndex:output_type -> mind.gateway.v1.GetInstructionIndexResponse + 139, // 220: mind.gateway.v1.Gateway.GetSourceManifest:output_type -> mind.gateway.v1.GetSourceManifestResponse + 142, // 221: mind.gateway.v1.Gateway.DiscoverCodeUnits:output_type -> mind.gateway.v1.DiscoverCodeUnitsResponse + 147, // 222: mind.gateway.v1.Gateway.OpenTerminal:output_type -> mind.gateway.v1.OpenTerminalResponse + 149, // 223: mind.gateway.v1.Gateway.AttachTerminal:output_type -> mind.gateway.v1.TerminalOutput + 151, // 224: mind.gateway.v1.Gateway.ResizeTerminal:output_type -> mind.gateway.v1.ResizeTerminalResponse + 153, // 225: mind.gateway.v1.Gateway.CloseTerminal:output_type -> mind.gateway.v1.CloseTerminalResponse + 156, // 226: mind.gateway.v1.Gateway.ListTerminals:output_type -> mind.gateway.v1.ListTerminalsResponse + 167, // [167:227] is the sub-list for method output_type + 107, // [107:167] is the sub-list for method input_type 107, // [107:107] is the sub-list for extension type_name 107, // [107:107] is the sub-list for extension extendee 0, // [0:107] is the sub-list for field type_name diff --git a/generated/go/mind/gateway/v1/gateway_grpc.pb.go b/generated/go/mind/gateway/v1/gateway_grpc.pb.go index d9cef068..200c1df4 100644 --- a/generated/go/mind/gateway/v1/gateway_grpc.pb.go +++ b/generated/go/mind/gateway/v1/gateway_grpc.pb.go @@ -9,6 +9,7 @@ package gatewayv1 import ( context "context" + v0 "github.com/codefly-dev/core/generated/go/codefly/base/v0" grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" @@ -22,6 +23,9 @@ const _ = grpc.SupportPackageIsVersion9 const ( Gateway_ListServices_FullMethodName = "/mind.gateway.v1.Gateway/ListServices" Gateway_EvaluateStorageCapacity_FullMethodName = "/mind.gateway.v1.Gateway/EvaluateStorageCapacity" + Gateway_CreateWorkspaceCheckpoint_FullMethodName = "/mind.gateway.v1.Gateway/CreateWorkspaceCheckpoint" + Gateway_RestoreWorkspaceCheckpoint_FullMethodName = "/mind.gateway.v1.Gateway/RestoreWorkspaceCheckpoint" + Gateway_ReleaseWorkspaceCheckpoint_FullMethodName = "/mind.gateway.v1.Gateway/ReleaseWorkspaceCheckpoint" Gateway_ReadFile_FullMethodName = "/mind.gateway.v1.Gateway/ReadFile" Gateway_WriteFile_FullMethodName = "/mind.gateway.v1.Gateway/WriteFile" Gateway_ListFiles_FullMethodName = "/mind.gateway.v1.Gateway/ListFiles" @@ -94,6 +98,16 @@ type GatewayClient interface { // an observation, not a reservation; no host path or project content crosses // the boundary. EvaluateStorageCapacity(ctx context.Context, in *EvaluateStorageCapacityRequest, opts ...grpc.CallOption) (*EvaluateStorageCapacityResponse, error) + // CreateWorkspaceCheckpoint durably preserves the current dirty project + // state inside Codefly after a version compare-and-swap and storage + // admission. No project bytes or storage paths cross this boundary. + CreateWorkspaceCheckpoint(ctx context.Context, in *v0.CreateWorkspaceCheckpointRequest, opts ...grpc.CallOption) (*v0.CreateWorkspaceCheckpointResponse, error) + // RestoreWorkspaceCheckpoint atomically restores one lease-bound candidate + // when the live workspace still matches the caller's expected version. + RestoreWorkspaceCheckpoint(ctx context.Context, in *v0.RestoreWorkspaceCheckpointRequest, opts ...grpc.CallOption) (*v0.RestoreWorkspaceCheckpointResponse, error) + // ReleaseWorkspaceCheckpoint idempotently releases retained candidate + // storage without accepting a caller-supplied filesystem path. + ReleaseWorkspaceCheckpoint(ctx context.Context, in *v0.ReleaseWorkspaceCheckpointRequest, opts ...grpc.CallOption) (*v0.ReleaseWorkspaceCheckpointResponse, error) // ReadFile reads a file from a service's source tree. ReadFile(ctx context.Context, in *ReadFileRequest, opts ...grpc.CallOption) (*ReadFileResponse, error) // WriteFile writes (creates or overwrites) a file in a service's source tree. @@ -254,6 +268,36 @@ func (c *gatewayClient) EvaluateStorageCapacity(ctx context.Context, in *Evaluat return out, nil } +func (c *gatewayClient) CreateWorkspaceCheckpoint(ctx context.Context, in *v0.CreateWorkspaceCheckpointRequest, opts ...grpc.CallOption) (*v0.CreateWorkspaceCheckpointResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(v0.CreateWorkspaceCheckpointResponse) + err := c.cc.Invoke(ctx, Gateway_CreateWorkspaceCheckpoint_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gatewayClient) RestoreWorkspaceCheckpoint(ctx context.Context, in *v0.RestoreWorkspaceCheckpointRequest, opts ...grpc.CallOption) (*v0.RestoreWorkspaceCheckpointResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(v0.RestoreWorkspaceCheckpointResponse) + err := c.cc.Invoke(ctx, Gateway_RestoreWorkspaceCheckpoint_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *gatewayClient) ReleaseWorkspaceCheckpoint(ctx context.Context, in *v0.ReleaseWorkspaceCheckpointRequest, opts ...grpc.CallOption) (*v0.ReleaseWorkspaceCheckpointResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(v0.ReleaseWorkspaceCheckpointResponse) + err := c.cc.Invoke(ctx, Gateway_ReleaseWorkspaceCheckpoint_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *gatewayClient) ReadFile(ctx context.Context, in *ReadFileRequest, opts ...grpc.CallOption) (*ReadFileResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ReadFileResponse) @@ -831,6 +875,16 @@ type GatewayServer interface { // an observation, not a reservation; no host path or project content crosses // the boundary. EvaluateStorageCapacity(context.Context, *EvaluateStorageCapacityRequest) (*EvaluateStorageCapacityResponse, error) + // CreateWorkspaceCheckpoint durably preserves the current dirty project + // state inside Codefly after a version compare-and-swap and storage + // admission. No project bytes or storage paths cross this boundary. + CreateWorkspaceCheckpoint(context.Context, *v0.CreateWorkspaceCheckpointRequest) (*v0.CreateWorkspaceCheckpointResponse, error) + // RestoreWorkspaceCheckpoint atomically restores one lease-bound candidate + // when the live workspace still matches the caller's expected version. + RestoreWorkspaceCheckpoint(context.Context, *v0.RestoreWorkspaceCheckpointRequest) (*v0.RestoreWorkspaceCheckpointResponse, error) + // ReleaseWorkspaceCheckpoint idempotently releases retained candidate + // storage without accepting a caller-supplied filesystem path. + ReleaseWorkspaceCheckpoint(context.Context, *v0.ReleaseWorkspaceCheckpointRequest) (*v0.ReleaseWorkspaceCheckpointResponse, error) // ReadFile reads a file from a service's source tree. ReadFile(context.Context, *ReadFileRequest) (*ReadFileResponse, error) // WriteFile writes (creates or overwrites) a file in a service's source tree. @@ -977,6 +1031,15 @@ func (UnimplementedGatewayServer) ListServices(context.Context, *ListServicesReq func (UnimplementedGatewayServer) EvaluateStorageCapacity(context.Context, *EvaluateStorageCapacityRequest) (*EvaluateStorageCapacityResponse, error) { return nil, status.Error(codes.Unimplemented, "method EvaluateStorageCapacity not implemented") } +func (UnimplementedGatewayServer) CreateWorkspaceCheckpoint(context.Context, *v0.CreateWorkspaceCheckpointRequest) (*v0.CreateWorkspaceCheckpointResponse, error) { + return nil, status.Error(codes.Unimplemented, "method CreateWorkspaceCheckpoint not implemented") +} +func (UnimplementedGatewayServer) RestoreWorkspaceCheckpoint(context.Context, *v0.RestoreWorkspaceCheckpointRequest) (*v0.RestoreWorkspaceCheckpointResponse, error) { + return nil, status.Error(codes.Unimplemented, "method RestoreWorkspaceCheckpoint not implemented") +} +func (UnimplementedGatewayServer) ReleaseWorkspaceCheckpoint(context.Context, *v0.ReleaseWorkspaceCheckpointRequest) (*v0.ReleaseWorkspaceCheckpointResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ReleaseWorkspaceCheckpoint not implemented") +} func (UnimplementedGatewayServer) ReadFile(context.Context, *ReadFileRequest) (*ReadFileResponse, error) { return nil, status.Error(codes.Unimplemented, "method ReadFile not implemented") } @@ -1199,6 +1262,60 @@ func _Gateway_EvaluateStorageCapacity_Handler(srv interface{}, ctx context.Conte return interceptor(ctx, in, info, handler) } +func _Gateway_CreateWorkspaceCheckpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(v0.CreateWorkspaceCheckpointRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GatewayServer).CreateWorkspaceCheckpoint(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Gateway_CreateWorkspaceCheckpoint_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GatewayServer).CreateWorkspaceCheckpoint(ctx, req.(*v0.CreateWorkspaceCheckpointRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Gateway_RestoreWorkspaceCheckpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(v0.RestoreWorkspaceCheckpointRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GatewayServer).RestoreWorkspaceCheckpoint(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Gateway_RestoreWorkspaceCheckpoint_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GatewayServer).RestoreWorkspaceCheckpoint(ctx, req.(*v0.RestoreWorkspaceCheckpointRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Gateway_ReleaseWorkspaceCheckpoint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(v0.ReleaseWorkspaceCheckpointRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GatewayServer).ReleaseWorkspaceCheckpoint(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Gateway_ReleaseWorkspaceCheckpoint_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GatewayServer).ReleaseWorkspaceCheckpoint(ctx, req.(*v0.ReleaseWorkspaceCheckpointRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Gateway_ReadFile_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ReadFileRequest) if err := dec(in); err != nil { @@ -2186,6 +2303,18 @@ var Gateway_ServiceDesc = grpc.ServiceDesc{ MethodName: "EvaluateStorageCapacity", Handler: _Gateway_EvaluateStorageCapacity_Handler, }, + { + MethodName: "CreateWorkspaceCheckpoint", + Handler: _Gateway_CreateWorkspaceCheckpoint_Handler, + }, + { + MethodName: "RestoreWorkspaceCheckpoint", + Handler: _Gateway_RestoreWorkspaceCheckpoint_Handler, + }, + { + MethodName: "ReleaseWorkspaceCheckpoint", + Handler: _Gateway_ReleaseWorkspaceCheckpoint_Handler, + }, { MethodName: "ReadFile", Handler: _Gateway_ReadFile_Handler, diff --git a/generated/go/mind/gateway/v1/gatewayv1connect/gateway.connect.go b/generated/go/mind/gateway/v1/gatewayv1connect/gateway.connect.go index 22358e58..085ee61b 100644 --- a/generated/go/mind/gateway/v1/gatewayv1connect/gateway.connect.go +++ b/generated/go/mind/gateway/v1/gatewayv1connect/gateway.connect.go @@ -11,6 +11,7 @@ import ( strings "strings" connect "connectrpc.com/connect" + v0 "github.com/codefly-dev/core/generated/go/codefly/base/v0" v1 "github.com/codefly-dev/core/generated/go/mind/gateway/v1" ) @@ -39,6 +40,15 @@ const ( // GatewayEvaluateStorageCapacityProcedure is the fully-qualified name of the Gateway's // EvaluateStorageCapacity RPC. GatewayEvaluateStorageCapacityProcedure = "/mind.gateway.v1.Gateway/EvaluateStorageCapacity" + // GatewayCreateWorkspaceCheckpointProcedure is the fully-qualified name of the Gateway's + // CreateWorkspaceCheckpoint RPC. + GatewayCreateWorkspaceCheckpointProcedure = "/mind.gateway.v1.Gateway/CreateWorkspaceCheckpoint" + // GatewayRestoreWorkspaceCheckpointProcedure is the fully-qualified name of the Gateway's + // RestoreWorkspaceCheckpoint RPC. + GatewayRestoreWorkspaceCheckpointProcedure = "/mind.gateway.v1.Gateway/RestoreWorkspaceCheckpoint" + // GatewayReleaseWorkspaceCheckpointProcedure is the fully-qualified name of the Gateway's + // ReleaseWorkspaceCheckpoint RPC. + GatewayReleaseWorkspaceCheckpointProcedure = "/mind.gateway.v1.Gateway/ReleaseWorkspaceCheckpoint" // GatewayReadFileProcedure is the fully-qualified name of the Gateway's ReadFile RPC. GatewayReadFileProcedure = "/mind.gateway.v1.Gateway/ReadFile" // GatewayWriteFileProcedure is the fully-qualified name of the Gateway's WriteFile RPC. @@ -179,6 +189,16 @@ type GatewayClient interface { // an observation, not a reservation; no host path or project content crosses // the boundary. EvaluateStorageCapacity(context.Context, *connect.Request[v1.EvaluateStorageCapacityRequest]) (*connect.Response[v1.EvaluateStorageCapacityResponse], error) + // CreateWorkspaceCheckpoint durably preserves the current dirty project + // state inside Codefly after a version compare-and-swap and storage + // admission. No project bytes or storage paths cross this boundary. + CreateWorkspaceCheckpoint(context.Context, *connect.Request[v0.CreateWorkspaceCheckpointRequest]) (*connect.Response[v0.CreateWorkspaceCheckpointResponse], error) + // RestoreWorkspaceCheckpoint atomically restores one lease-bound candidate + // when the live workspace still matches the caller's expected version. + RestoreWorkspaceCheckpoint(context.Context, *connect.Request[v0.RestoreWorkspaceCheckpointRequest]) (*connect.Response[v0.RestoreWorkspaceCheckpointResponse], error) + // ReleaseWorkspaceCheckpoint idempotently releases retained candidate + // storage without accepting a caller-supplied filesystem path. + ReleaseWorkspaceCheckpoint(context.Context, *connect.Request[v0.ReleaseWorkspaceCheckpointRequest]) (*connect.Response[v0.ReleaseWorkspaceCheckpointResponse], error) // ReadFile reads a file from a service's source tree. ReadFile(context.Context, *connect.Request[v1.ReadFileRequest]) (*connect.Response[v1.ReadFileResponse], error) // WriteFile writes (creates or overwrites) a file in a service's source tree. @@ -334,6 +354,24 @@ func NewGatewayClient(httpClient connect.HTTPClient, baseURL string, opts ...con connect.WithSchema(gatewayMethods.ByName("EvaluateStorageCapacity")), connect.WithClientOptions(opts...), ), + createWorkspaceCheckpoint: connect.NewClient[v0.CreateWorkspaceCheckpointRequest, v0.CreateWorkspaceCheckpointResponse]( + httpClient, + baseURL+GatewayCreateWorkspaceCheckpointProcedure, + connect.WithSchema(gatewayMethods.ByName("CreateWorkspaceCheckpoint")), + connect.WithClientOptions(opts...), + ), + restoreWorkspaceCheckpoint: connect.NewClient[v0.RestoreWorkspaceCheckpointRequest, v0.RestoreWorkspaceCheckpointResponse]( + httpClient, + baseURL+GatewayRestoreWorkspaceCheckpointProcedure, + connect.WithSchema(gatewayMethods.ByName("RestoreWorkspaceCheckpoint")), + connect.WithClientOptions(opts...), + ), + releaseWorkspaceCheckpoint: connect.NewClient[v0.ReleaseWorkspaceCheckpointRequest, v0.ReleaseWorkspaceCheckpointResponse]( + httpClient, + baseURL+GatewayReleaseWorkspaceCheckpointProcedure, + connect.WithSchema(gatewayMethods.ByName("ReleaseWorkspaceCheckpoint")), + connect.WithClientOptions(opts...), + ), readFile: connect.NewClient[v1.ReadFileRequest, v1.ReadFileResponse]( httpClient, baseURL+GatewayReadFileProcedure, @@ -671,6 +709,9 @@ func NewGatewayClient(httpClient connect.HTTPClient, baseURL string, opts ...con type gatewayClient struct { listServices *connect.Client[v1.ListServicesRequest, v1.ListServicesResponse] evaluateStorageCapacity *connect.Client[v1.EvaluateStorageCapacityRequest, v1.EvaluateStorageCapacityResponse] + createWorkspaceCheckpoint *connect.Client[v0.CreateWorkspaceCheckpointRequest, v0.CreateWorkspaceCheckpointResponse] + restoreWorkspaceCheckpoint *connect.Client[v0.RestoreWorkspaceCheckpointRequest, v0.RestoreWorkspaceCheckpointResponse] + releaseWorkspaceCheckpoint *connect.Client[v0.ReleaseWorkspaceCheckpointRequest, v0.ReleaseWorkspaceCheckpointResponse] readFile *connect.Client[v1.ReadFileRequest, v1.ReadFileResponse] writeFile *connect.Client[v1.WriteFileRequest, v1.WriteFileResponse] listFiles *connect.Client[v1.ListFilesRequest, v1.ListFilesResponse] @@ -738,6 +779,21 @@ func (c *gatewayClient) EvaluateStorageCapacity(ctx context.Context, req *connec return c.evaluateStorageCapacity.CallUnary(ctx, req) } +// CreateWorkspaceCheckpoint calls mind.gateway.v1.Gateway.CreateWorkspaceCheckpoint. +func (c *gatewayClient) CreateWorkspaceCheckpoint(ctx context.Context, req *connect.Request[v0.CreateWorkspaceCheckpointRequest]) (*connect.Response[v0.CreateWorkspaceCheckpointResponse], error) { + return c.createWorkspaceCheckpoint.CallUnary(ctx, req) +} + +// RestoreWorkspaceCheckpoint calls mind.gateway.v1.Gateway.RestoreWorkspaceCheckpoint. +func (c *gatewayClient) RestoreWorkspaceCheckpoint(ctx context.Context, req *connect.Request[v0.RestoreWorkspaceCheckpointRequest]) (*connect.Response[v0.RestoreWorkspaceCheckpointResponse], error) { + return c.restoreWorkspaceCheckpoint.CallUnary(ctx, req) +} + +// ReleaseWorkspaceCheckpoint calls mind.gateway.v1.Gateway.ReleaseWorkspaceCheckpoint. +func (c *gatewayClient) ReleaseWorkspaceCheckpoint(ctx context.Context, req *connect.Request[v0.ReleaseWorkspaceCheckpointRequest]) (*connect.Response[v0.ReleaseWorkspaceCheckpointResponse], error) { + return c.releaseWorkspaceCheckpoint.CallUnary(ctx, req) +} + // ReadFile calls mind.gateway.v1.Gateway.ReadFile. func (c *gatewayClient) ReadFile(ctx context.Context, req *connect.Request[v1.ReadFileRequest]) (*connect.Response[v1.ReadFileResponse], error) { return c.readFile.CallUnary(ctx, req) @@ -1023,6 +1079,16 @@ type GatewayHandler interface { // an observation, not a reservation; no host path or project content crosses // the boundary. EvaluateStorageCapacity(context.Context, *connect.Request[v1.EvaluateStorageCapacityRequest]) (*connect.Response[v1.EvaluateStorageCapacityResponse], error) + // CreateWorkspaceCheckpoint durably preserves the current dirty project + // state inside Codefly after a version compare-and-swap and storage + // admission. No project bytes or storage paths cross this boundary. + CreateWorkspaceCheckpoint(context.Context, *connect.Request[v0.CreateWorkspaceCheckpointRequest]) (*connect.Response[v0.CreateWorkspaceCheckpointResponse], error) + // RestoreWorkspaceCheckpoint atomically restores one lease-bound candidate + // when the live workspace still matches the caller's expected version. + RestoreWorkspaceCheckpoint(context.Context, *connect.Request[v0.RestoreWorkspaceCheckpointRequest]) (*connect.Response[v0.RestoreWorkspaceCheckpointResponse], error) + // ReleaseWorkspaceCheckpoint idempotently releases retained candidate + // storage without accepting a caller-supplied filesystem path. + ReleaseWorkspaceCheckpoint(context.Context, *connect.Request[v0.ReleaseWorkspaceCheckpointRequest]) (*connect.Response[v0.ReleaseWorkspaceCheckpointResponse], error) // ReadFile reads a file from a service's source tree. ReadFile(context.Context, *connect.Request[v1.ReadFileRequest]) (*connect.Response[v1.ReadFileResponse], error) // WriteFile writes (creates or overwrites) a file in a service's source tree. @@ -1174,6 +1240,24 @@ func NewGatewayHandler(svc GatewayHandler, opts ...connect.HandlerOption) (strin connect.WithSchema(gatewayMethods.ByName("EvaluateStorageCapacity")), connect.WithHandlerOptions(opts...), ) + gatewayCreateWorkspaceCheckpointHandler := connect.NewUnaryHandler( + GatewayCreateWorkspaceCheckpointProcedure, + svc.CreateWorkspaceCheckpoint, + connect.WithSchema(gatewayMethods.ByName("CreateWorkspaceCheckpoint")), + connect.WithHandlerOptions(opts...), + ) + gatewayRestoreWorkspaceCheckpointHandler := connect.NewUnaryHandler( + GatewayRestoreWorkspaceCheckpointProcedure, + svc.RestoreWorkspaceCheckpoint, + connect.WithSchema(gatewayMethods.ByName("RestoreWorkspaceCheckpoint")), + connect.WithHandlerOptions(opts...), + ) + gatewayReleaseWorkspaceCheckpointHandler := connect.NewUnaryHandler( + GatewayReleaseWorkspaceCheckpointProcedure, + svc.ReleaseWorkspaceCheckpoint, + connect.WithSchema(gatewayMethods.ByName("ReleaseWorkspaceCheckpoint")), + connect.WithHandlerOptions(opts...), + ) gatewayReadFileHandler := connect.NewUnaryHandler( GatewayReadFileProcedure, svc.ReadFile, @@ -1510,6 +1594,12 @@ func NewGatewayHandler(svc GatewayHandler, opts ...connect.HandlerOption) (strin gatewayListServicesHandler.ServeHTTP(w, r) case GatewayEvaluateStorageCapacityProcedure: gatewayEvaluateStorageCapacityHandler.ServeHTTP(w, r) + case GatewayCreateWorkspaceCheckpointProcedure: + gatewayCreateWorkspaceCheckpointHandler.ServeHTTP(w, r) + case GatewayRestoreWorkspaceCheckpointProcedure: + gatewayRestoreWorkspaceCheckpointHandler.ServeHTTP(w, r) + case GatewayReleaseWorkspaceCheckpointProcedure: + gatewayReleaseWorkspaceCheckpointHandler.ServeHTTP(w, r) case GatewayReadFileProcedure: gatewayReadFileHandler.ServeHTTP(w, r) case GatewayWriteFileProcedure: @@ -1637,6 +1727,18 @@ func (UnimplementedGatewayHandler) EvaluateStorageCapacity(context.Context, *con return nil, connect.NewError(connect.CodeUnimplemented, errors.New("mind.gateway.v1.Gateway.EvaluateStorageCapacity is not implemented")) } +func (UnimplementedGatewayHandler) CreateWorkspaceCheckpoint(context.Context, *connect.Request[v0.CreateWorkspaceCheckpointRequest]) (*connect.Response[v0.CreateWorkspaceCheckpointResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("mind.gateway.v1.Gateway.CreateWorkspaceCheckpoint is not implemented")) +} + +func (UnimplementedGatewayHandler) RestoreWorkspaceCheckpoint(context.Context, *connect.Request[v0.RestoreWorkspaceCheckpointRequest]) (*connect.Response[v0.RestoreWorkspaceCheckpointResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("mind.gateway.v1.Gateway.RestoreWorkspaceCheckpoint is not implemented")) +} + +func (UnimplementedGatewayHandler) ReleaseWorkspaceCheckpoint(context.Context, *connect.Request[v0.ReleaseWorkspaceCheckpointRequest]) (*connect.Response[v0.ReleaseWorkspaceCheckpointResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("mind.gateway.v1.Gateway.ReleaseWorkspaceCheckpoint is not implemented")) +} + func (UnimplementedGatewayHandler) ReadFile(context.Context, *connect.Request[v1.ReadFileRequest]) (*connect.Response[v1.ReadFileResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("mind.gateway.v1.Gateway.ReadFile is not implemented")) } diff --git a/proto/codefly/base/v0/workspace_checkpoint.proto b/proto/codefly/base/v0/workspace_checkpoint.proto new file mode 100644 index 00000000..a272d279 --- /dev/null +++ b/proto/codefly/base/v0/workspace_checkpoint.proto @@ -0,0 +1,150 @@ +syntax = "proto3"; + +package codefly.base.v0; + +import "codefly/base/v0/failure.proto"; +import "codefly/base/v0/storage.proto"; +import "google/protobuf/timestamp.proto"; + +option go_package = "github.com/codefly-dev/core/generated/go/codefly/base/v0;basev0"; + +// WorkspaceLeaseIdentity binds mutable state to one active workspace lease. +message WorkspaceLeaseIdentity { + // workspace_id identifies the stable mutable workspace. + string workspace_id = 1; + // lease_id identifies the active authority lifetime for the workspace. + string lease_id = 2; +} + +// WorkspaceStateIdentity is Codefly's body-free identity for exact project +// bytes. version is the canonical compare-and-swap token; digest is retained +// separately so journals never need to interpret the version encoding. +message WorkspaceStateIdentity { + // version is the canonical token used for compare-and-swap operations. + string version = 1; + // digest is the SHA-256 identity of the exact project state. + string digest = 2; +} + +// WorkspaceCheckpointRetentionIntent selects the maximum retention lifetime. +enum WorkspaceCheckpointRetentionIntent { + // UNSPECIFIED is rejected by checkpoint creation. + WORKSPACE_CHECKPOINT_RETENTION_INTENT_UNSPECIFIED = 0; + // UNTIL_RELEASE_OR_LEASE_END retains the checkpoint across run-control + // segments until explicit release or termination of its owning lease. + WORKSPACE_CHECKPOINT_RETENTION_INTENT_UNTIL_RELEASE_OR_LEASE_END = 1; +} + +// WorkspaceCheckpoint is the source-free receipt for one durable dirty +// workspace candidate. checkpoint_id is opaque and never a filesystem path. +message WorkspaceCheckpoint { + // checkpoint_id is the opaque, lease- and content-bound lookup identity. + string checkpoint_id = 1; + // lease is the only workspace lease authorized to restore or release it. + WorkspaceLeaseIdentity lease = 2; + // source identifies the exact project state stored by the checkpoint. + WorkspaceStateIdentity source = 3; + // storage_requirement is the pre-admitted conservative storage demand. + StorageCapacityRequirement storage_requirement = 4; + // storage_admission records the capacity authority's create-time decision. + StorageCapacityAdmission storage_admission = 5; + // retention_intent records the caller's admitted retention lifetime. + WorkspaceCheckpointRetentionIntent retention_intent = 6; + // created_at is when the checkpoint became durable. + google.protobuf.Timestamp created_at = 7; +} + +// CreateWorkspaceCheckpointRequest preserves an exact live workspace state. +message CreateWorkspaceCheckpointRequest { + // lease binds the checkpoint to one active workspace authority lifetime. + WorkspaceLeaseIdentity lease = 1; + // expected_workspace_version is compared with authoritative project state. + string expected_workspace_version = 2; + // caller_id identifies the actor preserving the candidate. + string caller_id = 3; + // idempotency_key makes retries return the original checkpoint receipt. + string idempotency_key = 4; + // retention_intent requests the supported checkpoint lifetime. + WorkspaceCheckpointRetentionIntent retention_intent = 5; +} + +// CreateWorkspaceCheckpointResponse returns a durable receipt or typed failure. +message CreateWorkspaceCheckpointResponse { + // checkpoint is present when creation succeeded. + WorkspaceCheckpoint checkpoint = 1; + // failure is the structured reason creation did not complete. + Failure failure = 2; +} + +// WorkspaceCheckpointRestoreReceipt records the exact compare-and-swap +// transition without exposing files, patches, Git objects, or host paths. +message WorkspaceCheckpointRestoreReceipt { + // checkpoint_id is the opaque checkpoint that supplied restored state. + string checkpoint_id = 1; + // lease identifies the workspace authority that admitted the transition. + WorkspaceLeaseIdentity lease = 2; + // prior identifies project state immediately before restore. + WorkspaceStateIdentity prior = 3; + // restored identifies authoritative project state after restore. + WorkspaceStateIdentity restored = 4; + // authorization_id identifies the authority decision admitting restore. + string authorization_id = 5; + // idempotency_key binds retries to this exact transition receipt. + string idempotency_key = 6; + // restored_at is when the restored state became authoritative. + google.protobuf.Timestamp restored_at = 7; +} + +// RestoreWorkspaceCheckpointRequest applies one lease-bound candidate by CAS. +message RestoreWorkspaceCheckpointRequest { + // checkpoint_id is the opaque checkpoint selected by the caller. + string checkpoint_id = 1; + // lease must exactly match the checkpoint's owning lease. + WorkspaceLeaseIdentity lease = 2; + // expected_workspace_version is compared before any project mutation. + string expected_workspace_version = 3; + // authorization_id identifies the restore authorization decision. + string authorization_id = 4; + // idempotency_key makes retries return the original transition receipt. + string idempotency_key = 5; +} + +// RestoreWorkspaceCheckpointResponse reports authoritative restored state. +message RestoreWorkspaceCheckpointResponse { + // workspace identifies the new authoritative project state. + WorkspaceStateIdentity workspace = 1; + // receipt records the exact source-free restore transition. + WorkspaceCheckpointRestoreReceipt receipt = 2; + // failure is the structured reason restore did not complete. + Failure failure = 3; +} + +// WorkspaceCheckpointReleaseReceipt records durable checkpoint cleanup. +message WorkspaceCheckpointReleaseReceipt { + // checkpoint_id is the opaque checkpoint that was released. + string checkpoint_id = 1; + // lease identifies the workspace authority that owned the checkpoint. + WorkspaceLeaseIdentity lease = 2; + // released_at is stable across idempotent release retries. + google.protobuf.Timestamp released_at = 3; +} + +// ReleaseWorkspaceCheckpointRequest releases one lease-bound checkpoint. +message ReleaseWorkspaceCheckpointRequest { + // checkpoint_id is the opaque checkpoint selected for cleanup. + string checkpoint_id = 1; + // lease must exactly match the checkpoint's owning lease. + WorkspaceLeaseIdentity lease = 2; + // caller_id identifies the actor requesting cleanup. + string caller_id = 3; + // idempotency_key identifies this cleanup request. + string idempotency_key = 4; +} + +// ReleaseWorkspaceCheckpointResponse returns a stable cleanup receipt. +message ReleaseWorkspaceCheckpointResponse { + // receipt records when the checkpoint was released. + WorkspaceCheckpointReleaseReceipt receipt = 1; + // failure is the structured reason release did not complete. + Failure failure = 2; +} diff --git a/proto/codefly/services/code/v0/code.proto b/proto/codefly/services/code/v0/code.proto index d84bcdaf..5ec5f334 100644 --- a/proto/codefly/services/code/v0/code.proto +++ b/proto/codefly/services/code/v0/code.proto @@ -5,6 +5,7 @@ import "codefly/base/v0/failure.proto"; import "codefly/base/v0/instruction.proto"; import "codefly/base/v0/semantic.proto"; import "codefly/base/v0/source.proto"; +import "codefly/base/v0/workspace_checkpoint.proto"; /* Code service: provides code-level access to a service's codebase. @@ -726,6 +727,12 @@ message CodeRequest { // against a workspace. Mind never calls os/exec directly; it goes // through this RPC so the plugin boundary owns all process spawning. ShellExecRequest shell_exec = 37; + + // Workspace checkpoint operations keep dirty project bytes and storage + // paths inside the owning Codefly agent. + codefly.base.v0.CreateWorkspaceCheckpointRequest create_workspace_checkpoint = 38; + codefly.base.v0.RestoreWorkspaceCheckpointRequest restore_workspace_checkpoint = 39; + codefly.base.v0.ReleaseWorkspaceCheckpointRequest release_workspace_checkpoint = 40; } } @@ -784,6 +791,10 @@ message CodeResponse { // shell_exec returns process exit status and captured streams. ShellExecResponse shell_exec = 37; + + codefly.base.v0.CreateWorkspaceCheckpointResponse create_workspace_checkpoint = 38; + codefly.base.v0.RestoreWorkspaceCheckpointResponse restore_workspace_checkpoint = 39; + codefly.base.v0.ReleaseWorkspaceCheckpointResponse release_workspace_checkpoint = 40; } } diff --git a/proto/mind/gateway/v1/gateway.proto b/proto/mind/gateway/v1/gateway.proto index 583cca8d..f7f311b6 100644 --- a/proto/mind/gateway/v1/gateway.proto +++ b/proto/mind/gateway/v1/gateway.proto @@ -6,6 +6,7 @@ import "codefly/base/v0/failure.proto"; import "codefly/base/v0/instruction.proto"; import "codefly/base/v0/semantic.proto"; import "codefly/base/v0/storage.proto"; +import "codefly/base/v0/workspace_checkpoint.proto"; import "codefly/services/builder/v0/builder.proto"; import "codefly/services/runtime/v0/runtime.proto"; import "google/protobuf/timestamp.proto"; @@ -34,6 +35,19 @@ service Gateway { // the boundary. rpc EvaluateStorageCapacity(EvaluateStorageCapacityRequest) returns (EvaluateStorageCapacityResponse); + // CreateWorkspaceCheckpoint durably preserves the current dirty project + // state inside Codefly after a version compare-and-swap and storage + // admission. No project bytes or storage paths cross this boundary. + rpc CreateWorkspaceCheckpoint(codefly.base.v0.CreateWorkspaceCheckpointRequest) returns (codefly.base.v0.CreateWorkspaceCheckpointResponse); + + // RestoreWorkspaceCheckpoint atomically restores one lease-bound candidate + // when the live workspace still matches the caller's expected version. + rpc RestoreWorkspaceCheckpoint(codefly.base.v0.RestoreWorkspaceCheckpointRequest) returns (codefly.base.v0.RestoreWorkspaceCheckpointResponse); + + // ReleaseWorkspaceCheckpoint idempotently releases retained candidate + // storage without accepting a caller-supplied filesystem path. + rpc ReleaseWorkspaceCheckpoint(codefly.base.v0.ReleaseWorkspaceCheckpointRequest) returns (codefly.base.v0.ReleaseWorkspaceCheckpointResponse); + // ─── File Operations ──────────────────────────────────────── // ReadFile reads a file from a service's source tree.