@alpic-ai/sdk 0.0.0 → 1.142.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.
- package/dist/api/auth-scope.d.ts +2 -0
- package/dist/api/auth-scope.js +7 -0
- package/dist/api/auth-scope.js.map +1 -0
- package/dist/api/client.d.ts +11 -0
- package/dist/api/client.js +25 -0
- package/dist/api/client.js.map +1 -0
- package/dist/auth/constants.d.ts +2 -0
- package/dist/auth/constants.js +3 -0
- package/dist/auth/constants.js.map +1 -0
- package/dist/auth/index.d.ts +21 -0
- package/dist/auth/index.js +34 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/login.d.ts +7 -0
- package/dist/auth/login.js +21 -0
- package/dist/auth/login.js.map +1 -0
- package/dist/auth/oauth-client.d.ts +28 -0
- package/dist/auth/oauth-client.js +116 -0
- package/dist/auth/oauth-client.js.map +1 -0
- package/dist/auth/server/assets/alpic-mountain.png +0 -0
- package/dist/auth/server/assets/authorize.html +234 -0
- package/dist/auth/server/assets/callback.html +88 -0
- package/dist/auth/server/index.d.ts +14 -0
- package/dist/auth/server/index.js +128 -0
- package/dist/auth/server/index.js.map +1 -0
- package/dist/auth/store.d.ts +30 -0
- package/dist/auth/store.js +76 -0
- package/dist/auth/store.js.map +1 -0
- package/dist/client.d.ts +9 -0
- package/dist/client.js +18 -0
- package/dist/client.js.map +1 -0
- package/dist/env.d.ts +4 -0
- package/dist/env.js +10 -0
- package/dist/env.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/emitter.d.ts +7 -0
- package/dist/lib/emitter.js +29 -0
- package/dist/lib/emitter.js.map +1 -0
- package/dist/tunnel/tunnel.d.ts +43 -0
- package/dist/tunnel/tunnel.js +127 -0
- package/dist/tunnel/tunnel.js.map +1 -0
- package/dist/vendor/pipenet.d.ts +1 -0
- package/dist/vendor/pipenet.js +16877 -0
- package/dist/vendor/pipenet.js.LEGAL.txt +17 -0
- package/dist/vendor/pipenet.js.map +7 -0
- package/package.json +55 -4
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { LOOPBACK_HOST, LOOPBACK_PORT } from "../constants.js";
|
|
6
|
+
import { getOAuthClient } from "../oauth-client.js";
|
|
7
|
+
const noopLogger = { log: () => { }, error: () => { } };
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const ASSETS_DIR = path.join(__dirname, "assets");
|
|
10
|
+
const createCallbackServer = ({ state, nonce, codeVerifier, authorizeUrl, onSuccess, onError, logger = noopLogger, }) => createServer(async (req, res) => {
|
|
11
|
+
const url = req.url ?? "/";
|
|
12
|
+
const pathname = url.split("?")[0] ?? "/";
|
|
13
|
+
if (pathname === "/login") {
|
|
14
|
+
try {
|
|
15
|
+
const htmlPath = path.join(ASSETS_DIR, "authorize.html");
|
|
16
|
+
let html = await readFile(htmlPath, "utf-8");
|
|
17
|
+
if (authorizeUrl) {
|
|
18
|
+
html = html.replace(/%AUTH_BASE_URL%/g, authorizeUrl);
|
|
19
|
+
}
|
|
20
|
+
res.writeHead(200, {
|
|
21
|
+
"Content-Type": "text/html",
|
|
22
|
+
Connection: "close",
|
|
23
|
+
});
|
|
24
|
+
res.end(html);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
29
|
+
logger.error(msg);
|
|
30
|
+
res.writeHead(500).end();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (pathname === "/assets/alpic-mountain.png") {
|
|
35
|
+
const filePath = path.join(ASSETS_DIR, "alpic-mountain.png");
|
|
36
|
+
const content = await readFile(filePath);
|
|
37
|
+
res.writeHead(200, { "Content-Type": "image/png", Connection: "close" });
|
|
38
|
+
res.end(content);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!url.startsWith("/callback")) {
|
|
42
|
+
res.writeHead(404).end();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const tokens = await getOAuthClient().exchangeAuthorizationCode({
|
|
47
|
+
url: new URL(url, `http://${req.headers.host}`),
|
|
48
|
+
codeVerifier,
|
|
49
|
+
state,
|
|
50
|
+
nonce,
|
|
51
|
+
});
|
|
52
|
+
const sub = tokens.claims()?.sub;
|
|
53
|
+
if (!sub || !tokens.refresh_token) {
|
|
54
|
+
throw new Error("ID token did not contain sub");
|
|
55
|
+
}
|
|
56
|
+
const stored = {
|
|
57
|
+
access_token: tokens.access_token,
|
|
58
|
+
refresh_token: tokens.refresh_token,
|
|
59
|
+
expires_at: getOAuthClient().getExpiresAt(tokens.expires_in),
|
|
60
|
+
sub,
|
|
61
|
+
};
|
|
62
|
+
const msg = "Authentication successful";
|
|
63
|
+
logger.log(msg);
|
|
64
|
+
const successHtmlPath = path.join(ASSETS_DIR, "callback.html");
|
|
65
|
+
let html = await readFile(successHtmlPath, "utf-8");
|
|
66
|
+
html = html
|
|
67
|
+
.replace(/__CALLBACK_STATUS__/g, "success")
|
|
68
|
+
.replace(/__CALLBACK_TITLE__/g, "You are logged in")
|
|
69
|
+
.replace(/__CALLBACK_SUB__/g, "You can close this page.");
|
|
70
|
+
res.writeHead(200, { "Content-Type": "text/html", Connection: "close" });
|
|
71
|
+
res.end(html);
|
|
72
|
+
onSuccess(stored);
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
76
|
+
logger.error(msg);
|
|
77
|
+
const callbackHtmlPath = path.join(ASSETS_DIR, "callback.html");
|
|
78
|
+
let html = await readFile(callbackHtmlPath, "utf-8");
|
|
79
|
+
html = html
|
|
80
|
+
.replace(/__CALLBACK_STATUS__/g, "error")
|
|
81
|
+
.replace(/__CALLBACK_TITLE__/g, "An error occurred. You can close this page.")
|
|
82
|
+
.replace(/__CALLBACK_SUB__/g, "");
|
|
83
|
+
res.writeHead(500, { "Content-Type": "text/html", Connection: "close" });
|
|
84
|
+
res.end(html);
|
|
85
|
+
onError(e instanceof Error ? e : new Error(String(e)));
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
export const getLoginPageUrl = () => `http://${LOOPBACK_HOST}:${LOOPBACK_PORT}/login`;
|
|
89
|
+
const LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
90
|
+
export const listenToOAuthCallback = ({ state, nonce, codeVerifier, authorizeUrl, logger = noopLogger, }) => {
|
|
91
|
+
return new Promise((resolve, reject) => {
|
|
92
|
+
let timeoutHandle;
|
|
93
|
+
const callbackServer = createCallbackServer({
|
|
94
|
+
state,
|
|
95
|
+
nonce,
|
|
96
|
+
codeVerifier,
|
|
97
|
+
authorizeUrl,
|
|
98
|
+
logger,
|
|
99
|
+
onSuccess: (storedToken) => {
|
|
100
|
+
clearTimeout(timeoutHandle);
|
|
101
|
+
callbackServer.close();
|
|
102
|
+
resolve(storedToken);
|
|
103
|
+
},
|
|
104
|
+
onError: (err) => {
|
|
105
|
+
clearTimeout(timeoutHandle);
|
|
106
|
+
callbackServer.close();
|
|
107
|
+
reject(err);
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
callbackServer.on("error", (err) => {
|
|
111
|
+
clearTimeout(timeoutHandle);
|
|
112
|
+
callbackServer.close();
|
|
113
|
+
if (err.code === "EADDRINUSE") {
|
|
114
|
+
reject(new Error(`Port ${LOOPBACK_PORT} is already in use. Another login may be in progress. Please try again.`));
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
reject(err);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
timeoutHandle = setTimeout(() => {
|
|
121
|
+
callbackServer.close();
|
|
122
|
+
reject(new Error("Login timed out after 5 minutes. Please try again."));
|
|
123
|
+
}, LOGIN_TIMEOUT_MS);
|
|
124
|
+
timeoutHandle.unref();
|
|
125
|
+
callbackServer.listen(LOOPBACK_PORT, LOOPBACK_HOST);
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/auth/server/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface Credentials {
|
|
2
|
+
access_token: string;
|
|
3
|
+
refresh_token: string;
|
|
4
|
+
expires_at: number;
|
|
5
|
+
sub: string;
|
|
6
|
+
}
|
|
7
|
+
export interface Config {
|
|
8
|
+
machineId: string;
|
|
9
|
+
telemetry: {
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export declare class GlobalStore {
|
|
14
|
+
private readonly configDir;
|
|
15
|
+
constructor(opts?: {
|
|
16
|
+
configDir?: string;
|
|
17
|
+
});
|
|
18
|
+
getCredentials(): Credentials | null;
|
|
19
|
+
saveCredentials(credentials: Credentials): void;
|
|
20
|
+
clearCredentials(): void;
|
|
21
|
+
getConfig(): Config;
|
|
22
|
+
saveConfig(config: Config): void;
|
|
23
|
+
clearConfig(): void;
|
|
24
|
+
private getCredentialsFilePath;
|
|
25
|
+
private getConfigFilePath;
|
|
26
|
+
private parseJsonFile;
|
|
27
|
+
private saveJsonFile;
|
|
28
|
+
private clear;
|
|
29
|
+
}
|
|
30
|
+
export declare const globalStore: GlobalStore;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import envPaths from "env-paths";
|
|
5
|
+
export class GlobalStore {
|
|
6
|
+
configDir;
|
|
7
|
+
constructor(opts) {
|
|
8
|
+
const configDirOverride = opts?.configDir ?? process.env.ALPIC_GLOBAL_CONFIG_DIR;
|
|
9
|
+
this.configDir = configDirOverride ?? envPaths("alpic", { suffix: "" }).config;
|
|
10
|
+
}
|
|
11
|
+
getCredentials() {
|
|
12
|
+
const token = this.parseJsonFile(this.getCredentialsFilePath());
|
|
13
|
+
if (!token) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
return token;
|
|
17
|
+
}
|
|
18
|
+
saveCredentials(credentials) {
|
|
19
|
+
this.saveJsonFile(this.getCredentialsFilePath(), credentials);
|
|
20
|
+
}
|
|
21
|
+
clearCredentials() {
|
|
22
|
+
this.clear(this.getCredentialsFilePath());
|
|
23
|
+
}
|
|
24
|
+
getConfig() {
|
|
25
|
+
const existing = this.parseJsonFile(this.getConfigFilePath());
|
|
26
|
+
if (existing?.machineId && existing?.telemetry !== undefined) {
|
|
27
|
+
return existing;
|
|
28
|
+
}
|
|
29
|
+
const config = {
|
|
30
|
+
machineId: existing?.machineId ?? crypto.randomUUID(),
|
|
31
|
+
telemetry: {
|
|
32
|
+
enabled: existing?.telemetry?.enabled ?? true,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
this.saveConfig(config);
|
|
36
|
+
return config;
|
|
37
|
+
}
|
|
38
|
+
saveConfig(config) {
|
|
39
|
+
this.saveJsonFile(this.getConfigFilePath(), config);
|
|
40
|
+
}
|
|
41
|
+
clearConfig() {
|
|
42
|
+
this.clear(this.getConfigFilePath());
|
|
43
|
+
}
|
|
44
|
+
getCredentialsFilePath() {
|
|
45
|
+
return join(this.configDir, "credentials.json");
|
|
46
|
+
}
|
|
47
|
+
getConfigFilePath() {
|
|
48
|
+
return join(this.configDir, "config.json");
|
|
49
|
+
}
|
|
50
|
+
parseJsonFile(filePath) {
|
|
51
|
+
if (!existsSync(filePath)) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
return JSON.parse(readFileSync(filePath, "utf-8"));
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
this.clear(filePath);
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
saveJsonFile(filePath, data) {
|
|
63
|
+
mkdirSync(this.configDir, { recursive: true });
|
|
64
|
+
writeFileSync(filePath, JSON.stringify(data, null, 2), {
|
|
65
|
+
encoding: "utf-8",
|
|
66
|
+
mode: 0o600,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
clear(filePath) {
|
|
70
|
+
if (existsSync(filePath)) {
|
|
71
|
+
unlinkSync(filePath);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export const globalStore = new GlobalStore();
|
|
76
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/auth/store.ts"],"names":[],"mappings":"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"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type AlpicApi } from "./api/client.js";
|
|
2
|
+
import { AlpicAuth } from "./auth/index.js";
|
|
3
|
+
import { Tunnel } from "./tunnel/tunnel.js";
|
|
4
|
+
export declare class Alpic {
|
|
5
|
+
readonly auth: AlpicAuth;
|
|
6
|
+
readonly api: AlpicApi;
|
|
7
|
+
readonly tunnel: Tunnel;
|
|
8
|
+
constructor();
|
|
9
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createApiClient } from "./api/client.js";
|
|
2
|
+
import { AlpicAuth } from "./auth/index.js";
|
|
3
|
+
import { env } from "./env.js";
|
|
4
|
+
import { Tunnel } from "./tunnel/tunnel.js";
|
|
5
|
+
export class Alpic {
|
|
6
|
+
auth;
|
|
7
|
+
api;
|
|
8
|
+
tunnel;
|
|
9
|
+
constructor() {
|
|
10
|
+
this.auth = new AlpicAuth();
|
|
11
|
+
this.api = createApiClient({
|
|
12
|
+
apiBaseUrl: env.ALPIC_API_BASE_URL,
|
|
13
|
+
auth: this.auth,
|
|
14
|
+
});
|
|
15
|
+
this.tunnel = new Tunnel({ api: this.api });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,OAAO,KAAK;IACP,IAAI,CAAY;IAChB,GAAG,CAAW;IACd,MAAM,CAAS;IAExB;QACE,IAAI,CAAC,IAAI,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,IAAI,CAAC,GAAG,GAAG,eAAe,CAAC;YACzB,UAAU,EAAE,GAAG,CAAC,kBAAkB;YAClC,IAAI,EAAE,IAAI,CAAC,IAAI;SAChB,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC9C,CAAC;CACF"}
|
package/dist/env.d.ts
ADDED
package/dist/env.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createEnv } from "@t3-oss/env-core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export const env = createEnv({
|
|
4
|
+
server: {
|
|
5
|
+
ALPIC_API_BASE_URL: z.string().default("https://api.alpic.ai"),
|
|
6
|
+
ALPIC_COGNITO_CLIENT_ID: z.string().default("h9nqsttp8ddb40ddi1aoni91h"),
|
|
7
|
+
},
|
|
8
|
+
runtimeEnv: process.env,
|
|
9
|
+
});
|
|
10
|
+
//# sourceMappingURL=env.js.map
|
package/dist/env.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,GAAG,GAAG,SAAS,CAAC;IAC3B,MAAM,EAAE;QACN,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,sBAAsB,CAAC;QAC9D,uBAAuB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,2BAA2B,CAAC;KACzE;IACD,UAAU,EAAE,OAAO,CAAC,GAAG;CACxB,CAAC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type { AlpicApi } from "./api/client.js";
|
|
2
|
+
export type { AuthEvents, Credentials, LoginOptions } from "./auth/index.js";
|
|
3
|
+
export { AlpicAuth } from "./auth/index.js";
|
|
4
|
+
export { getOAuthClient } from "./auth/oauth-client.js";
|
|
5
|
+
export { GlobalStore, globalStore } from "./auth/store.js";
|
|
6
|
+
export { Alpic } from "./client.js";
|
|
7
|
+
export type { OpenTunnelOptions, TunnelCloseInfo, TunnelHandle, TunnelRequest } from "./tunnel/tunnel.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type Listener<T> = (payload: T) => void;
|
|
2
|
+
export declare class TypedEventEmitter<EventMap extends Record<string, unknown>> {
|
|
3
|
+
private readonly listeners;
|
|
4
|
+
on<K extends keyof EventMap>(event: K, listener: Listener<EventMap[K]>): void;
|
|
5
|
+
off<K extends keyof EventMap>(event: K, listener: Listener<EventMap[K]>): void;
|
|
6
|
+
emit<K extends keyof EventMap>(event: K, payload: EventMap[K]): void;
|
|
7
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Minimal typed emitter for SDK lifecycle hooks. Unlike node's EventEmitter it
|
|
2
|
+
// never throws on an unlistened "error" event, and swallows listener errors so
|
|
3
|
+
// observability can never break the operation that emitted the event.
|
|
4
|
+
export class TypedEventEmitter {
|
|
5
|
+
listeners = new Map();
|
|
6
|
+
on(event, listener) {
|
|
7
|
+
const set = this.listeners.get(event) ?? new Set();
|
|
8
|
+
set.add(listener);
|
|
9
|
+
this.listeners.set(event, set);
|
|
10
|
+
}
|
|
11
|
+
off(event, listener) {
|
|
12
|
+
this.listeners.get(event)?.delete(listener);
|
|
13
|
+
}
|
|
14
|
+
emit(event, payload) {
|
|
15
|
+
const set = this.listeners.get(event);
|
|
16
|
+
if (!set) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
for (const listener of set) {
|
|
20
|
+
try {
|
|
21
|
+
listener(payload);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Listener errors must never break the emitting operation.
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=emitter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emitter.js","sourceRoot":"","sources":["../../src/lib/emitter.ts"],"names":[],"mappings":"AAEA,+EAA+E;AAC/E,+EAA+E;AAC/E,sEAAsE;AACtE,MAAM,OAAO,iBAAiB;IACX,SAAS,GAAG,IAAI,GAAG,EAA0C,CAAC;IAE/E,EAAE,CAA2B,KAAQ,EAAE,QAA+B;QACpE,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,GAAG,EAAqB,CAAC;QACtE,GAAG,CAAC,GAAG,CAAC,QAA6B,CAAC,CAAC;QACvC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACjC,CAAC;IAED,GAAG,CAA2B,KAAQ,EAAE,QAA+B;QACrE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,QAA6B,CAAC,CAAC;IACnE,CAAC;IAED,IAAI,CAA2B,KAAQ,EAAE,OAAoB;QAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO;QACT,CAAC;QACD,KAAK,MAAM,QAAQ,IAAI,GAAG,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACF,QAAkC,CAAC,OAAO,CAAC,CAAC;YAC/C,CAAC;YAAC,MAAM,CAAC;gBACP,2DAA2D;YAC7D,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AlpicApi } from "../api/client.js";
|
|
2
|
+
export type TunnelRequest = {
|
|
3
|
+
method: string;
|
|
4
|
+
path: string;
|
|
5
|
+
};
|
|
6
|
+
export type TunnelCloseInfo = {
|
|
7
|
+
displaced?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type TunnelHandle = {
|
|
10
|
+
readonly url: string;
|
|
11
|
+
on(event: "request", listener: (req: TunnelRequest) => void): void;
|
|
12
|
+
on(event: "error", listener: (err: Error) => void): void;
|
|
13
|
+
on(event: "close", listener: (info?: TunnelCloseInfo) => void): void;
|
|
14
|
+
close(): void;
|
|
15
|
+
};
|
|
16
|
+
export type OpenTunnelOptions = {
|
|
17
|
+
port: number;
|
|
18
|
+
};
|
|
19
|
+
type PipenetSocket = {
|
|
20
|
+
on(event: string, listener: (...args: unknown[]) => void): void;
|
|
21
|
+
tunnelCluster?: {
|
|
22
|
+
on(event: string, listener: (...args: unknown[]) => void): void;
|
|
23
|
+
};
|
|
24
|
+
close(): void;
|
|
25
|
+
};
|
|
26
|
+
type Pipenet = (opts: {
|
|
27
|
+
port: number;
|
|
28
|
+
host: string;
|
|
29
|
+
subdomain: string;
|
|
30
|
+
headers: Record<string, string>;
|
|
31
|
+
keepAliveDelayMs: number;
|
|
32
|
+
offlineTimeoutMs: number;
|
|
33
|
+
}) => Promise<PipenetSocket>;
|
|
34
|
+
export type TunnelDeps = {
|
|
35
|
+
api: AlpicApi;
|
|
36
|
+
pipenet?: Pipenet;
|
|
37
|
+
};
|
|
38
|
+
export declare class Tunnel {
|
|
39
|
+
private readonly deps;
|
|
40
|
+
constructor(deps: TunnelDeps);
|
|
41
|
+
open(opts: OpenTunnelOptions): Promise<TunnelHandle>;
|
|
42
|
+
}
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
const CONNECT_TIMEOUT_MS = 10_000;
|
|
2
|
+
const KEEPALIVE_DELAY_MS = 30_000;
|
|
3
|
+
const OFFLINE_TIMEOUT_MS = 30_000;
|
|
4
|
+
// Takeover detection thresholds — see the "dead" listener in open().
|
|
5
|
+
const FLAP_WINDOW_MS = 5_000;
|
|
6
|
+
const FLAP_MIN_SPAN_MS = 2_000;
|
|
7
|
+
const FLAP_THRESHOLD = 15;
|
|
8
|
+
export class Tunnel {
|
|
9
|
+
deps;
|
|
10
|
+
constructor(deps) {
|
|
11
|
+
this.deps = deps;
|
|
12
|
+
}
|
|
13
|
+
async open(opts) {
|
|
14
|
+
// Load the patched pipenet bundle lazily so non-tunnel consumers (every
|
|
15
|
+
// `alpic` command, the skybridge dev server) don't pay its ~500KB load
|
|
16
|
+
// cost at startup — only when a tunnel is actually opened.
|
|
17
|
+
const pipenet = this.deps.pipenet ?? (await import("../vendor/pipenet.js")).pipenet;
|
|
18
|
+
const ticket = await this.deps.api.tunnels.getTicket.v1();
|
|
19
|
+
// ALPIC_TUNNEL_HOST points a real ticket at a locally-run tunnel server. It's a dev
|
|
20
|
+
// override for a local (plain-HTTP) server, so default to http regardless of the host;
|
|
21
|
+
// pass an explicit https:// to force TLS.
|
|
22
|
+
const hostOverride = process.env.ALPIC_TUNNEL_HOST;
|
|
23
|
+
let tunnelHost;
|
|
24
|
+
let proto;
|
|
25
|
+
if (hostOverride) {
|
|
26
|
+
proto = hostOverride.startsWith("https://") ? "https" : "http";
|
|
27
|
+
tunnelHost = hostOverride.replace(/^https?:\/\//, "");
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
tunnelHost = ticket.tunnelHost;
|
|
31
|
+
proto = tunnelHost.startsWith("localhost") ? "http" : "https";
|
|
32
|
+
}
|
|
33
|
+
let timeoutId;
|
|
34
|
+
const socket = await Promise.race([
|
|
35
|
+
pipenet({
|
|
36
|
+
port: opts.port,
|
|
37
|
+
host: `${proto}://${tunnelHost}`,
|
|
38
|
+
subdomain: ticket.subdomain,
|
|
39
|
+
headers: { Authorization: `Bearer ${ticket.ticket}` },
|
|
40
|
+
keepAliveDelayMs: KEEPALIVE_DELAY_MS,
|
|
41
|
+
offlineTimeoutMs: OFFLINE_TIMEOUT_MS,
|
|
42
|
+
}).then((socket) => {
|
|
43
|
+
clearTimeout(timeoutId);
|
|
44
|
+
return socket;
|
|
45
|
+
}),
|
|
46
|
+
new Promise((_, reject) => {
|
|
47
|
+
timeoutId = setTimeout(() => reject(new Error("Failed to connect to tunnel server")), CONNECT_TIMEOUT_MS);
|
|
48
|
+
timeoutId.unref?.();
|
|
49
|
+
}),
|
|
50
|
+
]);
|
|
51
|
+
const url = `${proto}://${ticket.subdomain}.${tunnelHost}`;
|
|
52
|
+
const requestListeners = new Set();
|
|
53
|
+
const errorListeners = new Set();
|
|
54
|
+
const closeListeners = new Set();
|
|
55
|
+
let lastRequestAt = 0;
|
|
56
|
+
let displaced = false;
|
|
57
|
+
let closed = false;
|
|
58
|
+
socket.on("request", (req) => {
|
|
59
|
+
lastRequestAt = Date.now();
|
|
60
|
+
for (const listener of requestListeners) {
|
|
61
|
+
listener(req);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
socket.on("error", (err) => {
|
|
65
|
+
for (const listener of errorListeners) {
|
|
66
|
+
listener(err);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
socket.on("close", () => {
|
|
70
|
+
closed = true;
|
|
71
|
+
const info = displaced ? { displaced: true } : undefined;
|
|
72
|
+
for (const listener of closeListeners) {
|
|
73
|
+
listener(info);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
// Detect being displaced by a newer tunnel. When another tunnel takes over this
|
|
77
|
+
// account's subdomain, the server closes our tunnel and rejects our reconnections
|
|
78
|
+
// (they target a session id that no longer exists), so each one connects and is
|
|
79
|
+
// dropped within milliseconds. pipenet's offlineTimeoutMs never fires for this — it
|
|
80
|
+
// only triggers when the server is unreachable entirely (failed connects emit no
|
|
81
|
+
// "dead"). A "dead" with a prior "open" means a socket reached the server and was
|
|
82
|
+
// dropped, so a sustained storm of them is the takeover signature.
|
|
83
|
+
//
|
|
84
|
+
// Normal serving also churns sockets: with the server's small keep-alive pool, most
|
|
85
|
+
// concurrent requests get their socket destroyed right after serving, emitting a
|
|
86
|
+
// "dead" each. A heavy page load can fire 15+ in a few seconds and look identical to
|
|
87
|
+
// a takeover. The discriminator: a real takeover routes all traffic to the new
|
|
88
|
+
// tunnel, so we serve zero requests during the storm. Ignore it while requests flow.
|
|
89
|
+
let drops = [];
|
|
90
|
+
socket.tunnelCluster?.on("dead", () => {
|
|
91
|
+
if (displaced || closed) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const now = Date.now();
|
|
95
|
+
if (now - lastRequestAt < FLAP_WINDOW_MS) {
|
|
96
|
+
drops = [];
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
drops.push(now);
|
|
100
|
+
drops = drops.filter((at) => now - at < FLAP_WINDOW_MS);
|
|
101
|
+
const oldest = drops[0] ?? now;
|
|
102
|
+
if (drops.length >= FLAP_THRESHOLD && now - oldest >= FLAP_MIN_SPAN_MS) {
|
|
103
|
+
displaced = true;
|
|
104
|
+
socket.close();
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
url,
|
|
109
|
+
on(event, listener) {
|
|
110
|
+
if (event === "request") {
|
|
111
|
+
requestListeners.add(listener);
|
|
112
|
+
}
|
|
113
|
+
else if (event === "error") {
|
|
114
|
+
errorListeners.add(listener);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
closeListeners.add(listener);
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
close() {
|
|
121
|
+
closed = true;
|
|
122
|
+
socket.close();
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=tunnel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel.js","sourceRoot":"","sources":["../../src/tunnel/tunnel.ts"],"names":[],"mappings":"AAEA,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAClC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAClC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC,qEAAqE;AACrE,MAAM,cAAc,GAAG,KAAK,CAAC;AAC7B,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAC/B,MAAM,cAAc,GAAG,EAAE,CAAC;AAsC1B,MAAM,OAAO,MAAM;IACY;IAA7B,YAA6B,IAAgB;QAAhB,SAAI,GAAJ,IAAI,CAAY;IAAG,CAAC;IAEjD,KAAK,CAAC,IAAI,CAAC,IAAuB;QAChC,wEAAwE;QACxE,uEAAuE;QACvE,2DAA2D;QAC3D,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,IAAK,CAAC,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC,OAA8B,CAAC;QAE5G,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,CAAC;QAE1D,oFAAoF;QACpF,uFAAuF;QACvF,0CAA0C;QAC1C,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;QACnD,IAAI,UAAkB,CAAC;QACvB,IAAI,KAAa,CAAC;QAClB,IAAI,YAAY,EAAE,CAAC;YACjB,KAAK,GAAG,YAAY,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;YAC/D,UAAU,GAAG,YAAY,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;QACxD,CAAC;aAAM,CAAC;YACN,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;YAC/B,KAAK,GAAG,UAAU,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;QAChE,CAAC;QAED,IAAI,SAAoD,CAAC;QACzD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;YAChC,OAAO,CAAC;gBACN,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,GAAG,KAAK,MAAM,UAAU,EAAE;gBAChC,SAAS,EAAE,MAAM,CAAC,SAAS;gBAC3B,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE,EAAE;gBACrD,gBAAgB,EAAE,kBAAkB;gBACpC,gBAAgB,EAAE,kBAAkB;aACrC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;gBACjB,YAAY,CAAC,SAAS,CAAC,CAAC;gBACxB,OAAO,MAAM,CAAC;YAChB,CAAC,CAAC;YACF,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;gBAC/B,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC;gBAC1G,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC;YACtB,CAAC,CAAC;SACH,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,GAAG,KAAK,MAAM,MAAM,CAAC,SAAS,IAAI,UAAU,EAAE,CAAC;QAE3D,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAgC,CAAC;QACjE,MAAM,cAAc,GAAG,IAAI,GAAG,EAAwB,CAAC;QACvD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAoC,CAAC;QAEnE,IAAI,aAAa,GAAG,CAAC,CAAC;QACtB,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,IAAI,MAAM,GAAG,KAAK,CAAC;QAEnB,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;YAC3B,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC3B,KAAK,MAAM,QAAQ,IAAI,gBAAgB,EAAE,CAAC;gBACxC,QAAQ,CAAC,GAAoB,CAAC,CAAC;YACjC,CAAC;QACH,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACzB,KAAK,MAAM,QAAQ,IAAI,cAAc,EAAE,CAAC;gBACtC,QAAQ,CAAC,GAAY,CAAC,CAAC;YACzB,CAAC;QACH,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACtB,MAAM,GAAG,IAAI,CAAC;YACd,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;YACzD,KAAK,MAAM,QAAQ,IAAI,cAAc,EAAE,CAAC;gBACtC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACjB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,gFAAgF;QAChF,kFAAkF;QAClF,gFAAgF;QAChF,oFAAoF;QACpF,iFAAiF;QACjF,kFAAkF;QAClF,mEAAmE;QACnE,EAAE;QACF,oFAAoF;QACpF,iFAAiF;QACjF,qFAAqF;QACrF,+EAA+E;QAC/E,qFAAqF;QACrF,IAAI,KAAK,GAAa,EAAE,CAAC;QACzB,MAAM,CAAC,aAAa,EAAE,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;YACpC,IAAI,SAAS,IAAI,MAAM,EAAE,CAAC;gBACxB,OAAO;YACT,CAAC;YACD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,IAAI,GAAG,GAAG,aAAa,GAAG,cAAc,EAAE,CAAC;gBACzC,KAAK,GAAG,EAAE,CAAC;gBACX,OAAO;YACT,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAChB,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,GAAG,EAAE,GAAG,cAAc,CAAC,CAAC;YACxD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;YAC/B,IAAI,KAAK,CAAC,MAAM,IAAI,cAAc,IAAI,GAAG,GAAG,MAAM,IAAI,gBAAgB,EAAE,CAAC;gBACvE,SAAS,GAAG,IAAI,CAAC;gBACjB,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO;YACL,GAAG;YACH,EAAE,CAAC,KAAK,EAAE,QAAQ;gBAChB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;oBACxB,gBAAgB,CAAC,GAAG,CAAC,QAAwC,CAAC,CAAC;gBACjE,CAAC;qBAAM,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;oBAC7B,cAAc,CAAC,GAAG,CAAC,QAAgC,CAAC,CAAC;gBACvD,CAAC;qBAAM,CAAC;oBACN,cAAc,CAAC,GAAG,CAAC,QAA4C,CAAC,CAAC;gBACnE,CAAC;YACH,CAAC;YACD,KAAK;gBACH,MAAM,GAAG,IAAI,CAAC;gBACd,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,CAAC;SACF,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { pipenet } from "pipenet";
|