@singbox-iac/cli 0.1.4 → 0.1.6
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-en.md +62 -11
- package/README.md +62 -11
- package/assets/rule-set/geoip-cn.srs +0 -0
- package/assets/rule-set/geosite-anthropic.srs +0 -0
- package/assets/rule-set/geosite-cn.srs +0 -0
- package/assets/rule-set/geosite-cursor.srs +0 -0
- package/assets/rule-set/geosite-figma.srs +0 -0
- package/assets/rule-set/geosite-github-copilot.srs +0 -0
- package/assets/rule-set/geosite-github.srs +0 -0
- package/assets/rule-set/geosite-google-deepmind.srs +0 -0
- package/assets/rule-set/geosite-google-gemini.srs +0 -0
- package/assets/rule-set/geosite-google.srs +0 -0
- package/dist/cli/command-helpers.js +1 -1
- package/dist/cli/command-helpers.js.map +1 -1
- package/dist/cli/commands/author.d.ts +28 -0
- package/dist/cli/commands/author.js +119 -121
- package/dist/cli/commands/author.js.map +1 -1
- package/dist/cli/commands/build.js +8 -35
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/go.d.ts +2 -0
- package/dist/cli/commands/go.js +38 -0
- package/dist/cli/commands/go.js.map +1 -0
- package/dist/cli/commands/proxifier.d.ts +2 -0
- package/dist/cli/commands/proxifier.js +61 -0
- package/dist/cli/commands/proxifier.js.map +1 -0
- package/dist/cli/commands/quickstart.d.ts +2 -0
- package/dist/cli/commands/quickstart.js +54 -0
- package/dist/cli/commands/quickstart.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +30 -0
- package/dist/cli/commands/setup.js +277 -233
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/commands/update.js +16 -2
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/commands/use.d.ts +2 -0
- package/dist/cli/commands/use.js +45 -0
- package/dist/cli/commands/use.js.map +1 -0
- package/dist/cli/index.js +43 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/modules/compiler/index.js +15 -10
- package/dist/modules/compiler/index.js.map +1 -1
- package/dist/modules/fetcher/index.d.ts +1 -0
- package/dist/modules/fetcher/index.js +66 -19
- package/dist/modules/fetcher/index.js.map +1 -1
- package/dist/modules/manager/index.d.ts +1 -0
- package/dist/modules/manager/index.js +15 -0
- package/dist/modules/manager/index.js.map +1 -1
- package/dist/modules/natural-language/index.js +3 -2
- package/dist/modules/natural-language/index.js.map +1 -1
- package/dist/modules/proxifier/index.d.ts +27 -0
- package/dist/modules/proxifier/index.js +184 -0
- package/dist/modules/proxifier/index.js.map +1 -0
- package/dist/modules/rule-set-sync/index.d.ts +10 -0
- package/dist/modules/rule-set-sync/index.js +72 -12
- package/dist/modules/rule-set-sync/index.js.map +1 -1
- package/dist/modules/update/index.d.ts +1 -0
- package/dist/modules/update/index.js +4 -1
- package/dist/modules/update/index.js.map +1 -1
- package/dist/modules/verification/index.d.ts +15 -0
- package/dist/modules/verification/index.js +89 -44
- package/dist/modules/verification/index.js.map +1 -1
- package/docs/proxifier-onboarding.md +65 -0
- package/docs/runtime-on-macos.md +24 -7
- package/examples/builder.config.yaml +8 -27
- package/package.json +11 -2
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface ProxifierListenerConfig {
|
|
2
|
+
readonly host: string;
|
|
3
|
+
readonly port: number;
|
|
4
|
+
}
|
|
5
|
+
export interface ProxifierBundle {
|
|
6
|
+
readonly id: string;
|
|
7
|
+
readonly name: string;
|
|
8
|
+
readonly description: string;
|
|
9
|
+
readonly processes: readonly string[];
|
|
10
|
+
}
|
|
11
|
+
export interface ProxifierScaffoldInput {
|
|
12
|
+
readonly listener: ProxifierListenerConfig;
|
|
13
|
+
readonly outputDir: string;
|
|
14
|
+
readonly bundleIds?: readonly string[];
|
|
15
|
+
}
|
|
16
|
+
export interface ProxifierScaffoldResult {
|
|
17
|
+
readonly outputDir: string;
|
|
18
|
+
readonly guidePath: string;
|
|
19
|
+
readonly proxyEndpointPath: string;
|
|
20
|
+
readonly customProcessesPath: string;
|
|
21
|
+
readonly combinedProcessesPath?: string;
|
|
22
|
+
readonly bundlePaths: readonly string[];
|
|
23
|
+
readonly bundles: readonly ProxifierBundle[];
|
|
24
|
+
}
|
|
25
|
+
export declare function listProxifierBundles(): readonly ProxifierBundle[];
|
|
26
|
+
export declare function selectProxifierBundlesFromPrompt(prompt: string): readonly string[];
|
|
27
|
+
export declare function writeProxifierScaffold(input: ProxifierScaffoldInput): Promise<ProxifierScaffoldResult>;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const bundleDefinitions = [
|
|
4
|
+
{
|
|
5
|
+
id: "antigravity",
|
|
6
|
+
name: "Antigravity",
|
|
7
|
+
description: "Recommended process patterns for Google Antigravity and its language services.",
|
|
8
|
+
processes: [
|
|
9
|
+
"Antigravity.app",
|
|
10
|
+
"Antigravity",
|
|
11
|
+
"com.google.antigravity",
|
|
12
|
+
"*Antigravity*",
|
|
13
|
+
"language_server_macos_arm",
|
|
14
|
+
"antigravity-auto-updater",
|
|
15
|
+
"*antigravity*",
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "cursor",
|
|
20
|
+
name: "Cursor",
|
|
21
|
+
description: "Common Cursor desktop and helper process names.",
|
|
22
|
+
processes: ["Cursor", "Cursor.app", "Cursor Helper", "cursor", "*cursor*"],
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "developer-ai-cli",
|
|
26
|
+
name: "Developer AI CLI",
|
|
27
|
+
description: "Common developer-facing AI and coding tool process names.",
|
|
28
|
+
processes: [
|
|
29
|
+
"claude",
|
|
30
|
+
"codex",
|
|
31
|
+
"gemini",
|
|
32
|
+
"codebuddy",
|
|
33
|
+
"cbc",
|
|
34
|
+
"opencode",
|
|
35
|
+
"qoder",
|
|
36
|
+
"qodercli",
|
|
37
|
+
"trae",
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
const promptBundleAliases = {
|
|
42
|
+
antigravity: ["antigravity", "google antigravity", "google.antigravity"],
|
|
43
|
+
cursor: ["cursor"],
|
|
44
|
+
"developer-ai-cli": [
|
|
45
|
+
"claude",
|
|
46
|
+
"codex",
|
|
47
|
+
"gemini",
|
|
48
|
+
"codebuddy",
|
|
49
|
+
"opencode",
|
|
50
|
+
"qoder",
|
|
51
|
+
"trae",
|
|
52
|
+
"ai ide",
|
|
53
|
+
"ai cli",
|
|
54
|
+
],
|
|
55
|
+
};
|
|
56
|
+
export function listProxifierBundles() {
|
|
57
|
+
return bundleDefinitions;
|
|
58
|
+
}
|
|
59
|
+
export function selectProxifierBundlesFromPrompt(prompt) {
|
|
60
|
+
const normalized = prompt.toLowerCase();
|
|
61
|
+
const matched = new Set();
|
|
62
|
+
if (normalized.includes("proxifier") ||
|
|
63
|
+
normalized.includes("进程级") ||
|
|
64
|
+
normalized.includes("process-level") ||
|
|
65
|
+
normalized.includes("独立入口")) {
|
|
66
|
+
matched.add("developer-ai-cli");
|
|
67
|
+
matched.add("antigravity");
|
|
68
|
+
}
|
|
69
|
+
for (const [bundleId, aliases] of Object.entries(promptBundleAliases)) {
|
|
70
|
+
if (aliases.some((alias) => normalized.includes(alias))) {
|
|
71
|
+
matched.add(bundleId);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return [...matched];
|
|
75
|
+
}
|
|
76
|
+
export async function writeProxifierScaffold(input) {
|
|
77
|
+
const bundles = resolveBundles(input.bundleIds);
|
|
78
|
+
await mkdir(input.outputDir, { recursive: true });
|
|
79
|
+
const bundlesDir = path.join(input.outputDir, "bundles");
|
|
80
|
+
await mkdir(bundlesDir, { recursive: true });
|
|
81
|
+
const guidePath = path.join(input.outputDir, "README.md");
|
|
82
|
+
const proxyEndpointPath = path.join(input.outputDir, "proxy-endpoint.txt");
|
|
83
|
+
const customProcessesPath = path.join(input.outputDir, "custom-processes.txt");
|
|
84
|
+
const proxyEndpointText = [
|
|
85
|
+
"Proxy Endpoint",
|
|
86
|
+
"Protocol: SOCKS5",
|
|
87
|
+
`Host: ${input.listener.host}`,
|
|
88
|
+
`Port: ${input.listener.port}`,
|
|
89
|
+
"",
|
|
90
|
+
"Recommended Proxifier target",
|
|
91
|
+
`SOCKS5 ${input.listener.host}:${input.listener.port}`,
|
|
92
|
+
"",
|
|
93
|
+
].join("\n");
|
|
94
|
+
await writeFile(proxyEndpointPath, proxyEndpointText, "utf8");
|
|
95
|
+
await writeFile(customProcessesPath, [
|
|
96
|
+
"# Add your own process names here, one per line.",
|
|
97
|
+
"# Example:",
|
|
98
|
+
"# MyIDE.app",
|
|
99
|
+
"# my-language-server",
|
|
100
|
+
"",
|
|
101
|
+
].join("\n"), "utf8");
|
|
102
|
+
const bundlePaths = [];
|
|
103
|
+
const combinedProcesses = new Set();
|
|
104
|
+
for (const bundle of bundles) {
|
|
105
|
+
const filePath = path.join(bundlesDir, `${bundle.id}.txt`);
|
|
106
|
+
await writeFile(filePath, [`# ${bundle.name}`, `# ${bundle.description}`, ...bundle.processes, ""].join("\n"), "utf8");
|
|
107
|
+
bundlePaths.push(filePath);
|
|
108
|
+
for (const processName of bundle.processes) {
|
|
109
|
+
combinedProcesses.add(processName);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
let combinedProcessesPath;
|
|
113
|
+
if (combinedProcesses.size > 0) {
|
|
114
|
+
combinedProcessesPath = path.join(input.outputDir, "all-processes.txt");
|
|
115
|
+
await writeFile(combinedProcessesPath, [...combinedProcesses].join("\n").concat("\n"), "utf8");
|
|
116
|
+
}
|
|
117
|
+
await writeFile(guidePath, renderGuide({
|
|
118
|
+
listener: input.listener,
|
|
119
|
+
bundles,
|
|
120
|
+
bundlePaths,
|
|
121
|
+
customProcessesPath,
|
|
122
|
+
...(combinedProcessesPath ? { combinedProcessesPath } : {}),
|
|
123
|
+
}), "utf8");
|
|
124
|
+
return {
|
|
125
|
+
outputDir: input.outputDir,
|
|
126
|
+
guidePath,
|
|
127
|
+
proxyEndpointPath,
|
|
128
|
+
customProcessesPath,
|
|
129
|
+
...(combinedProcessesPath ? { combinedProcessesPath } : {}),
|
|
130
|
+
bundlePaths,
|
|
131
|
+
bundles,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function resolveBundles(bundleIds) {
|
|
135
|
+
if (!bundleIds || bundleIds.length === 0) {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
const requested = new Set(bundleIds);
|
|
139
|
+
return bundleDefinitions.filter((bundle) => requested.has(bundle.id));
|
|
140
|
+
}
|
|
141
|
+
function renderGuide(input) {
|
|
142
|
+
const lines = [
|
|
143
|
+
"# Proxifier Onboarding",
|
|
144
|
+
"",
|
|
145
|
+
"Use this directory to configure process-aware routing into `in-proxifier`.",
|
|
146
|
+
"",
|
|
147
|
+
"## Proxy Server",
|
|
148
|
+
"",
|
|
149
|
+
"- Protocol: `SOCKS5`",
|
|
150
|
+
`- Host: \`${input.listener.host}\``,
|
|
151
|
+
`- Port: \`${input.listener.port}\``,
|
|
152
|
+
"",
|
|
153
|
+
"## Suggested Steps",
|
|
154
|
+
"",
|
|
155
|
+
"1. Open Proxifier and create a new proxy server using the SOCKS5 endpoint above.",
|
|
156
|
+
"2. Create or update a Proxification Rule that sends selected applications to that proxy server.",
|
|
157
|
+
"3. Import process names from the bundle files below, then add any project-specific processes to `custom-processes.txt`.",
|
|
158
|
+
"4. Keep the catch-all browser and normal system traffic on the regular mixed listener instead of sending everything through Proxifier.",
|
|
159
|
+
"",
|
|
160
|
+
"## Generated Files",
|
|
161
|
+
"",
|
|
162
|
+
"- Proxy endpoint: `proxy-endpoint.txt`",
|
|
163
|
+
`- Custom process list: \`${path.basename(input.customProcessesPath)}\``,
|
|
164
|
+
];
|
|
165
|
+
if (input.combinedProcessesPath) {
|
|
166
|
+
lines.push(`- Combined process list: \`${path.basename(input.combinedProcessesPath)}\``);
|
|
167
|
+
}
|
|
168
|
+
if (input.bundles.length > 0) {
|
|
169
|
+
lines.push("", "## Suggested Bundles", "");
|
|
170
|
+
for (let index = 0; index < input.bundles.length; index += 1) {
|
|
171
|
+
const bundle = input.bundles[index];
|
|
172
|
+
const bundlePath = input.bundlePaths[index];
|
|
173
|
+
if (!bundle || !bundlePath) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
lines.push(`- \`${bundle.name}\`: \`${path.relative(path.dirname(input.customProcessesPath), bundlePath)}\``);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
lines.push("", "## Suggested Bundles", "", "- No specific bundle matched the current prompt. Start with `custom-processes.txt` and add your target app names manually.");
|
|
181
|
+
}
|
|
182
|
+
return `${lines.join("\n")}\n`;
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/proxifier/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,8 @@ import type { BuilderConfig } from "../../config/schema.js";
|
|
|
2
2
|
export interface SyncRuleSetsInput {
|
|
3
3
|
readonly ruleSets: BuilderConfig["ruleSets"];
|
|
4
4
|
readonly force?: boolean;
|
|
5
|
+
readonly timeoutMs?: number;
|
|
6
|
+
readonly onProgress?: (event: RuleSetSyncProgressEvent) => void;
|
|
5
7
|
}
|
|
6
8
|
export interface SyncRuleSetsResult {
|
|
7
9
|
readonly downloaded: readonly string[];
|
|
@@ -12,5 +14,13 @@ export interface SyncRuleSetsResult {
|
|
|
12
14
|
reason: string;
|
|
13
15
|
}>;
|
|
14
16
|
}
|
|
17
|
+
export interface RuleSetSyncProgressEvent {
|
|
18
|
+
readonly tag: string;
|
|
19
|
+
readonly path: string;
|
|
20
|
+
readonly index: number;
|
|
21
|
+
readonly total: number;
|
|
22
|
+
readonly phase: "start" | "downloaded" | "bundled" | "skipped" | "failed";
|
|
23
|
+
readonly reason?: string;
|
|
24
|
+
}
|
|
15
25
|
export declare function syncLocalRuleSets(input: SyncRuleSetsInput): Promise<SyncRuleSetsResult>;
|
|
16
26
|
export declare function resolveRuleSetDownloadUrl(tag: string): string;
|
|
@@ -1,35 +1,63 @@
|
|
|
1
1
|
import { constants } from "node:fs";
|
|
2
|
-
import { access, mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
4
5
|
export async function syncLocalRuleSets(input) {
|
|
5
6
|
const downloaded = [];
|
|
6
7
|
const skipped = [];
|
|
7
8
|
const failed = [];
|
|
8
|
-
|
|
9
|
+
const total = input.ruleSets.length;
|
|
10
|
+
for (const [index, ruleSet] of input.ruleSets.entries()) {
|
|
11
|
+
const progressBase = {
|
|
12
|
+
tag: ruleSet.tag,
|
|
13
|
+
path: ruleSet.path,
|
|
14
|
+
index: index + 1,
|
|
15
|
+
total,
|
|
16
|
+
};
|
|
9
17
|
if (input.force !== true && (await pathExists(ruleSet.path))) {
|
|
10
18
|
skipped.push(ruleSet.tag);
|
|
19
|
+
input.onProgress?.({
|
|
20
|
+
...progressBase,
|
|
21
|
+
phase: "skipped",
|
|
22
|
+
});
|
|
11
23
|
continue;
|
|
12
24
|
}
|
|
13
25
|
try {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
const bundledPayload = await readBundledRuleSet(ruleSet.tag);
|
|
27
|
+
if (bundledPayload) {
|
|
28
|
+
await mkdir(path.dirname(ruleSet.path), { recursive: true });
|
|
29
|
+
await writeFile(ruleSet.path, bundledPayload);
|
|
30
|
+
downloaded.push(ruleSet.tag);
|
|
31
|
+
input.onProgress?.({
|
|
32
|
+
...progressBase,
|
|
33
|
+
phase: "bundled",
|
|
34
|
+
});
|
|
35
|
+
continue;
|
|
22
36
|
}
|
|
23
|
-
|
|
37
|
+
input.onProgress?.({
|
|
38
|
+
...progressBase,
|
|
39
|
+
phase: "start",
|
|
40
|
+
});
|
|
41
|
+
const payload = await downloadRuleSetWithRetry(ruleSet.tag, input.timeoutMs);
|
|
24
42
|
await mkdir(path.dirname(ruleSet.path), { recursive: true });
|
|
25
43
|
await writeFile(ruleSet.path, payload);
|
|
26
44
|
downloaded.push(ruleSet.tag);
|
|
45
|
+
input.onProgress?.({
|
|
46
|
+
...progressBase,
|
|
47
|
+
phase: "downloaded",
|
|
48
|
+
});
|
|
27
49
|
}
|
|
28
50
|
catch (error) {
|
|
51
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
29
52
|
failed.push({
|
|
30
53
|
tag: ruleSet.tag,
|
|
31
54
|
path: ruleSet.path,
|
|
32
|
-
reason
|
|
55
|
+
reason,
|
|
56
|
+
});
|
|
57
|
+
input.onProgress?.({
|
|
58
|
+
...progressBase,
|
|
59
|
+
phase: "failed",
|
|
60
|
+
reason,
|
|
33
61
|
});
|
|
34
62
|
}
|
|
35
63
|
}
|
|
@@ -43,6 +71,38 @@ export function resolveRuleSetDownloadUrl(tag) {
|
|
|
43
71
|
const repo = tag.startsWith("geoip-") ? "sing-geoip" : "sing-geosite";
|
|
44
72
|
return `https://raw.githubusercontent.com/SagerNet/${repo}/rule-set/${tag}.srs`;
|
|
45
73
|
}
|
|
74
|
+
async function downloadRuleSetWithRetry(tag, timeoutMs = 15000) {
|
|
75
|
+
let lastError;
|
|
76
|
+
for (let attempt = 1; attempt <= 3; attempt += 1) {
|
|
77
|
+
try {
|
|
78
|
+
const response = await fetch(resolveRuleSetDownloadUrl(tag), {
|
|
79
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
80
|
+
headers: {
|
|
81
|
+
accept: "application/octet-stream,*/*",
|
|
82
|
+
"user-agent": "singbox-iac/0.1.0",
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
if (!response.ok) {
|
|
86
|
+
throw new Error(`HTTP ${response.status} ${response.statusText}`);
|
|
87
|
+
}
|
|
88
|
+
return Buffer.from(await response.arrayBuffer());
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
lastError = error;
|
|
92
|
+
if (attempt < 3) {
|
|
93
|
+
await new Promise((resolve) => setTimeout(resolve, attempt * 500));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
98
|
+
}
|
|
99
|
+
async function readBundledRuleSet(tag) {
|
|
100
|
+
const bundledPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../../assets/rule-set", `${tag}.srs`);
|
|
101
|
+
if (!(await pathExists(bundledPath))) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
return readFile(bundledPath);
|
|
105
|
+
}
|
|
46
106
|
async function pathExists(filePath) {
|
|
47
107
|
try {
|
|
48
108
|
await access(filePath, constants.F_OK);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/rule-set-sync/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/rule-set-sync/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { buildConfigArtifact } from "../build/index.js";
|
|
2
2
|
import { applyConfig } from "../manager/index.js";
|
|
3
|
+
import { shouldAutoReloadRuntime } from "../manager/index.js";
|
|
3
4
|
import { assertVerificationReportPassed, verifyConfigRoutes, } from "../verification/index.js";
|
|
4
5
|
export async function runUpdate(input) {
|
|
5
6
|
const build = await buildConfigArtifact({
|
|
@@ -20,12 +21,13 @@ export async function runUpdate(input) {
|
|
|
20
21
|
}
|
|
21
22
|
const livePath = input.livePath ?? input.config.output.livePath;
|
|
22
23
|
const backupPath = input.backupPath ?? input.config.output.backupPath;
|
|
24
|
+
const reloaded = input.reload ?? (await shouldAutoReloadRuntime(input.config.runtime.reload));
|
|
23
25
|
await applyConfig({
|
|
24
26
|
stagingPath: build.outputPath,
|
|
25
27
|
livePath,
|
|
26
28
|
...(backupPath ? { backupPath } : {}),
|
|
27
29
|
...(input.singBoxBinary ? { singBoxBinary: input.singBoxBinary } : {}),
|
|
28
|
-
reload:
|
|
30
|
+
reload: reloaded,
|
|
29
31
|
runtime: input.config.runtime.reload,
|
|
30
32
|
});
|
|
31
33
|
return {
|
|
@@ -33,6 +35,7 @@ export async function runUpdate(input) {
|
|
|
33
35
|
...(verification ? { verification } : {}),
|
|
34
36
|
livePath,
|
|
35
37
|
...(backupPath ? { backupPath } : {}),
|
|
38
|
+
reloaded,
|
|
36
39
|
};
|
|
37
40
|
}
|
|
38
41
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/update/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAEL,8BAA8B,EAC9B,kBAAkB,GACnB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/modules/update/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAEL,8BAA8B,EAC9B,kBAAkB,GACnB,MAAM,0BAA0B,CAAC;AAuBlC,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAAqB;IACnD,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC;QACtC,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7D,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/E,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC7E,CAAC,CAAC;IAEH,IAAI,YAA4C,CAAC;IACjD,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QAC3B,YAAY,GAAG,MAAM,kBAAkB,CAAC;YACtC,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACtE,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnE,mBAAmB,EAAE,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,SAAS;SACzD,CAAC,CAAC;QACH,8BAA8B,CAAC,YAAY,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;IAChE,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC;IACtE,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,MAAM,uBAAuB,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAE9F,MAAM,WAAW,CAAC;QAChB,WAAW,EAAE,KAAK,CAAC,UAAU;QAC7B,QAAQ;QACR,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrC,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,MAAM,EAAE,QAAQ;QAChB,OAAO,EAAE,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM;KACrC,CAAC,CAAC;IAEH,OAAO;QACL,KAAK;QACL,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzC,QAAQ;QACR,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrC,QAAQ;KACT,CAAC;AACJ,CAAC"}
|
|
@@ -46,6 +46,14 @@ interface PreparedVerificationConfig {
|
|
|
46
46
|
readonly mixedPort: number;
|
|
47
47
|
readonly proxifierPort: number;
|
|
48
48
|
}
|
|
49
|
+
interface RuntimeScenario {
|
|
50
|
+
readonly id: string;
|
|
51
|
+
readonly name: string;
|
|
52
|
+
readonly url: string;
|
|
53
|
+
readonly inboundTag: "in-mixed" | "in-proxifier";
|
|
54
|
+
readonly proxyPort: number;
|
|
55
|
+
readonly expectedOutboundTag: string;
|
|
56
|
+
}
|
|
49
57
|
export interface ConfiguredVerificationScenario {
|
|
50
58
|
readonly id: BuilderConfig["verification"]["scenarios"][number]["id"];
|
|
51
59
|
readonly name: BuilderConfig["verification"]["scenarios"][number]["name"];
|
|
@@ -53,7 +61,14 @@ export interface ConfiguredVerificationScenario {
|
|
|
53
61
|
readonly inbound: BuilderConfig["verification"]["scenarios"][number]["inbound"];
|
|
54
62
|
readonly expectedOutbound: BuilderConfig["verification"]["scenarios"][number]["expectedOutbound"];
|
|
55
63
|
}
|
|
64
|
+
interface RequestRunResult {
|
|
65
|
+
readonly exitCode: number;
|
|
66
|
+
readonly stdout: string;
|
|
67
|
+
readonly stderr: string;
|
|
68
|
+
readonly timedOut: boolean;
|
|
69
|
+
}
|
|
56
70
|
export declare function verifyConfigRoutes(input: VerifyConfigRoutesInput): Promise<VerificationReport>;
|
|
71
|
+
export declare function isRouteLevelProxySuccess(scenario: Pick<RuntimeScenario, "inboundTag">, requestResult: RequestRunResult): boolean;
|
|
57
72
|
export declare function resolveChromeBinary(explicitPath?: string): Promise<string>;
|
|
58
73
|
export declare function openVisibleChromeWindows(input: {
|
|
59
74
|
readonly scenarios: readonly VisibleVerificationScenario[];
|
|
@@ -60,29 +60,7 @@ export async function verifyConfigRoutes(input) {
|
|
|
60
60
|
await waitForLog(logBuffer, /sing-box started/, 15_000, "Timed out waiting for sing-box startup during verification.");
|
|
61
61
|
const results = [];
|
|
62
62
|
for (const scenario of scenarios) {
|
|
63
|
-
|
|
64
|
-
const requestResult = await runProxyRequestScenario({
|
|
65
|
-
requestBinary,
|
|
66
|
-
proxyPort: scenario.proxyPort,
|
|
67
|
-
url: scenario.url,
|
|
68
|
-
});
|
|
69
|
-
const expectedLog = scenario.expectedOutboundTag === "direct"
|
|
70
|
-
? new RegExp(`outbound/direct\\[direct\\]: outbound connection to ${escapeRegExp(new URL(scenario.url).hostname)}:443`)
|
|
71
|
-
: new RegExp(`outbound/trojan\\[${escapeRegExp(scenario.expectedOutboundTag)}\\]: outbound connection to ${escapeRegExp(new URL(scenario.url).hostname)}:443`);
|
|
72
|
-
const inboundLog = new RegExp(`inbound/mixed\\[${escapeRegExp(scenario.inboundTag)}\\]: inbound connection to ${escapeRegExp(new URL(scenario.url).hostname)}:443`);
|
|
73
|
-
const excerpt = await waitForScenarioLogs(logBuffer, offset, [inboundLog, expectedLog], 20_000);
|
|
74
|
-
const requestFailure = detectRequestFailure(requestResult);
|
|
75
|
-
results.push({
|
|
76
|
-
name: scenario.name,
|
|
77
|
-
passed: excerpt !== undefined && requestFailure === undefined,
|
|
78
|
-
details: excerpt !== undefined && requestFailure === undefined
|
|
79
|
-
? excerpt.trim()
|
|
80
|
-
: (requestFailure ??
|
|
81
|
-
`Expected logs were not observed for ${new URL(scenario.url).hostname} within the timeout.`),
|
|
82
|
-
url: scenario.url,
|
|
83
|
-
inboundTag: scenario.inboundTag,
|
|
84
|
-
expectedOutboundTag: scenario.expectedOutboundTag,
|
|
85
|
-
});
|
|
63
|
+
results.push(await verifyRuntimeScenario(scenario, logBuffer, requestBinary));
|
|
86
64
|
}
|
|
87
65
|
return {
|
|
88
66
|
configPath: input.configPath,
|
|
@@ -99,6 +77,57 @@ export async function verifyConfigRoutes(input) {
|
|
|
99
77
|
await Promise.race([exitPromise, new Promise((resolve) => setTimeout(resolve, 2_000))]);
|
|
100
78
|
}
|
|
101
79
|
}
|
|
80
|
+
async function verifyRuntimeScenario(scenario, logBuffer, requestBinary) {
|
|
81
|
+
const hostname = new URL(scenario.url).hostname;
|
|
82
|
+
const expectedLog = scenario.expectedOutboundTag === "direct"
|
|
83
|
+
? new RegExp(`outbound/direct\\[direct\\]: outbound connection to ${escapeRegExp(hostname)}:443`)
|
|
84
|
+
: new RegExp(`outbound/trojan\\[${escapeRegExp(scenario.expectedOutboundTag)}\\]: outbound connection to ${escapeRegExp(hostname)}:443`);
|
|
85
|
+
const inboundLog = new RegExp(`inbound/mixed\\[${escapeRegExp(scenario.inboundTag)}\\]: inbound connection to ${escapeRegExp(hostname)}:443`);
|
|
86
|
+
let lastFailure = `Expected logs were not observed for ${hostname} within the timeout.`;
|
|
87
|
+
for (let attempt = 1; attempt <= 2; attempt += 1) {
|
|
88
|
+
const offset = logBuffer.text.length;
|
|
89
|
+
const requestResult = await runProxyRequestScenario({
|
|
90
|
+
requestBinary,
|
|
91
|
+
proxyPort: scenario.proxyPort,
|
|
92
|
+
url: scenario.url,
|
|
93
|
+
});
|
|
94
|
+
const excerpt = await waitForScenarioLogs(logBuffer, offset, [inboundLog, expectedLog], 20_000);
|
|
95
|
+
const requestFailure = detectRequestFailure(requestResult);
|
|
96
|
+
const routeLevelProxySuccess = isRouteLevelProxySuccess(scenario, requestResult);
|
|
97
|
+
if (excerpt !== undefined && (requestFailure === undefined || routeLevelProxySuccess)) {
|
|
98
|
+
return {
|
|
99
|
+
name: scenario.name,
|
|
100
|
+
passed: true,
|
|
101
|
+
details: routeLevelProxySuccess
|
|
102
|
+
? `${excerpt.trim()}\nRoute matched and proxy CONNECT succeeded; upstream TLS did not finish before curl timed out.`
|
|
103
|
+
: excerpt.trim(),
|
|
104
|
+
url: scenario.url,
|
|
105
|
+
inboundTag: scenario.inboundTag,
|
|
106
|
+
expectedOutboundTag: scenario.expectedOutboundTag,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
lastFailure =
|
|
110
|
+
requestFailure ?? `Expected logs were not observed for ${hostname} within the timeout.`;
|
|
111
|
+
if (attempt < 2) {
|
|
112
|
+
await sleep(500);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
name: scenario.name,
|
|
117
|
+
passed: false,
|
|
118
|
+
details: lastFailure,
|
|
119
|
+
url: scenario.url,
|
|
120
|
+
inboundTag: scenario.inboundTag,
|
|
121
|
+
expectedOutboundTag: scenario.expectedOutboundTag,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
export function isRouteLevelProxySuccess(scenario, requestResult) {
|
|
125
|
+
if (scenario.inboundTag !== "in-proxifier") {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
return (requestResult.exitCode === 28 &&
|
|
129
|
+
`${requestResult.stdout}\n${requestResult.stderr}`.includes("HTTP/1.1 200 Connection established"));
|
|
130
|
+
}
|
|
102
131
|
export async function resolveChromeBinary(explicitPath) {
|
|
103
132
|
const candidates = [
|
|
104
133
|
explicitPath,
|
|
@@ -180,6 +209,17 @@ export async function prepareVerificationConfig(config) {
|
|
|
180
209
|
}
|
|
181
210
|
}
|
|
182
211
|
cloned.log = { level: "debug" };
|
|
212
|
+
const dns = asObject(cloned.dns, "Config is missing dns.");
|
|
213
|
+
dns.servers = [
|
|
214
|
+
{
|
|
215
|
+
type: "local",
|
|
216
|
+
tag: "dns-local-verify",
|
|
217
|
+
prefer_go: true,
|
|
218
|
+
},
|
|
219
|
+
];
|
|
220
|
+
dns.final = "dns-local-verify";
|
|
221
|
+
const route = asObject(cloned.route, "Config is missing route.");
|
|
222
|
+
route.default_domain_resolver = "dns-local-verify";
|
|
183
223
|
const outbounds = ensureArray(cloned.outbounds, "Config is missing outbounds.");
|
|
184
224
|
const globalIndex = outbounds.findIndex((outbound) => outbound.tag === "Global");
|
|
185
225
|
if (globalIndex >= 0) {
|
|
@@ -227,27 +267,32 @@ export function validateConfigInvariants(config) {
|
|
|
227
267
|
const chinaIndex = rules.findIndex((rule) => Array.isArray(rule.rule_set) &&
|
|
228
268
|
rule.rule_set.includes("geosite-cn") &&
|
|
229
269
|
rule.outbound === "direct");
|
|
230
|
-
|
|
231
|
-
quicIndex,
|
|
232
|
-
dnsIndex,
|
|
233
|
-
proxifierIndex,
|
|
234
|
-
stitchIndex,
|
|
235
|
-
explicitAiIndex,
|
|
236
|
-
aiRuleSetIndex,
|
|
237
|
-
devRuleSetIndex,
|
|
238
|
-
chinaIndex,
|
|
239
|
-
]
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
270
|
+
const priorityOrder = [
|
|
271
|
+
{ name: "quic", index: quicIndex, required: true },
|
|
272
|
+
{ name: "dns", index: dnsIndex, required: true },
|
|
273
|
+
{ name: "proxifier", index: proxifierIndex, required: true },
|
|
274
|
+
{ name: "stitch", index: stitchIndex, required: true },
|
|
275
|
+
{ name: "explicitAi", index: explicitAiIndex, required: true },
|
|
276
|
+
{ name: "aiRuleSet", index: aiRuleSetIndex, required: false },
|
|
277
|
+
{ name: "devRuleSet", index: devRuleSetIndex, required: false },
|
|
278
|
+
{ name: "china", index: chinaIndex, required: false },
|
|
279
|
+
];
|
|
280
|
+
const routePriorityPassed = priorityOrder.every((entry) => !entry.required || entry.index >= 0) &&
|
|
281
|
+
priorityOrder
|
|
282
|
+
.filter((entry) => entry.index >= 0)
|
|
283
|
+
.every((entry, index, entries) => {
|
|
284
|
+
if (index === 0) {
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
const previous = entries[index - 1];
|
|
288
|
+
return previous !== undefined && previous.index < entry.index;
|
|
289
|
+
});
|
|
290
|
+
checks.push(makeCheck("route-priority", routePriorityPassed, `Rule order indices: quic=${quicIndex}, dns=${dnsIndex}, proxifier=${proxifierIndex}, stitch=${stitchIndex}, explicitAi=${explicitAiIndex}, aiRuleSet=${aiRuleSetIndex}, devRuleSet=${devRuleSetIndex}, china=${chinaIndex}`));
|
|
247
291
|
checks.push(makeCheck("default-domain-resolver", typeof route.default_domain_resolver === "string" &&
|
|
248
292
|
dnsServers.some((server) => server.tag === route.default_domain_resolver), `default_domain_resolver=${String(route.default_domain_resolver)}`));
|
|
249
|
-
checks.push(makeCheck("dns-shape", dnsServers.some((server) => server.type === "
|
|
250
|
-
dnsServers.some((server) => server.type === "udp" && server.server === "
|
|
293
|
+
checks.push(makeCheck("dns-shape", dnsServers.some((server) => server.type === "local" && server.tag === "dns-local-default") &&
|
|
294
|
+
dnsServers.some((server) => (server.type === "tcp" || server.type === "udp") && server.server === "1.1.1.1") &&
|
|
295
|
+
dnsServers.some((server) => (server.type === "tcp" || server.type === "udp") && server.server === "223.5.5.5"), `dns servers=${dnsServers
|
|
251
296
|
.map((server) => `${String(server.tag)}:${String(server.server)}`)
|
|
252
297
|
.join(", ")}`));
|
|
253
298
|
return checks;
|
|
@@ -334,14 +379,14 @@ const defaultConfiguredScenarios = [
|
|
|
334
379
|
{
|
|
335
380
|
id: "cn-direct",
|
|
336
381
|
name: "China traffic routes direct on mixed inbound",
|
|
337
|
-
url: "https://www.
|
|
382
|
+
url: "https://www.qq.com/favicon.ico",
|
|
338
383
|
inbound: "in-mixed",
|
|
339
384
|
expectedOutbound: "direct",
|
|
340
385
|
},
|
|
341
386
|
{
|
|
342
387
|
id: "proxifier-precedence",
|
|
343
388
|
name: "Proxifier inbound overrides CN direct rule",
|
|
344
|
-
url: "https://www.
|
|
389
|
+
url: "https://www.qq.com/favicon.ico",
|
|
345
390
|
inbound: "in-proxifier",
|
|
346
391
|
expectedOutbound: "Process-Proxy",
|
|
347
392
|
},
|