chore: bump version to 1.0.0-beta.6 build 152 (#161)

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Peter Steinberger 2025-07-01 05:54:43 +01:00 committed by GitHub
parent 31a48e7a65
commit 4bdb21da41
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 103 additions and 87 deletions

View file

@ -33,20 +33,20 @@ struct VibeTunnelApp: App {
#endif
}
}
private var colorScheme: ColorScheme? {
switch colorSchemePreferenceRaw {
case "light": return .light
case "dark": return .dark
default: return nil // System default
default: return nil
}
}
#if targetEnvironment(macCatalyst)
private func getStoredWindowStyle() -> MacWindowStyle {
let styleRaw = UserDefaults.standard.string(forKey: "macWindowStyle") ?? "standard"
return styleRaw == "inline" ? .inline : .standard
}
private func getStoredWindowStyle() -> MacWindowStyle {
let styleRaw = UserDefaults.standard.string(forKey: "macWindowStyle") ?? "standard"
return styleRaw == "inline" ? .inline : .standard
}
#endif
private func handleURL(_ url: URL) {

View file

@ -131,7 +131,7 @@ struct TerminalInput: Codable {
case ctrlE = "\u{0005}"
// MARK: - Function Keys
/// F1 key.
case f1 = "\u{001B}OP"
/// F2 key.
@ -156,9 +156,9 @@ struct TerminalInput: Codable {
case f11 = "\u{001B}[23~"
/// F12 key.
case f12 = "\u{001B}[24~"
// MARK: - Additional Special Characters
/// Backslash character.
case backslash = "\\"
/// Pipe character.

View file

@ -161,7 +161,7 @@ struct TerminalToolbar: View {
}
}
}
Spacer()
}
@ -181,7 +181,7 @@ struct TerminalToolbar: View {
}
}
}
Spacer()
}
@ -212,7 +212,7 @@ struct TerminalToolbar: View {
// Send Ctrl+E for end
onSpecialKey(.ctrlE)
}
Spacer()
}

View file

@ -1,8 +1,8 @@
// VibeTunnel Version Configuration
// This file contains the version and build number for the app
MARKETING_VERSION = 1.0.0-beta.4
CURRENT_PROJECT_VERSION = 109
MARKETING_VERSION = 1.0.0-beta.6
CURRENT_PROJECT_VERSION = 152
// Domain and GitHub configuration
APP_DOMAIN = vibetunnel.sh

View file

@ -2,7 +2,7 @@
// This file contains the version and build number for the app
MARKETING_VERSION = 1.0.0-beta.6
CURRENT_PROJECT_VERSION = 151
CURRENT_PROJECT_VERSION = 152
// Domain and GitHub configuration
APP_DOMAIN = vibetunnel.sh

View file

