@opendatalabs/personal-server-ts-server 0.0.1-canary.016adf9 → 0.0.1-canary.052d47e
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.d.ts +31 -0
- package/dist/api-auth.d.ts.map +1 -0
- package/dist/api-auth.js +148 -0
- package/dist/api-auth.js.map +1 -0
- package/dist/api.d.ts +4 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +3 -0
- package/dist/api.js.map +1 -1
- package/dist/app.d.ts +77 -2
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +165 -9
- package/dist/app.js.map +1 -1
- package/dist/bootstrap.d.ts +36 -3
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +434 -39
- package/dist/bootstrap.js.map +1 -1
- package/dist/client.d.ts +17 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +369 -0
- package/dist/client.js.map +1 -0
- package/dist/config/defaults.d.ts +12 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +14 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/index.d.ts +4 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +4 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/loader.d.ts +8 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +78 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/paths.d.ts +9 -0
- package/dist/config/paths.d.ts.map +1 -0
- package/dist/config/paths.js +22 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/enclave-main.d.ts +10 -0
- package/dist/enclave-main.d.ts.map +1 -0
- package/dist/enclave-main.js +115 -0
- package/dist/enclave-main.js.map +1 -0
- package/dist/hono-context.d.ts +14 -0
- package/dist/hono-context.d.ts.map +1 -0
- package/dist/hono-context.js +2 -0
- package/dist/hono-context.js.map +1 -0
- package/dist/index.js +48 -12
- package/dist/index.js.map +1 -1
- package/dist/jobs/types.d.ts +17 -0
- package/dist/jobs/types.d.ts.map +1 -0
- package/dist/jobs/types.js +3 -0
- package/dist/jobs/types.js.map +1 -0
- package/dist/jobs/worker.d.ts +30 -0
- package/dist/jobs/worker.d.ts.map +1 -0
- package/dist/jobs/worker.js +217 -0
- package/dist/jobs/worker.js.map +1 -0
- package/dist/keys/server-account.d.ts +28 -0
- package/dist/keys/server-account.d.ts.map +1 -0
- package/dist/keys/server-account.js +48 -0
- package/dist/keys/server-account.js.map +1 -0
- package/dist/listen.d.ts +11 -0
- package/dist/listen.d.ts.map +1 -0
- package/dist/listen.js +23 -0
- package/dist/listen.js.map +1 -0
- package/dist/logger/index.d.ts +5 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/logger/index.js +9 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/logging/access-log.d.ts +4 -0
- package/dist/logging/access-log.d.ts.map +1 -0
- package/dist/logging/access-log.js +25 -0
- package/dist/logging/access-log.js.map +1 -0
- package/dist/logging/access-reader.d.ts +19 -0
- package/dist/logging/access-reader.d.ts.map +1 -0
- package/dist/logging/access-reader.js +50 -0
- package/dist/logging/access-reader.js.map +1 -0
- package/dist/middleware/access-log.d.ts.map +1 -1
- package/dist/middleware/access-log.js +8 -2
- package/dist/middleware/access-log.js.map +1 -1
- package/dist/middleware/builder-check.d.ts +2 -2
- package/dist/middleware/builder-check.d.ts.map +1 -1
- package/dist/middleware/builder-check.js +7 -2
- package/dist/middleware/builder-check.js.map +1 -1
- package/dist/middleware/cors.d.ts +18 -0
- package/dist/middleware/cors.d.ts.map +1 -0
- package/dist/middleware/cors.js +51 -0
- package/dist/middleware/cors.js.map +1 -0
- package/dist/middleware/owner-check.js +1 -1
- package/dist/middleware/owner-check.js.map +1 -1
- package/dist/middleware/web3-auth.d.ts +12 -0
- package/dist/middleware/web3-auth.d.ts.map +1 -1
- package/dist/middleware/web3-auth.js +27 -31
- package/dist/middleware/web3-auth.js.map +1 -1
- package/dist/migrations/detect-stuck-version-ledger.d.ts +43 -0
- package/dist/migrations/detect-stuck-version-ledger.d.ts.map +1 -0
- package/dist/migrations/detect-stuck-version-ledger.js +73 -0
- package/dist/migrations/detect-stuck-version-ledger.js.map +1 -0
- package/dist/migrations/local-state.d.ts +35 -0
- package/dist/migrations/local-state.d.ts.map +1 -0
- package/dist/migrations/local-state.js +133 -0
- package/dist/migrations/local-state.js.map +1 -0
- package/dist/migrations/state-migrations.d.ts +78 -0
- package/dist/migrations/state-migrations.d.ts.map +1 -0
- package/dist/migrations/state-migrations.js +72 -0
- package/dist/migrations/state-migrations.js.map +1 -0
- package/dist/pending-blob-deletions.d.ts +10 -0
- package/dist/pending-blob-deletions.d.ts.map +1 -0
- package/dist/pending-blob-deletions.js +38 -0
- package/dist/pending-blob-deletions.js.map +1 -0
- package/dist/routes/access-logs.d.ts +7 -0
- package/dist/routes/access-logs.d.ts.map +1 -1
- package/dist/routes/access-logs.js +11 -17
- package/dist/routes/access-logs.js.map +1 -1
- package/dist/routes/auth-device.d.ts +49 -0
- package/dist/routes/auth-device.d.ts.map +1 -0
- package/dist/routes/auth-device.js +581 -0
- package/dist/routes/auth-device.js.map +1 -0
- package/dist/routes/data.d.ts +67 -2
- package/dist/routes/data.d.ts.map +1 -1
- package/dist/routes/data.js +41 -201
- package/dist/routes/data.js.map +1 -1
- package/dist/routes/derivatives.d.ts +33 -0
- package/dist/routes/derivatives.d.ts.map +1 -0
- package/dist/routes/derivatives.js +33 -0
- package/dist/routes/derivatives.js.map +1 -0
- package/dist/routes/enclave-jobs.d.ts +9 -0
- package/dist/routes/enclave-jobs.d.ts.map +1 -0
- package/dist/routes/enclave-jobs.js +104 -0
- package/dist/routes/enclave-jobs.js.map +1 -0
- package/dist/routes/grants.d.ts +9 -2
- package/dist/routes/grants.d.ts.map +1 -1
- package/dist/routes/grants.js +18 -196
- package/dist/routes/grants.js.map +1 -1
- package/dist/routes/health.d.ts +7 -1
- package/dist/routes/health.d.ts.map +1 -1
- package/dist/routes/health.js +39 -2
- package/dist/routes/health.js.map +1 -1
- package/dist/routes/mcp.d.ts +115 -0
- package/dist/routes/mcp.d.ts.map +1 -0
- package/dist/routes/mcp.js +676 -0
- package/dist/routes/mcp.js.map +1 -0
- package/dist/routes/oauth-token.d.ts +56 -0
- package/dist/routes/oauth-token.d.ts.map +1 -0
- package/dist/routes/oauth-token.js +49 -0
- package/dist/routes/oauth-token.js.map +1 -0
- package/dist/routes/sync.d.ts +7 -0
- package/dist/routes/sync.d.ts.map +1 -1
- package/dist/routes/sync.js +12 -39
- package/dist/routes/sync.js.map +1 -1
- package/dist/routes/ui-config.d.ts.map +1 -1
- package/dist/routes/ui-config.js +17 -82
- package/dist/routes/ui-config.js.map +1 -1
- package/dist/routes/ui-registration.d.ts +9 -0
- package/dist/routes/ui-registration.d.ts.map +1 -0
- package/dist/routes/ui-registration.js +217 -0
- package/dist/routes/ui-registration.js.map +1 -0
- package/dist/routes/ui.d.ts +1 -0
- package/dist/routes/ui.d.ts.map +1 -1
- package/dist/routes/ui.js +46 -4
- package/dist/routes/ui.js.map +1 -1
- package/dist/routes/write-session.d.ts +37 -0
- package/dist/routes/write-session.d.ts.map +1 -0
- package/dist/routes/write-session.js +34 -0
- package/dist/routes/write-session.js.map +1 -0
- package/dist/storage/hierarchy.d.ts +36 -0
- package/dist/storage/hierarchy.d.ts.map +1 -0
- package/dist/storage/hierarchy.js +282 -0
- package/dist/storage/hierarchy.js.map +1 -0
- package/dist/storage/index-manager.d.ts +60 -0
- package/dist/storage/index-manager.d.ts.map +1 -0
- package/dist/storage/index-manager.js +174 -0
- package/dist/storage/index-manager.js.map +1 -0
- package/dist/storage/index-schema.d.ts +5 -0
- package/dist/storage/index-schema.d.ts.map +1 -0
- package/dist/storage/index-schema.js +69 -0
- package/dist/storage/index-schema.js.map +1 -0
- package/dist/storage/node-data-storage.d.ts +9 -0
- package/dist/storage/node-data-storage.d.ts.map +1 -0
- package/dist/storage/node-data-storage.js +105 -0
- package/dist/storage/node-data-storage.js.map +1 -0
- package/dist/storage/question-store.d.ts +9 -0
- package/dist/storage/question-store.d.ts.map +1 -0
- package/dist/storage/question-store.js +151 -0
- package/dist/storage/question-store.js.map +1 -0
- package/dist/sync-cursor.d.ts +15 -0
- package/dist/sync-cursor.d.ts.map +1 -0
- package/dist/sync-cursor.js +110 -0
- package/dist/sync-cursor.js.map +1 -0
- package/dist/token-store.d.ts +22 -0
- package/dist/token-store.d.ts.map +1 -0
- package/dist/token-store.js +120 -0
- package/dist/token-store.js.map +1 -0
- package/dist/tunnel/auth.d.ts +4 -0
- package/dist/tunnel/auth.d.ts.map +1 -1
- package/dist/tunnel/auth.js +2 -1
- package/dist/tunnel/auth.js.map +1 -1
- package/dist/tunnel/config.d.ts +1 -0
- package/dist/tunnel/config.d.ts.map +1 -1
- package/dist/tunnel/config.js +1 -1
- package/dist/tunnel/config.js.map +1 -1
- package/dist/tunnel/manager.d.ts +21 -1
- package/dist/tunnel/manager.d.ts.map +1 -1
- package/dist/tunnel/manager.js +78 -23
- package/dist/tunnel/manager.js.map +1 -1
- package/dist/ui/browser_tls_rustls_bg.wasm +0 -0
- package/dist/ui/index.html +2192 -0
- package/dist/ui/ps-lite-debug.d.ts +93 -0
- package/dist/ui/ps-lite-debug.d.ts.map +1 -0
- package/dist/ui/ps-lite-debug.js +135347 -0
- package/dist/ui/ps-lite-debug.js.map +1 -0
- package/package.json +20 -6
- package/dist/middleware/grant-check.d.ts +0 -12
- package/dist/middleware/grant-check.d.ts.map +0 -1
- package/dist/middleware/grant-check.js +0 -90
- package/dist/middleware/grant-check.js.map +0 -1
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, basename } from "node:path";
|
|
3
|
+
import { loadConfig } from "./config/index.js";
|
|
4
|
+
async function readCursorFile(cursorPath) {
|
|
5
|
+
try {
|
|
6
|
+
const raw = await readFile(cursorPath, "utf8");
|
|
7
|
+
const parsed = JSON.parse(raw);
|
|
8
|
+
return typeof parsed.lastProcessedTimestamp === "string"
|
|
9
|
+
? parsed.lastProcessedTimestamp
|
|
10
|
+
: null;
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
if (err instanceof Error &&
|
|
14
|
+
"code" in err &&
|
|
15
|
+
err.code === "ENOENT") {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
throw err;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async function writeCursorFile(cursorPath, timestamp) {
|
|
22
|
+
await mkdir(dirname(cursorPath), { recursive: true });
|
|
23
|
+
const tmpPath = `${cursorPath}.tmp`;
|
|
24
|
+
const state = {
|
|
25
|
+
version: 1,
|
|
26
|
+
lastProcessedTimestamp: timestamp,
|
|
27
|
+
};
|
|
28
|
+
await writeFile(tmpPath, `${JSON.stringify(state, null, 2)}\n`);
|
|
29
|
+
await rename(tmpPath, cursorPath);
|
|
30
|
+
}
|
|
31
|
+
function createLegacyConfigCursor(configPath) {
|
|
32
|
+
return {
|
|
33
|
+
async read() {
|
|
34
|
+
const config = await loadConfig({ configPath });
|
|
35
|
+
return config.sync.lastProcessedTimestamp;
|
|
36
|
+
},
|
|
37
|
+
// Write only the sync cursor field through a raw read/merge/atomic-rename
|
|
38
|
+
// so we preserve every other field exactly as it lives on disk. The
|
|
39
|
+
// previous implementation round-tripped the whole config through
|
|
40
|
+
// loadConfig + saveConfig, which applies zod schema defaults (notably
|
|
41
|
+
// `storage.backend = "local"`) and silently clobbered runtime overrides
|
|
42
|
+
// like `storage.backend = "vana"` set via
|
|
43
|
+
// startPersonalServer({ configDefaults }). The result was that any
|
|
44
|
+
// consumer relying on configDefaults to set a non-default backend would
|
|
45
|
+
// silently revert to local storage after the first sync tick.
|
|
46
|
+
async write(timestamp) {
|
|
47
|
+
let raw = "{}";
|
|
48
|
+
try {
|
|
49
|
+
raw = await readFile(configPath, "utf8");
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
if (!(err instanceof Error &&
|
|
53
|
+
"code" in err &&
|
|
54
|
+
err.code === "ENOENT")) {
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
let parsed;
|
|
59
|
+
try {
|
|
60
|
+
const candidate = JSON.parse(raw);
|
|
61
|
+
parsed =
|
|
62
|
+
typeof candidate === "object" && candidate !== null
|
|
63
|
+
? candidate
|
|
64
|
+
: {};
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
parsed = {};
|
|
68
|
+
}
|
|
69
|
+
const existingSync = typeof parsed.sync === "object" && parsed.sync !== null
|
|
70
|
+
? parsed.sync
|
|
71
|
+
: {};
|
|
72
|
+
const next = {
|
|
73
|
+
...parsed,
|
|
74
|
+
sync: { ...existingSync, lastProcessedTimestamp: timestamp },
|
|
75
|
+
};
|
|
76
|
+
await mkdir(dirname(configPath), { recursive: true });
|
|
77
|
+
const tmpPath = `${configPath}.tmp`;
|
|
78
|
+
await writeFile(tmpPath, `${JSON.stringify(next, null, 2)}\n`);
|
|
79
|
+
await rename(tmpPath, configPath);
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Creates a cursor backed by a separate JSON state file.
|
|
85
|
+
* Passing config.json without options preserves the legacy config-backed API.
|
|
86
|
+
*/
|
|
87
|
+
export function createSyncCursor(cursorPath, options = {}) {
|
|
88
|
+
if (!options.legacyConfigPath && basename(cursorPath) === "config.json") {
|
|
89
|
+
return createLegacyConfigCursor(cursorPath);
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
async read() {
|
|
93
|
+
const cursor = await readCursorFile(cursorPath);
|
|
94
|
+
if (cursor !== null) {
|
|
95
|
+
return cursor;
|
|
96
|
+
}
|
|
97
|
+
if (options.legacyConfigPath) {
|
|
98
|
+
const config = await loadConfig({
|
|
99
|
+
configPath: options.legacyConfigPath,
|
|
100
|
+
});
|
|
101
|
+
return config.sync.lastProcessedTimestamp;
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
},
|
|
105
|
+
async write(timestamp) {
|
|
106
|
+
await writeCursorFile(cursorPath, timestamp);
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=sync-cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-cursor.js","sourceRoot":"","sources":["../src/sync-cursor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent token store — manages Personal Server session tokens on disk.
|
|
3
|
+
*
|
|
4
|
+
* Tokens are stored in a JSON file (tokens.json) in the PS data directory
|
|
5
|
+
* so they survive restarts. Supports multiple concurrent tokens.
|
|
6
|
+
*/
|
|
7
|
+
import type { Logger } from "pino";
|
|
8
|
+
export interface AddTokenOptions {
|
|
9
|
+
expiresAt?: string | Date | null;
|
|
10
|
+
}
|
|
11
|
+
export interface TokenStore {
|
|
12
|
+
/** All currently valid tokens. */
|
|
13
|
+
getTokens(): Promise<string[]>;
|
|
14
|
+
/** Check if a token is valid. */
|
|
15
|
+
isValid(token: string): Promise<boolean>;
|
|
16
|
+
/** Add a new token and persist to disk. */
|
|
17
|
+
addToken(token: string, options?: AddTokenOptions): Promise<void>;
|
|
18
|
+
/** Remove a token and persist to disk. */
|
|
19
|
+
removeToken(token: string): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export declare function createTokenStore(filePath: string, logger: Logger): TokenStore;
|
|
22
|
+
//# sourceMappingURL=token-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../src/token-store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,kCAAkC;IAClC,SAAS,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/B,iCAAiC;IACjC,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACzC,2CAA2C;IAC3C,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,0CAA0C;IAC1C,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C;AAOD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CA6H7E"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent token store — manages Personal Server session tokens on disk.
|
|
3
|
+
*
|
|
4
|
+
* Tokens are stored in a JSON file (tokens.json) in the PS data directory
|
|
5
|
+
* so they survive restarts. Supports multiple concurrent tokens.
|
|
6
|
+
*/
|
|
7
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
8
|
+
import { dirname } from "node:path";
|
|
9
|
+
export function createTokenStore(filePath, logger) {
|
|
10
|
+
const tokens = new Map();
|
|
11
|
+
let loaded = false;
|
|
12
|
+
function normalizeExpiresAt(value) {
|
|
13
|
+
if (value == null)
|
|
14
|
+
return null;
|
|
15
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
16
|
+
if (Number.isNaN(date.getTime())) {
|
|
17
|
+
throw new Error("Invalid token expiry");
|
|
18
|
+
}
|
|
19
|
+
return date.toISOString();
|
|
20
|
+
}
|
|
21
|
+
function isExpired(expiresAt) {
|
|
22
|
+
if (!expiresAt)
|
|
23
|
+
return false;
|
|
24
|
+
return new Date(expiresAt).getTime() <= Date.now();
|
|
25
|
+
}
|
|
26
|
+
function purgeExpired() {
|
|
27
|
+
let removed = false;
|
|
28
|
+
for (const [token, expiresAt] of tokens.entries()) {
|
|
29
|
+
if (isExpired(expiresAt)) {
|
|
30
|
+
tokens.delete(token);
|
|
31
|
+
removed = true;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return removed;
|
|
35
|
+
}
|
|
36
|
+
async function loadFromDisk() {
|
|
37
|
+
if (loaded)
|
|
38
|
+
return;
|
|
39
|
+
try {
|
|
40
|
+
const raw = await readFile(filePath, "utf-8");
|
|
41
|
+
const data = JSON.parse(raw);
|
|
42
|
+
if (Array.isArray(data.tokens)) {
|
|
43
|
+
for (const entry of data.tokens) {
|
|
44
|
+
if (typeof entry === "string" && entry.length > 0) {
|
|
45
|
+
tokens.set(entry, null);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (typeof entry === "object" &&
|
|
49
|
+
entry !== null &&
|
|
50
|
+
typeof entry.token === "string" &&
|
|
51
|
+
entry.token.length > 0) {
|
|
52
|
+
const expiresAt = normalizeExpiresAt(entry.expiresAt);
|
|
53
|
+
if (!isExpired(expiresAt)) {
|
|
54
|
+
tokens.set(entry.token, expiresAt);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
purgeExpired();
|
|
60
|
+
logger.info({ count: tokens.size }, "Loaded Personal Server session tokens from disk");
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
if (err.code !== "ENOENT") {
|
|
64
|
+
logger.warn({ err }, "Failed to read tokens file");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
loaded = true;
|
|
68
|
+
}
|
|
69
|
+
async function saveToDisk() {
|
|
70
|
+
purgeExpired();
|
|
71
|
+
const data = {
|
|
72
|
+
version: 1,
|
|
73
|
+
tokens: Array.from(tokens.entries()).map(([token, expiresAt]) => expiresAt ? { token, expiresAt } : token),
|
|
74
|
+
};
|
|
75
|
+
try {
|
|
76
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
77
|
+
await writeFile(filePath, JSON.stringify(data, null, 2), {
|
|
78
|
+
mode: 0o600,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
logger.error({ err }, "Failed to persist tokens file");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Load eagerly on first access
|
|
86
|
+
const ready = loadFromDisk();
|
|
87
|
+
return {
|
|
88
|
+
async getTokens() {
|
|
89
|
+
await ready;
|
|
90
|
+
if (purgeExpired()) {
|
|
91
|
+
await saveToDisk();
|
|
92
|
+
}
|
|
93
|
+
return Array.from(tokens.keys());
|
|
94
|
+
},
|
|
95
|
+
async isValid(token) {
|
|
96
|
+
await ready;
|
|
97
|
+
const expiresAt = tokens.get(token);
|
|
98
|
+
if (expiresAt === undefined) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
if (isExpired(expiresAt)) {
|
|
102
|
+
tokens.delete(token);
|
|
103
|
+
await saveToDisk();
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
return true;
|
|
107
|
+
},
|
|
108
|
+
async addToken(token, options) {
|
|
109
|
+
await ready;
|
|
110
|
+
tokens.set(token, normalizeExpiresAt(options?.expiresAt));
|
|
111
|
+
await saveToDisk();
|
|
112
|
+
},
|
|
113
|
+
async removeToken(token) {
|
|
114
|
+
await ready;
|
|
115
|
+
tokens.delete(token);
|
|
116
|
+
await saveToDisk();
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=token-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-store.js","sourceRoot":"","sources":["../src/token-store.ts"],"names":[],"mappings":"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"}
|
package/dist/tunnel/auth.d.ts
CHANGED
|
@@ -11,6 +11,9 @@ export interface SignedClaim {
|
|
|
11
11
|
claim: string;
|
|
12
12
|
sig: string;
|
|
13
13
|
}
|
|
14
|
+
export interface TunnelClaimSigner {
|
|
15
|
+
signMessage(message: string): Promise<`0x${string}`>;
|
|
16
|
+
}
|
|
14
17
|
export interface ClaimPayload {
|
|
15
18
|
aud: string;
|
|
16
19
|
iat: number;
|
|
@@ -25,6 +28,7 @@ export interface ClaimConfig {
|
|
|
25
28
|
walletAddress: string;
|
|
26
29
|
runId: string;
|
|
27
30
|
serverKeypair: ServerAccount;
|
|
31
|
+
signer?: TunnelClaimSigner;
|
|
28
32
|
}
|
|
29
33
|
/** Encode a UTF-8 string to base64url (no padding). */
|
|
30
34
|
export declare function base64urlEncode(input: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/tunnel/auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4CAA4C,CAAC;AAEhF,kEAAkE;AAClE,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/tunnel/auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4CAA4C,CAAC;AAEhF,kEAAkE;AAClE,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,aAAa,CAAC;IAC7B,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAED,uDAAuD;AACvD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMrD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,WAAW,CAAC,CAkBtB"}
|
package/dist/tunnel/auth.js
CHANGED
|
@@ -40,7 +40,8 @@ export async function generateSignedClaim(config) {
|
|
|
40
40
|
runId: config.runId,
|
|
41
41
|
};
|
|
42
42
|
const claim = base64urlEncode(JSON.stringify(payload));
|
|
43
|
-
const
|
|
43
|
+
const signer = config.signer ?? config.serverKeypair;
|
|
44
|
+
const sig = await signer.signMessage(claim);
|
|
44
45
|
return { claim, sig };
|
|
45
46
|
}
|
|
46
47
|
//# sourceMappingURL=auth.js.map
|
package/dist/tunnel/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/tunnel/auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,kEAAkE;AAClE,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/tunnel/auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,kEAAkE;AAClE,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AA6BrC,uDAAuD;AACvD,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC;SAC/B,QAAQ,CAAC,QAAQ,CAAC;SAClB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACxB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAmB;IAEnB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAE1C,MAAM,OAAO,GAAiB;QAC5B,GAAG,EAAE,yBAAyB;QAC9B,GAAG,EAAE,GAAG;QACR,GAAG,EAAE,GAAG,GAAG,iBAAiB;QAC5B,KAAK,EAAE,MAAM,CAAC,YAAY;QAC1B,MAAM,EAAE,MAAM,CAAC,aAAa;QAC5B,SAAS,EAAE,MAAM,CAAC,aAAa,CAAC,WAAW,EAAE;QAC7C,KAAK,EAAE,MAAM,CAAC,KAAK;KACpB,CAAC;IAEF,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IACvD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,aAAa,CAAC;IACrD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAE5C,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AACxB,CAAC"}
|
package/dist/tunnel/config.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/tunnel/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAsBrE"}
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/tunnel/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,CAsBrE"}
|
package/dist/tunnel/config.js
CHANGED
|
@@ -28,7 +28,7 @@ metadatas.auth_claim = "${options.authClaim}"
|
|
|
28
28
|
metadatas.auth_sig = "${options.authSig}"
|
|
29
29
|
|
|
30
30
|
[[proxies]]
|
|
31
|
-
name = "personal-server"
|
|
31
|
+
name = "${options.proxyName ?? `personal-server-${options.subdomain.toLowerCase()}`}"
|
|
32
32
|
type = "http"
|
|
33
33
|
localIP = "127.0.0.1"
|
|
34
34
|
localPort = ${options.localPort}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/tunnel/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/tunnel/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAeH;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAA0B;IAC3D,OAAO;;;gBAGO,OAAO,CAAC,UAAU;eACnB,OAAO,CAAC,UAAU;;;;sBAIX,OAAO,CAAC,aAAa;qBACtB,OAAO,CAAC,YAAY;sBACnB,OAAO,CAAC,KAAK;0BACT,OAAO,CAAC,SAAS;wBACnB,OAAO,CAAC,OAAO;;;UAG7B,OAAO,CAAC,SAAS,IAAI,mBAAmB,OAAO,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE;;;cAGrE,OAAO,CAAC,SAAS;eAChB,OAAO,CAAC,SAAS;CAC/B,CAAC;AACF,CAAC"}
|
package/dist/tunnel/manager.d.ts
CHANGED
|
@@ -9,18 +9,22 @@
|
|
|
9
9
|
* - Provide status for health endpoint
|
|
10
10
|
*/
|
|
11
11
|
import type { ServerAccount } from "@opendatalabs/personal-server-ts-core/keys";
|
|
12
|
+
import { type TunnelClaimSigner } from "./auth.js";
|
|
12
13
|
export type TunnelStatus = "stopped" | "starting" | "connected" | "disconnected" | "error";
|
|
13
14
|
export interface TunnelStatusInfo {
|
|
14
15
|
enabled: boolean;
|
|
15
16
|
status: TunnelStatus;
|
|
16
17
|
publicUrl: string | null;
|
|
17
18
|
connectedSince: string | null;
|
|
19
|
+
routable?: boolean;
|
|
20
|
+
warning?: string;
|
|
18
21
|
error?: string;
|
|
19
22
|
}
|
|
20
23
|
export interface TunnelConfig {
|
|
21
24
|
walletAddress: string;
|
|
22
25
|
ownerAddress: string;
|
|
23
26
|
serverKeypair: ServerAccount;
|
|
27
|
+
tunnelSigner?: TunnelClaimSigner;
|
|
24
28
|
runId: string;
|
|
25
29
|
serverAddr: string;
|
|
26
30
|
serverPort: number;
|
|
@@ -33,16 +37,32 @@ export declare class TunnelManager {
|
|
|
33
37
|
private publicUrl;
|
|
34
38
|
private connectedSince;
|
|
35
39
|
private lastError;
|
|
40
|
+
private routable;
|
|
41
|
+
private routeWarning;
|
|
36
42
|
private config;
|
|
37
43
|
private binaryPath;
|
|
38
44
|
private refreshTimer;
|
|
39
45
|
private refreshing;
|
|
40
46
|
constructor(storageRoot: string);
|
|
41
47
|
/**
|
|
42
|
-
*
|
|
48
|
+
* Reserve the tunnel's public URL without starting frpc. The URL is
|
|
49
|
+
* deterministic (wallet-derived subdomain), so callers can register the
|
|
50
|
+
* server with the gateway before any tunnel traffic is attempted. Dialing
|
|
51
|
+
* the relay pre-registration is what poisoned its delegation cache and
|
|
52
|
+
* caused the ~70s "Signer is not a registered server" window (BUI-611).
|
|
53
|
+
*/
|
|
54
|
+
reserve(config: TunnelConfig, binaryPath: string): string;
|
|
55
|
+
/**
|
|
56
|
+
* Start the frpc process for a previously reserved tunnel.
|
|
43
57
|
* Returns the public URL once the tunnel is established.
|
|
44
58
|
* Automatically schedules periodic claim refresh.
|
|
45
59
|
*/
|
|
60
|
+
connect(): Promise<string>;
|
|
61
|
+
/**
|
|
62
|
+
* Reserve and immediately connect. Prefer reserve() + connect() gated on
|
|
63
|
+
* server registration; this remains for callers that are already
|
|
64
|
+
* registered.
|
|
65
|
+
*/
|
|
46
66
|
start(config: TunnelConfig, binaryPath: string): Promise<string>;
|
|
47
67
|
/**
|
|
48
68
|
* Stop the frpc process gracefully.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/tunnel/manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4CAA4C,CAAC;
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/tunnel/manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4CAA4C,CAAC;AAChF,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,WAAW,CAAC;AAInB,MAAM,MAAM,YAAY,GACtB,SAAS,GAAG,UAAU,GAAG,WAAW,GAAG,cAAc,GAAG,OAAO,CAAC;AAElE,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,aAAa,CAAC;IAC7B,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAQD,qBAAa,aAAa;IACxB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,YAAY,CAA8C;IAClE,OAAO,CAAC,UAAU,CAAS;gBAEf,WAAW,EAAE,MAAM;IAI/B;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM;IAYzD;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC;IAsChC;;;;OAIG;IACG,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKtE;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgC3B;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAOrD;;OAEG;IACH,SAAS,IAAI,gBAAgB;IAY7B;;OAEG;IACH,YAAY,IAAI,MAAM,GAAG,IAAI;IAQ7B;;OAEG;YACW,gBAAgB;IA+B9B;;;OAGG;YACW,YAAY;IA0F1B;;OAEG;YACW,WAAW;IAsBzB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,iBAAiB;IAOzB;;;OAGG;YACW,YAAY;CA0B3B"}
|
package/dist/tunnel/manager.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { spawn } from "node:child_process";
|
|
12
12
|
import { writeFile, mkdir, chmod, access, constants } from "node:fs/promises";
|
|
13
13
|
import { join } from "node:path";
|
|
14
|
-
import { generateSignedClaim, CLAIM_TTL_SECONDS } from "./auth.js";
|
|
14
|
+
import { generateSignedClaim, CLAIM_TTL_SECONDS, } from "./auth.js";
|
|
15
15
|
import { generateFrpcConfig } from "./config.js";
|
|
16
16
|
import { buildTunnelUrl } from "./verify.js";
|
|
17
17
|
/** Refresh at 80% of TTL to leave buffer before expiry. */
|
|
@@ -25,6 +25,8 @@ export class TunnelManager {
|
|
|
25
25
|
publicUrl = null;
|
|
26
26
|
connectedSince = null;
|
|
27
27
|
lastError = null;
|
|
28
|
+
routable;
|
|
29
|
+
routeWarning = null;
|
|
28
30
|
config = null;
|
|
29
31
|
binaryPath = null;
|
|
30
32
|
refreshTimer = null;
|
|
@@ -33,18 +35,40 @@ export class TunnelManager {
|
|
|
33
35
|
this.storageRoot = storageRoot;
|
|
34
36
|
}
|
|
35
37
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
38
|
+
* Reserve the tunnel's public URL without starting frpc. The URL is
|
|
39
|
+
* deterministic (wallet-derived subdomain), so callers can register the
|
|
40
|
+
* server with the gateway before any tunnel traffic is attempted. Dialing
|
|
41
|
+
* the relay pre-registration is what poisoned its delegation cache and
|
|
42
|
+
* caused the ~70s "Signer is not a registered server" window (BUI-611).
|
|
39
43
|
*/
|
|
40
|
-
|
|
44
|
+
reserve(config, binaryPath) {
|
|
41
45
|
if (this.process) {
|
|
42
46
|
throw new Error("Tunnel already running");
|
|
43
47
|
}
|
|
44
48
|
this.config = config;
|
|
45
49
|
this.binaryPath = binaryPath;
|
|
50
|
+
const subdomain = config.walletAddress.toLowerCase();
|
|
51
|
+
this.publicUrl = buildTunnelUrl(subdomain, config.serverAddr);
|
|
52
|
+
return this.publicUrl;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Start the frpc process for a previously reserved tunnel.
|
|
56
|
+
* Returns the public URL once the tunnel is established.
|
|
57
|
+
* Automatically schedules periodic claim refresh.
|
|
58
|
+
*/
|
|
59
|
+
async connect() {
|
|
60
|
+
if (this.process) {
|
|
61
|
+
throw new Error("Tunnel already running");
|
|
62
|
+
}
|
|
63
|
+
const config = this.config;
|
|
64
|
+
const binaryPath = this.binaryPath;
|
|
65
|
+
if (!config || !binaryPath || !this.publicUrl) {
|
|
66
|
+
throw new Error("Tunnel not reserved");
|
|
67
|
+
}
|
|
46
68
|
this.status = "starting";
|
|
47
69
|
this.lastError = null;
|
|
70
|
+
this.routable = undefined;
|
|
71
|
+
this.routeWarning = null;
|
|
48
72
|
// Verify binary exists and is executable
|
|
49
73
|
try {
|
|
50
74
|
await access(binaryPath, constants.X_OK);
|
|
@@ -57,13 +81,20 @@ export class TunnelManager {
|
|
|
57
81
|
throw new Error(`frpc binary not found or not executable: ${binaryPath}`);
|
|
58
82
|
}
|
|
59
83
|
}
|
|
60
|
-
const
|
|
61
|
-
this.publicUrl = buildTunnelUrl(subdomain, config.serverAddr);
|
|
62
|
-
const configPath = await this.writeFreshConfig(subdomain);
|
|
84
|
+
const configPath = await this.writeFreshConfig(config.walletAddress.toLowerCase());
|
|
63
85
|
await this.spawnProcess(configPath);
|
|
64
86
|
this.scheduleRefresh();
|
|
65
87
|
return this.publicUrl;
|
|
66
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Reserve and immediately connect. Prefer reserve() + connect() gated on
|
|
91
|
+
* server registration; this remains for callers that are already
|
|
92
|
+
* registered.
|
|
93
|
+
*/
|
|
94
|
+
async start(config, binaryPath) {
|
|
95
|
+
this.reserve(config, binaryPath);
|
|
96
|
+
return this.connect();
|
|
97
|
+
}
|
|
67
98
|
/**
|
|
68
99
|
* Stop the frpc process gracefully.
|
|
69
100
|
*/
|
|
@@ -85,6 +116,8 @@ export class TunnelManager {
|
|
|
85
116
|
this.status = "stopped";
|
|
86
117
|
this.publicUrl = null;
|
|
87
118
|
this.connectedSince = null;
|
|
119
|
+
this.routable = undefined;
|
|
120
|
+
this.routeWarning = null;
|
|
88
121
|
resolve();
|
|
89
122
|
});
|
|
90
123
|
// Send SIGTERM for graceful shutdown
|
|
@@ -101,14 +134,10 @@ export class TunnelManager {
|
|
|
101
134
|
* Update status based on external verification of the tunnel URL.
|
|
102
135
|
*/
|
|
103
136
|
setVerified(reachable, error) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
else {
|
|
109
|
-
this.status = "error";
|
|
110
|
-
this.lastError = error ?? "Tunnel URL not reachable";
|
|
111
|
-
}
|
|
137
|
+
this.routable = reachable;
|
|
138
|
+
this.routeWarning = reachable
|
|
139
|
+
? null
|
|
140
|
+
: (error ?? "Tunnel URL not reachable");
|
|
112
141
|
}
|
|
113
142
|
/**
|
|
114
143
|
* Get the current tunnel status.
|
|
@@ -119,6 +148,8 @@ export class TunnelManager {
|
|
|
119
148
|
status: this.status,
|
|
120
149
|
publicUrl: this.publicUrl,
|
|
121
150
|
connectedSince: this.connectedSince?.toISOString() ?? null,
|
|
151
|
+
routable: this.routable,
|
|
152
|
+
warning: this.routeWarning ?? undefined,
|
|
122
153
|
error: this.lastError ?? undefined,
|
|
123
154
|
};
|
|
124
155
|
}
|
|
@@ -141,6 +172,7 @@ export class TunnelManager {
|
|
|
141
172
|
walletAddress: config.walletAddress,
|
|
142
173
|
runId: config.runId,
|
|
143
174
|
serverKeypair: config.serverKeypair,
|
|
175
|
+
signer: config.tunnelSigner,
|
|
144
176
|
});
|
|
145
177
|
const frpcConfig = generateFrpcConfig({
|
|
146
178
|
serverAddr: config.serverAddr,
|
|
@@ -171,15 +203,30 @@ export class TunnelManager {
|
|
|
171
203
|
this.process = proc;
|
|
172
204
|
let startupOutput = "";
|
|
173
205
|
let resolved = false;
|
|
206
|
+
const cleanMessage = (message) => message.replace(/\x1b\[[0-9;]*m/g, "").trim();
|
|
207
|
+
const markRetrying = (message) => {
|
|
208
|
+
const warning = cleanMessage(message);
|
|
209
|
+
this.routeWarning = warning || "Waiting for tunnel connection";
|
|
210
|
+
};
|
|
174
211
|
const onData = (data) => {
|
|
175
212
|
const text = data.toString();
|
|
176
213
|
startupOutput += text;
|
|
177
|
-
if (text.includes("start
|
|
178
|
-
text.includes("
|
|
214
|
+
if (text.includes("start error:") ||
|
|
215
|
+
text.includes("connect to server error:")) {
|
|
216
|
+
markRetrying(startupOutput);
|
|
217
|
+
if (!resolved) {
|
|
218
|
+
resolved = true;
|
|
219
|
+
resolve();
|
|
220
|
+
}
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
if (text.includes("start proxy success")) {
|
|
224
|
+
this.status = "connected";
|
|
225
|
+
this.connectedSince ??= new Date();
|
|
226
|
+
this.routable = undefined;
|
|
227
|
+
this.routeWarning = null;
|
|
179
228
|
if (!resolved) {
|
|
180
229
|
resolved = true;
|
|
181
|
-
this.status = "connected";
|
|
182
|
-
this.connectedSince = new Date();
|
|
183
230
|
resolve();
|
|
184
231
|
}
|
|
185
232
|
}
|
|
@@ -208,13 +255,21 @@ export class TunnelManager {
|
|
|
208
255
|
else if (this.status === "connected") {
|
|
209
256
|
this.status = "disconnected";
|
|
210
257
|
}
|
|
258
|
+
else if (code !== 0) {
|
|
259
|
+
this.status = "error";
|
|
260
|
+
this.lastError = `frpc exited with code ${code}: ${startupOutput}`;
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
this.status = "disconnected";
|
|
264
|
+
}
|
|
211
265
|
});
|
|
212
|
-
// Timeout for startup — resolve optimistically
|
|
266
|
+
// Timeout for startup — resolve optimistically. frpc is generated with
|
|
267
|
+
// loginFailExit=false, so auth/registration/network failures can recover
|
|
268
|
+
// after the server is registered or connectivity returns.
|
|
213
269
|
setTimeout(() => {
|
|
214
270
|
if (!resolved) {
|
|
215
271
|
resolved = true;
|
|
216
|
-
|
|
217
|
-
this.connectedSince = new Date();
|
|
272
|
+
markRetrying(startupOutput || "Waiting for proxy startup");
|
|
218
273
|
resolve();
|
|
219
274
|
}
|
|
220
275
|
}, 10_000);
|