@ian2018cs/agenthub 0.1.0 → 0.1.2

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.
@@ -0,0 +1,220 @@
1
+ ---
2
+ name: deploy-frontend
3
+ description: 将生成的 HTML 或前端页面部署到 Docker nginx 容器。用于快速部署和预览前端项目,自动配置端口隔离,返回可访问的内网 URL。使用场景:(1) 部署刚生成的 HTML/React/Vue 等前端项目 (2) 需要让用户通过浏览器查看前端页面 (3) 需要为不同项目分配独立的访问端口 (4) 清理或列出已部署的项目
4
+ ---
5
+
6
+ # Deploy Frontend
7
+
8
+ 将前端项目部署到共享的 nginx Docker 容器,通过不同端口和配置文件实现项目隔离。
9
+
10
+ ## 配置
11
+
12
+ 默认 nginx 目录已配置为:`/home/xubuntu001/AI/nginx`
13
+
14
+ 如需修改,编辑脚本文件中的 `DEFAULT_NGINX_BASE_DIR` 变量:
15
+ - `scripts/deploy.py` 第 14 行
16
+ - `scripts/cleanup.py` 第 10 行
17
+
18
+ ## 工作原理
19
+
20
+ - **单容器架构**:所有项目共享一个 nginx 容器(节省资源)
21
+ - **配置隔离**:每个项目在 `config/conf.d/` 下有独立的 `.conf` 文件
22
+ - **端口隔离**:自动分配不同端口(从 8080 开始递增)
23
+ - **目录隔离**:每个项目的文件存放在 `html/project-{timestamp}/`
24
+
25
+ ## 部署前端项目
26
+
27
+ 使用 `scripts/deploy.py` 部署项目:
28
+
29
+ ```bash
30
+ python scripts/deploy.py <前端项目目录>
31
+ ```
32
+
33
+ **参数说明:**
34
+ - `<前端项目目录>`:包含 index.html 等前端文件的目录(必需)
35
+
36
+ 默认使用 `/home/xubuntu001/AI/nginx` 作为 nginx 基础目录,无需额外指定。
37
+
38
+ **脚本执行流程:**
39
+ 1. 生成唯一的项目 ID(project-{timestamp})
40
+ 2. 查找可用端口(从 8080 开始)
41
+ 3. 复制前端文件到 `html/{project_id}/`
42
+ 4. 在 `config/conf.d/` 创建 nginx 配置文件
43
+ 5. 重新加载 nginx(如果容器未运行则启动)
44
+ 6. 返回访问 URL(内网IP:端口)
45
+ 7. 保存部署信息到 `deployments/{project_id}.json`
46
+
47
+ **示例:**
48
+ ```bash
49
+ # 部署前端项目(使用默认 nginx 目录)
50
+ python .claude/skills/deploy-frontend/scripts/deploy.py ./my-frontend-app
51
+ ```
52
+
53
+ **输出示例:**
54
+ ```
55
+ ✅ 部署成功!
56
+ 项目 ID: project-1738151234
57
+ 端口: 8080
58
+ 访问地址: http://192.168.1.100:8080
59
+ HTML 目录: /path/to/nginx/html/project-1738151234
60
+ 配置文件: /path/to/nginx/config/conf.d/project-1738151234.conf
61
+ ```
62
+
63
+ ## 管理部署
64
+
65
+ 使用 `scripts/cleanup.py` 管理已部署的项目。
66
+
67
+ ### 列出所有部署
68
+
69
+ ```bash
70
+ python scripts/cleanup.py list
71
+ ```
72
+
73
+ ### 清理指定项目
74
+
75
+ ```bash
76
+ python scripts/cleanup.py <project_id>
77
+ ```
78
+
79
+ 会删除:
80
+ - HTML 目录
81
+ - nginx 配置文件
82
+ - 部署信息文件
83
+
84
+ 并重新加载 nginx 配置。
85
+
86
+ ### 清理所有项目
87
+
88
+ ```bash
89
+ python scripts/cleanup.py all
90
+ ```
91
+
92
+ ## 目录结构
93
+
94
+ 部署后的 nginx 基础目录结构:
95
+
96
+ ```
97
+ nginx-base/
98
+ ├── docker-compose.yml # nginx 容器配置
99
+ ├── html/ # 前端文件
100
+ │ ├── project-1738151234/ # 项目1
101
+ │ │ ├── index.html
102
+ │ │ └── ...
103
+ │ └── project-1738151456/ # 项目2
104
+ │ ├── index.html
105
+ │ └── ...
106
+ ├── config/
107
+ │ └── conf.d/ # nginx 配置
108
+ │ ├── project-1738151234.conf
109
+ │ └── project-1738151456.conf
110
+ ├── logs/ # nginx 日志
111
+ └── deployments/ # 部署信息
112
+ ├── project-1738151234.json
113
+ └── project-1738151456.json
114
+ ```
115
+
116
+ ## 使用流程
117
+
118
+ ### 典型工作流
119
+
120
+ 1. **生成前端代码**:创建 HTML/React/Vue 项目
121
+ 2. **调用部署脚本**:
122
+ ```python
123
+ from pathlib import Path
124
+ import subprocess
125
+
126
+ result = subprocess.run([
127
+ "python",
128
+ str(Path.home() / ".claude/skills/deploy-frontend/scripts/deploy.py"),
129
+ "./frontend-output"
130
+ ], capture_output=True, text=True)
131
+
132
+ print(result.stdout)
133
+ ```
134
+ 3. **提取访问 URL**:从输出中获取 URL 返回给用户
135
+ 4. **提醒用户访问**:告知用户可以通过浏览器访问该 URL
136
+
137
+ ### 在对话中使用
138
+
139
+ 当用户要求生成前端页面时:
140
+
141
+ 1. 生成前端代码(HTML/CSS/JS 等)
142
+ 2. 将文件写入临时目录
143
+ 3. 调用 deploy.py 部署
144
+ 4. 将访问 URL 返回给用户
145
+
146
+ 示例代码模式:
147
+ ```python
148
+ # 1. 创建输出目录
149
+ output_dir = Path("/tmp/frontend-{timestamp}")
150
+ output_dir.mkdir(parents=True, exist_ok=True)
151
+
152
+ # 2. 写入前端文件
153
+ (output_dir / "index.html").write_text(html_content)
154
+ (output_dir / "style.css").write_text(css_content)
155
+
156
+ # 3. 部署
157
+ deploy_script = Path.home() / ".claude/skills/deploy-frontend/scripts/deploy.py"
158
+ result = subprocess.run(
159
+ ["python", str(deploy_script), str(output_dir)],
160
+ capture_output=True,
161
+ text=True
162
+ )
163
+
164
+ # 4. 提取 URL(从输出中解析)
165
+ for line in result.stdout.split('\n'):
166
+ if line.startswith('访问地址:'):
167
+ url = line.split(':', 1)[1].strip()
168
+ print(f"您的前端页面已部署: {url}")
169
+ ```
170
+
171
+ ## 前置要求
172
+
173
+ - Docker 和 docker-compose 已安装
174
+ - nginx 基础目录位于 `/home/xubuntu001/AI/nginx`
175
+ - docker-compose.yml 使用 `network_mode: host`
176
+ - Python 3.6+
177
+
178
+ ## Docker 权限处理
179
+
180
+ 脚本已内置自动权限处理机制:
181
+
182
+ 1. **自动检测权限**:脚本会首先尝试直接运行 docker 命令
183
+ 2. **自动降级处理**:如果遇到权限错误,会自动使用 `sg docker -c` 运行
184
+ 3. **无需手动干预**:用户无需关心是否在 docker 组中
185
+
186
+ **注意**:如果您刚刚被添加到 docker 组,可能需要重新登录或使用以下命令激活组权限:
187
+ ```bash
188
+ newgrp docker
189
+ ```
190
+
191
+ ## 更新日志
192
+
193
+ ### v1.1 (2026-01-30)
194
+ - ✅ 修复正则表达式转义警告
195
+ - ✅ 添加自动 Docker 权限处理(`run_docker_command` 函数)
196
+ - ✅ 使用 `docker compose` 替代旧版 `docker-compose` 命令
197
+ - ✅ deploy.py 和 cleanup.py 都支持自动权限处理
198
+
199
+ ### v1.0
200
+ - 初始版本:支持多项目部署和端口隔离
201
+
202
+ ## 故障排查
203
+
204
+ **容器未启动**:脚本会自动执行 `docker compose up -d`
205
+
206
+ **端口冲突**:脚本会自动查找可用端口(8080-8179 范围)
207
+
208
+ **权限问题**:脚本会自动使用 `sg docker -c` 处理权限问题
209
+
210
+ **配置未生效**:手动重新加载 nginx:
211
+ ```bash
212
+ docker exec nginx-web nginx -s reload
213
+ # 或如果有权限问题:
214
+ sg docker -c "docker exec nginx-web nginx -s reload"
215
+ ```
216
+
217
+ **查看 nginx 日志**:
218
+ ```bash
219
+ docker logs nginx-web
220
+ ```
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 清理部署的前端项目
4
+ """
5
+ import subprocess
6
+ import sys
7
+ from pathlib import Path
8
+ import shutil
9
+ import json
10
+
11
+ # 默认 nginx 基础目录(与 deploy.py 保持一致)
12
+ DEFAULT_NGINX_BASE_DIR = "/home/xubuntu001/AI/nginx"
13
+
14
+ def run_docker_command(cmd, **kwargs):
15
+ """
16
+ 运行 docker 命令,自动处理权限问题
17
+
18
+ Args:
19
+ cmd: 命令列表
20
+ **kwargs: 传递给 subprocess.run 的其他参数
21
+
22
+ Returns:
23
+ subprocess.CompletedProcess 对象
24
+ """
25
+ try:
26
+ # 首先尝试直接运行
27
+ return subprocess.run(cmd, **kwargs)
28
+ except (subprocess.CalledProcessError, PermissionError) as e:
29
+ # 如果失败,尝试使用 sg docker -c 运行
30
+ if 'check' in kwargs:
31
+ del kwargs['check'] # sg 会处理 check
32
+
33
+ # 将命令转换为 sg docker -c 格式
34
+ cmd_str = ' '.join(str(arg) for arg in cmd)
35
+ sg_cmd = ['sg', 'docker', '-c', cmd_str]
36
+
37
+ return subprocess.run(sg_cmd, **kwargs)
38
+
39
+ def cleanup_deployment(project_id, nginx_base_dir=None):
40
+ """
41
+ 清理指定部署
42
+
43
+ Args:
44
+ project_id: 项目 ID
45
+ nginx_base_dir: nginx 基础目录,默认为 DEFAULT_NGINX_BASE_DIR
46
+ """
47
+ if nginx_base_dir is None:
48
+ nginx_base_dir = Path(DEFAULT_NGINX_BASE_DIR)
49
+ else:
50
+ nginx_base_dir = Path(nginx_base_dir)
51
+
52
+ deployments_dir = nginx_base_dir / "deployments"
53
+ info_file = deployments_dir / f"{project_id}.json"
54
+
55
+ if not info_file.exists():
56
+ print(f"❌ 项目不存在: {project_id}")
57
+ return False
58
+
59
+ try:
60
+ with open(info_file) as f:
61
+ info = json.load(f)
62
+
63
+ # 删除 HTML 目录
64
+ html_dir = Path(info['html_dir'])
65
+ if html_dir.exists():
66
+ print(f"删除 HTML 目录: {html_dir}")
67
+ shutil.rmtree(html_dir)
68
+
69
+ # 删除 nginx 配置文件
70
+ conf_file = Path(info['conf_file'])
71
+ if conf_file.exists():
72
+ print(f"删除配置文件: {conf_file}")
73
+ conf_file.unlink()
74
+
75
+ # 重新加载 nginx
76
+ print("重新加载 nginx 配置...")
77
+ run_docker_command(
78
+ ["docker", "exec", "nginx-web", "nginx", "-s", "reload"],
79
+ check=True
80
+ )
81
+
82
+ # 删除部署信息文件
83
+ info_file.unlink()
84
+
85
+ print(f"✅ 清理完成: {project_id}")
86
+ return True
87
+
88
+ except Exception as e:
89
+ print(f"❌ 清理失败: {e}")
90
+ import traceback
91
+ traceback.print_exc()
92
+ return False
93
+
94
+ def list_deployments(nginx_base_dir=None):
95
+ """列出所有部署"""
96
+ if nginx_base_dir is None:
97
+ nginx_base_dir = Path(DEFAULT_NGINX_BASE_DIR)
98
+ else:
99
+ nginx_base_dir = Path(nginx_base_dir)
100
+
101
+ deployments_dir = nginx_base_dir / "deployments"
102
+
103
+ if not deployments_dir.exists() or not list(deployments_dir.glob("*.json")):
104
+ print("没有找到部署")
105
+ return
106
+
107
+ print("\n当前部署:")
108
+ print("-" * 80)
109
+
110
+ for info_file in sorted(deployments_dir.glob("*.json")):
111
+ with open(info_file) as f:
112
+ info = json.load(f)
113
+
114
+ print(f"项目 ID: {info['project_id']}")
115
+ print(f" 部署时间: {info.get('deployed_at', 'N/A')}")
116
+ print(f" 访问地址: {info['url']}")
117
+ print(f" HTML 目录: {info['html_dir']}")
118
+ print()
119
+
120
+ def cleanup_all(nginx_base_dir=None):
121
+ """清理所有部署"""
122
+ if nginx_base_dir is None:
123
+ nginx_base_dir = Path(DEFAULT_NGINX_BASE_DIR)
124
+ else:
125
+ nginx_base_dir = Path(nginx_base_dir)
126
+
127
+ deployments_dir = nginx_base_dir / "deployments"
128
+
129
+ if not deployments_dir.exists():
130
+ print("没有找到部署")
131
+ return
132
+
133
+ for info_file in deployments_dir.glob("*.json"):
134
+ project_id = info_file.stem
135
+ print(f"\n清理项目: {project_id}")
136
+ cleanup_deployment(project_id, nginx_base_dir)
137
+
138
+ def main():
139
+ if len(sys.argv) < 2:
140
+ print("用法:")
141
+ print(f" 默认 nginx 目录: {DEFAULT_NGINX_BASE_DIR}")
142
+ print(" 列出所有部署: python cleanup.py list [nginx基础目录]")
143
+ print(" 清理指定项目: python cleanup.py <project_id> [nginx基础目录]")
144
+ print(" 清理所有项目: python cleanup.py all [nginx基础目录]")
145
+ sys.exit(1)
146
+
147
+ command = sys.argv[1]
148
+ nginx_base_dir = sys.argv[2] if len(sys.argv) > 2 else None
149
+
150
+ if command == "list":
151
+ list_deployments(nginx_base_dir)
152
+ elif command == "all":
153
+ cleanup_all(nginx_base_dir)
154
+ else:
155
+ cleanup_deployment(command, nginx_base_dir)
156
+
157
+ if __name__ == "__main__":
158
+ main()
@@ -0,0 +1,216 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 部署前端项目到共享的 nginx 容器(通过 conf.d 配置隔离)
4
+ """
5
+ import os
6
+ import sys
7
+ import subprocess
8
+ import socket
9
+ from pathlib import Path
10
+ import shutil
11
+ import json
12
+ import time
13
+
14
+ # 默认 nginx 基础目录(可在这里修改)
15
+ DEFAULT_NGINX_BASE_DIR = "/home/xubuntu001/AI/nginx"
16
+
17
+ def run_docker_command(cmd, **kwargs):
18
+ """
19
+ 运行 docker 命令,自动处理权限问题
20
+
21
+ Args:
22
+ cmd: 命令列表
23
+ **kwargs: 传递给 subprocess.run 的其他参数
24
+
25
+ Returns:
26
+ subprocess.CompletedProcess 对象
27
+ """
28
+ try:
29
+ # 首先尝试直接运行
30
+ return subprocess.run(cmd, **kwargs)
31
+ except (subprocess.CalledProcessError, PermissionError) as e:
32
+ # 如果失败,尝试使用 sg docker -c 运行
33
+ if 'check' in kwargs:
34
+ del kwargs['check'] # sg 会处理 check
35
+
36
+ # 将命令转换为 sg docker -c 格式
37
+ cmd_str = ' '.join(str(arg) for arg in cmd)
38
+ sg_cmd = ['sg', 'docker', '-c', cmd_str]
39
+
40
+ return subprocess.run(sg_cmd, **kwargs)
41
+
42
+ def find_available_port(start_port=8080, max_attempts=100):
43
+ """查找可用端口"""
44
+ for port in range(start_port, start_port + max_attempts):
45
+ try:
46
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
47
+ s.bind(('', port))
48
+ return port
49
+ except OSError:
50
+ continue
51
+ raise RuntimeError(f"无法在 {start_port}-{start_port + max_attempts} 范围内找到可用端口")
52
+
53
+ def get_local_ip():
54
+ """获取本机内网 IP"""
55
+ try:
56
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
57
+ s.connect(("8.8.8.8", 80))
58
+ ip = s.getsockname()[0]
59
+ s.close()
60
+ return ip
61
+ except Exception:
62
+ return "localhost"
63
+
64
+ def generate_project_id():
65
+ """生成项目 ID"""
66
+ return f"project-{int(time.time())}"
67
+
68
+ def create_nginx_conf(project_id, port, nginx_base_dir):
69
+ """创建项目的 nginx 配置文件"""
70
+ config_content = f"""server {{
71
+ listen {port};
72
+ server_name localhost;
73
+
74
+ root /usr/share/nginx/html/{project_id};
75
+ index index.html index.htm;
76
+
77
+ location / {{
78
+ try_files $uri $uri/ /index.html;
79
+ }}
80
+
81
+ # 启用 gzip 压缩
82
+ gzip on;
83
+ gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
84
+
85
+ # 缓存静态资源
86
+ location ~* \\.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {{
87
+ expires 1y;
88
+ add_header Cache-Control "public, immutable";
89
+ }}
90
+ }}
91
+ """
92
+ conf_dir = nginx_base_dir / "config" / "conf.d"
93
+ conf_dir.mkdir(parents=True, exist_ok=True)
94
+
95
+ conf_file = conf_dir / f"{project_id}.conf"
96
+ conf_file.write_text(config_content)
97
+ return conf_file
98
+
99
+ def reload_nginx(nginx_base_dir):
100
+ """重新加载 nginx 配置"""
101
+ compose_file = nginx_base_dir / "docker-compose.yml"
102
+ if not compose_file.exists():
103
+ raise RuntimeError(f"docker-compose.yml 不存在: {compose_file}")
104
+
105
+ # 检查容器是否运行
106
+ result = run_docker_command(
107
+ ["docker", "ps", "--filter", "name=nginx-web", "--format", "{{.Names}}"],
108
+ capture_output=True,
109
+ text=True,
110
+ cwd=nginx_base_dir
111
+ )
112
+
113
+ if "nginx-web" not in result.stdout:
114
+ # 容器未运行,启动它
115
+ print("启动 nginx 容器...")
116
+ run_docker_command(
117
+ ["docker", "compose", "up", "-d"],
118
+ cwd=nginx_base_dir,
119
+ check=True
120
+ )
121
+ else:
122
+ # 重新加载配置
123
+ print("重新加载 nginx 配置...")
124
+ run_docker_command(
125
+ ["docker", "exec", "nginx-web", "nginx", "-s", "reload"],
126
+ check=True
127
+ )
128
+
129
+ def deploy_frontend(source_dir, nginx_base_dir=None):
130
+ """
131
+ 部署前端项目
132
+
133
+ Args:
134
+ source_dir: 源代码目录路径
135
+ nginx_base_dir: nginx docker-compose 所在目录,默认为 DEFAULT_NGINX_BASE_DIR
136
+
137
+ Returns:
138
+ dict: 包含部署信息的字典
139
+ """
140
+ source_path = Path(source_dir).resolve()
141
+ if not source_path.exists():
142
+ raise ValueError(f"源目录不存在: {source_dir}")
143
+
144
+ # 设置 nginx 基础目录
145
+ if nginx_base_dir is None:
146
+ nginx_base_dir = Path(DEFAULT_NGINX_BASE_DIR)
147
+ else:
148
+ nginx_base_dir = Path(nginx_base_dir).resolve()
149
+
150
+ # 生成项目信息
151
+ project_id = generate_project_id()
152
+
153
+ # 查找可用端口
154
+ port = find_available_port()
155
+
156
+ # 复制前端文件到 html/project-id/
157
+ html_dir = nginx_base_dir / "html" / project_id
158
+ if html_dir.exists():
159
+ shutil.rmtree(html_dir)
160
+ shutil.copytree(source_path, html_dir)
161
+
162
+ # 创建 nginx 配置
163
+ conf_file = create_nginx_conf(project_id, port, nginx_base_dir)
164
+
165
+ # 重新加载 nginx
166
+ reload_nginx(nginx_base_dir)
167
+
168
+ # 获取访问 URL
169
+ local_ip = get_local_ip()
170
+ url = f"http://{local_ip}:{port}"
171
+
172
+ # 保存部署信息到部署目录
173
+ deployments_dir = nginx_base_dir / "deployments"
174
+ deployments_dir.mkdir(exist_ok=True)
175
+
176
+ deploy_info = {
177
+ "project_id": project_id,
178
+ "port": port,
179
+ "url": url,
180
+ "html_dir": str(html_dir),
181
+ "conf_file": str(conf_file),
182
+ "source_dir": str(source_path),
183
+ "deployed_at": time.strftime("%Y-%m-%d %H:%M:%S")
184
+ }
185
+
186
+ info_file = deployments_dir / f"{project_id}.json"
187
+ info_file.write_text(json.dumps(deploy_info, indent=2, ensure_ascii=False))
188
+
189
+ return deploy_info
190
+
191
+ def main():
192
+ if len(sys.argv) < 2:
193
+ print("用法: python deploy.py <前端项目目录> [nginx基础目录]")
194
+ print(f"默认 nginx 目录: {DEFAULT_NGINX_BASE_DIR}")
195
+ print("示例: python deploy.py ./my-app")
196
+ sys.exit(1)
197
+
198
+ source_dir = sys.argv[1]
199
+ nginx_base_dir = sys.argv[2] if len(sys.argv) > 2 else None
200
+
201
+ try:
202
+ info = deploy_frontend(source_dir, nginx_base_dir)
203
+ print(f"\n✅ 部署成功!")
204
+ print(f"项目 ID: {info['project_id']}")
205
+ print(f"端口: {info['port']}")
206
+ print(f"访问地址: {info['url']}")
207
+ print(f"HTML 目录: {info['html_dir']}")
208
+ print(f"配置文件: {info['conf_file']}")
209
+ except Exception as e:
210
+ print(f"\n❌ 部署失败: {e}")
211
+ import traceback
212
+ traceback.print_exc()
213
+ sys.exit(1)
214
+
215
+ if __name__ == "__main__":
216
+ main()
package/server/cli.js CHANGED
@@ -74,10 +74,11 @@ function loadEnvFile() {
74
74
  // Get the database path (same logic as db.js)
75
75
  function getDatabasePath() {
76
76
  loadEnvFile();
77
- // Resolve relative paths from project root (one level up from server/)
77
+ const projectRoot = path.join(__dirname, '..');
78
+ const dataDir = process.env.DATA_DIR || path.join(projectRoot, 'data');
78
79
  return process.env.DATABASE_PATH
79
- ? path.resolve(path.join(__dirname, '..'), process.env.DATABASE_PATH)
80
- : path.join(__dirname, 'database', 'auth.db');
80
+ ? path.resolve(projectRoot, process.env.DATABASE_PATH)
81
+ : path.join(dataDir, 'auth.db');
81
82
  }
82
83
 
83
84
  // Get the installation directory
@@ -205,7 +206,7 @@ function isNewerVersion(v1, v2) {
205
206
  async function checkForUpdates(silent = false) {
206
207
  try {
207
208
  const { execSync } = await import('child_process');
208
- const latestVersion = execSync('npm show @siteboon/claude-code-ui version', { encoding: 'utf8' }).trim();
209
+ const latestVersion = execSync('npm show @ian2018cs/agenthub version', { encoding: 'utf8' }).trim();
209
210
  const currentVersion = packageJson.version;
210
211
 
211
212
  if (isNewerVersion(latestVersion, currentVersion)) {
@@ -238,11 +239,11 @@ async function updatePackage() {
238
239
  }
239
240
 
240
241
  console.log(`${c.info('[INFO]')} Updating from ${currentVersion} to ${latestVersion}...`);
241
- execSync('npm update -g @siteboon/claude-code-ui', { stdio: 'inherit' });
242
+ execSync('npm update -g @ian2018cs/agenthub', { stdio: 'inherit' });
242
243
  console.log(`${c.ok('[OK]')} Update complete! Restart cloudcli to use the new version.`);
243
244
  } catch (e) {
244
245
  console.error(`${c.error('[ERROR]')} Update failed: ${e.message}`);
245
- console.log(`${c.tip('[TIP]')} Try running manually: npm update -g @siteboon/claude-code-ui`);
246
+ console.log(`${c.tip('[TIP]')} Try running manually: npm update -g @ian2018cs/agenthub`);
246
247
  }
247
248
  }
248
249