Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
36fb5c9
docs(specs): analyze codebase and define cluster-agent feature spec
arielshad Apr 15, 2026
d8fba81
docs(specs): define requirements and product questions for cluster-agent
arielshad Apr 15, 2026
14ec911
docs(specs): research technical decisions and library choices for clu…
arielshad Apr 15, 2026
6f02731
docs(specs): create implementation plan and task breakdown for cluste…
arielshad Apr 15, 2026
837fa25
feat(tsp): define cluster status enum with six lifecycle states
arielshad Apr 15, 2026
682960c
feat(tsp): define cluster entity extending soft-deletable entity
arielshad Apr 15, 2026
f49b98c
feat(tsp): define cluster junction entities for many-to-many relation…
arielshad Apr 15, 2026
1f667e2
feat(domain): add cluster defaults factory with slug generation
arielshad Apr 15, 2026
775ee21
feat(domain): add sqlite migrations for clusters tables and feature flag
arielshad Apr 15, 2026
5a9f973
feat(domain): add cluster mapper with database row conversion
arielshad Apr 15, 2026
42501ff
feat(domain): add cluster repository port interface and sqlite implem…
arielshad Apr 15, 2026
a3ad2b1
feat(domain): add cluster port interfaces and 12 use cases with tests
arielshad Apr 16, 2026
66d7f92
feat(domain): add infrastructure services for cluster management
arielshad Apr 16, 2026
aac25d5
feat(agents): add cluster agent langgraph state, nodes, and graph
arielshad Apr 16, 2026
d99d2db
feat(agents): add cluster agent worker process with cli arg parsing
arielshad Apr 16, 2026
2d3bc9e
feat(web): wire clusters feature flag through all layers
arielshad Apr 16, 2026
4bef9a7
feat(domain): add cluster di registration module and wire container
arielshad Apr 16, 2026
72a983e
feat(cli): add cluster command group with 7 subcommands
arielshad Apr 16, 2026
188b7cd
feat(web): add cluster web ui components and canvas integration
arielshad Apr 16, 2026
3497400
chore(agents): capture evidence for cluster-agent feature implementation
arielshad Apr 16, 2026
83b8ae9
chore(agents): recapture evidence for cluster-agent feature validation
arielshad Apr 16, 2026
ef8da35
chore(agents): refresh evidence for cluster-agent feature validation
arielshad Apr 16, 2026
b41f857
chore(specs): mark evidence phase complete for cluster-agent feature
arielshad Apr 16, 2026
cb34a5b
chore(specs): update cluster-agent feature timestamp
arielshad Apr 16, 2026
7e47ccc
fix(ci): attempt 1/10 — increase beforeeach hook timeout on windows
arielshad Apr 16, 2026
f6cce9e
feat(web): add cluster management page and cluster prerequisite tools
arielshad Apr 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions apis/json-schema/Cluster.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
$schema: https://json-schema.org/draft/2020-12/schema
$id: Cluster.yaml
type: object
properties:
name:
type: string
description: Human-readable cluster name
slug:
type: string
description: URL-friendly identifier (unique among non-deleted clusters)
description:
type: string
description: Optional description of the cluster's purpose
status:
$ref: ClusterStatus.yaml
default: Stopped
description: Current operational status of the cluster
k3dClusterName:
type: string
description: The k3d-internal cluster name (set during provisioning)
kubeconfigPath:
type: string
description: Absolute path to the kubeconfig file
argoCdEnabled:
type: boolean
default: false
description: Whether ArgoCD is enabled for this cluster
argoCdNamespace:
type: string
default: argocd
description: Kubernetes namespace where ArgoCD is installed
nodeCount:
type: integer
minimum: -2147483648
maximum: 2147483647
default: 1
description: Number of k3s nodes (default 1, reserved for future multi-node support)
lastProvisionedAt:
type: string
format: date-time
description: Timestamp when the cluster was last successfully provisioned
lastHealthCheckAt:
type: string
format: date-time
description: Timestamp of the most recent health check
errorMessage:
type: string
description: Error message from the last failed operation (set when status = Error)
required:
- name
- slug
- status
- argoCdEnabled
- argoCdNamespace
- nodeCount
allOf:
- $ref: SoftDeletableEntity.yaml
description: A managed Kubernetes cluster provisioned via k3s-in-Docker
16 changes: 16 additions & 0 deletions apis/json-schema/ClusterApplication.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
$schema: https://json-schema.org/draft/2020-12/schema
$id: ClusterApplication.yaml
type: object
properties:
clusterId:
$ref: UUID.yaml
description: The cluster ID
applicationId:
$ref: UUID.yaml
description: The application ID
required:
- clusterId
- applicationId
allOf:
- $ref: BaseEntity.yaml
description: Junction entity linking Clusters to Applications (many-to-many)
16 changes: 16 additions & 0 deletions apis/json-schema/ClusterRepository.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
$schema: https://json-schema.org/draft/2020-12/schema
$id: ClusterRepository.yaml
type: object
properties:
clusterId:
$ref: UUID.yaml
description: The cluster ID
repositoryId:
$ref: UUID.yaml
description: The repository ID
required:
- clusterId
- repositoryId
allOf:
- $ref: BaseEntity.yaml
description: Junction entity linking Clusters to Repositories (many-to-many)
11 changes: 11 additions & 0 deletions apis/json-schema/ClusterStatus.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
$schema: https://json-schema.org/draft/2020-12/schema
$id: ClusterStatus.yaml
type: string
enum:
- Provisioning
- Ready
- Stopping
- Stopped
- Error
- Destroying
description: Operational states for managed Kubernetes clusters
5 changes: 5 additions & 0 deletions apis/json-schema/FeatureFlags.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ properties:
type: boolean
default: false
description: Enable Projects pages and navigation (project management)
clusters:
type: boolean
default: false
description: Enable Clusters navigation and Kubernetes cluster management in the web UI
required:
- skills
- envDeploy
Expand All @@ -48,4 +52,5 @@ required:
- reactFileManager
- inventory
- projects
- clusters
description: Feature flag toggles for runtime feature control
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Cluster Repository Interface (Output Port)
*
* Defines the contract for Cluster entity persistence operations
* including CRUD and junction table management for many-to-many
* relationships with Repositories and Applications.
*/

