@stanleysun233/oneproxy-cli 1.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.
- package/bin/onep.js +2 -0
- package/dist/commands.d.ts +15 -0
- package/dist/commands.js +216 -0
- package/dist/commands.js.map +1 -0
- package/dist/control-plane.d.ts +41 -0
- package/dist/control-plane.js +284 -0
- package/dist/control-plane.js.map +1 -0
- package/dist/daemon/http-proxy.d.ts +11 -0
- package/dist/daemon/http-proxy.js +148 -0
- package/dist/daemon/http-proxy.js.map +1 -0
- package/dist/daemon/lifecycle.d.ts +129 -0
- package/dist/daemon/lifecycle.js +350 -0
- package/dist/daemon/lifecycle.js.map +1 -0
- package/dist/daemon/port-selection.d.ts +9 -0
- package/dist/daemon/port-selection.js +61 -0
- package/dist/daemon/port-selection.js.map +1 -0
- package/dist/daemon/router.d.ts +35 -0
- package/dist/daemon/router.js +105 -0
- package/dist/daemon/router.js.map +1 -0
- package/dist/doctor.d.ts +29 -0
- package/dist/doctor.js +178 -0
- package/dist/doctor.js.map +1 -0
- package/dist/init.d.ts +2 -0
- package/dist/init.js +199 -0
- package/dist/init.js.map +1 -0
- package/dist/main.d.ts +5 -0
- package/dist/main.js +130 -0
- package/dist/main.js.map +1 -0
- package/dist/profile.d.ts +2 -0
- package/dist/profile.js +53 -0
- package/dist/profile.js.map +1 -0
- package/dist/session-env.d.ts +8 -0
- package/dist/session-env.js +219 -0
- package/dist/session-env.js.map +1 -0
- package/dist/shell.d.ts +3 -0
- package/dist/shell.js +50 -0
- package/dist/shell.js.map +1 -0
- package/dist/ssh.d.ts +19 -0
- package/dist/ssh.js +83 -0
- package/dist/ssh.js.map +1 -0
- package/dist/storage.d.ts +115 -0
- package/dist/storage.js +224 -0
- package/dist/storage.js.map +1 -0
- package/package.json +27 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as net from 'node:net';
|
|
2
|
+
export const excludedCommonPorts = [
|
|
3
|
+
20, 21, 22, 25, 53, 80, 110, 143, 443, 3306, 5432, 6379, 8080
|
|
4
|
+
];
|
|
5
|
+
const excludedPortSet = new Set(excludedCommonPorts);
|
|
6
|
+
const defaultStartPort = 10000;
|
|
7
|
+
const defaultEndPort = 60999;
|
|
8
|
+
const loopbackHost = '127.0.0.1';
|
|
9
|
+
const scanBatchSize = 256;
|
|
10
|
+
export async function selectProxyPorts() {
|
|
11
|
+
const candidatePorts = await scanAvailableCandidatePorts();
|
|
12
|
+
const pairs = consecutivePairs(candidatePorts);
|
|
13
|
+
if (pairs.length === 0) {
|
|
14
|
+
throw new Error('No available consecutive loopback proxy port pair');
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
candidatePorts,
|
|
18
|
+
selectedPair: pairs[Math.floor(Math.random() * pairs.length)],
|
|
19
|
+
excludedCommonPorts
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export async function scanAvailableCandidatePorts(start = defaultStartPort, end = defaultEndPort) {
|
|
23
|
+
const candidatePorts = [];
|
|
24
|
+
for (let batchStart = start; batchStart <= end; batchStart += scanBatchSize) {
|
|
25
|
+
const batchEnd = Math.min(batchStart + scanBatchSize - 1, end);
|
|
26
|
+
const ports = Array.from({ length: batchEnd - batchStart + 1 }, (_, index) => batchStart + index);
|
|
27
|
+
const results = await Promise.all(ports.map(async (port) => ({
|
|
28
|
+
port,
|
|
29
|
+
usable: await isUsablePort(port)
|
|
30
|
+
})));
|
|
31
|
+
for (const result of results) {
|
|
32
|
+
if (result.usable) {
|
|
33
|
+
candidatePorts.push(result.port);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return candidatePorts;
|
|
38
|
+
}
|
|
39
|
+
export async function isUsablePort(port) {
|
|
40
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535 || excludedPortSet.has(port)) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const server = net.createServer();
|
|
44
|
+
return await new Promise((resolve) => {
|
|
45
|
+
server.once('error', () => resolve(false));
|
|
46
|
+
server.listen(port, loopbackHost, () => {
|
|
47
|
+
server.close(() => resolve(true));
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function consecutivePairs(candidatePorts) {
|
|
52
|
+
const pairs = [];
|
|
53
|
+
const candidates = new Set(candidatePorts);
|
|
54
|
+
for (const port of candidatePorts) {
|
|
55
|
+
if (candidates.has(port + 1)) {
|
|
56
|
+
pairs.push([port, port + 1]);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return pairs;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=port-selection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port-selection.js","sourceRoot":"","sources":["../../src/daemon/port-selection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { OneProxyConfig, OneProxyState, RouteRule } from './lifecycle.ts';
|
|
2
|
+
export type RouteMode = 'direct' | 'proxy';
|
|
3
|
+
export type RouteResolverInput = {
|
|
4
|
+
config: OneProxyConfig;
|
|
5
|
+
state: OneProxyState;
|
|
6
|
+
target: string;
|
|
7
|
+
protocol?: string;
|
|
8
|
+
};
|
|
9
|
+
export type RouteResult = {
|
|
10
|
+
target: string;
|
|
11
|
+
host: string;
|
|
12
|
+
port: number;
|
|
13
|
+
mode: RouteMode;
|
|
14
|
+
matched: {
|
|
15
|
+
source: 'local_override_direct' | 'local_override_proxy' | 'policy' | 'default_direct';
|
|
16
|
+
ruleId?: string;
|
|
17
|
+
ruleType?: RouteRule['type'];
|
|
18
|
+
pattern?: string;
|
|
19
|
+
};
|
|
20
|
+
tenant: {
|
|
21
|
+
id?: string;
|
|
22
|
+
name?: string;
|
|
23
|
+
};
|
|
24
|
+
group: {
|
|
25
|
+
id?: string;
|
|
26
|
+
name?: string;
|
|
27
|
+
};
|
|
28
|
+
topology: {
|
|
29
|
+
entryNodeId: string;
|
|
30
|
+
entryHost: string;
|
|
31
|
+
entryPort: number;
|
|
32
|
+
protocol: string;
|
|
33
|
+
} | null;
|
|
34
|
+
};
|
|
35
|
+
export declare function resolveRoute(input: RouteResolverInput): RouteResult;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
export function resolveRoute(input) {
|
|
2
|
+
const target = parseTarget(input.target, input.protocol);
|
|
3
|
+
const host = target.host.toLowerCase();
|
|
4
|
+
const group = activeRouteGroup(input);
|
|
5
|
+
const entryNode = firstEntryNode(input.state);
|
|
6
|
+
if (matchesOverride(host, input.config.overrides?.direct ?? [])) {
|
|
7
|
+
return routeResult(input, target, 'direct', { source: 'local_override_direct' }, group, null);
|
|
8
|
+
}
|
|
9
|
+
if (matchesOverride(host, input.config.overrides?.proxy ?? [])) {
|
|
10
|
+
return routeResult(input, target, 'proxy', { source: 'local_override_proxy' }, group, entryNode);
|
|
11
|
+
}
|
|
12
|
+
const rule = group?.rules.find((candidate) => matchesRule(host, candidate));
|
|
13
|
+
if (rule) {
|
|
14
|
+
return routeResult(input, target, rule.mode, { source: 'policy', ruleId: rule.id, ruleType: rule.type, pattern: rule.pattern }, group, rule.mode === 'proxy' ? entryNode : null);
|
|
15
|
+
}
|
|
16
|
+
return routeResult(input, target, 'direct', { source: 'default_direct' }, group, null);
|
|
17
|
+
}
|
|
18
|
+
function activeRouteGroup(input) {
|
|
19
|
+
return (input.state.routeGroups ?? []).find((group) => group.id === input.config.activeGroupId);
|
|
20
|
+
}
|
|
21
|
+
function firstEntryNode(state) {
|
|
22
|
+
const node = state.bootstrap?.entryNodes?.[0];
|
|
23
|
+
return node ? node : null;
|
|
24
|
+
}
|
|
25
|
+
function routeResult(input, target, mode, matched, group, entryNode) {
|
|
26
|
+
return {
|
|
27
|
+
target: target.original,
|
|
28
|
+
host: target.host,
|
|
29
|
+
port: target.port,
|
|
30
|
+
mode,
|
|
31
|
+
matched,
|
|
32
|
+
tenant: { id: input.config.activeTenantId },
|
|
33
|
+
group: { id: group?.id, name: group?.name },
|
|
34
|
+
topology: mode === 'proxy' && entryNode ? {
|
|
35
|
+
entryNodeId: entryNode.id,
|
|
36
|
+
entryHost: entryNode.host,
|
|
37
|
+
entryPort: entryNode.port,
|
|
38
|
+
protocol: entryNode.protocol
|
|
39
|
+
} : null
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function parseTarget(target, protocol = 'https') {
|
|
43
|
+
const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(target) ? target : `${protocol}://${target}`;
|
|
44
|
+
const url = new URL(withScheme);
|
|
45
|
+
const port = url.port ? Number(url.port) : defaultPort(url.protocol);
|
|
46
|
+
return {
|
|
47
|
+
original: target,
|
|
48
|
+
host: url.hostname.toLowerCase(),
|
|
49
|
+
port
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function defaultPort(protocol) {
|
|
53
|
+
if (protocol === 'http:') {
|
|
54
|
+
return 80;
|
|
55
|
+
}
|
|
56
|
+
if (protocol === 'ssh:') {
|
|
57
|
+
return 22;
|
|
58
|
+
}
|
|
59
|
+
return 443;
|
|
60
|
+
}
|
|
61
|
+
function matchesOverride(host, overrides) {
|
|
62
|
+
return overrides.some((override) => hostMatchesPattern(host, override.toLowerCase()));
|
|
63
|
+
}
|
|
64
|
+
function matchesRule(host, rule) {
|
|
65
|
+
const pattern = rule.pattern.toLowerCase();
|
|
66
|
+
if (rule.type === 'wildcard') {
|
|
67
|
+
return pattern === '*' || hostMatchesPattern(host, pattern);
|
|
68
|
+
}
|
|
69
|
+
if (rule.type === 'suffix') {
|
|
70
|
+
return host === pattern || host.endsWith(`.${pattern.replace(/^\./, '')}`);
|
|
71
|
+
}
|
|
72
|
+
if (rule.type === 'domain') {
|
|
73
|
+
return host === pattern;
|
|
74
|
+
}
|
|
75
|
+
if (rule.type === 'cidr') {
|
|
76
|
+
return matchesIpv4Cidr(host, pattern);
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
function hostMatchesPattern(host, pattern) {
|
|
81
|
+
if (pattern.startsWith('*.')) {
|
|
82
|
+
const suffix = pattern.slice(2);
|
|
83
|
+
return host.endsWith(`.${suffix}`);
|
|
84
|
+
}
|
|
85
|
+
return host === pattern || host.endsWith(`.${pattern}`);
|
|
86
|
+
}
|
|
87
|
+
function matchesIpv4Cidr(host, cidr) {
|
|
88
|
+
const [range, prefixText] = cidr.split('/');
|
|
89
|
+
const prefix = Number(prefixText);
|
|
90
|
+
const hostValue = ipv4ToNumber(host);
|
|
91
|
+
const rangeValue = ipv4ToNumber(range);
|
|
92
|
+
if (hostValue === null || rangeValue === null || !Number.isInteger(prefix) || prefix < 0 || prefix > 32) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const mask = prefix === 0 ? 0 : (0xffffffff << (32 - prefix)) >>> 0;
|
|
96
|
+
return (hostValue & mask) === (rangeValue & mask);
|
|
97
|
+
}
|
|
98
|
+
function ipv4ToNumber(value) {
|
|
99
|
+
const parts = value.split('.').map(Number);
|
|
100
|
+
if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
return (((parts[0] << 24) >>> 0) + (parts[1] << 16) + (parts[2] << 8) + parts[3]) >>> 0;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/daemon/router.ts"],"names":[],"mappings":"AAsCA,MAAM,UAAU,YAAY,CAAC,KAAyB;IACpD,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACzD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IACvC,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAE9C,IAAI,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;QAChE,OAAO,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,uBAAuB,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IAChG,CAAC;IACD,IAAI,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;QAC/D,OAAO,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,sBAAsB,EAAE,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IACnG,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;IAC5E,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,WAAW,CAChB,KAAK,EACL,MAAM,EACN,IAAI,CAAC,IAAI,EACT,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EACjF,KAAK,EACL,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CACzC,CAAC;IACJ,CAAC;IAED,OAAO,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,gBAAgB,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;AACzF,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAyB;IACjD,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;AAClG,CAAC;AAED,SAAS,cAAc,CAAC,KAAoB;IAC1C,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9C,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5B,CAAC;AAED,SAAS,WAAW,CAClB,KAAyB,EACzB,MAAwD,EACxD,IAAe,EACf,OAA+B,EAC/B,KAA0C,EAC1C,SAA2B;IAE3B,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,QAAQ;QACvB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,IAAI;QACJ,OAAO;QACP,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,MAAM,CAAC,cAAc,EAAE;QAC3C,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;QAC3C,QAAQ,EAAE,IAAI,KAAK,OAAO,IAAI,SAAS,CAAC,CAAC,CAAC;YACxC,WAAW,EAAE,SAAS,CAAC,EAAE;YACzB,SAAS,EAAE,SAAS,CAAC,IAAI;YACzB,SAAS,EAAE,SAAS,CAAC,IAAI;YACzB,QAAQ,EAAE,SAAS,CAAC,QAAQ;SAC7B,CAAC,CAAC,CAAC,IAAI;KACT,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,MAAc,EAAE,QAAQ,GAAG,OAAO;IACrD,MAAM,UAAU,GAAG,0BAA0B,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,QAAQ,MAAM,MAAM,EAAE,CAAC;IAChG,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;IAChC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACrE,OAAO;QACL,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE;QAChC,IAAI;KACL,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,QAAgB;IACnC,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACxB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,eAAe,CAAC,IAAY,EAAE,SAAmB;IACxD,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,kBAAkB,CAAC,IAAI,EAAE,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;AACxF,CAAC;AAED,SAAS,WAAW,CAAC,IAAY,EAAE,IAAe;IAChD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;IAC3C,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAC7B,OAAO,OAAO,KAAK,GAAG,IAAI,kBAAkB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC3B,OAAO,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC3B,OAAO,IAAI,KAAK,OAAO,CAAC;IAC1B,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACzB,OAAO,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAY,EAAE,OAAe;IACvD,IAAI,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAChC,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,OAAO,EAAE,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,eAAe,CAAC,IAAY,EAAE,IAAY;IACjD,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;IAClC,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,UAAU,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,SAAS,KAAK,IAAI,IAAI,UAAU,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,EAAE,EAAE,CAAC;QACxG,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC;IACpE,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;AACpD,CAAC;AAED,SAAS,YAAY,CAAC,KAAa;IACjC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;QAClG,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AAC1F,CAAC"}
|
package/dist/doctor.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type RouteResult } from './daemon/router.ts';
|
|
2
|
+
export type CheckStatus = 'pass' | 'warn' | 'fail';
|
|
3
|
+
export type DoctorCheck = {
|
|
4
|
+
name: string;
|
|
5
|
+
status: CheckStatus;
|
|
6
|
+
message: string;
|
|
7
|
+
action?: string;
|
|
8
|
+
};
|
|
9
|
+
export type ProbeResult = {
|
|
10
|
+
name: string;
|
|
11
|
+
status: CheckStatus;
|
|
12
|
+
latencyMs?: number;
|
|
13
|
+
message: string;
|
|
14
|
+
};
|
|
15
|
+
export type TestResult = {
|
|
16
|
+
route: RouteResult;
|
|
17
|
+
probes: ProbeResult[];
|
|
18
|
+
};
|
|
19
|
+
export type DoctorResult = {
|
|
20
|
+
summary: {
|
|
21
|
+
status: CheckStatus;
|
|
22
|
+
passed: number;
|
|
23
|
+
warned: number;
|
|
24
|
+
failed: number;
|
|
25
|
+
};
|
|
26
|
+
checks: DoctorCheck[];
|
|
27
|
+
};
|
|
28
|
+
export declare function probeTarget(target: string): Promise<TestResult>;
|
|
29
|
+
export declare function runDoctor(routeTarget?: string): Promise<DoctorResult>;
|
package/dist/doctor.js
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import * as dns from 'node:dns/promises';
|
|
2
|
+
import * as http from 'node:http';
|
|
3
|
+
import * as https from 'node:https';
|
|
4
|
+
import * as net from 'node:net';
|
|
5
|
+
import { loopbackHost, probeDaemon, readConfig, readDaemonMetadata, readState, readTokens, startDaemonRuntime, storagePath } from "./daemon/lifecycle.js";
|
|
6
|
+
import { isUsablePort } from "./daemon/port-selection.js";
|
|
7
|
+
import { resolveRoute } from "./daemon/router.js";
|
|
8
|
+
export async function probeTarget(target) {
|
|
9
|
+
const [config, state] = await Promise.all([readConfig(), readState()]);
|
|
10
|
+
const route = resolveRoute({ config, state, target });
|
|
11
|
+
const probes = [await dnsProbe(route.host)];
|
|
12
|
+
if (route.mode === 'direct') {
|
|
13
|
+
probes.push(await tcpProbe('direct_connect', route.host, route.port));
|
|
14
|
+
}
|
|
15
|
+
else if (route.topology) {
|
|
16
|
+
probes.push(await tcpProbe('proxy_connect', route.topology.entryHost, route.topology.entryPort));
|
|
17
|
+
}
|
|
18
|
+
const metadata = await readDaemonMetadata();
|
|
19
|
+
if (metadata) {
|
|
20
|
+
probes.push(await tcpProbe('http_proxy', metadata.bindings.host, metadata.bindings.httpPort));
|
|
21
|
+
probes.push(await tcpProbe('https_proxy', metadata.bindings.host, metadata.bindings.httpsPort));
|
|
22
|
+
}
|
|
23
|
+
return { route, probes };
|
|
24
|
+
}
|
|
25
|
+
export async function runDoctor(routeTarget = 'https://example.com') {
|
|
26
|
+
const checks = [];
|
|
27
|
+
const config = await readConfig().then((value) => {
|
|
28
|
+
checks.push({ name: 'config', status: 'pass', message: 'Config file is readable' });
|
|
29
|
+
return value;
|
|
30
|
+
}, () => {
|
|
31
|
+
checks.push({ name: 'config', status: 'fail', message: 'Config file is not readable', action: `Check ${storagePath('config')}` });
|
|
32
|
+
return null;
|
|
33
|
+
});
|
|
34
|
+
const tokens = await readTokens();
|
|
35
|
+
checks.push(tokens ? { name: 'token_readability', status: 'pass', message: 'Token file is readable' } : {
|
|
36
|
+
name: 'token_readability',
|
|
37
|
+
status: 'fail',
|
|
38
|
+
message: 'Token file is missing',
|
|
39
|
+
action: 'Run onep login'
|
|
40
|
+
});
|
|
41
|
+
const state = await readState();
|
|
42
|
+
checks.push(await controlPlaneHealthCheck(config?.controlPlaneUrl));
|
|
43
|
+
checks.push(bootstrapSyncCheck(state.fetchedAt, state.bootstrap?.entryNodes?.length ?? 0));
|
|
44
|
+
const runtime = await startDaemonRuntime();
|
|
45
|
+
const metadata = runtime.metadata;
|
|
46
|
+
const health = await probeDaemon(metadata);
|
|
47
|
+
checks.push(health ? { name: 'daemon_status', status: 'pass', message: 'Daemon health endpoint is reachable' } : {
|
|
48
|
+
name: 'daemon_status',
|
|
49
|
+
status: 'fail',
|
|
50
|
+
message: 'Daemon health endpoint is not reachable',
|
|
51
|
+
action: 'Run a command that starts the daemon, such as onep env on'
|
|
52
|
+
});
|
|
53
|
+
checks.push(await localPortsCheck(metadata));
|
|
54
|
+
if (config) {
|
|
55
|
+
const route = resolveRoute({ config, state, target: routeTarget });
|
|
56
|
+
checks.push({ name: 'route_calculation', status: 'pass', message: `Route calculation returned ${route.mode}` });
|
|
57
|
+
checks.push(await entryNodeReachabilityCheck(route));
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
checks.push({ name: 'route_calculation', status: 'fail', message: 'Route calculation requires readable config' });
|
|
61
|
+
checks.push({ name: 'entry_node_reachability', status: 'fail', message: 'Entry node reachability requires route calculation' });
|
|
62
|
+
}
|
|
63
|
+
await runtime.close();
|
|
64
|
+
return summarize(checks);
|
|
65
|
+
}
|
|
66
|
+
async function controlPlaneHealthCheck(controlPlaneUrl) {
|
|
67
|
+
if (!controlPlaneUrl) {
|
|
68
|
+
return { name: 'control_plane_health', status: 'fail', message: 'Control plane URL is not configured', action: 'Run onep login' };
|
|
69
|
+
}
|
|
70
|
+
const reachable = await httpHealth(controlPlaneUrl);
|
|
71
|
+
return reachable ? { name: 'control_plane_health', status: 'pass', message: 'Control plane is reachable' } : {
|
|
72
|
+
name: 'control_plane_health',
|
|
73
|
+
status: 'warn',
|
|
74
|
+
message: 'Control plane health endpoint is not reachable'
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function bootstrapSyncCheck(fetchedAt, entryNodeCount) {
|
|
78
|
+
if (!fetchedAt || entryNodeCount === 0) {
|
|
79
|
+
return { name: 'bootstrap_sync', status: 'fail', message: 'Bootstrap state is missing entry nodes', action: 'Run onep sync' };
|
|
80
|
+
}
|
|
81
|
+
return { name: 'bootstrap_sync', status: 'pass', message: 'Bootstrap state has entry nodes' };
|
|
82
|
+
}
|
|
83
|
+
async function localPortsCheck(metadata) {
|
|
84
|
+
if (!metadata) {
|
|
85
|
+
return { name: 'local_ports', status: 'fail', message: 'Daemon metadata is missing' };
|
|
86
|
+
}
|
|
87
|
+
const { httpPort, httpsPort } = metadata.bindings;
|
|
88
|
+
if (httpsPort !== httpPort + 1) {
|
|
89
|
+
return { name: 'local_ports', status: 'fail', message: 'HTTP and HTTPS proxy ports are not consecutive' };
|
|
90
|
+
}
|
|
91
|
+
const [httpOpen, httpsOpen] = await Promise.all([
|
|
92
|
+
tcpOpen(loopbackHost, httpPort),
|
|
93
|
+
tcpOpen(loopbackHost, httpsPort)
|
|
94
|
+
]);
|
|
95
|
+
if (!httpOpen || !httpsOpen || await isUsablePort(httpPort) || await isUsablePort(httpsPort)) {
|
|
96
|
+
return { name: 'local_ports', status: 'fail', message: 'Local proxy ports are not both listening' };
|
|
97
|
+
}
|
|
98
|
+
return { name: 'local_ports', status: 'pass', message: 'Local HTTP and HTTPS proxy ports are listening' };
|
|
99
|
+
}
|
|
100
|
+
async function entryNodeReachabilityCheck(route) {
|
|
101
|
+
if (route.mode === 'direct') {
|
|
102
|
+
return { name: 'entry_node_reachability', status: 'pass', message: 'Route is direct; no entry node required' };
|
|
103
|
+
}
|
|
104
|
+
if (!route.topology) {
|
|
105
|
+
return { name: 'entry_node_reachability', status: 'fail', message: 'Proxied route has no entry node', action: 'Run onep sync' };
|
|
106
|
+
}
|
|
107
|
+
const open = await tcpOpen(route.topology.entryHost, route.topology.entryPort);
|
|
108
|
+
return open ? { name: 'entry_node_reachability', status: 'pass', message: 'Entry node is reachable' } : {
|
|
109
|
+
name: 'entry_node_reachability',
|
|
110
|
+
status: 'fail',
|
|
111
|
+
message: 'Entry node is not reachable'
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
async function dnsProbe(host) {
|
|
115
|
+
const started = Date.now();
|
|
116
|
+
try {
|
|
117
|
+
await dns.lookup(host);
|
|
118
|
+
return { name: 'dns', status: 'pass', latencyMs: Date.now() - started, message: `Resolved ${host}` };
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return { name: 'dns', status: 'fail', latencyMs: Date.now() - started, message: `Could not resolve ${host}` };
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async function tcpProbe(name, host, port) {
|
|
125
|
+
const started = Date.now();
|
|
126
|
+
const open = await tcpOpen(host, port);
|
|
127
|
+
return {
|
|
128
|
+
name,
|
|
129
|
+
status: open ? 'pass' : 'fail',
|
|
130
|
+
latencyMs: Date.now() - started,
|
|
131
|
+
message: open ? `Connected to ${host}:${port}` : `Could not connect to ${host}:${port}`
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
async function tcpOpen(host, port) {
|
|
135
|
+
const socket = net.connect({ host, port });
|
|
136
|
+
socket.setTimeout(1000);
|
|
137
|
+
return await new Promise((resolve) => {
|
|
138
|
+
socket.once('connect', () => {
|
|
139
|
+
socket.destroy();
|
|
140
|
+
resolve(true);
|
|
141
|
+
});
|
|
142
|
+
socket.once('error', () => resolve(false));
|
|
143
|
+
socket.once('timeout', () => {
|
|
144
|
+
socket.destroy();
|
|
145
|
+
resolve(false);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
async function httpHealth(controlPlaneUrl) {
|
|
150
|
+
const url = new URL('/healthz', controlPlaneUrl);
|
|
151
|
+
const client = url.protocol === 'https:' ? https : http;
|
|
152
|
+
return await new Promise((resolve) => {
|
|
153
|
+
const request = client.get(url, (response) => {
|
|
154
|
+
response.resume();
|
|
155
|
+
resolve((response.statusCode ?? 500) < 500);
|
|
156
|
+
});
|
|
157
|
+
request.setTimeout(1500, () => {
|
|
158
|
+
request.destroy();
|
|
159
|
+
resolve(false);
|
|
160
|
+
});
|
|
161
|
+
request.on('error', () => resolve(false));
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
function summarize(checks) {
|
|
165
|
+
const passed = checks.filter((check) => check.status === 'pass').length;
|
|
166
|
+
const warned = checks.filter((check) => check.status === 'warn').length;
|
|
167
|
+
const failed = checks.filter((check) => check.status === 'fail').length;
|
|
168
|
+
return {
|
|
169
|
+
summary: {
|
|
170
|
+
status: failed > 0 ? 'fail' : warned > 0 ? 'warn' : 'pass',
|
|
171
|
+
passed,
|
|
172
|
+
warned,
|
|
173
|
+
failed
|
|
174
|
+
},
|
|
175
|
+
checks
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../src/doctor.ts"],"names":[],"mappings":"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"}
|
package/dist/init.d.ts
ADDED
package/dist/init.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import * as readline from 'node:readline/promises';
|
|
2
|
+
import { emitKeypressEvents } from 'node:readline';
|
|
3
|
+
import { stdin as input, stdout as output } from 'node:process';
|
|
4
|
+
import { startActivatedShell } from "./shell.js";
|
|
5
|
+
import { addProfile, writeConfig, writeState, writeTokens } from "./storage.js";
|
|
6
|
+
import { routeRulesFromBootstrap } from "./control-plane.js";
|
|
7
|
+
function endpoint(baseUrl, path) {
|
|
8
|
+
return `${baseUrl.replace(/\/+$/, '')}/api${path}`;
|
|
9
|
+
}
|
|
10
|
+
function normalizePanelUrl(input) {
|
|
11
|
+
const raw = input.trim();
|
|
12
|
+
const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(raw) ? raw : `https://${raw}`;
|
|
13
|
+
const url = new URL(withScheme);
|
|
14
|
+
url.pathname = url.pathname.replace(/\/+$/, '');
|
|
15
|
+
url.search = '';
|
|
16
|
+
url.hash = '';
|
|
17
|
+
return url.toString().replace(/\/+$/, '');
|
|
18
|
+
}
|
|
19
|
+
async function request(controlPlaneUrl, path, options = {}) {
|
|
20
|
+
const headers = new Headers();
|
|
21
|
+
if (options.accessToken) {
|
|
22
|
+
headers.set('X-One-Proxy-Access-Token', options.accessToken);
|
|
23
|
+
}
|
|
24
|
+
if (options.tenantId) {
|
|
25
|
+
headers.set('X-One-Proxy-Tenant-ID', options.tenantId);
|
|
26
|
+
}
|
|
27
|
+
if (options.body !== undefined) {
|
|
28
|
+
headers.set('Content-Type', 'application/json');
|
|
29
|
+
}
|
|
30
|
+
const response = await fetch(endpoint(controlPlaneUrl, path), {
|
|
31
|
+
method: options.method ?? 'GET',
|
|
32
|
+
headers,
|
|
33
|
+
body: options.body === undefined ? undefined : JSON.stringify(options.body)
|
|
34
|
+
});
|
|
35
|
+
const raw = await response.text();
|
|
36
|
+
const envelope = raw ? JSON.parse(raw) : null;
|
|
37
|
+
if (!response.ok || !envelope || envelope.code !== 0) {
|
|
38
|
+
throw Object.assign(new Error(envelope?.message || `Control plane request failed with HTTP ${response.status}.`), {
|
|
39
|
+
code: response.status === 401 ? 'AUTH_REQUIRED' : 'CONTROL_PLANE_UNAVAILABLE'
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return envelope.data;
|
|
43
|
+
}
|
|
44
|
+
async function healthCheck(controlPlaneUrl) {
|
|
45
|
+
const response = await fetch(`${controlPlaneUrl.replace(/\/+$/, '')}/healthz`);
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
throw Object.assign(new Error(`Panel health check failed with HTTP ${response.status}.`), { code: 'CONTROL_PLANE_UNAVAILABLE' });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function prompt(label) {
|
|
51
|
+
const rl = readline.createInterface({ input, output });
|
|
52
|
+
const answer = (await rl.question(label)).trim();
|
|
53
|
+
rl.close();
|
|
54
|
+
return answer;
|
|
55
|
+
}
|
|
56
|
+
function tenantIdOf(tenant) {
|
|
57
|
+
return tenant.id || tenant.tenantId || '';
|
|
58
|
+
}
|
|
59
|
+
function tenantNameOf(tenant) {
|
|
60
|
+
return tenant.name || tenant.tenantName || tenantIdOf(tenant);
|
|
61
|
+
}
|
|
62
|
+
function tokenFromLogin(result) {
|
|
63
|
+
return {
|
|
64
|
+
schemaVersion: 1,
|
|
65
|
+
account: result.account,
|
|
66
|
+
accessToken: result.tokens?.accessToken || result.accessToken,
|
|
67
|
+
refreshToken: result.tokens?.refreshToken || result.refreshToken,
|
|
68
|
+
proxyToken: undefined,
|
|
69
|
+
accessTokenExpiresAt: result.tokens?.expiresAt || result.expiresAt,
|
|
70
|
+
refreshTokenExpiresAt: undefined,
|
|
71
|
+
proxyTokenExpiresAt: undefined
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function profileNameFromUrl(panelUrl) {
|
|
75
|
+
const url = new URL(panelUrl);
|
|
76
|
+
return `${url.hostname}${url.port ? `-${url.port}` : ''}`.toLowerCase().replace(/[^a-z0-9._-]+/g, '-');
|
|
77
|
+
}
|
|
78
|
+
async function selectTenant(tenants) {
|
|
79
|
+
if (!input.isTTY) {
|
|
80
|
+
throw Object.assign(new Error('onep init requires an interactive terminal for tenant selection.'), { code: 'SYNTAX_ERROR', exitCode: 2 });
|
|
81
|
+
}
|
|
82
|
+
let index = 0;
|
|
83
|
+
emitKeypressEvents(input);
|
|
84
|
+
input.setRawMode(true);
|
|
85
|
+
input.resume();
|
|
86
|
+
output.write('\nSelect tenant:\n');
|
|
87
|
+
const render = () => {
|
|
88
|
+
output.write('\x1b[?25l');
|
|
89
|
+
output.write(`\x1b[${tenants.length}F`);
|
|
90
|
+
for (let i = 0; i < tenants.length; i += 1) {
|
|
91
|
+
const tenant = tenants[i];
|
|
92
|
+
output.write(`${i === index ? '>' : ' '} ${tenantNameOf(tenant)} (${tenantIdOf(tenant)})\x1b[K\n`);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
for (const tenant of tenants) {
|
|
96
|
+
output.write(` ${tenantNameOf(tenant)} (${tenantIdOf(tenant)})\n`);
|
|
97
|
+
}
|
|
98
|
+
render();
|
|
99
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
100
|
+
let handler;
|
|
101
|
+
const selected = await new Promise((resolve) => {
|
|
102
|
+
handler = (_chunk, key) => {
|
|
103
|
+
if (key.name === 'up') {
|
|
104
|
+
index = index === 0 ? tenants.length - 1 : index - 1;
|
|
105
|
+
render();
|
|
106
|
+
}
|
|
107
|
+
if (key.name === 'down') {
|
|
108
|
+
index = index === tenants.length - 1 ? 0 : index + 1;
|
|
109
|
+
render();
|
|
110
|
+
}
|
|
111
|
+
if (key.name === 'return') {
|
|
112
|
+
resolve(tenants[index]);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
input.on('keypress', handler);
|
|
116
|
+
});
|
|
117
|
+
if (handler) {
|
|
118
|
+
input.off('keypress', handler);
|
|
119
|
+
}
|
|
120
|
+
input.setRawMode(false);
|
|
121
|
+
output.write('\x1b[?25h\n');
|
|
122
|
+
return selected;
|
|
123
|
+
}
|
|
124
|
+
export async function initCommand(_args, _context) {
|
|
125
|
+
const panelUrl = normalizePanelUrl(await prompt('Panel URL: '));
|
|
126
|
+
process.stdout.write('Testing panel reachability...\n');
|
|
127
|
+
await healthCheck(panelUrl);
|
|
128
|
+
process.stdout.write('Panel reachable.\n');
|
|
129
|
+
const account = await prompt('Account: ');
|
|
130
|
+
const password = await prompt('Password: ');
|
|
131
|
+
const profileName = profileNameFromUrl(panelUrl);
|
|
132
|
+
process.env.ONEPROXY_PROFILE = profileName;
|
|
133
|
+
await addProfile(profileName, panelUrl);
|
|
134
|
+
const login = await request(panelUrl, '/auth/login', {
|
|
135
|
+
method: 'POST',
|
|
136
|
+
body: { account, password }
|
|
137
|
+
});
|
|
138
|
+
const tokens = tokenFromLogin(login);
|
|
139
|
+
await writeTokens(tokens);
|
|
140
|
+
const tenants = await request(panelUrl, '/tenants', { accessToken: tokens.accessToken }).then((result) => result.tenants);
|
|
141
|
+
if (tenants.length === 0) {
|
|
142
|
+
throw Object.assign(new Error('No tenants are available for this account.'), { code: 'TENANT_REQUIRED' });
|
|
143
|
+
}
|
|
144
|
+
const tenant = await selectTenant(tenants);
|
|
145
|
+
const activeTenantId = tenantIdOf(tenant);
|
|
146
|
+
await writeConfig({
|
|
147
|
+
schemaVersion: 1,
|
|
148
|
+
profileName,
|
|
149
|
+
controlPlaneUrl: panelUrl,
|
|
150
|
+
activeTenantId,
|
|
151
|
+
overrides: { direct: [], proxy: [] }
|
|
152
|
+
});
|
|
153
|
+
const bootstrap = await request(panelUrl, '/proxy/extension/bootstrap', {
|
|
154
|
+
accessToken: tokens.accessToken,
|
|
155
|
+
tenantId: activeTenantId
|
|
156
|
+
});
|
|
157
|
+
const routeGroups = (bootstrap.groups ?? []).map((group) => ({
|
|
158
|
+
id: group.id,
|
|
159
|
+
tenantId: activeTenantId,
|
|
160
|
+
name: group.name,
|
|
161
|
+
rules: routeRulesFromBootstrap(group)
|
|
162
|
+
}));
|
|
163
|
+
const activeGroup = routeGroups.find((group) => group.id);
|
|
164
|
+
const entryGroup = (bootstrap.groups ?? []).find((group) => group.id === activeGroup?.id);
|
|
165
|
+
await writeState({
|
|
166
|
+
schemaVersion: 1,
|
|
167
|
+
bootstrap: {
|
|
168
|
+
tenantId: activeTenantId,
|
|
169
|
+
groupId: activeGroup?.id,
|
|
170
|
+
entryNodes: entryGroup?.proxyHost
|
|
171
|
+
? [{ id: entryGroup.entryNodeId || entryGroup.id, host: entryGroup.proxyHost, port: entryGroup.proxyPort || 443, protocol: entryGroup.proxyScheme || 'connect' }]
|
|
172
|
+
: []
|
|
173
|
+
},
|
|
174
|
+
policyRevision: bootstrap.policyRevision,
|
|
175
|
+
fetchedAt: bootstrap.fetchedAt || new Date().toISOString(),
|
|
176
|
+
routeGroups
|
|
177
|
+
});
|
|
178
|
+
await writeTokens({
|
|
179
|
+
...tokens,
|
|
180
|
+
proxyToken: bootstrap.proxyToken || tokens.proxyToken,
|
|
181
|
+
proxyTokenExpiresAt: bootstrap.proxyTokenExpiresAt || tokens.proxyTokenExpiresAt
|
|
182
|
+
});
|
|
183
|
+
if (activeGroup?.id) {
|
|
184
|
+
await writeConfig({
|
|
185
|
+
schemaVersion: 1,
|
|
186
|
+
profileName,
|
|
187
|
+
controlPlaneUrl: panelUrl,
|
|
188
|
+
activeTenantId,
|
|
189
|
+
activeGroupId: activeGroup.id,
|
|
190
|
+
overrides: { direct: [], proxy: [] }
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
process.stdout.write(`Initialized profile ${profileName} with tenant ${tenantNameOf(tenant)}.\n`);
|
|
194
|
+
const activate = (await prompt('Enter an activated OneProxy shell now? [y/N]: ')).toLowerCase();
|
|
195
|
+
if (activate === 'y' || activate === 'yes') {
|
|
196
|
+
return startActivatedShell();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=init.js.map
|