chore: move source to repo root and standardize config

Phase 1 — Structural overhaul:
- Move all source from headlamp-sealed-secrets/ subdirectory to repo root
- Delete 23 AI-generated docs, 8 pre-built tarballs, release snapshots dir
- Remove all working-directory refs from CI/release workflows
- Update install-plugin.sh and typedoc.json paths

Phase 2 — Config standardization:
- Create .eslintrc.js and .prettierrc.js (standard Headlamp configs)
- Remove inline eslintConfig/prettier from package.json (drop jsx-a11y, prettier extends)
- Rewrite tsconfig.json (package name extend, add compilerOptions.types)
- Create vitest.config.mts and vitest.setup.ts (standard from polaris)
- Replace headlamp-plugin CLI scripts with direct tool invocation
- Rewrite .gitignore with standard baseline

Phase 3 — MCP & Claude settings:
- Create .mcp.json with github/kubernetes/flux/playwright servers
- Create .claude/settings.local.json
- Remove 7 specialized agents, keep 3 meta-orchestration agents

Phase 4 — Documentation:
- Rewrite CLAUDE.md (remove subdirectory refs, standard format)
- Add ArtifactHub badge, Architecture section, standardized install methods to README.md
- Create CONTRIBUTING.md and SECURITY.md
- Fix pre-existing test bugs in validators.test.ts (isValidNamespace returns boolean,
  not ValidationResult; error message string mismatches)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
DevContainer User
2026-03-03 21:31:12 +00:00
parent 604fe06f9c
commit af95c3795c
108 changed files with 704 additions and 14041 deletions
+188
View File
@@ -0,0 +1,188 @@
/**
* Retry logic with exponential backoff
*
* Provides utilities for retrying failed operations with configurable
* backoff strategies and error handling.
*/
import { AsyncResult, Err } from '../types';
/**
* Retry configuration options
*/
export interface RetryOptions {
/** Maximum number of retry attempts (default: 3) */
maxAttempts?: number;
/** Initial delay in milliseconds (default: 1000) */
initialDelayMs?: number;
/** Maximum delay in milliseconds (default: 10000) */
maxDelayMs?: number;
/** Backoff multiplier (default: 2 for exponential) */
backoffMultiplier?: number;
/** Whether to add jitter to delays (default: true) */
useJitter?: boolean;
/** Predicate to determine if error is retryable (default: all errors retryable) */
isRetryable?: (error: Error) => boolean;
}
/**
* Default retry options
*/
const DEFAULT_RETRY_OPTIONS: Required<RetryOptions> = {
maxAttempts: 3,
initialDelayMs: 1000,
maxDelayMs: 10000,
backoffMultiplier: 2,
useJitter: true,
isRetryable: () => true, // All errors retryable by default
};
/**
* Sleep for specified milliseconds
*/
function sleep(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* Calculate delay with exponential backoff and optional jitter
*
* @param attempt Current attempt number (0-indexed)
* @param options Retry options
* @returns Delay in milliseconds
*/
function calculateDelay(attempt: number, options: Required<RetryOptions>): number {
const { initialDelayMs, maxDelayMs, backoffMultiplier, useJitter } = options;
// Exponential backoff: initialDelay * (multiplier ^ attempt)
let delay = initialDelayMs * Math.pow(backoffMultiplier, attempt);
// Cap at max delay
delay = Math.min(delay, maxDelayMs);
// Add jitter (±25% random variation)
if (useJitter) {
const jitterRange = delay * 0.25;
const jitter = Math.random() * jitterRange * 2 - jitterRange;
delay = Math.max(0, delay + jitter);
}
return Math.floor(delay);
}
/**
* Retry an async operation with exponential backoff
*
* @param operation Async operation to retry (should return AsyncResult)
* @param options Retry configuration
* @returns Result of the operation or final error after all retries
*
* @example
* const result = await retryWithBackoff(
* async () => fetchPublicCertificate(config),
* { maxAttempts: 3, initialDelayMs: 1000 }
* );
*/
export async function retryWithBackoff<T, E>(
operation: () => AsyncResult<T, E>,
options: RetryOptions = {}
): AsyncResult<T, string> {
const opts = { ...DEFAULT_RETRY_OPTIONS, ...options };
const errors: string[] = [];
for (let attempt = 0; attempt < opts.maxAttempts; attempt++) {
try {
const result = await operation();
// If operation succeeded, return immediately
if (result.ok) {
return result;
}
// Operation returned an error - use explicit check for type narrowing
if (result.ok === false) {
const errorMessage = typeof result.error === 'string' ? result.error : String(result.error);
errors.push(`Attempt ${attempt + 1}: ${errorMessage}`);
}
// Check if we should retry
const isLastAttempt = attempt === opts.maxAttempts - 1;
if (isLastAttempt) {
// No more retries, return final error
return Err(
`Operation failed after ${opts.maxAttempts} attempts:\n${errors.join('\n')}`
);
}
// Wait before retrying
const delay = calculateDelay(attempt, opts);
await sleep(delay);
} catch (error) {
// Unexpected exception (shouldn't happen with AsyncResult, but handle it)
const errorMessage = error instanceof Error ? error.message : String(error);
errors.push(`Attempt ${attempt + 1}: ${errorMessage}`);
const isLastAttempt = attempt === opts.maxAttempts - 1;
if (isLastAttempt) {
return Err(
`Operation failed after ${opts.maxAttempts} attempts:\n${errors.join('\n')}`
);
}
const delay = calculateDelay(attempt, opts);
await sleep(delay);
}
}
// Should never reach here, but TypeScript needs it
return Err(`Operation failed after ${opts.maxAttempts} attempts:\n${errors.join('\n')}`);
}
/**
* Predicate to check if error is a network error (retryable)
*
* @param error Error to check
* @returns true if error is network-related
*/
export function isNetworkError(error: Error): boolean {
const message = error.message.toLowerCase();
return (
message.includes('network') ||
message.includes('timeout') ||
message.includes('fetch') ||
message.includes('connection') ||
message.includes('econnrefused') ||
message.includes('enotfound')
);
}
/**
* Predicate to check if HTTP error is retryable (5xx, 429, 408)
*
* @param error Error to check
* @returns true if HTTP status is retryable
*/
export function isRetryableHttpError(error: Error): boolean {
const message = error.message;
// Check for 5xx server errors
if (/5\d{2}/.test(message)) {
return true;
}
// Check for specific retryable status codes
return message.includes('429') || // Too Many Requests
message.includes('408') || // Request Timeout
message.includes('503') || // Service Unavailable
message.includes('504'); // Gateway Timeout
}
/**
* Combined predicate for network and HTTP errors
*
* @param error Error to check
* @returns true if error is retryable
*/
export function isRetryableError(error: Error): boolean {
return isNetworkError(error) || isRetryableHttpError(error);
}