mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 12:15:57 +00:00
fix(config): migrate all hardcoded paths to getCcsDir() and improve validation
- Replace os.homedir() + '.ccs' with getCcsDir() across 14 instances in 13 files: version-command, model-config, openrouter-catalog, config-checks, disk-cache, aggregator, auth-middleware, shell-completion, shared-manager, recovery-manager, auto-repair, delegation-validator, claude-dir-installer, cliproxy executor - Add isDirectory() validation for --config-dir argument in ccs.ts - Make detectCloudSyncPath() case-insensitive for cloud provider matching - Fix help-command.ts to use dynamic dirDisplay for all path references - Add 5 new tests: setGlobalConfigDir precedence, relative path resolution, reset behavior, getCcsDirSource with --config-dir, case-insensitive detection - Clean up unused os imports after migration
This commit is contained in:
@@ -5,10 +5,10 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { getCcsDir } from '../../utils/config-manager';
|
||||
|
||||
const OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/models';
|
||||
const CACHE_FILE = path.join(os.homedir(), '.ccs', 'openrouter-models-cache.json');
|
||||
const CACHE_FILE = path.join(getCcsDir(), 'openrouter-models-cache.json');
|
||||
const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
||||
|
||||
export interface OpenRouterModel {
|
||||
|
||||
+2
-2
@@ -286,8 +286,8 @@ async function main(): Promise<void> {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(configDirValue)) {
|
||||
console.error(fail(`Config directory not found: ${configDirValue}`));
|
||||
if (!fs.existsSync(configDirValue) || !fs.statSync(configDirValue).isDirectory()) {
|
||||
console.error(fail(`Config directory not found or not a directory: ${configDirValue}`));
|
||||
console.error(info('Create the directory first, then copy your config files into it.'));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -13,8 +13,10 @@
|
||||
import { spawn, ChildProcess } from 'child_process';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { ProgressIndicator } from '../../utils/progress-indicator';
|
||||
import { ok, fail, info, warn } from '../../utils/ui';
|
||||
import { getCcsDir } from '../../utils/config-manager';
|
||||
import { escapeShellArg } from '../../utils/shell-executor';
|
||||
import { ensureCLIProxyBinary } from '../binary-manager';
|
||||
import {
|
||||
@@ -655,7 +657,7 @@ export async function execClaudeWithCLIProxy(
|
||||
upstreamBaseUrl: postSanitizationBaseUrl,
|
||||
verbose,
|
||||
defaultEffort: 'medium',
|
||||
traceFilePath: traceEnabled ? `${os.homedir()}/.ccs/codex-reasoning-proxy.log` : '',
|
||||
traceFilePath: traceEnabled ? path.join(getCcsDir(), 'codex-reasoning-proxy.log') : '',
|
||||
modelMap: {
|
||||
defaultModel: initialEnvVars.ANTHROPIC_MODEL,
|
||||
opusModel: initialEnvVars.ANTHROPIC_DEFAULT_OPUS_MODEL,
|
||||
|
||||
@@ -7,15 +7,12 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { InteractivePrompt } from '../utils/prompt';
|
||||
import { getProviderCatalog, supportsModelConfig, ModelEntry } from './model-catalog';
|
||||
import { getProviderSettingsPath, getClaudeEnvVars } from './config-generator';
|
||||
import { CLIProxyProvider } from './types';
|
||||
import { initUI, color, bold, dim, ok, info, header } from '../utils/ui';
|
||||
|
||||
/** CCS directory */
|
||||
const CCS_DIR = path.join(os.homedir(), '.ccs');
|
||||
import { getCcsDir } from '../utils/config-manager';
|
||||
|
||||
/**
|
||||
* Check if provider has user settings configured
|
||||
@@ -185,8 +182,9 @@ export async function configureProviderModel(
|
||||
};
|
||||
|
||||
// Ensure CCS directory exists
|
||||
if (!fs.existsSync(CCS_DIR)) {
|
||||
fs.mkdirSync(CCS_DIR, { recursive: true });
|
||||
const ccsDir = getCcsDir();
|
||||
if (!fs.existsSync(ccsDir)) {
|
||||
fs.mkdirSync(ccsDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Write settings file
|
||||
|
||||
@@ -111,6 +111,10 @@ Run ${color('ccs config', 'command')} for web dashboard`.trim();
|
||||
);
|
||||
console.log('');
|
||||
|
||||
// Resolve display path for dynamic sections
|
||||
const [dirSource] = getCcsDirSource();
|
||||
const dirDisplay = dirSource === 'default' ? '~/.ccs' : getCcsDir();
|
||||
|
||||
// Usage section
|
||||
console.log(subheader('Usage:'));
|
||||
console.log(` ${color('ccs', 'command')} [profile] [claude-args...]`);
|
||||
@@ -122,7 +126,7 @@ Run ${color('ccs config', 'command')} for web dashboard`.trim();
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
printMajorSection(
|
||||
'API Key Profiles',
|
||||
['Configure in ~/.ccs/*.settings.json'],
|
||||
[`Configure in ${dirDisplay}/*.settings.json`],
|
||||
[
|
||||
['ccs', 'Use default Claude account'],
|
||||
['ccs glm', 'GLM 4.6 (API key required)'],
|
||||
@@ -267,8 +271,6 @@ Run ${color('ccs config', 'command')} for web dashboard`.trim();
|
||||
]);
|
||||
|
||||
// Configuration
|
||||
const [dirSource] = getCcsDirSource();
|
||||
const dirDisplay = dirSource === 'default' ? '~/.ccs' : getCcsDir();
|
||||
printConfigSection('Configuration', [
|
||||
['Config File:', isUnifiedMode() ? `${dirDisplay}/config.yaml` : `${dirDisplay}/config.json`],
|
||||
['Profiles:', `${dirDisplay}/profiles.json`],
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import { initUI, header, subheader, color, warn } from '../utils/ui';
|
||||
import { getActiveConfigPath } from '../utils/config-manager';
|
||||
import { getActiveConfigPath, getCcsDir } from '../utils/config-manager';
|
||||
import { getVersion } from '../utils/version';
|
||||
|
||||
/**
|
||||
@@ -23,24 +22,24 @@ export async function handleVersionCommand(): Promise<void> {
|
||||
const installLocation = process.argv[1] || '(not found)';
|
||||
console.log(` ${color('Location:'.padEnd(17), 'info')} ${installLocation}`);
|
||||
|
||||
const ccsDir = path.join(os.homedir(), '.ccs');
|
||||
const ccsDir = getCcsDir();
|
||||
console.log(` ${color('CCS Directory:'.padEnd(17), 'info')} ${ccsDir}`);
|
||||
|
||||
const configPath = getActiveConfigPath();
|
||||
console.log(` ${color('Config:'.padEnd(17), 'info')} ${configPath}`);
|
||||
|
||||
const profilesJson = path.join(os.homedir(), '.ccs', 'profiles.json');
|
||||
const profilesJson = path.join(ccsDir, 'profiles.json');
|
||||
console.log(` ${color('Profiles:'.padEnd(17), 'info')} ${profilesJson}`);
|
||||
|
||||
// Delegation status
|
||||
const delegationSessionsPath = path.join(os.homedir(), '.ccs', 'delegation-sessions.json');
|
||||
const delegationSessionsPath = path.join(ccsDir, 'delegation-sessions.json');
|
||||
const delegationConfigured = fs.existsSync(delegationSessionsPath);
|
||||
|
||||
const readyProfiles: string[] = [];
|
||||
|
||||
// Check for profiles with valid API keys
|
||||
for (const profile of ['glm', 'kimi']) {
|
||||
const settingsPath = path.join(os.homedir(), '.ccs', `${profile}.settings.json`);
|
||||
const settingsPath = path.join(ccsDir, `${profile}.settings.json`);
|
||||
if (fs.existsSync(settingsPath)) {
|
||||
try {
|
||||
const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { info } from '../utils/ui';
|
||||
import { getCcsHome, getCcsDir } from '../utils/config-manager';
|
||||
import { createEmptyUnifiedConfig, UNIFIED_CONFIG_VERSION } from '../config/unified-config-types';
|
||||
import {
|
||||
saveUnifiedConfig,
|
||||
@@ -15,13 +15,6 @@ import {
|
||||
loadUnifiedConfig,
|
||||
} from '../config/unified-config-loader';
|
||||
|
||||
/**
|
||||
* Get CCS home directory (respects CCS_HOME env for test isolation)
|
||||
*/
|
||||
function getCcsHome(): string {
|
||||
return process.env.CCS_HOME || os.homedir();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recovery Manager Class
|
||||
*/
|
||||
@@ -35,7 +28,7 @@ class RecoveryManager {
|
||||
|
||||
constructor() {
|
||||
this.homedir = getCcsHome();
|
||||
this.ccsDir = path.join(this.homedir, '.ccs');
|
||||
this.ccsDir = getCcsDir();
|
||||
this.claudeDir = path.join(this.homedir, '.claude');
|
||||
this.sharedDir = path.join(this.ccsDir, 'shared');
|
||||
this.completionsDir = path.join(this.ccsDir, 'completions');
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { ok, warn, fail, info, header, color } from '../../utils/ui';
|
||||
import { getCcsDir } from '../../utils/config-manager';
|
||||
import {
|
||||
CLIPROXY_DEFAULT_PORT,
|
||||
configNeedsRegeneration,
|
||||
@@ -28,8 +28,6 @@ const ora = createSpinner();
|
||||
* 4. OAuth callback ports blocked by CLIProxy
|
||||
*/
|
||||
export async function runAutoRepair(): Promise<void> {
|
||||
const homedir = os.homedir();
|
||||
|
||||
console.log('');
|
||||
console.log(header('AUTO-FIX MODE'));
|
||||
console.log('');
|
||||
@@ -111,7 +109,7 @@ export async function runAutoRepair(): Promise<void> {
|
||||
|
||||
// Fix 4: Fix shared symlinks (settings.json broken by Claude CLI toggle thinking, etc.)
|
||||
const symlinkSpinner = ora('Checking shared settings.json symlink').start();
|
||||
const sharedSettings = path.join(homedir, '.ccs', 'shared', 'settings.json');
|
||||
const sharedSettings = path.join(getCcsDir(), 'shared', 'settings.json');
|
||||
try {
|
||||
if (fs.existsSync(sharedSettings)) {
|
||||
const stats = fs.lstatSync(sharedSettings);
|
||||
|
||||
@@ -62,7 +62,7 @@ class SharedManager {
|
||||
const resolvedTarget = path.resolve(path.dirname(target), targetLink);
|
||||
|
||||
// Check if target points back to our shared dir or link path
|
||||
const sharedDir = path.join(this.homeDir, '.ccs', 'shared');
|
||||
const sharedDir = path.join(getCcsDir(), 'shared');
|
||||
if (resolvedTarget.startsWith(sharedDir) || resolvedTarget === linkPath) {
|
||||
console.log(warn(`Circular symlink detected: ${target} → ${resolvedTarget}`));
|
||||
return true;
|
||||
|
||||
@@ -197,12 +197,7 @@ export class ClaudeDirInstaller {
|
||||
cleanupDeprecated(silent = false): CleanupResult {
|
||||
const deprecatedFile = path.join(this.ccsClaudeDir, 'agents', 'ccs-delegator.md');
|
||||
const userSymlinkFile = path.join(this.homeDir, '.claude', 'agents', 'ccs-delegator.md');
|
||||
const migrationMarker = path.join(
|
||||
this.homeDir,
|
||||
'.ccs',
|
||||
'.migrations',
|
||||
'v435-delegator-cleanup'
|
||||
);
|
||||
const migrationMarker = path.join(getCcsDir(), '.migrations', 'v435-delegator-cleanup');
|
||||
|
||||
const cleanedFiles: string[] = [];
|
||||
|
||||
|
||||
@@ -72,10 +72,10 @@ const CLOUD_SYNC_PATTERNS = [
|
||||
* @returns Detected service name or null
|
||||
*/
|
||||
export function detectCloudSyncPath(dir: string): string | null {
|
||||
const normalized = dir.replace(/\\/g, '/');
|
||||
const normalized = dir.replace(/\\/g, '/').toLowerCase();
|
||||
for (const pattern of CLOUD_SYNC_PATTERNS) {
|
||||
if (normalized.includes(pattern)) {
|
||||
return pattern;
|
||||
if (normalized.includes(pattern.toLowerCase())) {
|
||||
return pattern; // Return original casing for display
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { Settings } from '../types';
|
||||
import { ValidationResult } from '../types/utils';
|
||||
import { getCcsDir } from './config-manager';
|
||||
|
||||
/**
|
||||
* Extended validation result for delegation profiles
|
||||
@@ -28,8 +28,7 @@ export class DelegationValidator {
|
||||
* @returns Validation result { valid: boolean, error?: string, settingsPath?: string }
|
||||
*/
|
||||
static validate(profileName: string): DelegationValidationResult {
|
||||
const homeDir = os.homedir();
|
||||
const settingsPath = path.join(homeDir, '.ccs', `${profileName}.settings.json`);
|
||||
const settingsPath = path.join(getCcsDir(), `${profileName}.settings.json`);
|
||||
|
||||
// Check if profile directory exists
|
||||
if (!fs.existsSync(settingsPath)) {
|
||||
@@ -144,8 +143,7 @@ export class DelegationValidator {
|
||||
* @returns List of profile names ready for delegation
|
||||
*/
|
||||
static getReadyProfiles(): string[] {
|
||||
const homeDir = os.homedir();
|
||||
const ccsDir = path.join(homeDir, '.ccs');
|
||||
const ccsDir = getCcsDir();
|
||||
const configPath = path.join(ccsDir, 'config.yaml');
|
||||
|
||||
if (!fs.existsSync(ccsDir)) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import { getCcsDir } from './config-manager';
|
||||
// import { execSync } from 'child_process'; // Currently unused
|
||||
|
||||
type ShellType = 'bash' | 'zsh' | 'fish' | 'powershell' | null;
|
||||
@@ -24,7 +25,7 @@ export class ShellCompletionInstaller {
|
||||
|
||||
constructor() {
|
||||
this.homeDir = os.homedir();
|
||||
this.ccsDir = path.join(this.homeDir, '.ccs');
|
||||
this.ccsDir = getCcsDir();
|
||||
this.completionDir = path.join(this.ccsDir, 'completions');
|
||||
this.scriptsDir = path.join(__dirname, '../../scripts/completion');
|
||||
}
|
||||
|
||||
@@ -6,9 +6,8 @@
|
||||
*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'path';
|
||||
import { getConfigPath } from '../../utils/config-manager';
|
||||
import { getConfigPath, getCcsDir } from '../../utils/config-manager';
|
||||
import { isUnifiedMode, hasUnifiedConfig } from '../../config/unified-config-loader';
|
||||
import type { HealthCheck } from './types';
|
||||
|
||||
@@ -18,7 +17,7 @@ import type { HealthCheck } from './types';
|
||||
export function checkConfigFile(): HealthCheck {
|
||||
// In unified mode, check config.yaml
|
||||
if (isUnifiedMode() || hasUnifiedConfig()) {
|
||||
const ccsDir = path.join(os.homedir(), '.ccs');
|
||||
const ccsDir = getCcsDir();
|
||||
const yamlPath = path.join(ccsDir, 'config.yaml');
|
||||
|
||||
if (!fs.existsSync(yamlPath)) {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { getDashboardAuthConfig } from '../../config/unified-config-loader';
|
||||
import crypto from 'crypto';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import { getCcsDir } from '../../utils/config-manager';
|
||||
|
||||
// Extend Express Request with session
|
||||
declare module 'express-session' {
|
||||
@@ -24,7 +24,7 @@ declare module 'express-session' {
|
||||
const PUBLIC_PATHS = ['/api/auth/login', '/api/auth/check', '/api/auth/setup', '/api/health'];
|
||||
|
||||
/** Path to persistent session secret file */
|
||||
const SESSION_SECRET_PATH = path.join(os.homedir(), '.ccs', '.session-secret');
|
||||
const SESSION_SECRET_PATH = path.join(getCcsDir(), '.session-secret');
|
||||
|
||||
/**
|
||||
* Generate or retrieve persistent session secret.
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import {
|
||||
loadDailyUsageData,
|
||||
loadMonthlyUsageData,
|
||||
@@ -25,13 +24,14 @@ import {
|
||||
getCacheAge,
|
||||
} from './disk-cache';
|
||||
import { ok, info, fail } from '../../utils/ui';
|
||||
import { getCcsDir } from '../../utils/config-manager';
|
||||
|
||||
// ============================================================================
|
||||
// Multi-Instance Support - Aggregate usage from CCS profiles
|
||||
// ============================================================================
|
||||
|
||||
/** Path to CCS instances directory */
|
||||
const CCS_INSTANCES_DIR = path.join(os.homedir(), '.ccs', 'instances');
|
||||
const CCS_INSTANCES_DIR = path.join(getCcsDir(), 'instances');
|
||||
|
||||
/**
|
||||
* Get list of CCS instance paths that have usage data
|
||||
|
||||
@@ -10,13 +10,12 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import type { DailyUsage, HourlyUsage, MonthlyUsage, SessionUsage } from './types';
|
||||
import { ok, info, warn } from '../../utils/ui';
|
||||
import { getCcsDir } from '../../utils/config-manager';
|
||||
|
||||
// Cache configuration
|
||||
const CCS_DIR = path.join(os.homedir(), '.ccs');
|
||||
const CACHE_DIR = path.join(CCS_DIR, 'cache');
|
||||
const CACHE_DIR = path.join(getCcsDir(), 'cache');
|
||||
const CACHE_FILE = path.join(CACHE_DIR, 'usage.json');
|
||||
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
||||
const STALE_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days (max age for stale data)
|
||||
|
||||
@@ -18,6 +18,9 @@ describe('Config Directory Override', function () {
|
||||
originalCcsHome = process.env.CCS_HOME;
|
||||
delete process.env.CCS_DIR;
|
||||
delete process.env.CCS_HOME;
|
||||
// Reset module-level state
|
||||
const { setGlobalConfigDir } = require('../../dist/utils/config-manager');
|
||||
setGlobalConfigDir(undefined);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
@@ -25,6 +28,9 @@ describe('Config Directory Override', function () {
|
||||
else delete process.env.CCS_DIR;
|
||||
if (originalCcsHome !== undefined) process.env.CCS_HOME = originalCcsHome;
|
||||
else delete process.env.CCS_HOME;
|
||||
// Reset module-level state
|
||||
const { setGlobalConfigDir } = require('../../dist/utils/config-manager');
|
||||
setGlobalConfigDir(undefined);
|
||||
});
|
||||
|
||||
describe('getCcsDir() precedence', function () {
|
||||
@@ -52,6 +58,29 @@ describe('Config Directory Override', function () {
|
||||
const { getCcsDir } = require('../../dist/utils/config-manager');
|
||||
assert.strictEqual(getCcsDir(), path.resolve('/tmp/ccs-dir'));
|
||||
});
|
||||
|
||||
it('should give setGlobalConfigDir() highest precedence', function () {
|
||||
process.env.CCS_DIR = '/tmp/env-dir';
|
||||
process.env.CCS_HOME = '/tmp/env-home';
|
||||
const { getCcsDir, setGlobalConfigDir } = require('../../dist/utils/config-manager');
|
||||
setGlobalConfigDir('/tmp/flag-dir');
|
||||
assert.strictEqual(getCcsDir(), path.resolve('/tmp/flag-dir'));
|
||||
});
|
||||
|
||||
it('should resolve relative paths in setGlobalConfigDir()', function () {
|
||||
const { getCcsDir, setGlobalConfigDir } = require('../../dist/utils/config-manager');
|
||||
setGlobalConfigDir('relative/path');
|
||||
assert.strictEqual(getCcsDir(), path.resolve('relative/path'));
|
||||
});
|
||||
|
||||
it('should clear override when setGlobalConfigDir(undefined) is called', function () {
|
||||
const { getCcsDir, setGlobalConfigDir } = require('../../dist/utils/config-manager');
|
||||
setGlobalConfigDir('/tmp/override');
|
||||
assert.strictEqual(getCcsDir(), path.resolve('/tmp/override'));
|
||||
setGlobalConfigDir(undefined);
|
||||
const expected = path.join(os.homedir(), '.ccs');
|
||||
assert.strictEqual(getCcsDir(), expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCcsDirSource()', function () {
|
||||
@@ -74,6 +103,14 @@ describe('Config Directory Override', function () {
|
||||
const [source] = getCcsDirSource();
|
||||
assert.strictEqual(source, 'CCS_HOME');
|
||||
});
|
||||
|
||||
it('should return "--config-dir" when setGlobalConfigDir() is set', function () {
|
||||
const { getCcsDirSource, setGlobalConfigDir } = require('../../dist/utils/config-manager');
|
||||
setGlobalConfigDir('/tmp/flag-test');
|
||||
const [source, dir] = getCcsDirSource();
|
||||
assert.strictEqual(source, '--config-dir');
|
||||
assert.strictEqual(dir, path.resolve('/tmp/flag-test'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('detectCloudSyncPath()', function () {
|
||||
@@ -100,6 +137,13 @@ describe('Config Directory Override', function () {
|
||||
assert.strictEqual(detectCloudSyncPath('/Users/kai/Google Drive/ccs'), 'Google Drive');
|
||||
});
|
||||
|
||||
it('should detect cloud paths case-insensitively', function () {
|
||||
const { detectCloudSyncPath } = require('../../dist/utils/config-manager');
|
||||
assert.strictEqual(detectCloudSyncPath('/Users/kai/dropbox/ccs'), 'Dropbox');
|
||||
assert.strictEqual(detectCloudSyncPath('C:\\Users\\kai\\onedrive\\ccs'), 'OneDrive');
|
||||
assert.strictEqual(detectCloudSyncPath('/Users/kai/google drive/ccs'), 'Google Drive');
|
||||
});
|
||||
|
||||
it('should return null for regular paths', function () {
|
||||
const { detectCloudSyncPath } = require('../../dist/utils/config-manager');
|
||||
assert.strictEqual(detectCloudSyncPath('/home/kai/.ccs'), null);
|
||||
|
||||
Reference in New Issue
Block a user