fogact 1.1.3

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 (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +244 -0
  3. package/README.zh-CN.md +244 -0
  4. package/bin/cli.js +9 -0
  5. package/bin/web-server.js +1434 -0
  6. package/config/upstream.example.json +14 -0
  7. package/frontend/activate.html +249 -0
  8. package/frontend/admin/admin-panel-v2.js +1899 -0
  9. package/frontend/admin/index.html +705 -0
  10. package/frontend/assets/market-ui.css +1876 -0
  11. package/frontend/color-test.html +136 -0
  12. package/frontend/index.html +191 -0
  13. package/frontend/user/assets/AnnouncementDetail-Dvxmwz0A.js +12 -0
  14. package/frontend/user/assets/Announcements-CS1tF2mx.js +11 -0
  15. package/frontend/user/assets/CardBind-CsCxihhP.js +21 -0
  16. package/frontend/user/assets/CardContent.vue_vue_type_script_setup_true_lang-D2L-uqSl.js +1 -0
  17. package/frontend/user/assets/CardDescription.vue_vue_type_script_setup_true_lang-D-v5Pl7F.js +1 -0
  18. package/frontend/user/assets/CardTitle.vue_vue_type_script_setup_true_lang-a0CCN6D5.js +1 -0
  19. package/frontend/user/assets/Dashboard-rPsmltm5.js +51 -0
  20. package/frontend/user/assets/DashboardLayout-BUCWGlXC.css +1 -0
  21. package/frontend/user/assets/DashboardLayout-DDkxHYFj.js +80 -0
  22. package/frontend/user/assets/Input.vue_vue_type_script_setup_true_lang-B0SyPmYb.js +6 -0
  23. package/frontend/user/assets/Label.vue_vue_type_script_setup_true_lang-CxYORSgN.js +1 -0
  24. package/frontend/user/assets/Progress.vue_vue_type_script_setup_true_lang-2_QbPsEQ.js +1 -0
  25. package/frontend/user/assets/QuotaPack-B_tJ7Psm.js +6 -0
  26. package/frontend/user/assets/Renewal-BSDhDmwv.js +6 -0
  27. package/frontend/user/assets/ScrollArea.vue_vue_type_script_setup_true_lang-DMYwcfpz.js +1 -0
  28. package/frontend/user/assets/Separator.vue_vue_type_script_setup_true_lang-Ckg8EXj_.js +1 -0
  29. package/frontend/user/assets/Settings-CBdAa3lw.js +11 -0
  30. package/frontend/user/assets/TooltipTrigger.vue_vue_type_script_setup_true_lang-DtSBjzGo.js +16 -0
  31. package/frontend/user/assets/Welcome-7IfzEli4.css +1 -0
  32. package/frontend/user/assets/Welcome-Dtfp6oER.js +1 -0
  33. package/frontend/user/assets/_plugin-vue_export-helper-5cjT4u0R.js +16 -0
  34. package/frontend/user/assets/activity-wYWtyqTJ.js +6 -0
  35. package/frontend/user/assets/announcement-35mOnjRL.js +16 -0
  36. package/frontend/user/assets/calendar-BFNuCata.js +6 -0
  37. package/frontend/user/assets/chart-vendor-CULJE59K.js +37 -0
  38. package/frontend/user/assets/chevron-down-kDbuU1Py.js +6 -0
  39. package/frontend/user/assets/chevron-right-BayASIm0.js +6 -0
  40. package/frontend/user/assets/eye-CY62vip0.js +6 -0
  41. package/frontend/user/assets/gauge-C5NQ-mV8.js +6 -0
  42. package/frontend/user/assets/index-B8QSyYhS.css +1 -0
  43. package/frontend/user/assets/index-Da98HOxL.js +91 -0
  44. package/frontend/user/assets/link-2-DT5R5nGO.js +6 -0
  45. package/frontend/user/assets/package-rUbExUEn.js +6 -0
  46. package/frontend/user/assets/plus-CQc6C8wG.js +11 -0
  47. package/frontend/user/assets/refresh-cw-Y9hCloPL.js +6 -0
  48. package/frontend/user/assets/useUserPageRefresh-BYZvpNR9.js +1 -0
  49. package/frontend/user/assets/zap-l5zbZqrM.js +11 -0
  50. package/frontend/user/index.html +67 -0
  51. package/install.sh +402 -0
  52. package/lib/commands/activate.js +144 -0
  53. package/lib/commands/restore.js +102 -0
  54. package/lib/commands/test.js +40 -0
  55. package/lib/config/claude.js +81 -0
  56. package/lib/config/codex.js +164 -0
  57. package/lib/config/upstream.js +79 -0
  58. package/lib/index.js +164 -0
  59. package/lib/platforms/claude-code.js +35 -0
  60. package/lib/platforms/codex-cli.js +35 -0
  61. package/lib/platforms/editor-codex.js +138 -0
  62. package/lib/platforms/index.js +32 -0
  63. package/lib/platforms/openclaw.js +118 -0
  64. package/lib/platforms/opencode.js +89 -0
  65. package/lib/services/activation-orchestrator.js +666 -0
  66. package/lib/services/backup-service.js +162 -0
  67. package/lib/services/cliproxy-api.js +174 -0
  68. package/lib/services/database.js +461 -0
  69. package/lib/services/newapi.js +97 -0
  70. package/lib/services/node-service.js +49 -0
  71. package/lib/utils/json-file.js +33 -0
  72. package/package.json +53 -0
@@ -0,0 +1,6 @@
1
+ import{c as e}from"./index-Da98HOxL.js";/**
2
+ * @license lucide-vue-next v0.468.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const n=e("Link2Icon",[["path",{d:"M9 17H7A5 5 0 0 1 7 7h2",key:"8i5ue5"}],["path",{d:"M15 7h2a5 5 0 1 1 0 10h-2",key:"1b9ql8"}],["line",{x1:"8",x2:"16",y1:"12",y2:"12",key:"1jonct"}]]);export{n as L};
@@ -0,0 +1,6 @@
1
+ import{c as a}from"./index-Da98HOxL.js";/**
2
+ * @license lucide-vue-next v0.468.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const c=a("PackageIcon",[["path",{d:"M11 21.73a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73z",key:"1a0edw"}],["path",{d:"M12 22V12",key:"d0xqtd"}],["path",{d:"m3.3 7 7.703 4.734a2 2 0 0 0 1.994 0L20.7 7",key:"yx3hmr"}],["path",{d:"m7.5 4.27 9 5.15",key:"1c824w"}]]);export{c as P};
@@ -0,0 +1,11 @@
1
+ import{c as a}from"./index-Da98HOxL.js";/**
2
+ * @license lucide-vue-next v0.468.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const s=a("LayersIcon",[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z",key:"zw3jo"}],["path",{d:"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12",key:"1wduqc"}],["path",{d:"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17",key:"kqbvx6"}]]);/**
7
+ * @license lucide-vue-next v0.468.0 - ISC
8
+ *
9
+ * This source code is licensed under the ISC license.
10
+ * See the LICENSE file in the root directory of this source tree.
11
+ */const t=a("PlusIcon",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);export{s as L,t as P};
@@ -0,0 +1,6 @@
1
+ import{c as e}from"./index-Da98HOxL.js";/**
2
+ * @license lucide-vue-next v0.468.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const t=e("RefreshCwIcon",[["path",{d:"M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8",key:"v9h5vc"}],["path",{d:"M21 3v5h-5",key:"1q7to0"}],["path",{d:"M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16",key:"3uifl3"}],["path",{d:"M8 16H3v5",key:"1cv678"}]]);export{t as R};
@@ -0,0 +1 @@
1
+ import{u as c}from"./index-Da98HOxL.js";import{w as i,L as d,f as h}from"./chart-vendor-CULJE59K.js";const o=Symbol("user-page-refresh");function v(l,e={}){const n=c(),f=h(o,null);let t=0;async function a(r="custom"){var u;const s=++t;(u=e.resetBeforeLoad)==null||u.call(e,r),await l({reason:r,runId:s,isLatest:()=>s===t})}return e.reloadOnKeyChange!==!1&&i(()=>n.activeKeyId,(r,s)=>{r!==s&&a("active-key-change")}),e.reloadOnManualRefresh!==!1&&f&&i(()=>f.refreshTick.value,()=>{a("manual-refresh")}),e.immediate!==!1&&d(()=>{a("initial")}),{reload:a}}export{v as a,o as u};
@@ -0,0 +1,11 @@
1
+ import{c as a}from"./index-Da98HOxL.js";/**
2
+ * @license lucide-vue-next v0.468.0 - ISC
3
+ *
4
+ * This source code is licensed under the ISC license.
5
+ * See the LICENSE file in the root directory of this source tree.
6
+ */const e=a("ShieldIcon",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}]]);/**
7
+ * @license lucide-vue-next v0.468.0 - ISC
8
+ *
9
+ * This source code is licensed under the ISC license.
10
+ * See the LICENSE file in the root directory of this source tree.
11
+ */const o=a("ZapIcon",[["path",{d:"M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z",key:"1xq2db"}]]);export{e as S,o as Z};
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN" data-theme="silk">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link
6
+ rel="icon"
7
+ type="image/svg+xml"
8
+ href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z'%3E%3C/path%3E%3C/svg%3E"
9
+ />
10
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
11
+ <title>CLIProxy - API 使用监控</title>
12
+ <meta name="description" content="CLIProxy API 使用量监控平台" />
13
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
14
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
15
+ <link href="https://fonts.googleapis.com/css2?family=Manrope:wght@600;700;800&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
16
+ <script>
17
+ // 阻塞式主题初始化 — 防止 FOUC + 设置内联背景色
18
+ ;(function () {
19
+ var t = localStorage.getItem('yunyi_user_theme') || 'system'
20
+ var d = t === 'dark' || (t === 'system' && matchMedia('(prefers-color-scheme: dark)').matches)
21
+ var el = document.documentElement
22
+ if (d) {
23
+ el.classList.add('dark')
24
+ el.style.colorScheme = 'dark'
25
+ el.style.backgroundColor = 'hsl(240, 10%, 3.9%)'
26
+ } else {
27
+ el.style.backgroundColor = 'hsl(210, 40%, 98%)'
28
+ }
29
+ })()
30
+ </script>
31
+ <script type="module" crossorigin src="assets/index-Da98HOxL.js"></script>
32
+ <link rel="modulepreload" crossorigin href="assets/chart-vendor-CULJE59K.js">
33
+ <link rel="stylesheet" crossorigin href="assets/index-B8QSyYhS.css">
34
+ <link rel="stylesheet" href="/assets/market-ui.css?v=20260616-upstream2" />
35
+ </head>
36
+ <body class="market-user min-h-screen">
37
+ <span class="market-mouse-light" aria-hidden="true"></span>
38
+ <div id="app">
39
+ <!-- 纯 CSS 加载占位,Vue 挂载后自动替换 -->
40
+ <div style="display:flex;align-items:center;justify-content:center;height:100vh;opacity:0.4">
41
+ <svg width="24" height="24" viewBox="0 0 24 24" style="animation:_s .8s linear infinite">
42
+ <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" fill="none"
43
+ stroke-dasharray="31.4 31.4" stroke-linecap="round"/>
44
+ </svg>
45
+ </div>
46
+ <style>@keyframes _s{to{transform:rotate(360deg)}}</style>
47
+ </div>
48
+ <script>
49
+ let marketPointerRaf = 0
50
+ window.addEventListener('load', function () {
51
+ requestAnimationFrame(function () {
52
+ document.body.classList.add('motion-ready')
53
+ })
54
+ }, { once: true })
55
+
56
+ window.addEventListener('pointermove', function (event) {
57
+ if (marketPointerRaf) return
58
+ marketPointerRaf = requestAnimationFrame(function () {
59
+ document.body.classList.add('is-pointer-active')
60
+ document.body.style.setProperty('--cursor-x', event.clientX + 'px')
61
+ document.body.style.setProperty('--cursor-y', event.clientY + 'px')
62
+ marketPointerRaf = 0
63
+ })
64
+ })
65
+ </script>
66
+ </body>
67
+ </html>
package/install.sh ADDED
@@ -0,0 +1,402 @@
1
+ #!/bin/sh
2
+ set -eu
3
+
4
+ PACKAGE_NAME="${FOGIDC_PACKAGE:-fogact}"
5
+ GITHUB_REPO="${FOGIDC_GITHUB_REPO:-FogMaly/cliproxy-activator}"
6
+ GIT_REF="${FOGIDC_GIT_REF:-main}"
7
+ INSTALL_METHOD="${FOGIDC_INSTALL_METHOD:-npm}"
8
+ INSTALL_DIR="${FOGIDC_INSTALL_DIR:-}"
9
+ SERVICE="${FOGIDC_SERVICE:-}"
10
+ CODE="${FOGIDC_CODE:-}"
11
+ API_KEY="${NEWAPI_API_KEY:-${FOGIDC_API_KEY:-}}"
12
+ BASE_URL="${NEWAPI_BASE_URL:-${FOGIDC_BASE_URL:-}}"
13
+ CLIPROXY_BASE="${CLIPROXY_API_BASE:-}"
14
+ PLATFORMS="${FOGIDC_PLATFORMS:-}"
15
+ RUN_WEB="${FOGIDC_RUN_WEB:-0}"
16
+ WEB_PORT="${PORT:-${FOGIDC_WEB_PORT:-34020}}"
17
+ ADMIN_PASSWORD_VALUE="${ADMIN_PASSWORD:-${FOGIDC_ADMIN_PASSWORD:-}}"
18
+ SKIP_VERIFY="${FOGIDC_SKIP_VERIFY:-0}"
19
+ ALL_PLATFORMS="${FOGIDC_ALL:-0}"
20
+ NO_ACTIVATE="${FOGIDC_NO_ACTIVATE:-0}"
21
+ NO_REDEEM="${FOGIDC_NO_REDEEM:-0}"
22
+ CREATE_SYSTEMD="${FOGIDC_SYSTEMD:-0}"
23
+
24
+ log() { printf '%s\n' "==> $*"; }
25
+ warn() { printf '%s\n' "WARN: $*" >&2; }
26
+ fail() { printf '%s\n' "ERROR: $*" >&2; exit 1; }
27
+ has() { command -v "$1" >/dev/null 2>&1; }
28
+ is_root() { [ "$(id -u 2>/dev/null || echo 1)" = "0" ]; }
29
+
30
+ usage() {
31
+ cat <<'EOF'
32
+ FogAct bootstrap installer
33
+
34
+ Usage:
35
+ curl -fsSL https://raw.githubusercontent.com/FogMaly/cliproxy-activator/main/install.sh | sh
36
+ curl -fsSL https://raw.githubusercontent.com/FogMaly/cliproxy-activator/main/install.sh | sh -s -- --service codex --code YOUR_CODE
37
+
38
+ Options:
39
+ --service <codex|claude> Activate a specific service after install
40
+ --code <code> Activation / redeem code
41
+ --api-key <key> NewAPI key for direct activation
42
+ --base-url <url> NewAPI base URL for direct activation
43
+ --cliproxy-api-base <url> Activation backend URL for code mode
44
+ --platforms <ids> Comma-separated target platform ids
45
+ --all Configure all creatable optional platforms
46
+ --skip-verify Skip NewAPI /v1/models verification
47
+ --no-redeem Do not mark activation code as redeemed
48
+ --no-activate Install only
49
+ --web Start local Web UI after install
50
+ --systemd Create and start a systemd Web UI service
51
+ --method <npm|github> Install from npm package or GitHub source
52
+ --install-dir <path> GitHub source install directory
53
+ -h, --help Show help
54
+
55
+ Environment variables mirror the options:
56
+ FOGIDC_PACKAGE=fogact, FOGIDC_SERVICE, FOGIDC_CODE, NEWAPI_BASE_URL, NEWAPI_API_KEY,
57
+ CLIPROXY_API_BASE, FOGIDC_PLATFORMS, FOGIDC_ALL=1,
58
+ FOGIDC_SKIP_VERIFY=1, FOGIDC_RUN_WEB=1, FOGIDC_SYSTEMD=1
59
+ EOF
60
+ }
61
+
62
+ while [ "$#" -gt 0 ]; do
63
+ case "$1" in
64
+ --service) SERVICE="${2:-}"; shift 2 ;;
65
+ --code) CODE="${2:-}"; shift 2 ;;
66
+ --api-key) API_KEY="${2:-}"; shift 2 ;;
67
+ --base-url) BASE_URL="${2:-}"; shift 2 ;;
68
+ --cliproxy-api-base) CLIPROXY_BASE="${2:-}"; shift 2 ;;
69
+ --platforms) PLATFORMS="${2:-}"; shift 2 ;;
70
+ --all) ALL_PLATFORMS=1; shift ;;
71
+ --skip-verify) SKIP_VERIFY=1; shift ;;
72
+ --no-redeem) NO_REDEEM=1; shift ;;
73
+ --no-activate) NO_ACTIVATE=1; shift ;;
74
+ --web) RUN_WEB=1; shift ;;
75
+ --systemd) CREATE_SYSTEMD=1; RUN_WEB=1; shift ;;
76
+ --method) INSTALL_METHOD="${2:-}"; shift 2 ;;
77
+ --install-dir) INSTALL_DIR="${2:-}"; shift 2 ;;
78
+ -h|--help) usage; exit 0 ;;
79
+ *) fail "Unknown option: $1" ;;
80
+ esac
81
+ done
82
+
83
+ run_sudo() {
84
+ if is_root; then
85
+ "$@"
86
+ elif has sudo; then
87
+ sudo "$@"
88
+ else
89
+ fail "Need root or sudo to install missing system packages. Install Node.js 16+ manually, then rerun."
90
+ fi
91
+ }
92
+
93
+ node_major() {
94
+ node -p "Number(process.versions.node.split('.')[0])" 2>/dev/null || echo 0
95
+ }
96
+
97
+ node_ok() {
98
+ has node && has npm && [ "$(node_major)" -ge 16 ]
99
+ }
100
+
101
+ install_node_system() {
102
+ log "Node.js 16+ not found; installing Node.js"
103
+
104
+ if has apt-get; then
105
+ run_sudo apt-get update
106
+ run_sudo apt-get install -y ca-certificates curl gnupg
107
+ if has curl; then
108
+ if curl -fsSL https://deb.nodesource.com/setup_20.x | run_sudo bash -; then
109
+ run_sudo apt-get install -y nodejs
110
+ else
111
+ warn "NodeSource setup failed; falling back to distro nodejs/npm"
112
+ run_sudo apt-get install -y nodejs npm
113
+ fi
114
+ else
115
+ run_sudo apt-get install -y nodejs npm
116
+ fi
117
+ return
118
+ fi
119
+
120
+ if has dnf; then
121
+ run_sudo dnf install -y ca-certificates curl
122
+ if has curl && curl -fsSL https://rpm.nodesource.com/setup_20.x | run_sudo bash -; then
123
+ run_sudo dnf install -y nodejs
124
+ else
125
+ warn "NodeSource setup failed; falling back to distro nodejs/npm"
126
+ run_sudo dnf install -y nodejs npm
127
+ fi
128
+ return
129
+ fi
130
+
131
+ if has yum; then
132
+ run_sudo yum install -y ca-certificates curl
133
+ if has curl && curl -fsSL https://rpm.nodesource.com/setup_20.x | run_sudo bash -; then
134
+ run_sudo yum install -y nodejs
135
+ else
136
+ warn "NodeSource setup failed; falling back to distro nodejs/npm"
137
+ run_sudo yum install -y nodejs npm
138
+ fi
139
+ return
140
+ fi
141
+
142
+ if has apk; then
143
+ run_sudo apk add --no-cache nodejs npm
144
+ return
145
+ fi
146
+
147
+ if has pacman; then
148
+ run_sudo pacman -Sy --noconfirm nodejs npm
149
+ return
150
+ fi
151
+
152
+ if has zypper; then
153
+ run_sudo zypper --non-interactive install nodejs20 npm20 || run_sudo zypper --non-interactive install nodejs npm
154
+ return
155
+ fi
156
+
157
+ if has brew; then
158
+ brew install node
159
+ return
160
+ fi
161
+
162
+ fail "Unsupported OS package manager. Install Node.js 16+ and npm, then rerun."
163
+ }
164
+
165
+ ensure_node() {
166
+ if node_ok; then
167
+ log "Node.js $(node -v) and npm $(npm -v) detected"
168
+ return
169
+ fi
170
+
171
+ install_node_system
172
+
173
+ if ! node_ok; then
174
+ fail "Node.js 16+ and npm are still unavailable after installation."
175
+ fi
176
+
177
+ log "Node.js $(node -v) and npm $(npm -v) ready"
178
+ }
179
+
180
+ ensure_global_npm_prefix() {
181
+ prefix="$(npm config get prefix 2>/dev/null || echo /usr/local)"
182
+ bindir="$prefix/bin"
183
+
184
+ if is_root || [ -w "$prefix" ] || [ -w "$bindir" ]; then
185
+ return
186
+ fi
187
+
188
+ user_prefix="${HOME:-$PWD}/.local"
189
+ log "Global npm prefix is not writable; using $user_prefix"
190
+ mkdir -p "$user_prefix/bin"
191
+ npm config set prefix "$user_prefix" >/dev/null
192
+ PATH="$user_prefix/bin:$PATH"
193
+ export PATH
194
+
195
+ case ":$PATH:" in
196
+ *":$user_prefix/bin:"*) : ;;
197
+ *) PATH="$user_prefix/bin:$PATH"; export PATH ;;
198
+ esac
199
+
200
+ shell_rc="${HOME:-}/.profile"
201
+ if [ -n "${HOME:-}" ] && [ -f "$shell_rc" ] && ! grep -q 'export PATH="$HOME/.local/bin:$PATH"' "$shell_rc" 2>/dev/null; then
202
+ printf '\n# FogAct npm global binaries\nexport PATH="$HOME/.local/bin:$PATH"\n' >> "$shell_rc" || true
203
+ fi
204
+ }
205
+
206
+ ensure_git() {
207
+ if has git; then
208
+ return
209
+ fi
210
+
211
+ log "git not found; installing git"
212
+ if has apt-get; then run_sudo apt-get update && run_sudo apt-get install -y git; return; fi
213
+ if has dnf; then run_sudo dnf install -y git; return; fi
214
+ if has yum; then run_sudo yum install -y git; return; fi
215
+ if has apk; then run_sudo apk add --no-cache git; return; fi
216
+ if has pacman; then run_sudo pacman -Sy --noconfirm git; return; fi
217
+ if has zypper; then run_sudo zypper --non-interactive install git; return; fi
218
+ if has brew; then brew install git; return; fi
219
+ fail "git is required for --method github. Install git, then rerun."
220
+ }
221
+
222
+ install_from_npm() {
223
+ ensure_global_npm_prefix
224
+ log "Installing $PACKAGE_NAME from npm"
225
+ npm install -g "$PACKAGE_NAME@latest"
226
+ }
227
+
228
+ install_from_github() {
229
+ ensure_git
230
+ if [ -z "$INSTALL_DIR" ]; then
231
+ if is_root; then
232
+ INSTALL_DIR="/opt/fogact"
233
+ else
234
+ INSTALL_DIR="${HOME:-$PWD}/.local/share/fogact"
235
+ fi
236
+ fi
237
+
238
+ log "Installing from GitHub: $GITHUB_REPO#$GIT_REF"
239
+ if [ -d "$INSTALL_DIR/.git" ]; then
240
+ git -C "$INSTALL_DIR" fetch --depth 1 origin "$GIT_REF"
241
+ git -C "$INSTALL_DIR" checkout -q FETCH_HEAD
242
+ else
243
+ mkdir -p "$(dirname "$INSTALL_DIR")"
244
+ rm -rf "$INSTALL_DIR"
245
+ git clone --depth 1 --branch "$GIT_REF" "https://github.com/$GITHUB_REPO.git" "$INSTALL_DIR"
246
+ fi
247
+
248
+ (cd "$INSTALL_DIR" && npm install)
249
+
250
+ fogact_cli() {
251
+ node "$INSTALL_DIR/bin/cli.js" "$@"
252
+ }
253
+ fogact_web() {
254
+ node "$INSTALL_DIR/bin/web-server.js" "$@"
255
+ }
256
+ }
257
+
258
+ find_public_ip() {
259
+ if has curl; then
260
+ curl -fsS --max-time 3 https://api.ipify.org 2>/dev/null || true
261
+ elif has wget; then
262
+ wget -qO- --timeout=3 https://api.ipify.org 2>/dev/null || true
263
+ fi
264
+ }
265
+
266
+ start_web_background() {
267
+ log "Starting FogAct Web UI on port $WEB_PORT"
268
+ log_dir="${HOME:-$PWD}/.fogact/logs"
269
+ mkdir -p "$log_dir"
270
+
271
+ if has fogact-web; then
272
+ PORT="$WEB_PORT" ADMIN_PASSWORD="$ADMIN_PASSWORD_VALUE" nohup fogact-web > "$log_dir/web.log" 2>&1 &
273
+ else
274
+ PORT="$WEB_PORT" ADMIN_PASSWORD="$ADMIN_PASSWORD_VALUE" nohup sh -c 'node "$1"' sh "$INSTALL_DIR/bin/web-server.js" > "$log_dir/web.log" 2>&1 &
275
+ fi
276
+
277
+ sleep 2
278
+ log "Web UI log: $log_dir/web.log"
279
+ log "Local: http://127.0.0.1:$WEB_PORT/"
280
+ public_ip="$(find_public_ip)"
281
+ if [ -n "$public_ip" ]; then
282
+ log "Public: http://$public_ip:$WEB_PORT/"
283
+ fi
284
+ }
285
+
286
+ create_systemd_service() {
287
+ if ! has systemctl; then
288
+ warn "systemd not found; falling back to background start"
289
+ start_web_background
290
+ return
291
+ fi
292
+
293
+ if ! is_root; then
294
+ warn "systemd service creation needs root; falling back to background start"
295
+ start_web_background
296
+ return
297
+ fi
298
+
299
+ web_bin="$(command -v fogact-web 2>/dev/null || true)"
300
+ if [ -z "$web_bin" ] && [ -n "$INSTALL_DIR" ]; then
301
+ web_bin="/usr/bin/node $INSTALL_DIR/bin/web-server.js"
302
+ fi
303
+ [ -n "$web_bin" ] || fail "Cannot locate fogact-web binary for systemd service."
304
+
305
+ cat > /etc/systemd/system/fogact-web.service <<EOF
306
+ [Unit]
307
+ Description=FogAct Web UI
308
+ After=network.target
309
+
310
+ [Service]
311
+ Type=simple
312
+ Environment=PORT=$WEB_PORT
313
+ Environment=ADMIN_PASSWORD=$ADMIN_PASSWORD_VALUE
314
+ ExecStart=$web_bin
315
+ Restart=on-failure
316
+ RestartSec=3
317
+
318
+ [Install]
319
+ WantedBy=multi-user.target
320
+ EOF
321
+
322
+ systemctl daemon-reload
323
+ systemctl enable --now fogact-web.service
324
+ log "systemd service started: fogact-web.service"
325
+ }
326
+
327
+ run_activation() {
328
+ if [ "$NO_ACTIVATE" = "1" ]; then
329
+ return
330
+ fi
331
+
332
+ if [ -z "$SERVICE" ] && [ -z "$CODE" ] && [ -z "$API_KEY" ]; then
333
+ log "Install complete. No activation options supplied."
334
+ print_next_steps
335
+ return
336
+ fi
337
+
338
+ if [ -n "$BASE_URL" ]; then export NEWAPI_BASE_URL="$BASE_URL"; fi
339
+ if [ -n "$API_KEY" ]; then export NEWAPI_API_KEY="$API_KEY"; fi
340
+ if [ -n "$CLIPROXY_BASE" ]; then export CLIPROXY_API_BASE="$CLIPROXY_BASE"; fi
341
+
342
+ set -- wizard --yes
343
+ if [ -n "$SERVICE" ]; then set -- "$@" --service "$SERVICE"; fi
344
+ if [ -n "$CODE" ]; then set -- "$@" --code "$CODE"; fi
345
+ if [ -n "$API_KEY" ]; then set -- "$@" --api-key "$API_KEY"; fi
346
+ if [ -n "$PLATFORMS" ]; then set -- "$@" --platforms "$PLATFORMS"; fi
347
+ if [ "$ALL_PLATFORMS" = "1" ]; then set -- "$@" --all; fi
348
+ if [ "$SKIP_VERIFY" = "1" ]; then set -- "$@" --skip-verify; fi
349
+ if [ "$NO_REDEEM" = "1" ]; then set -- "$@" --no-redeem; fi
350
+
351
+ log "Running activation"
352
+ if [ "$INSTALL_METHOD" = "github" ]; then
353
+ node "$INSTALL_DIR/bin/cli.js" "$@"
354
+ else
355
+ fogact "$@"
356
+ fi
357
+ }
358
+
359
+ print_next_steps() {
360
+ cat <<EOF
361
+
362
+ Next commands:
363
+ fogact --help
364
+ fogact wizard --code YOUR_CODE --yes
365
+ fogact activate --service codex --yes
366
+ fogact-web
367
+
368
+ For code mode with a remote activation backend:
369
+ export CLIPROXY_API_BASE="https://your-activator.example.com"
370
+ fogact wizard --code YOUR_CODE --yes
371
+
372
+ For direct NewAPI mode:
373
+ export NEWAPI_BASE_URL="https://newapi.example.com"
374
+ export NEWAPI_API_KEY="sk-your-key"
375
+ fogact activate --service codex --yes
376
+ EOF
377
+ }
378
+
379
+ main() {
380
+ log "FogAct bootstrap"
381
+ ensure_node
382
+
383
+ case "$INSTALL_METHOD" in
384
+ npm) install_from_npm ;;
385
+ github) install_from_github ;;
386
+ *) fail "Unsupported install method: $INSTALL_METHOD" ;;
387
+ esac
388
+
389
+ run_activation
390
+
391
+ if [ "$RUN_WEB" = "1" ]; then
392
+ if [ "$CREATE_SYSTEMD" = "1" ]; then
393
+ create_systemd_service
394
+ else
395
+ start_web_background
396
+ fi
397
+ fi
398
+
399
+ log "Done"
400
+ }
401
+
402
+ main
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+
3
+ const prompts = require("prompts");
4
+ const { verifyActivationCode, getNodes } = require("../services/cliproxy-api");
5
+ const { testNodes, selectBestNode, formatNodeResults } = require("../services/node-service");
6
+ const { createBackup } = require("../services/backup-service");
7
+ const { writeClaudeConfig, getClaudeConfigPath } = require("../config/claude");
8
+ const { writeCodexConfig, getCodexConfigPath } = require("../config/codex");
9
+ const { runActivationWizard, runNewApiActivation } = require("../services/activation-orchestrator");
10
+
11
+ async function runLegacyCodeActivation(options = {}) {
12
+ console.log("");
13
+ console.log("=== CLIProxy Activation (Legacy Code Mode) ===");
14
+ console.log("");
15
+
16
+ let service = options.service;
17
+ if (!service) {
18
+ const response = await prompts({
19
+ type: "select",
20
+ name: "service",
21
+ message: "Select service to activate",
22
+ choices: [
23
+ { title: "Claude Code", value: "claude" },
24
+ { title: "Codex", value: "codex" },
25
+ ],
26
+ });
27
+
28
+ if (!response.service) {
29
+ console.log("Activation cancelled.");
30
+ return;
31
+ }
32
+
33
+ service = response.service;
34
+ }
35
+
36
+ let code = options.code;
37
+ if (!code) {
38
+ const response = await prompts({
39
+ type: "text",
40
+ name: "code",
41
+ message: "Enter activation code",
42
+ validate: (value) => value.length > 0 || "Activation code is required",
43
+ });
44
+
45
+ if (!response.code) {
46
+ console.log("Activation cancelled.");
47
+ return;
48
+ }
49
+
50
+ code = response.code;
51
+ }
52
+
53
+ console.log("");
54
+ console.log("Verifying activation code...");
55
+
56
+ const verification = await verifyActivationCode(code, service);
57
+
58
+ if (!verification.valid) {
59
+ console.log("");
60
+ console.log("✗ Activation failed:", verification.error);
61
+ return;
62
+ }
63
+
64
+ console.log("✓ Activation code verified");
65
+ console.log("");
66
+ console.log("Testing available nodes...");
67
+
68
+ let nodes = verification.nodes;
69
+ if (!nodes || nodes.length === 0) {
70
+ nodes = await getNodes(service);
71
+ }
72
+
73
+ if (nodes.length === 0) {
74
+ console.log("✗ No nodes available");
75
+ return;
76
+ }
77
+
78
+ const testResults = await testNodes(nodes);
79
+ console.log("");
80
+ console.log(formatNodeResults(testResults));
81
+
82
+ const bestNode = selectBestNode(testResults);
83
+
84
+ if (!bestNode) {
85
+ console.log("");
86
+ console.log("✗ No available nodes found");
87
+ return;
88
+ }
89
+
90
+ console.log("");
91
+ console.log(`✓ Selected node: ${bestNode.name || bestNode.url} (${bestNode.latency}ms)`);
92
+
93
+ const configPath = service === "claude" ? getClaudeConfigPath() : getCodexConfigPath();
94
+
95
+ console.log("");
96
+ console.log("Creating backup...");
97
+
98
+ const backupPath = createBackup(service, configPath);
99
+
100
+ if (backupPath) {
101
+ console.log(`✓ Backup created: ${backupPath}`);
102
+ } else {
103
+ console.log("ℹ No existing config to backup");
104
+ }
105
+
106
+ console.log("");
107
+ console.log("Writing configuration...");
108
+
109
+ let writtenPath;
110
+ if (service === "claude") {
111
+ writtenPath = writeClaudeConfig(code, bestNode.url);
112
+ } else {
113
+ writtenPath = writeCodexConfig(code, bestNode.url);
114
+ }
115
+
116
+ console.log(`✓ Configuration written: ${writtenPath}`);
117
+ console.log("");
118
+ console.log("=== Activation Complete ===");
119
+ console.log("");
120
+ console.log(`Service: ${service === "claude" ? "Claude Code" : "Codex"}`);
121
+ console.log(`Node: ${bestNode.url}`);
122
+ console.log(`Config: ${writtenPath}`);
123
+ console.log("");
124
+ console.log("Please restart your application to apply changes.");
125
+ console.log("");
126
+ }
127
+
128
+ async function runActivateCommand(options = {}) {
129
+ if (options.legacy && options.code) {
130
+ await runLegacyCodeActivation(options);
131
+ return;
132
+ }
133
+
134
+ if (options.service && options.apiKey && (options.yes || options.auto)) {
135
+ console.log("");
136
+ console.log("=== NewAPI Multi-Platform Activation ===");
137
+ await runNewApiActivation(options);
138
+ return;
139
+ }
140
+
141
+ await runActivationWizard(options);
142
+ }
143
+
144
+ module.exports = { runActivateCommand, runLegacyCodeActivation };