@kaitranntt/ccs 5.9.0-dev.3 → 5.9.0-dev.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.
Files changed (53) hide show
  1. package/README.md +30 -2
  2. package/VERSION +1 -1
  3. package/dist/ccs.js +6 -0
  4. package/dist/ccs.js.map +1 -1
  5. package/dist/cliproxy/binary-manager.d.ts +2 -2
  6. package/dist/cliproxy/binary-manager.js +2 -2
  7. package/dist/cliproxy/platform-detector.d.ts +2 -2
  8. package/dist/cliproxy/platform-detector.js +1 -1
  9. package/dist/commands/cliproxy-command.d.ts.map +1 -1
  10. package/dist/commands/cliproxy-command.js +37 -28
  11. package/dist/commands/cliproxy-command.js.map +1 -1
  12. package/dist/commands/config-command.d.ts +11 -0
  13. package/dist/commands/config-command.d.ts.map +1 -0
  14. package/dist/commands/config-command.js +111 -0
  15. package/dist/commands/config-command.js.map +1 -0
  16. package/dist/commands/help-command.d.ts.map +1 -1
  17. package/dist/commands/help-command.js +3 -1
  18. package/dist/commands/help-command.js.map +1 -1
  19. package/dist/web-server/file-watcher.d.ts +15 -0
  20. package/dist/web-server/file-watcher.d.ts.map +1 -0
  21. package/dist/web-server/file-watcher.js +88 -0
  22. package/dist/web-server/file-watcher.js.map +1 -0
  23. package/dist/web-server/health-service.d.ts +35 -0
  24. package/dist/web-server/health-service.d.ts.map +1 -0
  25. package/dist/web-server/health-service.js +223 -0
  26. package/dist/web-server/health-service.js.map +1 -0
  27. package/dist/web-server/index.d.ts +25 -0
  28. package/dist/web-server/index.d.ts.map +1 -0
  29. package/dist/web-server/index.js +89 -0
  30. package/dist/web-server/index.js.map +1 -0
  31. package/dist/web-server/overview-routes.d.ts +7 -0
  32. package/dist/web-server/overview-routes.d.ts.map +1 -0
  33. package/dist/web-server/overview-routes.js +80 -0
  34. package/dist/web-server/overview-routes.js.map +1 -0
  35. package/dist/web-server/routes.d.ts +7 -0
  36. package/dist/web-server/routes.d.ts.map +1 -0
  37. package/dist/web-server/routes.js +444 -0
  38. package/dist/web-server/routes.js.map +1 -0
  39. package/dist/web-server/shared-routes.d.ts +7 -0
  40. package/dist/web-server/shared-routes.d.ts.map +1 -0
  41. package/dist/web-server/shared-routes.js +167 -0
  42. package/dist/web-server/shared-routes.js.map +1 -0
  43. package/dist/web-server/shutdown.d.ts +14 -0
  44. package/dist/web-server/shutdown.d.ts.map +1 -0
  45. package/dist/web-server/shutdown.js +36 -0
  46. package/dist/web-server/shutdown.js.map +1 -0
  47. package/dist/web-server/websocket.d.ts +14 -0
  48. package/dist/web-server/websocket.d.ts.map +1 -0
  49. package/dist/web-server/websocket.js +82 -0
  50. package/dist/web-server/websocket.js.map +1 -0
  51. package/package.json +25 -5
  52. package/scripts/pre-release.sh +39 -0
  53. package/scripts/verify-bundle.js +48 -0
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ /**
3
+ * Graceful Shutdown Handler
4
+ *
5
+ * Handles SIGINT/SIGTERM signals to gracefully close WebSocket connections
6
+ * and HTTP server before process exit.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.setupGracefulShutdown = void 0;
10
+ const SHUTDOWN_TIMEOUT = 10000; // 10 seconds
11
+ /**
12
+ * Setup graceful shutdown handlers for SIGINT and SIGTERM
13
+ */
14
+ function setupGracefulShutdown(server, _wss, cleanup) {
15
+ const shutdown = () => {
16
+ console.log('\n[i] Shutting down gracefully...');
17
+ // Run cleanup first (closes file watchers + WebSocket clients)
18
+ if (cleanup) {
19
+ cleanup();
20
+ }
21
+ // Close HTTP server
22
+ server.close(() => {
23
+ console.log('[OK] Server closed');
24
+ process.exit(0);
25
+ });
26
+ // Force shutdown if graceful shutdown takes too long
27
+ setTimeout(() => {
28
+ console.log('[!] Force shutdown (timeout exceeded)');
29
+ process.exit(1);
30
+ }, SHUTDOWN_TIMEOUT);
31
+ };
32
+ process.on('SIGINT', shutdown);
33
+ process.on('SIGTERM', shutdown);
34
+ }
35
+ exports.setupGracefulShutdown = setupGracefulShutdown;
36
+ //# sourceMappingURL=shutdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shutdown.js","sourceRoot":"","sources":["../../src/web-server/shutdown.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAKH,MAAM,gBAAgB,GAAG,KAAM,CAAC,CAAC,aAAa;AAE9C;;GAEG;AACH,SAAgB,qBAAqB,CACnC,MAAkB,EAClB,IAAqB,EACrB,OAAoB;IAEpB,MAAM,QAAQ,GAAG,GAAG,EAAE;QACpB,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;QAEjD,+DAA+D;QAC/D,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,oBAAoB;QACpB,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;YAChB,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;YAClC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;QAEH,qDAAqD;QACrD,UAAU,CAAC,GAAG,EAAE;YACd,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;YACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,EAAE,gBAAgB,CAAC,CAAC;IACvB,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAClC,CAAC;AA5BD,sDA4BC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * WebSocket Handler (Phase 04)
3
+ *
4
+ * Manages WebSocket connections, broadcasts file changes, and handles client messages.
5
+ */
6
+ import { WebSocketServer } from 'ws';
7
+ export interface WSMessage {
8
+ type: string;
9
+ [key: string]: unknown;
10
+ }
11
+ export declare function setupWebSocket(wss: WebSocketServer): {
12
+ cleanup: () => void;
13
+ };
14
+ //# sourceMappingURL=websocket.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../../src/web-server/websocket.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,eAAe,EAAa,MAAM,IAAI,CAAC;AAGhD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,eAAe,GAAG;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,CA8E5E"}
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ /**
3
+ * WebSocket Handler (Phase 04)
4
+ *
5
+ * Manages WebSocket connections, broadcasts file changes, and handles client messages.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.setupWebSocket = void 0;
9
+ const ws_1 = require("ws");
10
+ const file_watcher_1 = require("./file-watcher");
11
+ function setupWebSocket(wss) {
12
+ // Track connected clients
13
+ const clients = new Set();
14
+ // Broadcast message to all clients
15
+ function broadcast(message) {
16
+ const data = JSON.stringify(message);
17
+ clients.forEach((client) => {
18
+ if (client.readyState === ws_1.WebSocket.OPEN) {
19
+ client.send(data);
20
+ }
21
+ });
22
+ }
23
+ // Handle new connections
24
+ wss.on('connection', (ws) => {
25
+ clients.add(ws);
26
+ console.log(`[WS] Client connected (${clients.size} total)`);
27
+ // Send welcome message
28
+ ws.send(JSON.stringify({ type: 'connected', timestamp: Date.now() }));
29
+ // Handle client messages
30
+ ws.on('message', (data) => {
31
+ try {
32
+ const message = JSON.parse(data.toString());
33
+ handleClientMessage(ws, message);
34
+ }
35
+ catch {
36
+ console.log('[WS] Invalid message format');
37
+ }
38
+ });
39
+ // Handle disconnect
40
+ ws.on('close', () => {
41
+ clients.delete(ws);
42
+ console.log(`[WS] Client disconnected (${clients.size} remaining)`);
43
+ });
44
+ ws.on('error', (err) => {
45
+ console.log(`[WS] Error: ${err.message}`);
46
+ clients.delete(ws);
47
+ });
48
+ });
49
+ // Handle incoming client messages
50
+ function handleClientMessage(ws, message) {
51
+ switch (message.type) {
52
+ case 'ping':
53
+ ws.send(JSON.stringify({ type: 'pong', timestamp: Date.now() }));
54
+ break;
55
+ case 'subscribe':
56
+ // Future: selective subscriptions
57
+ break;
58
+ default:
59
+ console.log(`[WS] Unknown message type: ${message.type}`);
60
+ }
61
+ }
62
+ // Setup file watcher
63
+ const watcher = (0, file_watcher_1.createFileWatcher)((event) => {
64
+ console.log(`[FS] ${event.type}: ${event.path}`);
65
+ broadcast({
66
+ type: event.type,
67
+ path: event.path,
68
+ timestamp: event.timestamp,
69
+ });
70
+ });
71
+ // Cleanup function
72
+ const cleanup = () => {
73
+ watcher.close();
74
+ clients.forEach((client) => {
75
+ client.close(1001, 'Server shutting down');
76
+ });
77
+ clients.clear();
78
+ };
79
+ return { cleanup };
80
+ }
81
+ exports.setupWebSocket = setupWebSocket;
82
+ //# sourceMappingURL=websocket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"websocket.js","sourceRoot":"","sources":["../../src/web-server/websocket.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaitranntt/ccs",
3
- "version": "5.9.0-dev.3",
3
+ "version": "5.9.0-dev.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",
@@ -52,45 +52,65 @@
52
52
  "scripts": {
53
53
  "build": "tsc && node scripts/add-shebang.js",
54
54
  "build:watch": "tsc --watch",
55
+ "build:server": "tsc && node scripts/add-shebang.js",
56
+ "build:all": "bun run ui:build && bun run build:server",
55
57
  "prebuild": "rm -rf dist tsconfig.tsbuildinfo",
58
+ "prebuild:all": "rm -rf dist tsconfig.tsbuildinfo",
59
+ "postbuild:all": "node scripts/verify-bundle.js",
56
60
  "typecheck": "tsc --noEmit",
57
61
  "lint": "eslint src/",
58
62
  "lint:fix": "eslint src/ --fix",
59
63
  "format": "prettier --write src/",
60
64
  "format:check": "prettier --check src/",
61
65
  "validate": "bun run typecheck && bun run lint:fix && bun run format:check && bun run test",
66
+ "verify:bundle": "node scripts/verify-bundle.js",
62
67
  "test": "bun run build && bun run test:all",
63
68
  "test:all": "bun test",
64
69
  "test:unit": "bun test tests/unit/",
65
70
  "test:npm": "bun test tests/npm/",
66
71
  "test:native": "bash tests/native/unix/edge-cases.sh",
67
- "prepublishOnly": "npm run validate && node scripts/sync-version.js",
68
- "prepack": "npm run validate && node scripts/sync-version.js",
72
+ "dev": "bun run build:server && bun dist/ccs.js config --dev",
73
+ "ui:build": "cd ui && bun run build",
74
+ "ui:preview": "cd ui && bun run preview",
75
+ "ui:validate": "cd ui && bun run validate",
76
+ "prepublishOnly": "bun run validate && node scripts/sync-version.js",
77
+ "prepack": "bun run validate && node scripts/sync-version.js",
69
78
  "prepare": "husky",
70
79
  "postinstall": "node scripts/postinstall.js"
71
80
  },
72
81
  "dependencies": {
73
82
  "boxen": "^8.0.1",
74
83
  "chalk": "^5.6.2",
84
+ "chokidar": "^5.0.0",
75
85
  "cli-table3": "^0.6.5",
86
+ "express": "^4.18.2",
87
+ "get-port": "^7.0.0",
76
88
  "gradient-string": "^3.0.0",
77
89
  "listr2": "^9.0.5",
78
- "ora": "^9.0.0"
90
+ "open": "^10.1.0",
91
+ "ora": "^9.0.0",
92
+ "ws": "^8.16.0"
79
93
  },
80
94
  "devDependencies": {
81
95
  "@commitlint/cli": "^20.1.0",
82
96
  "@commitlint/config-conventional": "^20.0.0",
83
97
  "@semantic-release/changelog": "^6.0.3",
84
98
  "@semantic-release/git": "^10.0.1",
99
+ "@tailwindcss/vite": "^4.1.17",
100
+ "@types/chokidar": "^2.1.7",
101
+ "@types/express": "^4.17.21",
85
102
  "@types/node": "^20.19.25",
103
+ "@types/ws": "^8.5.10",
86
104
  "@typescript-eslint/eslint-plugin": "^8.48.0",
87
105
  "@typescript-eslint/parser": "^8.48.0",
106
+ "@vitejs/plugin-react": "^5.1.1",
88
107
  "eslint": "^9.39.1",
89
108
  "eslint-config-prettier": "^10.1.8",
90
109
  "husky": "^9.1.7",
91
110
  "mocha": "^11.7.5",
92
111
  "prettier": "^3.6.2",
93
112
  "semantic-release": "^25.0.2",
94
- "typescript": "5.3"
113
+ "typescript": "5.3",
114
+ "vite": "^7.2.4"
95
115
  }
96
116
  }
@@ -0,0 +1,39 @@
1
+ #!/bin/bash
2
+ # Pre-Release Checklist for CCS
3
+ set -euo pipefail
4
+
5
+ echo "=== Pre-Release Checklist ==="
6
+ echo ""
7
+
8
+ # 1. Version check
9
+ echo "[i] Current version: $(node -p "require('./package.json').version")"
10
+
11
+ # 2. Clean build
12
+ echo "[i] Clean build..."
13
+ rm -rf dist
14
+ bun run build:all
15
+
16
+ # 3. Bundle size
17
+ echo "[i] Bundle size check..."
18
+ node scripts/verify-bundle.js
19
+
20
+ # 4. Lint & typecheck
21
+ echo "[i] Lint & typecheck..."
22
+ bun run validate
23
+
24
+ # 5. Tests
25
+ echo "[i] Running tests..."
26
+ bun test
27
+
28
+ # 6. Help consistency check
29
+ echo "[i] Checking help text includes config command..."
30
+ if ! grep -q "ccs config" src/commands/help-command.ts; then
31
+ echo "[!] Missing config in help-command.ts"
32
+ fi
33
+
34
+ # 7. Package contents
35
+ echo "[i] Package contents..."
36
+ npm pack --dry-run 2>&1 | head -20
37
+
38
+ echo ""
39
+ echo "=== Ready for release ==="
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Verify UI bundle size is under 500KB gzipped
5
+ */
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const zlib = require('zlib');
10
+
11
+ const UI_DIR = path.join(__dirname, '../dist/ui');
12
+ const MAX_SIZE = 500 * 1024; // 500KB
13
+
14
+ function getGzipSize(filePath) {
15
+ const content = fs.readFileSync(filePath);
16
+ return zlib.gzipSync(content).length;
17
+ }
18
+
19
+ function walkDir(dir) {
20
+ let totalSize = 0;
21
+ const files = fs.readdirSync(dir, { withFileTypes: true });
22
+
23
+ for (const file of files) {
24
+ const filePath = path.join(dir, file.name);
25
+ if (file.isDirectory()) {
26
+ totalSize += walkDir(filePath);
27
+ } else {
28
+ totalSize += getGzipSize(filePath);
29
+ }
30
+ }
31
+
32
+ return totalSize;
33
+ }
34
+
35
+ if (!fs.existsSync(UI_DIR)) {
36
+ console.log('[!] dist/ui not found. Run bun run ui:build first.');
37
+ process.exit(1);
38
+ }
39
+
40
+ const totalSize = walkDir(UI_DIR);
41
+ const sizeKB = (totalSize / 1024).toFixed(1);
42
+
43
+ if (totalSize > MAX_SIZE) {
44
+ console.log(`[X] Bundle too large: ${sizeKB}KB gzipped (max: 500KB)`);
45
+ process.exit(1);
46
+ } else {
47
+ console.log(`[OK] Bundle size: ${sizeKB}KB gzipped`);
48
+ }