@kaitranntt/ccs 2.4.2 → 2.4.4

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 CHANGED
@@ -11,7 +11,7 @@ Switch between Claude Sonnet 4.5 and GLM 4.6 instantly. Stop hitting rate limits
11
11
 
12
12
  [![License](https://img.shields.io/badge/license-MIT-C15F3C?style=for-the-badge)](LICENSE)
13
13
  [![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-lightgrey?style=for-the-badge)]()
14
- [![npm](https://img.shields.io/npm/v/@kai/ccs?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/@kai/ccs)
14
+ [![npm](https://img.shields.io/npm/v/@kaitranntt/ccs?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/@kaitranntt/ccs)
15
15
  [![PoweredBy](https://img.shields.io/badge/PoweredBy-ClaudeKit-C15F3C?style=for-the-badge)](https://claudekit.cc?ref=HMNKXOHN)
16
16
 
17
17
  **Languages**: [English](README.md) | [Tiếng Việt](README.vi.md)
@@ -35,7 +35,7 @@ claude /login
35
35
 
36
36
  **macOS / Linux / Windows**
37
37
  ```bash
38
- npm install -g @kai/ccs
38
+ npm install -g @kaitranntt/ccs
39
39
  ```
40
40
 
41
41
  Compatible with npm, yarn, pnpm, and bun package managers.
@@ -73,20 +73,22 @@ All major package managers are supported:
73
73
 
74
74
  ```bash
75
75
  # npm (default)
76
- npm install -g @kai/ccs
76
+ npm install -g @kaitranntt/ccs
77
77
 
78
78
  # yarn
79
- yarn global add @kai/ccs
79
+ yarn global add @kaitranntt/ccs
80
80
 
81
81
  # pnpm (70% less disk space)
82
- pnpm add -g @kai/ccs
82
+ pnpm add -g @kaitranntt/ccs
83
83
 
84
84
  # bun (30x faster)
85
- bun add -g @kai/ccs
85
+ bun add -g @kaitranntt/ccs
86
86
  ```
87
87
 
88
88
  ### Configuration (Auto-created)
89
89
 
90
+ **CCS automatically creates configuration during installation** (via npm postinstall script).
91
+
90
92
  **~/.ccs/config.json**:
91
93
  ```json
92
94
  {
package/README.vi.md CHANGED
@@ -30,6 +30,17 @@ claude /login
30
30
 
31
31
  ### Phương Pháp Cài Đặt Chính
32
32
 
33
+ #### Option 1: npm Package (Được khuyến nghị)
34
+
35
+ **macOS / Linux / Windows**
36
+ ```bash
37
+ npm install -g @kaitranntt/ccs
38
+ ```
39
+
40
+ Tương thích với các trình quản lý package npm, yarn, pnpm, và bun.
41
+
42
+ #### Option 2: Cài Đặt Trực Tiếp (Truyền thống)
43
+
33
44
  **macOS / Linux**
34
45
  ```bash
35
46
  curl -fsSL ccs.kaitran.ca/install | bash
@@ -55,8 +66,28 @@ ccs "Debug issue này"
55
66
  ccs "Viết unit tests"
56
67
  ```
57
68
 
69
+ #### Package Manager Options
70
+
71
+ Tất cả các trình quản lý package chính đều được hỗ trợ:
72
+
73
+ ```bash
74
+ # npm (mặc định)
75
+ npm install -g @kaitranntt/ccs
76
+
77
+ # yarn
78
+ yarn global add @kaitranntt/ccs
79
+
80
+ # pnpm (ít hơn 70% dung lượng đĩa)
81
+ pnpm add -g @kaitranntt/ccs
82
+
83
+ # bun (nhanh hơn 30x)
84
+ bun add -g @kaitranntt/ccs
85
+ ```
86
+
58
87
  ### Cấu Hình (Tự Tạo)
59
88
 
89
+ **CCS tự động tạo cấu hình trong quá trình cài đặt** (thông qua script postinstall của npm).
90
+
60
91
  **~/.ccs/config.json**:
61
92
  ```json
62
93
  {
@@ -228,17 +259,17 @@ irm ccs.kaitran.ca/uninstall | iex
228
259
  ## 📖 Tài Liệu
229
260
 
230
261
  **Tài liệu đầy đủ trong [docs/](./docs/)**:
231
- - [Hướng dẫn Cài đặt](./docs/vi/installation.vi.md)
232
- - [Cấu hình](./docs/vi/configuration.vi.md)
233
- - [Ví dụ Sử dụng](./docs/vi/usage.vi.md)
234
- - [Khắc phục Sự cố](./docs/vi/troubleshooting.vi.md)
235
- - [Đóng góp](./docs/vi/contributing.vi.md)
262
+ - [Hướng dẫn Cài đặt](./docs/installation.md)
263
+ - [Cấu hình](./docs/configuration.md)
264
+ - [Ví dụ Sử dụng](./docs/usage.md)
265
+ - [Khắc phục Sự cố](./docs/troubleshooting.md)
266
+ - [Đóng góp](./docs/contributing.md)
236
267
 
237
268
  ---
238
269
 
239
270
  ## 🤝 Đóng Góp
240
271
 
241
- Chúng tôi chào mừng đóng góp! Xem [Hướng dẫn Đóng góp](./docs/contributing.md) để biết chi tiết.
272
+ Chúng tôi chào mừng đóng góp! Vui lòng xem [Hướng dẫn Đóng góp](./docs/contributing.md) để biết chi tiết.
242
273
 
243
274
  ---
244
275
 
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.4.2
1
+ 2.4.4
package/bin/ccs.js CHANGED
@@ -19,10 +19,22 @@ function getSpawnOptions(claudePath) {
19
19
 
20
20
  return {
21
21
  stdio: 'inherit',
22
- shell: needsShell // Required for .cmd files on Windows
22
+ shell: needsShell,
23
+ windowsHide: true // Hide the console window on Windows
23
24
  };
24
25
  }
25
26
 
27
+ // Helper: Escape arguments for shell execution to prevent security vulnerabilities
28
+ function escapeShellArg(arg) {
29
+ if (process.platform !== 'win32') {
30
+ // Unix-like systems: escape single quotes and wrap in single quotes
31
+ return "'" + arg.replace(/'/g, "'\"'\"'") + "'";
32
+ } else {
33
+ // Windows: escape double quotes and wrap in double quotes
34
+ return '"' + arg.replace(/"/g, '""') + '"';
35
+ }
36
+ }
37
+
26
38
  // Special command handlers
27
39
  function handleVersionCommand() {
28
40
  console.log(`CCS (Claude Code Switch) version ${CCS_VERSION}`);
@@ -48,7 +60,17 @@ function handleHelpCommand(remainingArgs) {
48
60
 
49
61
  // Execute claude --help
50
62
  const spawnOpts = getSpawnOptions(claudeCli);
51
- const child = spawn(claudeCli, ['--help', ...remainingArgs], spawnOpts);
63
+ let claudeArgs, child;
64
+
65
+ if (spawnOpts.shell) {
66
+ // When shell is required, escape arguments properly
67
+ claudeArgs = [claudeCli, '--help', ...remainingArgs].map(escapeShellArg).join(' ');
68
+ child = spawn(claudeArgs, spawnOpts);
69
+ } else {
70
+ // When no shell needed, use arguments array directly
71
+ claudeArgs = ['--help', ...remainingArgs];
72
+ child = spawn(claudeCli, claudeArgs, spawnOpts);
73
+ }
52
74
 
53
75
  child.on('exit', (code, signal) => {
54
76
  if (signal) {
@@ -138,7 +160,17 @@ function main() {
138
160
 
139
161
  // Execute claude with args
140
162
  const spawnOpts = getSpawnOptions(claudeCli);
141
- const child = spawn(claudeCli, remainingArgs, spawnOpts);
163
+ let claudeArgs, child;
164
+
165
+ if (spawnOpts.shell) {
166
+ // When shell is required, escape arguments properly
167
+ claudeArgs = [claudeCli, ...remainingArgs].map(escapeShellArg).join(' ');
168
+ child = spawn(claudeArgs, spawnOpts);
169
+ } else {
170
+ // When no shell needed, use arguments array directly
171
+ claudeArgs = remainingArgs;
172
+ child = spawn(claudeCli, claudeArgs, spawnOpts);
173
+ }
142
174
 
143
175
  child.on('exit', (code, signal) => {
144
176
  if (signal) {
@@ -169,9 +201,19 @@ function main() {
169
201
  }
170
202
 
171
203
  // Execute claude with --settings
172
- const claudeArgs = ['--settings', settingsPath, ...remainingArgs];
204
+ const claudeArgsList = ['--settings', settingsPath, ...remainingArgs];
173
205
  const spawnOpts = getSpawnOptions(claudeCli);
174
- const child = spawn(claudeCli, claudeArgs, spawnOpts);
206
+ let claudeArgs, child;
207
+
208
+ if (spawnOpts.shell) {
209
+ // When shell is required, escape arguments properly
210
+ claudeArgs = [claudeCli, ...claudeArgsList].map(escapeShellArg).join(' ');
211
+ child = spawn(claudeArgs, spawnOpts);
212
+ } else {
213
+ // When no shell needed, use arguments array directly
214
+ claudeArgs = claudeArgsList;
215
+ child = spawn(claudeCli, claudeArgs, spawnOpts);
216
+ }
175
217
 
176
218
  child.on('exit', (code, signal) => {
177
219
  if (signal) {
@@ -20,10 +20,13 @@ function readConfig() {
20
20
  showError(`Config file not found: ${configPath}
21
21
 
22
22
  Solutions:
23
- 1. Reinstall CCS:
23
+ 1. Reinstall CCS (auto-creates config):
24
+ npm install -g @kaitranntt/ccs --force
25
+
26
+ 2. Or use traditional installer:
24
27
  ${isWindows ? 'irm ccs.kaitran.ca/install | iex' : 'curl -fsSL ccs.kaitran.ca/install | bash'}
25
28
 
26
- 2. Or create config manually:
29
+ 3. Or create manually:
27
30
  mkdir -p ~/.ccs
28
31
  cat > ~/.ccs/config.json << 'EOF'
29
32
  {
@@ -32,7 +35,10 @@ Solutions:
32
35
  "default": "~/.claude/settings.json"
33
36
  }
34
37
  }
35
- EOF`);
38
+ EOF
39
+
40
+ Note: If you installed with npm --ignore-scripts, configs weren't created.
41
+ Reinstall without that flag: npm install -g @kaitranntt/ccs --force`);
36
42
  process.exit(1);
37
43
  }
38
44
 
package/lib/ccs CHANGED
@@ -2,7 +2,7 @@
2
2
  set -euo pipefail
3
3
 
4
4
  # Version (updated by scripts/bump-version.sh)
5
- CCS_VERSION="2.4.1"
5
+ CCS_VERSION="2.4.4"
6
6
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
7
 
8
8
  # --- Color/Format Functions ---
package/lib/ccs.ps1 CHANGED
@@ -72,7 +72,7 @@ Restart your terminal after installation.
72
72
  }
73
73
 
74
74
  # Version (updated by scripts/bump-version.sh)
75
- $CcsVersion = "2.4.1"
75
+ $CcsVersion = "2.4.4"
76
76
  $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
77
77
 
78
78
  # Installation function for commands and skills
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaitranntt/ccs",
3
- "version": "2.4.2",
3
+ "version": "2.4.4",
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
+ ]