@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.
Files changed (213) hide show
  1. package/dist/api-auth.d.ts +31 -0
  2. package/dist/api-auth.d.ts.map +1 -0
  3. package/dist/api-auth.js +148 -0
  4. package/dist/api-auth.js.map +1 -0
  5. package/dist/api.d.ts +4 -0
  6. package/dist/api.d.ts.map +1 -1
  7. package/dist/api.js +3 -0
  8. package/dist/api.js.map +1 -1
  9. package/dist/app.d.ts +77 -2
  10. package/dist/app.d.ts.map +1 -1
  11. package/dist/app.js +165 -9
  12. package/dist/app.js.map +1 -1
  13. package/dist/bootstrap.d.ts +36 -3
  14. package/dist/bootstrap.d.ts.map +1 -1
  15. package/dist/bootstrap.js +434 -39
  16. package/dist/bootstrap.js.map +1 -1
  17. package/dist/client.d.ts +17 -0
  18. package/dist/client.d.ts.map +1 -0
  19. package/dist/client.js +369 -0
  20. package/dist/client.js.map +1 -0
  21. package/dist/config/defaults.d.ts +12 -0
  22. package/dist/config/defaults.d.ts.map +1 -0
  23. package/dist/config/defaults.js +14 -0
  24. package/dist/config/defaults.js.map +1 -0
  25. package/dist/config/index.d.ts +4 -0
  26. package/dist/config/index.d.ts.map +1 -0
  27. package/dist/config/index.js +4 -0
  28. package/dist/config/index.js.map +1 -0
  29. package/dist/config/loader.d.ts +8 -0
  30. package/dist/config/loader.d.ts.map +1 -0
  31. package/dist/config/loader.js +78 -0
  32. package/dist/config/loader.js.map +1 -0
  33. package/dist/config/paths.d.ts +9 -0
  34. package/dist/config/paths.d.ts.map +1 -0
  35. package/dist/config/paths.js +22 -0
  36. package/dist/config/paths.js.map +1 -0
  37. package/dist/enclave-main.d.ts +10 -0
  38. package/dist/enclave-main.d.ts.map +1 -0
  39. package/dist/enclave-main.js +115 -0
  40. package/dist/enclave-main.js.map +1 -0
  41. package/dist/hono-context.d.ts +14 -0
  42. package/dist/hono-context.d.ts.map +1 -0
  43. package/dist/hono-context.js +2 -0
  44. package/dist/hono-context.js.map +1 -0
  45. package/dist/index.js +48 -12
  46. package/dist/index.js.map +1 -1
  47. package/dist/jobs/types.d.ts +17 -0
  48. package/dist/jobs/types.d.ts.map +1 -0
  49. package/dist/jobs/types.js +3 -0
  50. package/dist/jobs/types.js.map +1 -0
  51. package/dist/jobs/worker.d.ts +30 -0
  52. package/dist/jobs/worker.d.ts.map +1 -0
  53. package/dist/jobs/worker.js +217 -0
  54. package/dist/jobs/worker.js.map +1 -0
  55. package/dist/keys/server-account.d.ts +28 -0
  56. package/dist/keys/server-account.d.ts.map +1 -0
  57. package/dist/keys/server-account.js +48 -0
  58. package/dist/keys/server-account.js.map +1 -0
  59. package/dist/listen.d.ts +11 -0
  60. package/dist/listen.d.ts.map +1 -0
  61. package/dist/listen.js +23 -0
  62. package/dist/listen.js.map +1 -0
  63. package/dist/logger/index.d.ts +5 -0
  64. package/dist/logger/index.d.ts.map +1 -0
  65. package/dist/logger/index.js +9 -0
  66. package/dist/logger/index.js.map +1 -0
  67. package/dist/logging/access-log.d.ts +4 -0
  68. package/dist/logging/access-log.d.ts.map +1 -0
  69. package/dist/logging/access-log.js +25 -0
  70. package/dist/logging/access-log.js.map +1 -0
  71. package/dist/logging/access-reader.d.ts +19 -0
  72. package/dist/logging/access-reader.d.ts.map +1 -0
  73. package/dist/logging/access-reader.js +50 -0
  74. package/dist/logging/access-reader.js.map +1 -0
  75. package/dist/middleware/access-log.d.ts.map +1 -1
  76. package/dist/middleware/access-log.js +8 -2
  77. package/dist/middleware/access-log.js.map +1 -1
  78. package/dist/middleware/builder-check.d.ts +2 -2
  79. package/dist/middleware/builder-check.d.ts.map +1 -1
  80. package/dist/middleware/builder-check.js +7 -2
  81. package/dist/middleware/builder-check.js.map +1 -1
  82. package/dist/middleware/cors.d.ts +18 -0
  83. package/dist/middleware/cors.d.ts.map +1 -0
  84. package/dist/middleware/cors.js +51 -0
  85. package/dist/middleware/cors.js.map +1 -0
  86. package/dist/middleware/owner-check.js +1 -1
  87. package/dist/middleware/owner-check.js.map +1 -1
  88. package/dist/middleware/web3-auth.d.ts +12 -0
  89. package/dist/middleware/web3-auth.d.ts.map +1 -1
  90. package/dist/middleware/web3-auth.js +27 -31
  91. package/dist/middleware/web3-auth.js.map +1 -1
  92. package/dist/migrations/detect-stuck-version-ledger.d.ts +43 -0
  93. package/dist/migrations/detect-stuck-version-ledger.d.ts.map +1 -0
  94. package/dist/migrations/detect-stuck-version-ledger.js +73 -0
  95. package/dist/migrations/detect-stuck-version-ledger.js.map +1 -0
  96. package/dist/migrations/local-state.d.ts +35 -0
  97. package/dist/migrations/local-state.d.ts.map +1 -0
  98. package/dist/migrations/local-state.js +133 -0
  99. package/dist/migrations/local-state.js.map +1 -0
  100. package/dist/migrations/state-migrations.d.ts +78 -0
  101. package/dist/migrations/state-migrations.d.ts.map +1 -0
  102. package/dist/migrations/state-migrations.js +72 -0
  103. package/dist/migrations/state-migrations.js.map +1 -0
  104. package/dist/pending-blob-deletions.d.ts +10 -0
  105. package/dist/pending-blob-deletions.d.ts.map +1 -0
  106. package/dist/pending-blob-deletions.js +38 -0
  107. package/dist/pending-blob-deletions.js.map +1 -0
  108. package/dist/routes/access-logs.d.ts +7 -0
  109. package/dist/routes/access-logs.d.ts.map +1 -1
  110. package/dist/routes/access-logs.js +11 -17
  111. package/dist/routes/access-logs.js.map +1 -1
  112. package/dist/routes/auth-device.d.ts +49 -0
  113. package/dist/routes/auth-device.d.ts.map +1 -0
  114. package/dist/routes/auth-device.js +581 -0
  115. package/dist/routes/auth-device.js.map +1 -0
  116. package/dist/routes/data.d.ts +67 -2
  117. package/dist/routes/data.d.ts.map +1 -1
  118. package/dist/routes/data.js +41 -201
  119. package/dist/routes/data.js.map +1 -1
  120. package/dist/routes/derivatives.d.ts +33 -0
  121. package/dist/routes/derivatives.d.ts.map +1 -0
  122. package/dist/routes/derivatives.js +33 -0
  123. package/dist/routes/derivatives.js.map +1 -0
  124. package/dist/routes/enclave-jobs.d.ts +9 -0
  125. package/dist/routes/enclave-jobs.d.ts.map +1 -0
  126. package/dist/routes/enclave-jobs.js +104 -0
  127. package/dist/routes/enclave-jobs.js.map +1 -0
  128. package/dist/routes/grants.d.ts +9 -2
  129. package/dist/routes/grants.d.ts.map +1 -1
  130. package/dist/routes/grants.js +18 -196
  131. package/dist/routes/grants.js.map +1 -1
  132. package/dist/routes/health.d.ts +7 -1
  133. package/dist/routes/health.d.ts.map +1 -1
  134. package/dist/routes/health.js +39 -2
  135. package/dist/routes/health.js.map +1 -1
  136. package/dist/routes/mcp.d.ts +115 -0
  137. package/dist/routes/mcp.d.ts.map +1 -0
  138. package/dist/routes/mcp.js +676 -0
  139. package/dist/routes/mcp.js.map +1 -0
  140. package/dist/routes/oauth-token.d.ts +56 -0
  141. package/dist/routes/oauth-token.d.ts.map +1 -0
  142. package/dist/routes/oauth-token.js +49 -0
  143. package/dist/routes/oauth-token.js.map +1 -0
  144. package/dist/routes/sync.d.ts +7 -0
  145. package/dist/routes/sync.d.ts.map +1 -1
  146. package/dist/routes/sync.js +12 -39
  147. package/dist/routes/sync.js.map +1 -1
  148. package/dist/routes/ui-config.d.ts.map +1 -1
  149. package/dist/routes/ui-config.js +17 -82
  150. package/dist/routes/ui-config.js.map +1 -1
  151. package/dist/routes/ui-registration.d.ts +9 -0
  152. package/dist/routes/ui-registration.d.ts.map +1 -0
  153. package/dist/routes/ui-registration.js +217 -0
  154. package/dist/routes/ui-registration.js.map +1 -0
  155. package/dist/routes/ui.d.ts +1 -0
  156. package/dist/routes/ui.d.ts.map +1 -1
  157. package/dist/routes/ui.js +46 -4
  158. package/dist/routes/ui.js.map +1 -1
  159. package/dist/routes/write-session.d.ts +37 -0
  160. package/dist/routes/write-session.d.ts.map +1 -0
  161. package/dist/routes/write-session.js +34 -0
  162. package/dist/routes/write-session.js.map +1 -0
  163. package/dist/storage/hierarchy.d.ts +36 -0
  164. package/dist/storage/hierarchy.d.ts.map +1 -0
  165. package/dist/storage/hierarchy.js +282 -0
  166. package/dist/storage/hierarchy.js.map +1 -0
  167. package/dist/storage/index-manager.d.ts +60 -0
  168. package/dist/storage/index-manager.d.ts.map +1 -0
  169. package/dist/storage/index-manager.js +174 -0
  170. package/dist/storage/index-manager.js.map +1 -0
  171. package/dist/storage/index-schema.d.ts +5 -0
  172. package/dist/storage/index-schema.d.ts.map +1 -0
  173. package/dist/storage/index-schema.js +69 -0
  174. package/dist/storage/index-schema.js.map +1 -0
  175. package/dist/storage/node-data-storage.d.ts +9 -0
  176. package/dist/storage/node-data-storage.d.ts.map +1 -0
  177. package/dist/storage/node-data-storage.js +105 -0
  178. package/dist/storage/node-data-storage.js.map +1 -0
  179. package/dist/storage/question-store.d.ts +9 -0
  180. package/dist/storage/question-store.d.ts.map +1 -0
  181. package/dist/storage/question-store.js +151 -0
  182. package/dist/storage/question-store.js.map +1 -0
  183. package/dist/sync-cursor.d.ts +15 -0
  184. package/dist/sync-cursor.d.ts.map +1 -0
  185. package/dist/sync-cursor.js +110 -0
  186. package/dist/sync-cursor.js.map +1 -0
  187. package/dist/token-store.d.ts +22 -0
  188. package/dist/token-store.d.ts.map +1 -0
  189. package/dist/token-store.js +120 -0
  190. package/dist/token-store.js.map +1 -0
  191. package/dist/tunnel/auth.d.ts +4 -0
  192. package/dist/tunnel/auth.d.ts.map +1 -1
  193. package/dist/tunnel/auth.js +2 -1
  194. package/dist/tunnel/auth.js.map +1 -1
  195. package/dist/tunnel/config.d.ts +1 -0
  196. package/dist/tunnel/config.d.ts.map +1 -1
  197. package/dist/tunnel/config.js +1 -1
  198. package/dist/tunnel/config.js.map +1 -1
  199. package/dist/tunnel/manager.d.ts +21 -1
  200. package/dist/tunnel/manager.d.ts.map +1 -1
  201. package/dist/tunnel/manager.js +78 -23
  202. package/dist/tunnel/manager.js.map +1 -1
  203. package/dist/ui/browser_tls_rustls_bg.wasm +0 -0
  204. package/dist/ui/index.html +2192 -0
  205. package/dist/ui/ps-lite-debug.d.ts +93 -0
  206. package/dist/ui/ps-lite-debug.d.ts.map +1 -0
  207. package/dist/ui/ps-lite-debug.js +135347 -0
  208. package/dist/ui/ps-lite-debug.js.map +1 -0
  209. package/package.json +20 -6
  210. package/dist/middleware/grant-check.d.ts +0 -12
  211. package/dist/middleware/grant-check.d.ts.map +0 -1
  212. package/dist/middleware/grant-check.js +0 -90
  213. 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":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAmB/C,KAAK,UAAU,cAAc,CAAC,UAAkB;IAC9C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA6B,CAAC;QAC3D,OAAO,OAAO,MAAM,CAAC,sBAAsB,KAAK,QAAQ;YACtD,CAAC,CAAC,MAAM,CAAC,sBAAsB;YAC/B,CAAC,CAAC,IAAI,CAAC;IACX,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IACE,GAAG,YAAY,KAAK;YACpB,MAAM,IAAI,GAAG;YACZ,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAChD,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,KAAK,UAAU,eAAe,CAC5B,UAAkB,EAClB,SAAiB;IAEjB,MAAM,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,GAAG,UAAU,MAAM,CAAC;IACpC,MAAM,KAAK,GAAoB;QAC7B,OAAO,EAAE,CAAC;QACV,sBAAsB,EAAE,SAAS;KAClC,CAAC;IACF,MAAM,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAChE,MAAM,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,wBAAwB,CAAC,UAAkB;IAClD,OAAO;QACL,KAAK,CAAC,IAAI;YACR,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;YAChD,OAAO,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC;QAC5C,CAAC;QAED,0EAA0E;QAC1E,oEAAoE;QACpE,iEAAiE;QACjE,sEAAsE;QACtE,wEAAwE;QACxE,0CAA0C;QAC1C,mEAAmE;QACnE,wEAAwE;QACxE,8DAA8D;QAC9D,KAAK,CAAC,KAAK,CAAC,SAAS;YACnB,IAAI,GAAG,GAAG,IAAI,CAAC;YACf,IAAI,CAAC;gBACH,GAAG,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YAC3C,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,CAAC,CACH,GAAG,YAAY,KAAK;oBACpB,MAAM,IAAI,GAAG;oBACZ,GAA6B,CAAC,IAAI,KAAK,QAAQ,CACjD,EAAE,CAAC;oBACF,MAAM,GAAG,CAAC;gBACZ,CAAC;YACH,CAAC;YACD,IAAI,MAA+B,CAAC;YACpC,IAAI,CAAC;gBACH,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;gBAC7C,MAAM;oBACJ,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,KAAK,IAAI;wBACjD,CAAC,CAAE,SAAqC;wBACxC,CAAC,CAAC,EAAE,CAAC;YACX,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,GAAG,EAAE,CAAC;YACd,CAAC;YACD,MAAM,YAAY,GAChB,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI;gBACrD,CAAC,CAAE,MAAM,CAAC,IAAgC;gBAC1C,CAAC,CAAC,EAAE,CAAC;YACT,MAAM,IAAI,GAAG;gBACX,GAAG,MAAM;gBACT,IAAI,EAAE,EAAE,GAAG,YAAY,EAAE,sBAAsB,EAAE,SAAS,EAAE;aAC7D,CAAC;YACF,MAAM,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACtD,MAAM,OAAO,GAAG,GAAG,UAAU,MAAM,CAAC;YACpC,MAAM,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;YAC/D,MAAM,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QACpC,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,UAAkB,EAClB,UAA6B,EAAE;IAE/B,IAAI,CAAC,OAAO,CAAC,gBAAgB,IAAI,QAAQ,CAAC,UAAU,CAAC,KAAK,aAAa,EAAE,CAAC;QACxE,OAAO,wBAAwB,CAAC,UAAU,CAAC,CAAC;IAC9C,CAAC;IAED,OAAO;QACL,KAAK,CAAC,IAAI;YACR,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,UAAU,CAAC,CAAC;YAChD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBACpB,OAAO,MAAM,CAAC;YAChB,CAAC;YACD,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;gBAC7B,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC;oBAC9B,UAAU,EAAE,OAAO,CAAC,gBAAgB;iBACrC,CAAC,CAAC;gBACH,OAAO,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC;YAC5C,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,KAAK,CAAC,KAAK,CAAC,SAAS;YACnB,MAAM,eAAe,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QAC/C,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -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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"}