@ -59,18 +59,18 @@ const SHELL_SPECIFIC_PATTERNS = {
withEscape: /[$>#%❯➜]\s*\x1b\[/,
};
export class PromptDetector {
export namespace PromptDetector {
// Cache for regex test results to avoid repeated tests
private static endPromptCache = new Map<string, boolean>();
private static onlyPromptCache = new Map<string, boolean>();
private static cacheSize = 0;
private static readonly MAX_CACHE_SIZE = 1000;
const endPromptCache = new Map<string, boolean>();
const onlyPromptCache = new Map<string, boolean>();
let cacheSize = 0;
const MAX_CACHE_SIZE = 1000;
/**
* Check if the entire output is just a prompt (no other content)
* Used by activity detector to determine if output is meaningful
*/
static isPromptOnly(data: string): boolean {
export function isPromptOnly(data: string): boolean {
// Input validation
if (data.length > 10000) {
logger.warn('Unusually long prompt input detected', { length: data.length });
@ -80,14 +80,15 @@ export class PromptDetector {
const trimmed = data.trim();
// Check cache first
if (PromptDetector.onlyPromptCache.has(trimmed)) {
return PromptDetector.onlyPromptCache.get(trimmed) ?? false;
if (onlyPromptCache.has(trimmed)) {
const cachedResult = onlyPromptCache.get(trimmed);
return cachedResult ?? false;
}
const result = PROMPT_ONLY_REGEX.test(trimmed);
// Cache result
PromptDetector.cacheResult(PromptDetector.onlyPromptCache, trimmed, result);
cacheResult(onlyPromptCache, trimmed, result);
return result;
}
@ -96,14 +97,15 @@ export class PromptDetector {
* Check if output ends with a prompt (for title injection)
* This is used to determine when to inject terminal title sequences
*/
static endsWithPrompt(data: string): boolean {
export function endsWithPrompt(data: string): boolean {
// For title injection, we need to check the last part of the output
// Use last 100 chars as cache key to balance cache efficiency and accuracy
const cacheKey = data.slice(-100);
// Check cache first
if (PromptDetector.endPromptCache.has(cacheKey)) {
return PromptDetector.endPromptCache.get(cacheKey) ?? false;
if (endPromptCache.has(cacheKey)) {
const cachedResult = endPromptCache.get(cacheKey);
return cachedResult ?? false;
}
// Strip ANSI codes for more reliable detection
@ -111,7 +113,7 @@ export class PromptDetector {
const result = UNIFIED_PROMPT_END_REGEX.test(cleanData);
// Cache result
PromptDetector.cacheResult(PromptDetector.endPromptCache, cacheKey, result);
cacheResult(endPromptCache, cacheKey, result);
if (result) {
logger.debug('Detected prompt at end of output');
@ -124,7 +126,7 @@ export class PromptDetector {
* Get specific shell type based on prompt pattern
* This can be used for shell-specific optimizations in the future
*/
static getShellType(data: string): keyof typeof SHELL_SPECIFIC_PATTERNS | null {
export function getShellType(data: string): keyof typeof SHELL_SPECIFIC_PATTERNS | null {
const trimmed = data.trim();
// Check each shell pattern
@ -140,53 +142,53 @@ export class PromptDetector {
/**
* Helper to cache results with size limit
*/
private static cacheResult(cache: Map<string, boolean>, key: string, value: boolean): void {
if (PromptDetector.cacheSize >= PromptDetector.MAX_CACHE_SIZE) {
function cacheResult(cache: Map<string, boolean>, key: string, value: boolean): void {
if (cacheSize >= MAX_CACHE_SIZE) {
// Clear oldest entries when cache is full
const entriesToDelete = Math.floor(PromptDetector.MAX_CACHE_SIZE * 0.2); // Clear 20%
const entriesToDelete = Math.floor(MAX_CACHE_SIZE * 0.2); // Clear 20%
const iterator = cache.keys();
for (let i = 0; i < entriesToDelete; i++) {
const keyToDelete = iterator.next().value;
if (keyToDelete) {
cache.delete(keyToDelete);
PromptDetector.cacheSize--;
cacheSize--;
}
}
}
cache.set(key, value);
PromptDetector.cacheSize++;
cacheSize++;
}
/**
* Clear all caches (useful for tests or memory management)
*/
static clearCache(): void {
PromptDetector.endPromptCache.clear();
PromptDetector.onlyPromptCache.clear();
PromptDetector.cacheSize = 0;
export function clearCache(): void {
endPromptCache.clear();
onlyPromptCache.clear();
cacheSize = 0;
logger.debug('Prompt pattern caches cleared');
}
/**
* Get cache statistics for monitoring
*/
static getCacheStats(): {
export function getCacheStats(): {
size: number;
maxSize: number;
hitRate: { end: number; only: number };
} {
return {
size: PromptDetector.cacheSize,
maxSize: PromptDetector.MAX_CACHE_SIZE,
size: cacheSize,
maxSize: MAX_CACHE_SIZE,
hitRate: {
end: PromptDetector.endPromptCache.size,
only: PromptDetector.onlyPromptCache.size,
end: endPromptCache.size,
only: onlyPromptCache.size,
},
};
}
}
// Export for backward compatibility
export const isPromptOnly = PromptDetector.isPromptOnly.bind(PromptDetector);
export const endsWithPrompt = PromptDetector.endsWithPrompt.bind(PromptDetector);
export const isPromptOnly = PromptDetector.isPromptOnly;
export const endsWithPrompt = PromptDetector.endsWithPrompt;

View file

@ -2,11 +2,12 @@
// This file is updated during the build process
import chalk from 'chalk';
import packageJson from '../../package.json';
import { createLogger } from './utils/logger.js';
const logger = createLogger('version');
export const VERSION = '1.0.0-beta.3';
export const VERSION = packageJson.version;
// BUILD_DATE will be replaced by build script, fallback to current time in dev
export const BUILD_DATE = process.env.BUILD_DATE || new Date().toISOString();
export const BUILD_TIMESTAMP = process.env.BUILD_TIMESTAMP || Date.now();

View file

@ -178,31 +178,31 @@ export class TestSessionManager {
/**
* Creates test data factory for consistent test data generation
*/
export class TestDataFactory {
private static counters: Map<string, number> = new Map();
export namespace TestDataFactory {
const counters: Map<string, number> = new Map();
/**
* Generates sequential IDs for a given prefix
*/
static sequentialId(prefix: string): string {
const current = TestDataFactory.counters.get(prefix) || 0;
TestDataFactory.counters.set(prefix, current + 1);
export function sequentialId(prefix: string): string {
const current = counters.get(prefix) || 0;
counters.set(prefix, current + 1);
return `${prefix}-${current + 1}`;
}
/**
* Generates session name with optional prefix
*/
static sessionName(prefix = 'session'): string {
export function sessionName(prefix = 'session'): string {
const timestamp = new Date().toISOString().slice(11, 19).replace(/:/g, '');
const counter = TestDataFactory.sequentialId(prefix);
const counter = sequentialId(prefix);
return `${counter}-${timestamp}`;
}
/**
* Generates command for testing
*/
static command(type: 'echo' | 'sleep' | 'env' | 'file' = 'echo'): string {
export function command(type: 'echo' | 'sleep' | 'env' | 'file' = 'echo'): string {
switch (type) {
case 'echo':
return `echo "Test output ${Date.now()}"`;
@ -218,8 +218,8 @@ export class TestDataFactory {
/**
* Reset all counters
*/
static reset(): void {
TestDataFactory.counters.clear();
export function reset(): void {
counters.clear();
}
}

View file

@ -98,7 +98,9 @@ test.describe('Debug Session Tests', () => {
// Check the app component's state
const appHideExited = await page.evaluate(() => {
const app = document.querySelector('vibetunnel-app') as any;
const app = document.querySelector('vibetunnel-app') as HTMLElement & {
hideExited?: boolean;
};
return app?.hideExited;
});
console.log('App component hideExited:', appHideExited);
@ -140,7 +142,7 @@ test.describe('Debug Session Tests', () => {
// Check if all sessions are exited
const exitedCount = sessionsResponse.sessions.filter(
(s: any) => s.status === 'exited'
(s: { status: string }) => s.status === 'exited'
).length;
console.log(`Exited sessions: ${exitedCount} out of ${sessionsResponse.count}`);
}

View file

@ -44,10 +44,7 @@ test.describe.skip('Terminal Interaction', () => {
test('should execute multiple commands in sequence', async ({ page }) => {
// Execute sequence with expected outputs
await executeCommandSequence(page, [
{ command: 'echo "Test 1"', expectedOutput: 'Test 1' },
{ command: 'echo "Test 2"', expectedOutput: 'Test 2', waitBetween: 500 },
]);
await executeCommandSequence(page, ['echo "Test 1"', 'echo "Test 2"']);
// Both outputs should be visible
await assertTerminalContains(page, 'Test 1');
@ -56,9 +53,7 @@ test.describe.skip('Terminal Interaction', () => {
test('should handle long-running commands', async ({ page }) => {
// Execute and wait for completion
await executeAndVerifyCommand(page, 'sleep 1 && echo "Done sleeping"', 'Done sleeping', {
timeout: 3000,
});
await executeAndVerifyCommand(page, 'sleep 1 && echo "Done sleeping"', 'Done sleeping');
});
test('should handle command interruption', async ({ page }) => {
@ -134,7 +129,12 @@ test.describe.skip('Terminal Interaction', () => {
test('should handle terminal resize', async ({ page }) => {
// Get initial terminal dimensions
const initialDimensions = await page.evaluate(() => {
const terminal = document.querySelector('vibe-terminal') as any;
const terminal = document.querySelector('vibe-terminal') as HTMLElement & {
cols?: number;
rows?: number;
actualCols?: number;
actualRows?: number;
};
return {
cols: terminal?.cols || 80,
rows: terminal?.rows || 24,
@ -159,7 +159,12 @@ test.describe.skip('Terminal Interaction', () => {
// Wait for terminal-resize event or dimension change
await page.waitForFunction(
({ initial }) => {
const terminal = document.querySelector('vibe-terminal') as any;
const terminal = document.querySelector('vibe-terminal') as HTMLElement & {
cols?: number;
rows?: number;
actualCols?: number;
actualRows?: number;
};
const currentCols = terminal?.cols || 80;
const currentRows = terminal?.rows || 24;
const currentActualCols = terminal?.actualCols || currentCols;
@ -179,7 +184,12 @@ test.describe.skip('Terminal Interaction', () => {
// Verify terminal dimensions changed
const newDimensions = await page.evaluate(() => {
const terminal = document.querySelector('vibe-terminal') as any;
const terminal = document.querySelector('vibe-terminal') as HTMLElement & {
cols?: number;
rows?: number;
actualCols?: number;
actualRows?: number;
};
return {
cols: terminal?.cols || 80,
rows: terminal?.rows || 24,

View file

@ -5,36 +5,34 @@ import { logger } from './logger';
/**
* Optimized wait utilities with reduced timeouts and smarter strategies
*/
export class OptimizedWaitUtils {
export namespace OptimizedWaitUtils {
// Reduced default timeouts for faster test execution
private static readonly QUICK_TIMEOUT = TEST_TIMEOUTS.QUICK;
private static readonly DEFAULT_TIMEOUT = TEST_TIMEOUTS.DEFAULT;
private static readonly LONG_TIMEOUT = TEST_TIMEOUTS.LONG;
private static readonly logger = logger;
const QUICK_TIMEOUT = TEST_TIMEOUTS.QUICK;
const DEFAULT_TIMEOUT = TEST_TIMEOUTS.DEFAULT;
/**
* Wait for app initialization with optimized checks
*/
static async waitForAppReady(page: Page): Promise<void> {
export async function waitForAppReady(page: Page): Promise<void> {
// Wait for app element to attach (reduced timeout)
await page.waitForSelector('vibetunnel-app', {
state: 'attached',
timeout: OptimizedWaitUtils.DEFAULT_TIMEOUT,
timeout: DEFAULT_TIMEOUT,
});
// Use Promise.allSettled for better reliability
const results = await Promise.allSettled([
page.waitForSelector('button[title="Create New Session"]', {
state: 'visible',
timeout: OptimizedWaitUtils.QUICK_TIMEOUT,
timeout: QUICK_TIMEOUT,
}),
page.waitForSelector('session-card', {
state: 'visible',
timeout: OptimizedWaitUtils.QUICK_TIMEOUT,
timeout: QUICK_TIMEOUT,
}),
page.waitForSelector('auth-login', {
state: 'visible',
timeout: OptimizedWaitUtils.QUICK_TIMEOUT,
timeout: QUICK_TIMEOUT,
}),
]);
@ -52,7 +50,7 @@ export class OptimizedWaitUtils {
/**
* Wait for session card with specific name
*/
static async waitForSessionCard(
export async function waitForSessionCard(
page: Page,
sessionName: string,
timeout = 3000
@ -65,7 +63,7 @@ export class OptimizedWaitUtils {
/**
* Wait for terminal to be ready (optimized)
*/
static async waitForTerminalReady(page: Page, timeout = 3000): Promise<void> {
export async function waitForTerminalReady(page: Page, timeout = 3000): Promise<void> {
// Wait for xterm element
await page.waitForSelector('.xterm', { state: 'visible', timeout });
@ -84,7 +82,7 @@ export class OptimizedWaitUtils {
/**
* Wait for session state change
*/
static async waitForSessionState(
export async function waitForSessionState(
page: Page,
sessionName: string,
expectedState: 'RUNNING' | 'EXITED',
@ -117,7 +115,7 @@ export class OptimizedWaitUtils {
/**
* Smart wait for navigation with fallback
*/
static async waitForNavigation(page: Page, url: string, timeout = 3000): Promise<void> {
export async function waitForNavigation(page: Page, url: string, timeout = 3000): Promise<void> {
// Try to wait for URL change
try {
await page.waitForURL(url, { timeout });
@ -133,13 +131,13 @@ export class OptimizedWaitUtils {
/**
* Wait for element count with early exit
*/
static async waitForElementCount(
export async function waitForElementCount(
page: Page,
selector: string,
expectedCount: number,
options?: { operator?: 'exact' | 'minimum' | 'maximum'; timeout?: number }
): Promise<void> {
const { operator = 'exact', timeout = OptimizedWaitUtils.DEFAULT_TIMEOUT } = options || {};
const { operator = 'exact', timeout = DEFAULT_TIMEOUT } = options || {};
const pollInterval = 100;
const maxAttempts = timeout / pollInterval;
@ -164,7 +162,7 @@ export class OptimizedWaitUtils {
/**
* Wait for any text content (useful for terminal output)
*/
static async waitForAnyText(locator: Locator, timeout = 2000): Promise<string> {
export async function waitForAnyText(locator: Locator, timeout = 2000): Promise<string> {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
@ -181,7 +179,7 @@ export class OptimizedWaitUtils {
/**
* Fast visibility check with retry
*/
static async isEventuallyVisible(locator: Locator, timeout = 1000): Promise<boolean> {
export async function isEventuallyVisible(locator: Locator, timeout = 1000): Promise<boolean> {
try {
await locator.waitFor({ state: 'visible', timeout });
return true;
@ -194,7 +192,10 @@ export class OptimizedWaitUtils {
/**
* Wait for network idle with early exit
*/
static async waitForNetworkQuiet(page: Page, options?: { timeout?: number }): Promise<void> {
export async function waitForNetworkQuiet(
page: Page,
options?: { timeout?: number }
): Promise<void> {
const { timeout = TEST_TIMEOUTS.NETWORK_QUIET } = options || {};
// Track pending requests