my-singbox 0.0.0-stage → 2.0.0

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,43 @@
1
+ import { type Env } from './core/env.js';
2
+ export type Protocol = 'shadowsocks' | 'vless-reality';
3
+ export interface SsCreds {
4
+ password: string;
5
+ }
6
+ export interface RealityCreds {
7
+ uuid: string;
8
+ privateKey: string;
9
+ publicKey: string;
10
+ shortId: string;
11
+ }
12
+ export interface Node {
13
+ name: string;
14
+ protocol: Protocol;
15
+ inbound: {
16
+ tag: string;
17
+ port: number;
18
+ };
19
+ egress: 'direct' | 'isp-socks';
20
+ creds: SsCreds | RealityCreds;
21
+ }
22
+ export interface IspSocks {
23
+ host: string;
24
+ port: number;
25
+ addr: string;
26
+ user: string;
27
+ pass: string;
28
+ egressIp?: string;
29
+ }
30
+ export interface Model {
31
+ server: string;
32
+ nodes: Node[];
33
+ isp?: IspSocks;
34
+ }
35
+ export declare const SS_PORT = 8388, VLESS_PORT = 8443, SS_ISP_PORT = 13839, VLESS_ISP_PORT = 18444;
36
+ export declare const SS_TAG = "ss-in", VLESS_TAG = "vless-reality-in", SS_ISP_TAG = "ss-isp-in", VLESS_ISP_TAG = "vless-reality-isp-in", OUT_TAG = "isp-socks";
37
+ export declare const NODE_SS = "shadowsocks", NODE_VLESS = "vless-reality", NODE_SS_ISP = "shadowsocks-isp", NODE_VLESS_ISP = "vless-reality-isp";
38
+ export declare class ModelError extends Error {
39
+ name: string;
40
+ }
41
+ export declare function buildModel(env: Env, opts?: {
42
+ target?: 'all' | 'server' | 'client';
43
+ }): Model;
package/install.sh ADDED
@@ -0,0 +1,202 @@
1
+ #!/usr/bin/env bash
2
+ # Bootstrap Node without sudo. Overrides: MY_SINGBOX_NODE_DIR (default
3
+ # $HOME/.local/node), MY_SINGBOX_NODE_VERSION (v22.23.4),
4
+ # MY_SINGBOX_NODE_MIRROR (download base), MY_SINGBOX_FORCE_NODE_INSTALL=1.
5
+ # MY_SINGBOX_NODE_SHASUMS_BASE: test hook overriding the official checksum trust
6
+ # anchor (default https://nodejs.org/dist); never point it at a mirror.
7
+ legacy='https://github.com/zht7063/my-singbox/releases/tag/v1.1.0'
8
+ if [ -z "${BASH_VERSION+x}" ]; then
9
+ if command -v bash >/dev/null 2>&1; then
10
+ if [ -f "$0" ] && [ -r "$0" ]; then
11
+ exec bash "$0" "$@"
12
+ fi
13
+ echo 'ERROR: run this installer from a saved file: sh install.sh' >&2
14
+ else
15
+ echo "ERROR: bash not found; musl/Alpine is unsupported by official Node/this tool; use shell v1.1.0: $legacy" >&2
16
+ fi
17
+ exit 1
18
+ fi
19
+ set -euo pipefail
20
+
21
+ unsupported() {
22
+ echo "ERROR: $*; use shell v1.1.0: $legacy" >&2
23
+ exit 1
24
+ }
25
+
26
+ [[ $(uname -s) == Linux ]] || unsupported 'only Linux servers are supported'
27
+ case $(uname -m) in
28
+ x86_64|amd64)
29
+ arch=x64
30
+ ;;
31
+ aarch64|arm64)
32
+ arch=arm64
33
+ ;;
34
+ *)
35
+ unsupported 'unsupported architecture'
36
+ ;;
37
+ esac
38
+
39
+ # Check libc before looking for Node: official binaries do not support musl or old glibc.
40
+ libc=$(ldd --version 2>&1 || true)
41
+ [[ $libc != *musl* ]] || unsupported 'musl/Alpine is unsupported by official Node/this tool'
42
+ first_line=${libc%%$'\n'*}
43
+ libc_version=${first_line##* }
44
+ [[ $first_line == *GLIBC* || $first_line == *'GNU libc'* ||
45
+ $first_line == *'GNU C Library'* ]] || unsupported 'cannot determine libc'
46
+ [[ $libc_version =~ ^([0-9]+)\.([0-9]+)(\.[0-9]+)?$ ]] ||
47
+ unsupported 'cannot determine glibc version'
48
+ major=${BASH_REMATCH[1]}
49
+ minor=${BASH_REMATCH[2]}
50
+ ((10#$major > 2 || (10#$major == 2 && 10#$minor >= 28))) ||
51
+ unsupported "glibc $libc_version is unsupported by official Node/this tool (requires >=2.28)"
52
+
53
+ if ((EUID == 0)); then
54
+ echo 'Note: root is not needed; this installer never uses sudo.'
55
+ fi
56
+ node_dir=${MY_SINGBOX_NODE_DIR:-$HOME/.local/node}
57
+
58
+ hints() {
59
+ printf 'PATH hint: export PATH="%s/bin:$PATH"\n' "$node_dir"
60
+ echo 'Next: npm i -g my-singbox && my-singbox doctor (or: npx my-singbox doctor).'
61
+ }
62
+
63
+ usable_node() {
64
+ local detected
65
+ detected=$("$1" -v 2>/dev/null) || return 1
66
+ [[ $detected =~ ^v([0-9]+)\. ]] && ((10#${BASH_REMATCH[1]} >= 22))
67
+ }
68
+
69
+ if [[ ${MY_SINGBOX_FORCE_NODE_INSTALL:-0} != 1 ]]; then
70
+ if command -v node >/dev/null 2>&1 && usable_node node; then
71
+ echo "Using Node already on PATH: $(command -v node)"
72
+ echo 'Next: npm i -g my-singbox && my-singbox doctor (or: npx my-singbox doctor).'
73
+ exit 0
74
+ fi
75
+ if [[ -x $node_dir/bin/node ]] && usable_node "$node_dir/bin/node"; then
76
+ echo "Using Node at $node_dir/bin/node."
77
+ hints
78
+ exit 0
79
+ fi
80
+ fi
81
+
82
+ command -v curl >/dev/null 2>&1 || {
83
+ echo 'ERROR: curl is required' >&2
84
+ exit 1
85
+ }
86
+ if command -v sha256sum >/dev/null 2>&1; then
87
+ hash_tool=sha256sum
88
+ elif command -v shasum >/dev/null 2>&1; then
89
+ hash_tool=shasum
90
+ else
91
+ echo 'ERROR: sha256sum or shasum is required' >&2
92
+ exit 1
93
+ fi
94
+
95
+ version=${MY_SINGBOX_NODE_VERSION:-v22.23.4}
96
+ version=v${version#v}
97
+ [[ $version =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || {
98
+ echo 'ERROR: invalid Node version' >&2
99
+ exit 1
100
+ }
101
+ requested_major=${version#v}
102
+ requested_major=${requested_major%%.*}
103
+ ((10#$requested_major >= 22)) || {
104
+ echo 'ERROR: Node >=22 is required' >&2
105
+ exit 1
106
+ }
107
+ ext=gz
108
+ if command -v xz >/dev/null 2>&1; then
109
+ ext=xz
110
+ fi
111
+ file="node-$version-linux-$arch.tar.$ext"
112
+
113
+ if [[ -n ${MY_SINGBOX_NODE_MIRROR:-} ]]; then
114
+ base=${MY_SINGBOX_NODE_MIRROR%/}
115
+ elif curl --max-time 5 -fsI https://nodejs.org/dist >/dev/null 2>&1; then
116
+ base=https://nodejs.org/dist
117
+ else
118
+ base=https://npmmirror.com/mirrors/node
119
+ fi
120
+ if [[ $version == v22.23.4 ]]; then
121
+ checksum_source="built-in pin for $version"
122
+ else
123
+ checksum_source='official nodejs.org SHASUMS256.txt'
124
+ fi
125
+ echo "Using Node source: $base (tarball; checksum: $checksum_source)"
126
+
127
+ work=$(mktemp -d)
128
+ stage=''
129
+ cleanup() {
130
+ rm -rf -- "$work"
131
+ if [[ -n $stage ]]; then
132
+ rm -rf -- "$stage"
133
+ fi
134
+ }
135
+ trap cleanup EXIT
136
+
137
+ expected=''
138
+ if [[ $version == v22.23.4 ]]; then
139
+ case $file in
140
+ node-v22.23.4-linux-arm64.tar.gz)
141
+ expected=f7a1e486dbc8b286847815b477d4fb5e9fb963ed4489c3e1588853d685f589f9
142
+ ;;
143
+ node-v22.23.4-linux-arm64.tar.xz)
144
+ expected=6287aa46b6c375d816e63ae66d024fbbfd70404825aabdfdf898a970871d2f4b
145
+ ;;
146
+ node-v22.23.4-linux-x64.tar.gz)
147
+ expected=b8492284c76a54baf58d61668dd0e41ee0fc98adabf9503d30f05d08f55b4406
148
+ ;;
149
+ node-v22.23.4-linux-x64.tar.xz)
150
+ expected=1805bcd35e6815559bd2eb97db438d3fdb0eb3a5e6a977b7c1badca76337e9ce
151
+ ;;
152
+ esac
153
+ else
154
+ shasums_base=${MY_SINGBOX_NODE_SHASUMS_BASE:-https://nodejs.org/dist}
155
+ shasums_base=${shasums_base%/}
156
+ if ! curl -fsSL --retry 3 "$shasums_base/$version/SHASUMS256.txt" \
157
+ -o "$work/SHASUMS256.txt"; then
158
+ echo "ERROR: cannot fetch the official SHASUMS256.txt from $shasums_base/$version/" \
159
+ '(needed to verify a non-default Node version); refusing to use mirror checksums.' \
160
+ 'Use the default version or retry when nodejs.org is reachable.' >&2
161
+ exit 1
162
+ fi
163
+ expected=$(awk -v file="$file" '$2 == file {print $1}' "$work/SHASUMS256.txt")
164
+ fi
165
+ [[ $expected =~ ^[[:xdigit:]]{64}$ ]] || {
166
+ echo "ERROR: missing or invalid checksum entry for $file" >&2
167
+ exit 1
168
+ }
169
+ curl -fsSL --retry 3 "$base/$version/$file" -o "$work/$file"
170
+ if [[ $hash_tool == sha256sum ]]; then
171
+ actual=$(sha256sum "$work/$file")
172
+ else
173
+ actual=$(shasum -a 256 "$work/$file")
174
+ fi
175
+ actual=${actual%% *}
176
+ if [[ ${actual,,} != "${expected,,}" ]]; then
177
+ echo "ERROR: checksum mismatch for $file (expected $expected, actual $actual)" >&2
178
+ exit 1
179
+ fi
180
+
181
+ # Only verified archives reach extraction; the existing target stays intact until then.
182
+ parent=$(dirname -- "$node_dir")
183
+ mkdir -p -- "$parent"
184
+ stage=$(mktemp -d "$parent/.my-singbox-node.XXXXXX")
185
+ tar -xf "$work/$file" -C "$stage" --strip-components=1
186
+ [[ $("$stage/bin/node" --version) == "$version" ]] || {
187
+ echo 'ERROR: extracted Node version mismatch' >&2
188
+ exit 1
189
+ }
190
+ if [[ -e $node_dir || -L $node_dir ]]; then
191
+ backup="$node_dir.old-$(date +%Y%m%d%H%M%S)-$$"
192
+ [[ ! -e $backup ]] || {
193
+ echo 'ERROR: backup path exists' >&2
194
+ exit 1
195
+ }
196
+ mv -- "$node_dir" "$backup"
197
+ echo "Previous installation preserved at $backup"
198
+ fi
199
+ mv -- "$stage" "$node_dir"
200
+ stage=''
201
+ echo "Installed Node $version at $node_dir"
202
+ hints
package/package.json CHANGED
@@ -1,6 +1,54 @@
1
1
  {
2
2
  "name": "my-singbox",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "2.0.0",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "engines": {
7
+ "node": ">=22"
8
+ },
9
+ "bin": {
10
+ "my-singbox": "dist/cli.js"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/zht7063/my-singbox"
15
+ },
16
+ "homepage": "https://github.com/zht7063/my-singbox",
17
+ "bugs": {
18
+ "url": "https://github.com/zht7063/my-singbox/issues"
19
+ },
20
+ "main": "./dist/index.js",
21
+ "types": "./dist/index.d.ts",
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "import": "./dist/index.js"
26
+ }
27
+ },
28
+ "files": [
29
+ "dist/",
30
+ "templates/config.json.template",
31
+ "templates/clash.yaml.template",
32
+ "compose.yaml",
33
+ "install.sh",
34
+ "LICENSE",
35
+ "THIRD-PARTY-NOTICES.md",
36
+ "README.md",
37
+ "README_CN.md"
38
+ ],
39
+ "scripts": {
40
+ "build": "node tools/build.mjs",
41
+ "typecheck": "tsc --noEmit",
42
+ "test": "vitest run",
43
+ "license-scan": "node tools/license-scan.mjs",
44
+ "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
45
+ "prepack": "npm run build"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^22.20.5",
49
+ "esbuild": "^0.28.2",
50
+ "typescript": "^7.0.2",
51
+ "vitest": "^5.0.5",
52
+ "yaml": "2.9.1"
53
+ }
54
+ }
@@ -0,0 +1,123 @@
1
+ proxies:
2
+ - name: "shadowsocks"
3
+ type: ss
4
+ server: "${SERVER_ADDRESS}"
5
+ port: 8388
6
+ cipher: 2022-blake3-aes-128-gcm
7
+ password: "${SS_PASSWORD}"
8
+ udp: true
9
+ - name: vless-reality
10
+ type: vless
11
+ server: "${SERVER_ADDRESS}"
12
+ port: 8443
13
+ uuid: "${VLESS_UUID}"
14
+ network: tcp
15
+ tls: true
16
+ udp: true
17
+ packet-encoding: xudp
18
+ servername: www.microsoft.com
19
+ client-fingerprint: chrome
20
+ flow: xtls-rprx-vision
21
+ reality-opts:
22
+ public-key: "${REALITY_PUBLIC_KEY}"
23
+ short-id: "${REALITY_SHORT_ID}"
24
+
25
+ proxy-groups:
26
+ - name: Proxy
27
+ type: select
28
+ proxies:
29
+ - shadowsocks
30
+ - vless-reality
31
+ - DIRECT
32
+
33
+ # 分流规则,自上而下匹配
34
+ rules:
35
+ # 私有网络,直连
36
+ - RULE-SET,meta-private,DIRECT
37
+ - RULE-SET,meta-private-ip,DIRECT,no-resolve
38
+
39
+ # AI 服务,强制代理
40
+ - DOMAIN-SUFFIX,openai.com,Proxy
41
+ - DOMAIN-SUFFIX,chatgpt.com,Proxy
42
+ - DOMAIN-SUFFIX,oaistatic.com,Proxy
43
+ - DOMAIN-SUFFIX,oaiusercontent.com,Proxy
44
+ - DOMAIN-SUFFIX,anthropic.com,Proxy
45
+ - DOMAIN-SUFFIX,claude.ai,Proxy
46
+ - DOMAIN-SUFFIX,claude.com,Proxy
47
+ - DOMAIN-SUFFIX,claudeusercontent.com,Proxy
48
+
49
+ # 常规分流
50
+ - RULE-SET,meta-proxy,Proxy
51
+ - RULE-SET,meta-cn,DIRECT
52
+ - RULE-SET,meta-cn-ip,DIRECT
53
+ - MATCH,Proxy
54
+
55
+ # 远程规则集
56
+ rule-providers:
57
+ # 私有域名,直连
58
+ meta-private:
59
+ type: http
60
+ behavior: domain
61
+ format: mrs
62
+ url: https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/meta/geo/geosite/private.mrs
63
+ path: ./ruleset/meta-private.mrs
64
+ interval: 86400
65
+
66
+ # 私有 IP 段,直连
67
+ meta-private-ip:
68
+ type: http
69
+ behavior: ipcidr
70
+ format: mrs
71
+ url: https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/meta/geo/geoip/private.mrs
72
+ path: ./ruleset/meta-private-ip.mrs
73
+ interval: 86400
74
+
75
+ # 非中国大陆域名,走代理
76
+ meta-proxy:
77
+ type: http
78
+ behavior: domain
79
+ format: mrs
80
+ url: https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/meta/geo/geosite/geolocation-!cn.mrs
81
+ path: ./ruleset/meta-proxy.mrs
82
+ interval: 86400
83
+
84
+ # 中国大陆域名,直连
85
+ meta-cn:
86
+ type: http
87
+ behavior: domain
88
+ format: mrs
89
+ url: https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/meta/geo/geosite/cn.mrs
90
+ path: ./ruleset/meta-cn.mrs
91
+ interval: 86400
92
+
93
+ # 中国大陆 IP,直连
94
+ meta-cn-ip:
95
+ type: http
96
+ behavior: ipcidr
97
+ format: mrs
98
+ url: https://raw.githubusercontent.com/MetaCubeX/meta-rules-dat/meta/geo/geoip/cn.mrs
99
+ path: ./ruleset/meta-cn-ip.mrs
100
+ interval: 86400
101
+
102
+ # DNS 解析
103
+ dns:
104
+ enable: true
105
+ ipv6: false
106
+ listen: 127.0.0.1:1053
107
+ enhanced-mode: fake-ip
108
+ fake-ip-range: 198.18.0.1/16
109
+ fake-ip-filter:
110
+ - "*.lan"
111
+ - "*.local"
112
+ default-nameserver:
113
+ - 223.5.5.5
114
+ proxy-server-nameserver:
115
+ - "https://223.5.5.5/dns-query#DIRECT"
116
+ nameserver:
117
+ - "https://1.1.1.1/dns-query#Proxy"
118
+ - "https://8.8.8.8/dns-query#Proxy"
119
+ nameserver-policy:
120
+ "rule-set:meta-private":
121
+ - system
122
+ "rule-set:meta-cn":
123
+ - "https://223.5.5.5/dns-query#DIRECT"
@@ -0,0 +1,50 @@
1
+ {
2
+ "log": {
3
+ "level": "warn",
4
+ "timestamp": true
5
+ },
6
+ "inbounds": [
7
+ {
8
+ "type": "shadowsocks",
9
+ "tag": "ss-in",
10
+ "listen": "0.0.0.0",
11
+ "listen_port": 8388,
12
+ "method": "2022-blake3-aes-128-gcm",
13
+ "password": "${SS_PASSWORD}"
14
+ },
15
+ {
16
+ "type": "vless",
17
+ "tag": "vless-reality-in",
18
+ "listen": "0.0.0.0",
19
+ "listen_port": 8443,
20
+ "users": [
21
+ {
22
+ "name": "client-1",
23
+ "uuid": "${VLESS_UUID}",
24
+ "flow": "xtls-rprx-vision"
25
+ }
26
+ ],
27
+ "tls": {
28
+ "enabled": true,
29
+ "server_name": "www.microsoft.com",
30
+ "reality": {
31
+ "enabled": true,
32
+ "handshake": {
33
+ "server": "www.microsoft.com",
34
+ "server_port": 443
35
+ },
36
+ "private_key": "${REALITY_PRIVATE_KEY}",
37
+ "short_id": [
38
+ "${REALITY_SHORT_ID}"
39
+ ]
40
+ }
41
+ }
42
+ }
43
+ ],
44
+ "outbounds": [
45
+ {
46
+ "type": "direct",
47
+ "tag": "direct"
48
+ }
49
+ ]
50
+ }