@@ -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;CAC9B;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,CAiBtB"}
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"}
@@ -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 sig = await config.serverKeypair.signMessage(claim);
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
@@ -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;AAwBrC,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,GAAG,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IAE1D,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;AACxB,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"}
@@ -7,6 +7,7 @@ export interface FrpcConfigOptions {
7
7
  serverAddr: string;
8
8
  serverPort: number;
9
9
  localPort: number;
10
+ proxyName?: string;
10
11
  subdomain: string;
11
12
  walletAddress: string;
12
13
  ownerAddress: string;
@@ -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"}
@@ -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;AAcH;;;;;;;;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;;;;;;cAMzB,OAAO,CAAC,SAAS;eAChB,OAAO,CAAC,SAAS;CAC/B,CAAC;AACF,CAAC"}
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"}
@@ -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
- * Start the frpc process with the given configuration.
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;AAKhF,MAAM,MAAM,YAAY,GACpB,SAAS,GACT,UAAU,GACV,WAAW,GACX,cAAc,GACd,OAAO,CAAC;AAEZ,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,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,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,MAAM,CAA6B;IAC3C,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,YAAY,CAA8C;IAClE,OAAO,CAAC,UAAU,CAAS;gBAEf,WAAW,EAAE,MAAM;IAI/B;;;;OAIG;IACG,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkCtE;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA8B3B;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,WAAW,CAAC,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAUrD;;OAEG;IACH,SAAS,IAAI,gBAAgB;IAU7B;;OAEG;IACH,YAAY,IAAI,MAAM,GAAG,IAAI;IAQ7B;;OAEG;YACW,gBAAgB;IA8B9B;;;OAGG;YACW,YAAY;IAkE1B;;OAEG;YACW,WAAW;IAsBzB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,iBAAiB;IAOzB;;;OAGG;YACW,YAAY;CA0B3B"}
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"}
@@ -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
- * Start the frpc process with the given configuration.
37
- * Returns the public URL once the tunnel is established.
38
- * Automatically schedules periodic claim refresh.
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
- async start(config, binaryPath) {
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 subdomain = config.walletAddress.toLowerCase();
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
- if (reachable) {
105
- this.status = "connected";
106
- this.lastError = null;
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 proxy success") ||
178
- text.includes("login to server success")) {
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
- this.status = "connected";
217
- this.connectedSince = new Date();
272
+ markRetrying(startupOutput || "Waiting for proxy startup");
218
273
  resolve();
219
274
  }
220
275
  }, 10_000);