import type {
Cluster,
ClusterStatus,
ClusterRepository,
ClusterApplication,
Application,
} from '../../../../domain/generated/output.js';
import type { Repository } from '../../../../domain/generated/output.js';

export interface IClusterRepository {
create(cluster: Cluster): Promise<void>;
findById(id: string): Promise<Cluster | null>;
findBySlug(slug: string): Promise<Cluster | null>;
list(status?: ClusterStatus): Promise<Cluster[]>;
update(
id: string,
fields: Partial<
Pick<
Cluster,
| 'name'
| 'slug'
| 'description'
| 'status'
| 'k3dClusterName'
| 'kubeconfigPath'
| 'argoCdEnabled'
| 'argoCdNamespace'
| 'nodeCount'
| 'lastProvisionedAt'
| 'lastHealthCheckAt'
| 'errorMessage'
>
>
): Promise<void>;
softDelete(id: string): Promise<void>;

linkRepository(clusterId: string, repositoryId: string): Promise<ClusterRepository>;
unlinkRepository(clusterId: string, repositoryId: string): Promise<void>;
getLinkedRepositories(clusterId: string): Promise<Repository[]>;

linkApplication(clusterId: string, applicationId: string): Promise<ClusterApplication>;
unlinkApplication(clusterId: string, applicationId: string): Promise<void>;
getLinkedApplications(clusterId: string): Promise<Application[]>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* ArgoCD Service Interface (Output Port)
*
* Defines the contract for managing ArgoCD installations and applications
* within Kubernetes clusters. ArgoCD is installed via kubectl apply of the
* official manifests (no separate argocd CLI binary required).
*/

/** ArgoCD installation/sync status. */
export interface ArgoCdStatus {
/** Whether ArgoCD pods are running and ready. */
installed: boolean;
/** Number of running ArgoCD pods. */
podCount: number;
/** Whether the ArgoCD server is reachable. */
serverReady: boolean;
}

/** ArgoCD application sync state. */
export interface ArgoCdAppStatus {
/** The application name. */
name: string;
/** Current sync status (e.g., 'Synced', 'OutOfSync', 'Unknown'). */
syncStatus: string;
/** Current health status (e.g., 'Healthy', 'Progressing', 'Degraded'). */
healthStatus: string;
}

export interface IArgoCDService {
/**
* Install ArgoCD into a cluster using kubectl apply of official manifests.
*
* @param kubeconfigPath - Path to the kubeconfig file
* @param namespace - Kubernetes namespace for ArgoCD (defaults to 'argocd')
*/
install(kubeconfigPath: string, namespace?: string): Promise<void>;

/**
* Get the status of an ArgoCD installation.
*
* @param kubeconfigPath - Path to the kubeconfig file
* @param namespace - Kubernetes namespace for ArgoCD (defaults to 'argocd')
* @returns ArgoCD status information
*/
getStatus(kubeconfigPath: string, namespace?: string): Promise<ArgoCdStatus>;

/**
* Create an ArgoCD application.
*
* @param kubeconfigPath - Path to the kubeconfig file
* @param appName - Name for the ArgoCD application
* @param repoUrl - Git repository URL for the application source
* @param path - Path within the repo containing manifests
* @param namespace - ArgoCD namespace (defaults to 'argocd')
*/
createApp(
kubeconfigPath: string,
appName: string,
repoUrl: string,
path: string,
namespace?: string
): Promise<void>;

/**
* Sync an ArgoCD application.
*
* @param kubeconfigPath - Path to the kubeconfig file
* @param appName - The ArgoCD application name to sync
*/
syncApp(kubeconfigPath: string, appName: string): Promise<void>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Cluster Agent Process Service Interface (Output Port)
*
* Manages background worker processes for cluster agent execution.
* Follows the IFeatureAgentProcessService pattern: spawn detached
* Node.js workers, check liveness via PID, and support cleanup.
*/

/** Options for spawning a cluster agent worker. */
export interface ClusterAgentSpawnOptions {
/** Whether ArgoCD should be installed during provisioning. */
argoCdEnabled?: boolean;
/** ArgoCD namespace (defaults to 'argocd'). */
argoCdNamespace?: string;
/** Whether this is a resume of a previously failed provisioning. */
resume?: boolean;
/** Thread ID for checkpoint resume. */
threadId?: string;
}

export interface IClusterAgentProcessService {
/**
* Spawn a background worker process for cluster provisioning.
*
* @param clusterId - The cluster ID to provision
* @param runId - The agent run ID for tracking
* @param options - Optional spawn options
* @returns The PID of the spawned process
*/
spawn(clusterId: string, runId: string, options?: ClusterAgentSpawnOptions): number;

/**
* Check if a process is still alive.
*
* @param pid - The process ID to check
* @returns true if the process is running
*/
isAlive(pid: number): boolean;

/**
* Kill a running cluster agent worker process.
*
* @param pid - The process ID to kill
*/
kill(pid: number): void;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* Docker Health Service Interface (Output Port)
*
* Defines the contract for checking Docker daemon availability.
* Used as a prerequisite check before any cluster operation (NFR-3).
*/

export interface IDockerHealthService {
/**
* Check if the Docker daemon is running and accessible.
*
* @returns true if Docker is available, false otherwise
*/
isAvailable(): Promise<boolean>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* K3d Service Interface (Output Port)
*
* Defines the contract for managing k3s-in-Docker clusters via the k3d CLI.
* Infrastructure layer provides the concrete implementation wrapping k3d
* binary calls via execFile.
*/

/** Options for creating a k3d cluster. */
export interface K3dCreateClusterOptions {
/** Disable the default load balancer (--no-lb). */
noLb?: boolean;
/** Wait for cluster to be ready (--wait). */
wait?: boolean;
/** Timeout for waiting in seconds (--timeout). */
timeoutSeconds?: number;
}

/** Status information returned by k3d for a cluster. */
export interface K3dClusterStatus {
/** Whether the cluster exists in k3d. */
exists: boolean;
/** Whether the cluster's containers are running. */
running: boolean;
/** Number of server (control plane) nodes. */
serverCount: number;
}

export interface IK3dService {
/**
* Create a new k3d cluster.
*
* @param name - The k3d cluster name
* @param options - Optional creation options
*/
createCluster(name: string, options?: K3dCreateClusterOptions): Promise<void>;

/**
* Delete a k3d cluster.
*
* @param name - The k3d cluster name
*/
deleteCluster(name: string): Promise<void>;

/**
* Start a stopped k3d cluster.
*
* @param name - The k3d cluster name
*/
startCluster(name: string): Promise<void>;

/**
* Stop a running k3d cluster.
*
* @param name - The k3d cluster name
*/
stopCluster(name: string): Promise<void>;

/**
* Get the status of a k3d cluster.
*
* @param name - The k3d cluster name
* @returns Cluster status or null if not found
*/
getClusterStatus(name: string): Promise<K3dClusterStatus | null>;

/**
* Extract the kubeconfig for a k3d cluster.
*
* @param name - The k3d cluster name
* @returns The kubeconfig YAML content
*/
getKubeconfig(name: string): Promise<string>;
}
Loading
Loading