@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/README.md +29 -15
- package/README.vi.md +50 -7
- package/VERSION +1 -1
- package/bin/ccs.js +16 -109
- package/bin/claude-detector.js +5 -34
- package/bin/config-manager.js +8 -63
- package/bin/helpers.js +7 -23
- package/lib/ccs +107 -71
- package/lib/ccs.ps1 +210 -364
- package/package.json +13 -3
- package/scripts/bump-version.sh +136 -0
- package/scripts/check-executables.js +18 -0
- package/scripts/get-version.sh +16 -0
- package/scripts/postinstall.js +99 -0
- package/scripts/sync-version.js +15 -0
- package/scripts/worker.js +46 -0
- package/scripts/wrangler.toml +15 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaitranntt/ccs",
|
|
3
|
-
"version": "2.4.
|
|
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": "
|
|
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
|
+
]
|