@kaitranntt/ccs 2.4.3 → 2.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaitranntt/ccs",
3
- "version": "2.4.3",
3
+ "version": "2.4.5",
4
4
  "description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6",
5
5
  "keywords": [
6
6
  "cli",
@@ -29,6 +29,7 @@
29
29
  "files": [
30
30
  "bin/",
31
31
  "lib/",
32
+ "scripts/",
32
33
  "config/",
33
34
  "VERSION",
34
35
  "README.md",
@@ -44,9 +45,18 @@
44
45
  ],
45
46
  "preferGlobal": true,
46
47
  "scripts": {
47
- "test": "bash tests/edge-cases.sh",
48
+ "test": "npm run test:all",
49
+ "test:all": "npm run test:unit && npm run test:npm",
50
+ "test:unit": "npx mocha tests/shared/unit/**/*.test.js --timeout 5000",
51
+ "test:npm": "npx mocha tests/npm/**/*.test.js --timeout 10000",
52
+ "test:native": "bash tests/native/unix/edge-cases.sh",
53
+ "test:edge-cases": "bash tests/edge-cases.sh",
48
54
  "prepublishOnly": "node scripts/sync-version.js",
49
55
  "prepack": "node scripts/sync-version.js",
50
- "prepare": "node scripts/check-executables.js"
56
+ "prepare": "node scripts/check-executables.js",
57
+ "postinstall": "node scripts/postinstall.js"
58
+ },
59
+ "devDependencies": {
60
+ "mocha": "^11.7.5"
51
61
  }
52
62
  }
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env bash
2
+ # Bump CCS version
3
+ # Usage: ./scripts/bump-version.sh [major|minor|patch]
4
+
5
+ set -euo pipefail
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ CCS_DIR="$(dirname "$SCRIPT_DIR")"
9
+ VERSION_FILE="$CCS_DIR/VERSION"
10
+
11
+ # Check VERSION file exists
12
+ if [[ ! -f "$VERSION_FILE" ]]; then
13
+ echo "✗ Error: VERSION file not found at $VERSION_FILE"
14
+ exit 1
15
+ fi
16
+
17
+ # Read current version
18
+ CURRENT_VERSION=$(cat "$VERSION_FILE")
19
+ echo "Current version: $CURRENT_VERSION"
20
+
21
+ # Parse version
22
+ if [[ ! "$CURRENT_VERSION" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then
23
+ echo "✗ Error: Invalid version format in VERSION file"
24
+ echo "Expected: MAJOR.MINOR.PATCH (e.g., 1.2.3)"
25
+ exit 1
26
+ fi
27
+
28
+ MAJOR="${BASH_REMATCH[1]}"
29
+ MINOR="${BASH_REMATCH[2]}"
30
+ PATCH="${BASH_REMATCH[3]}"
31
+
32
+ # Determine bump type
33
+ BUMP_TYPE="${1:-patch}"
34
+
35
+ case "$BUMP_TYPE" in
36
+ major)
37
+ MAJOR=$((MAJOR + 1))
38
+ MINOR=0
39
+ PATCH=0
40
+ ;;
41
+ minor)
42
+ MINOR=$((MINOR + 1))
43
+ PATCH=0
44
+ ;;
45
+ patch)
46
+ PATCH=$((PATCH + 1))
47
+ ;;
48
+ *)
49
+ echo "✗ Error: Invalid bump type '$BUMP_TYPE'"
50
+ echo "Usage: $0 [major|minor|patch]"
51
+ exit 1
52
+ ;;
53
+ esac
54
+
55
+ NEW_VERSION="$MAJOR.$MINOR.$PATCH"
56
+
57
+ echo "New version: $NEW_VERSION"
58
+ echo ""
59
+ echo "This will update hardcoded versions in:"
60
+ echo " 1. VERSION file"
61
+ echo " 2. lib/ccs (bash executable)"
62
+ echo " 3. lib/ccs.ps1 (PowerShell executable)"
63
+ echo " 4. package.json (via sync-version.js)"
64
+ echo " 5. installers/install.sh"
65
+ echo " 6. installers/install.ps1"
66
+ echo ""
67
+ read -p "Continue? (y/N) " -n 1 -r
68
+ echo
69
+
70
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
71
+ echo "Cancelled."
72
+ exit 0
73
+ fi
74
+
75
+ # Update VERSION file
76
+ echo "$NEW_VERSION" > "$VERSION_FILE"
77
+ echo "✓ Updated VERSION file to $NEW_VERSION"
78
+
79
+ # Update ccs (bash executable)
80
+ CCS_BASH="$CCS_DIR/lib/ccs"
81
+ if [[ -f "$CCS_BASH" ]]; then
82
+ sed -i.bak "s/^CCS_VERSION=\".*\"/CCS_VERSION=\"$NEW_VERSION\"/" "$CCS_BASH"
83
+ rm -f "$CCS_BASH.bak"
84
+ echo "✓ Updated lib/ccs (bash executable)"
85
+ else
86
+ echo "⚠ lib/ccs not found, skipping"
87
+ fi
88
+
89
+ # Update ccs.ps1 (PowerShell executable)
90
+ CCS_PS1="$CCS_DIR/lib/ccs.ps1"
91
+ if [[ -f "$CCS_PS1" ]]; then
92
+ sed -i.bak "s/^\$CcsVersion = \".*\"/\$CcsVersion = \"$NEW_VERSION\"/" "$CCS_PS1"
93
+ rm -f "$CCS_PS1.bak"
94
+ echo "✓ Updated lib/ccs.ps1 (PowerShell executable)"
95
+ else
96
+ echo "⚠ lib/ccs.ps1 not found, skipping"
97
+ fi
98
+
99
+ # Update installers/install.sh
100
+ INSTALL_SH="$CCS_DIR/installers/install.sh"
101
+ if [[ -f "$INSTALL_SH" ]]; then
102
+ sed -i.bak "s/^CCS_VERSION=\".*\"/CCS_VERSION=\"$NEW_VERSION\"/" "$INSTALL_SH"
103
+ rm -f "$INSTALL_SH.bak"
104
+ echo "✓ Updated installers/install.sh"
105
+ else
106
+ echo "⚠ installers/install.sh not found, skipping"
107
+ fi
108
+
109
+ # Update installers/install.ps1
110
+ INSTALL_PS1="$CCS_DIR/installers/install.ps1"
111
+ if [[ -f "$INSTALL_PS1" ]]; then
112
+ sed -i.bak "s/^\$CcsVersion = \".*\"/\$CcsVersion = \"$NEW_VERSION\"/" "$INSTALL_PS1"
113
+ rm -f "$INSTALL_PS1.bak"
114
+ echo "✓ Updated installers/install.ps1"
115
+ else
116
+ echo "⚠ installers/install.ps1 not found, skipping"
117
+ fi
118
+
119
+ # Sync version to package.json
120
+ echo "Syncing version to package.json..."
121
+ if node "$SCRIPT_DIR/sync-version.js"; then
122
+ echo "✓ Synced version to package.json"
123
+ else
124
+ echo "✗ Error: Failed to sync version to package.json"
125
+ exit 1
126
+ fi
127
+
128
+ echo ""
129
+ echo "✓ Version bumped to $NEW_VERSION"
130
+ echo ""
131
+ echo "Next steps:"
132
+ echo " 1. Review changes: git diff"
133
+ echo " 2. Commit: git add VERSION package.json lib/ccs lib/ccs.ps1 installers/install.sh installers/install.ps1"
134
+ echo " 3. Commit: git commit -m \"chore: bump version to $NEW_VERSION\""
135
+ echo " 4. Tag: git tag v$NEW_VERSION"
136
+ echo " 5. Push: git push origin main && git push origin v$NEW_VERSION"
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ // Ensure executables have correct permissions (Unix only)
6
+ if (process.platform !== 'win32') {
7
+ const executables = [
8
+ path.join(__dirname, '..', 'bin', 'ccs.js'),
9
+ path.join(__dirname, '..', 'lib', 'ccs'),
10
+ ];
11
+
12
+ for (const file of executables) {
13
+ if (fs.existsSync(file)) {
14
+ fs.chmodSync(file, '755');
15
+ console.log(`✓ Set executable: ${path.basename(file)}`);
16
+ }
17
+ }
18
+ }
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env bash
2
+ # Get current CCS version
3
+ # Usage: ./scripts/get-version.sh
4
+
5
+ set -euo pipefail
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ CCS_DIR="$(dirname "$SCRIPT_DIR")"
9
+ VERSION_FILE="$CCS_DIR/VERSION"
10
+
11
+ if [[ -f "$VERSION_FILE" ]]; then
12
+ cat "$VERSION_FILE"
13
+ else
14
+ echo "unknown"
15
+ exit 1
16
+ fi
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const os = require('os');
7
+
8
+ /**
9
+ * CCS Postinstall Script
10
+ * Automatically creates config files in ~/.ccs/ after npm install
11
+ *
12
+ * Runs when: npm install -g @kaitranntt/ccs
13
+ * Idempotent: Safe to run multiple times (won't overwrite existing configs)
14
+ * Cross-platform: Works on Unix, macOS, Windows
15
+ */
16
+
17
+ function createConfigFiles() {
18
+ try {
19
+ // Get user home directory (cross-platform)
20
+ const homedir = os.homedir();
21
+ const ccsDir = path.join(homedir, '.ccs');
22
+
23
+ // Create ~/.ccs/ directory if missing
24
+ if (!fs.existsSync(ccsDir)) {
25
+ fs.mkdirSync(ccsDir, { recursive: true, mode: 0o755 });
26
+ console.log('[OK] Created directory: ~/.ccs/');
27
+ }
28
+
29
+ // Create config.json if missing
30
+ const configPath = path.join(ccsDir, 'config.json');
31
+ if (!fs.existsSync(configPath)) {
32
+ const config = {
33
+ profiles: {
34
+ glm: '~/.ccs/glm.settings.json',
35
+ default: '~/.claude/settings.json'
36
+ }
37
+ };
38
+
39
+ // Atomic write: temp file → rename
40
+ const tmpPath = `${configPath}.tmp`;
41
+ fs.writeFileSync(tmpPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
42
+ fs.renameSync(tmpPath, configPath);
43
+
44
+ console.log('[OK] Created config: ~/.ccs/config.json');
45
+ } else {
46
+ console.log('[OK] Config exists: ~/.ccs/config.json (preserved)');
47
+ }
48
+
49
+ // Create glm.settings.json if missing
50
+ const glmSettingsPath = path.join(ccsDir, 'glm.settings.json');
51
+ if (!fs.existsSync(glmSettingsPath)) {
52
+ const glmSettings = {
53
+ env: {
54
+ ANTHROPIC_BASE_URL: 'https://api.z.ai/api/anthropic',
55
+ ANTHROPIC_AUTH_TOKEN: 'YOUR_GLM_API_KEY_HERE',
56
+ ANTHROPIC_MODEL: 'glm-4.6',
57
+ ANTHROPIC_DEFAULT_OPUS_MODEL: 'glm-4.6',
58
+ ANTHROPIC_DEFAULT_SONNET_MODEL: 'glm-4.6',
59
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: 'glm-4.6'
60
+ }
61
+ };
62
+
63
+ // Atomic write
64
+ const tmpPath = `${glmSettingsPath}.tmp`;
65
+ fs.writeFileSync(tmpPath, JSON.stringify(glmSettings, null, 2) + '\n', 'utf8');
66
+ fs.renameSync(tmpPath, glmSettingsPath);
67
+
68
+ console.log('[OK] Created GLM profile: ~/.ccs/glm.settings.json');
69
+ console.log('');
70
+ console.log(' [!] Configure GLM API key:');
71
+ console.log(' 1. Get key from: https://api.z.ai');
72
+ console.log(' 2. Edit: ~/.ccs/glm.settings.json');
73
+ console.log(' 3. Replace: YOUR_GLM_API_KEY_HERE');
74
+ } else {
75
+ console.log('[OK] GLM profile exists: ~/.ccs/glm.settings.json (preserved)');
76
+ }
77
+
78
+ console.log('');
79
+ console.log('[OK] CCS configuration ready!');
80
+ console.log(' Run: ccs --version');
81
+
82
+ } catch (err) {
83
+ // Silent failure: don't break npm install
84
+ console.warn('');
85
+ console.warn('[!] Could not auto-create CCS configuration');
86
+ console.warn(` Error: ${err.message}`);
87
+ console.warn('');
88
+ console.warn(' Manual setup:');
89
+ console.warn(' mkdir -p ~/.ccs');
90
+ console.warn(' # See: https://github.com/kaitranntt/ccs#configuration');
91
+ console.warn('');
92
+
93
+ // Don't exit with error code - allow npm install to succeed
94
+ process.exit(0);
95
+ }
96
+ }
97
+
98
+ // Run postinstall
99
+ createConfigFiles();
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ // Read VERSION file
6
+ const versionFile = path.join(__dirname, '..', 'VERSION');
7
+ const version = fs.readFileSync(versionFile, 'utf8').trim();
8
+
9
+ // Update package.json
10
+ const pkgPath = path.join(__dirname, '..', 'package.json');
11
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
12
+ pkg.version = version;
13
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
14
+
15
+ console.log(`✓ Synced version ${version} to package.json`);
@@ -0,0 +1,46 @@
1
+ export default {
2
+ async fetch(request) {
3
+ const url = new URL(request.url);
4
+
5
+ // Detect platform from User-Agent header
6
+ const userAgent = request.headers.get('user-agent') || '';
7
+ const isWindows = userAgent.includes('Windows') || userAgent.includes('Win32');
8
+ const isPowerShell = userAgent.includes('PowerShell') || userAgent.includes('pwsh');
9
+
10
+ // Smart routing with platform detection
11
+ let filePath;
12
+ if (url.pathname === '/install' || url.pathname === '/install.sh') {
13
+ filePath = (isWindows && isPowerShell) ? 'installers/install.ps1' : 'installers/install.sh';
14
+ } else if (url.pathname === '/install.ps1') {
15
+ filePath = 'installers/install.ps1';
16
+ } else if (url.pathname === '/uninstall' || url.pathname === '/uninstall.sh') {
17
+ filePath = (isWindows && isPowerShell) ? 'installers/uninstall.ps1' : 'installers/uninstall.sh';
18
+ } else if (url.pathname === '/uninstall.ps1') {
19
+ filePath = 'installers/uninstall.ps1';
20
+ } else {
21
+ return new Response('Not Found', { status: 404 });
22
+ }
23
+
24
+ try {
25
+ const githubUrl = `https://raw.githubusercontent.com/kaitranntt/ccs/main/${filePath}`;
26
+ const response = await fetch(githubUrl);
27
+
28
+ if (!response.ok) {
29
+ return new Response('File not found on GitHub', { status: 404 });
30
+ }
31
+
32
+ const contentType = filePath.endsWith('.ps1')
33
+ ? 'text/plain; charset=utf-8'
34
+ : 'text/x-shellscript; charset=utf-8';
35
+
36
+ return new Response(response.body, {
37
+ headers: {
38
+ 'Content-Type': contentType,
39
+ 'Cache-Control': 'public, max-age=300'
40
+ }
41
+ });
42
+ } catch (error) {
43
+ return new Response('Server Error', { status: 500 });
44
+ }
45
+ }
46
+ };
@@ -0,0 +1,15 @@
1
+ name = "ccs-installer"
2
+ main = "worker.js"
3
+ compatibility_date = "2025-11-02"
4
+ workers_dev = true
5
+
6
+ # CloudFlare Worker configuration for CCS installer
7
+ # This worker serves platform-specific installation scripts
8
+
9
+ # Production routes on ccs.kaitran.ca
10
+ # Worker intercepts /install* and /uninstall* paths
11
+ # All other paths pass through to Vercel origin (landing page)
12
+ routes = [
13
+ { pattern = "ccs.kaitran.ca/install*", zone_name = "kaitran.ca" },
14
+ { pattern = "ccs.kaitran.ca/uninstall*", zone_name = "kaitran.ca" }
15
+ ]