uxnan-bridge 0.0.1-alpha.20260621

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 (255) hide show
  1. package/README.md +150 -0
  2. package/dist/src/account-status.d.ts +13 -0
  3. package/dist/src/account-status.js +78 -0
  4. package/dist/src/account-status.js.map +1 -0
  5. package/dist/src/adapters/base-adapter.d.ts +18 -0
  6. package/dist/src/adapters/base-adapter.js +15 -0
  7. package/dist/src/adapters/base-adapter.js.map +1 -0
  8. package/dist/src/adapters/claude-adapter.d.ts +102 -0
  9. package/dist/src/adapters/claude-adapter.js +486 -0
  10. package/dist/src/adapters/claude-adapter.js.map +1 -0
  11. package/dist/src/adapters/claude-tools.d.ts +25 -0
  12. package/dist/src/adapters/claude-tools.js +146 -0
  13. package/dist/src/adapters/claude-tools.js.map +1 -0
  14. package/dist/src/adapters/codex-adapter.d.ts +116 -0
  15. package/dist/src/adapters/codex-adapter.js +912 -0
  16. package/dist/src/adapters/codex-adapter.js.map +1 -0
  17. package/dist/src/adapters/codex-app-server.d.ts +74 -0
  18. package/dist/src/adapters/codex-app-server.js +225 -0
  19. package/dist/src/adapters/codex-app-server.js.map +1 -0
  20. package/dist/src/adapters/codex-approval.d.ts +88 -0
  21. package/dist/src/adapters/codex-approval.js +160 -0
  22. package/dist/src/adapters/codex-approval.js.map +1 -0
  23. package/dist/src/adapters/codex-tools.d.ts +18 -0
  24. package/dist/src/adapters/codex-tools.js +106 -0
  25. package/dist/src/adapters/codex-tools.js.map +1 -0
  26. package/dist/src/adapters/content-blocks.d.ts +68 -0
  27. package/dist/src/adapters/content-blocks.js +205 -0
  28. package/dist/src/adapters/content-blocks.js.map +1 -0
  29. package/dist/src/adapters/echo-agent-adapter.d.ts +23 -0
  30. package/dist/src/adapters/echo-agent-adapter.js +72 -0
  31. package/dist/src/adapters/echo-agent-adapter.js.map +1 -0
  32. package/dist/src/adapters/gemini-adapter.d.ts +87 -0
  33. package/dist/src/adapters/gemini-adapter.js +594 -0
  34. package/dist/src/adapters/gemini-adapter.js.map +1 -0
  35. package/dist/src/adapters/gemini-tools.d.ts +4 -0
  36. package/dist/src/adapters/gemini-tools.js +48 -0
  37. package/dist/src/adapters/gemini-tools.js.map +1 -0
  38. package/dist/src/adapters/opencode-adapter.d.ts +74 -0
  39. package/dist/src/adapters/opencode-adapter.js +418 -0
  40. package/dist/src/adapters/opencode-adapter.js.map +1 -0
  41. package/dist/src/adapters/opencode-tools.d.ts +2 -0
  42. package/dist/src/adapters/opencode-tools.js +41 -0
  43. package/dist/src/adapters/opencode-tools.js.map +1 -0
  44. package/dist/src/adapters/pi-adapter.d.ts +92 -0
  45. package/dist/src/adapters/pi-adapter.js +467 -0
  46. package/dist/src/adapters/pi-adapter.js.map +1 -0
  47. package/dist/src/adapters/pi-tools.d.ts +10 -0
  48. package/dist/src/adapters/pi-tools.js +72 -0
  49. package/dist/src/adapters/pi-tools.js.map +1 -0
  50. package/dist/src/adapters/process-agent-adapter.d.ts +24 -0
  51. package/dist/src/adapters/process-agent-adapter.js +111 -0
  52. package/dist/src/adapters/process-agent-adapter.js.map +1 -0
  53. package/dist/src/adapters/resolve-claude.d.ts +13 -0
  54. package/dist/src/adapters/resolve-claude.js +57 -0
  55. package/dist/src/adapters/resolve-claude.js.map +1 -0
  56. package/dist/src/adapters/resolve-codex.d.ts +13 -0
  57. package/dist/src/adapters/resolve-codex.js +48 -0
  58. package/dist/src/adapters/resolve-codex.js.map +1 -0
  59. package/dist/src/adapters/resolve-gemini.d.ts +13 -0
  60. package/dist/src/adapters/resolve-gemini.js +47 -0
  61. package/dist/src/adapters/resolve-gemini.js.map +1 -0
  62. package/dist/src/adapters/resolve-opencode.d.ts +11 -0
  63. package/dist/src/adapters/resolve-opencode.js +49 -0
  64. package/dist/src/adapters/resolve-opencode.js.map +1 -0
  65. package/dist/src/adapters/resolve-pi.d.ts +13 -0
  66. package/dist/src/adapters/resolve-pi.js +46 -0
  67. package/dist/src/adapters/resolve-pi.js.map +1 -0
  68. package/dist/src/adapters/run-options.d.ts +22 -0
  69. package/dist/src/adapters/run-options.js +48 -0
  70. package/dist/src/adapters/run-options.js.map +1 -0
  71. package/dist/src/adapters/spawn.d.ts +20 -0
  72. package/dist/src/adapters/spawn.js +16 -0
  73. package/dist/src/adapters/spawn.js.map +1 -0
  74. package/dist/src/agents/agent-manager.d.ts +98 -0
  75. package/dist/src/agents/agent-manager.js +433 -0
  76. package/dist/src/agents/agent-manager.js.map +1 -0
  77. package/dist/src/agents/attachments.d.ts +28 -0
  78. package/dist/src/agents/attachments.js +121 -0
  79. package/dist/src/agents/attachments.js.map +1 -0
  80. package/dist/src/bridge-context.d.ts +45 -0
  81. package/dist/src/bridge-context.js +2 -0
  82. package/dist/src/bridge-context.js.map +1 -0
  83. package/dist/src/bridge-status.d.ts +12 -0
  84. package/dist/src/bridge-status.js +17 -0
  85. package/dist/src/bridge-status.js.map +1 -0
  86. package/dist/src/bridge.d.ts +37 -0
  87. package/dist/src/bridge.js +446 -0
  88. package/dist/src/bridge.js.map +1 -0
  89. package/dist/src/cli.d.ts +2 -0
  90. package/dist/src/cli.js +194 -0
  91. package/dist/src/cli.js.map +1 -0
  92. package/dist/src/conversation/session-history.d.ts +27 -0
  93. package/dist/src/conversation/session-history.js +1082 -0
  94. package/dist/src/conversation/session-history.js.map +1 -0
  95. package/dist/src/conversation/thread-store.d.ts +74 -0
  96. package/dist/src/conversation/thread-store.js +366 -0
  97. package/dist/src/conversation/thread-store.js.map +1 -0
  98. package/dist/src/daemon-config.d.ts +123 -0
  99. package/dist/src/daemon-config.js +64 -0
  100. package/dist/src/daemon-config.js.map +1 -0
  101. package/dist/src/daemon-state.d.ts +27 -0
  102. package/dist/src/daemon-state.js +76 -0
  103. package/dist/src/daemon-state.js.map +1 -0
  104. package/dist/src/git/git-runner.d.ts +24 -0
  105. package/dist/src/git/git-runner.js +63 -0
  106. package/dist/src/git/git-runner.js.map +1 -0
  107. package/dist/src/git/git-service.d.ts +76 -0
  108. package/dist/src/git/git-service.js +435 -0
  109. package/dist/src/git/git-service.js.map +1 -0
  110. package/dist/src/handler-router.d.ts +34 -0
  111. package/dist/src/handler-router.js +67 -0
  112. package/dist/src/handler-router.js.map +1 -0
  113. package/dist/src/handlers/account-handler.d.ts +4 -0
  114. package/dist/src/handlers/account-handler.js +27 -0
  115. package/dist/src/handlers/account-handler.js.map +1 -0
  116. package/dist/src/handlers/agent-handler.d.ts +2 -0
  117. package/dist/src/handlers/agent-handler.js +8 -0
  118. package/dist/src/handlers/agent-handler.js.map +1 -0
  119. package/dist/src/handlers/bridge-control-handler.d.ts +2 -0
  120. package/dist/src/handlers/bridge-control-handler.js +64 -0
  121. package/dist/src/handlers/bridge-control-handler.js.map +1 -0
  122. package/dist/src/handlers/desktop-handler.d.ts +12 -0
  123. package/dist/src/handlers/desktop-handler.js +5 -0
  124. package/dist/src/handlers/desktop-handler.js.map +1 -0
  125. package/dist/src/handlers/git-handler.d.ts +2 -0
  126. package/dist/src/handlers/git-handler.js +82 -0
  127. package/dist/src/handlers/git-handler.js.map +1 -0
  128. package/dist/src/handlers/index.d.ts +8 -0
  129. package/dist/src/handlers/index.js +22 -0
  130. package/dist/src/handlers/index.js.map +1 -0
  131. package/dist/src/handlers/not-implemented.d.ts +10 -0
  132. package/dist/src/handlers/not-implemented.js +21 -0
  133. package/dist/src/handlers/not-implemented.js.map +1 -0
  134. package/dist/src/handlers/notifications-handler.d.ts +2 -0
  135. package/dist/src/handlers/notifications-handler.js +62 -0
  136. package/dist/src/handlers/notifications-handler.js.map +1 -0
  137. package/dist/src/handlers/params.d.ts +11 -0
  138. package/dist/src/handlers/params.js +72 -0
  139. package/dist/src/handlers/params.js.map +1 -0
  140. package/dist/src/handlers/project-handler.d.ts +2 -0
  141. package/dist/src/handlers/project-handler.js +6 -0
  142. package/dist/src/handlers/project-handler.js.map +1 -0
  143. package/dist/src/handlers/thread-context-handler.d.ts +2 -0
  144. package/dist/src/handlers/thread-context-handler.js +211 -0
  145. package/dist/src/handlers/thread-context-handler.js.map +1 -0
  146. package/dist/src/handlers/workspace-handler.d.ts +2 -0
  147. package/dist/src/handlers/workspace-handler.js +101 -0
  148. package/dist/src/handlers/workspace-handler.js.map +1 -0
  149. package/dist/src/hooks/claude-approval-hook.d.ts +7 -0
  150. package/dist/src/hooks/claude-approval-hook.js +95 -0
  151. package/dist/src/hooks/claude-approval-hook.js.map +1 -0
  152. package/dist/src/hooks/gemini-approval-hook.d.ts +7 -0
  153. package/dist/src/hooks/gemini-approval-hook.js +113 -0
  154. package/dist/src/hooks/gemini-approval-hook.js.map +1 -0
  155. package/dist/src/index.d.ts +62 -0
  156. package/dist/src/index.js +65 -0
  157. package/dist/src/index.js.map +1 -0
  158. package/dist/src/keyring-secret-store.d.ts +36 -0
  159. package/dist/src/keyring-secret-store.js +70 -0
  160. package/dist/src/keyring-secret-store.js.map +1 -0
  161. package/dist/src/lock-file.d.ts +18 -0
  162. package/dist/src/lock-file.js +60 -0
  163. package/dist/src/lock-file.js.map +1 -0
  164. package/dist/src/logger.d.ts +28 -0
  165. package/dist/src/logger.js +99 -0
  166. package/dist/src/logger.js.map +1 -0
  167. package/dist/src/pairing/pairing-code-service.d.ts +45 -0
  168. package/dist/src/pairing/pairing-code-service.js +183 -0
  169. package/dist/src/pairing/pairing-code-service.js.map +1 -0
  170. package/dist/src/projects/project-registry.d.ts +14 -0
  171. package/dist/src/projects/project-registry.js +60 -0
  172. package/dist/src/projects/project-registry.js.map +1 -0
  173. package/dist/src/push/push-sender.d.ts +21 -0
  174. package/dist/src/push/push-sender.js +96 -0
  175. package/dist/src/push/push-sender.js.map +1 -0
  176. package/dist/src/push/push-service.d.ts +122 -0
  177. package/dist/src/push/push-service.js +260 -0
  178. package/dist/src/push/push-service.js.map +1 -0
  179. package/dist/src/qr.d.ts +17 -0
  180. package/dist/src/qr.js +31 -0
  181. package/dist/src/qr.js.map +1 -0
  182. package/dist/src/secret-store.d.ts +23 -0
  183. package/dist/src/secret-store.js +27 -0
  184. package/dist/src/secret-store.js.map +1 -0
  185. package/dist/src/secure-device-state.d.ts +16 -0
  186. package/dist/src/secure-device-state.js +63 -0
  187. package/dist/src/secure-device-state.js.map +1 -0
  188. package/dist/src/service-installer.d.ts +57 -0
  189. package/dist/src/service-installer.js +254 -0
  190. package/dist/src/service-installer.js.map +1 -0
  191. package/dist/src/session-state.d.ts +14 -0
  192. package/dist/src/session-state.js +19 -0
  193. package/dist/src/session-state.js.map +1 -0
  194. package/dist/src/transport/crypto.d.ts +43 -0
  195. package/dist/src/transport/crypto.js +78 -0
  196. package/dist/src/transport/crypto.js.map +1 -0
  197. package/dist/src/transport/lan-server.d.ts +33 -0
  198. package/dist/src/transport/lan-server.js +105 -0
  199. package/dist/src/transport/lan-server.js.map +1 -0
  200. package/dist/src/transport/local-hosts.d.ts +17 -0
  201. package/dist/src/transport/local-hosts.js +30 -0
  202. package/dist/src/transport/local-hosts.js.map +1 -0
  203. package/dist/src/transport/mdns-advertiser.d.ts +83 -0
  204. package/dist/src/transport/mdns-advertiser.js +282 -0
  205. package/dist/src/transport/mdns-advertiser.js.map +1 -0
  206. package/dist/src/transport/message-io.d.ts +33 -0
  207. package/dist/src/transport/message-io.js +87 -0
  208. package/dist/src/transport/message-io.js.map +1 -0
  209. package/dist/src/transport/outbound-log.d.ts +24 -0
  210. package/dist/src/transport/outbound-log.js +78 -0
  211. package/dist/src/transport/outbound-log.js.map +1 -0
  212. package/dist/src/transport/relay-client.d.ts +19 -0
  213. package/dist/src/transport/relay-client.js +27 -0
  214. package/dist/src/transport/relay-client.js.map +1 -0
  215. package/dist/src/transport/secure-channel.d.ts +33 -0
  216. package/dist/src/transport/secure-channel.js +81 -0
  217. package/dist/src/transport/secure-channel.js.map +1 -0
  218. package/dist/src/transport/server-handshake.d.ts +49 -0
  219. package/dist/src/transport/server-handshake.js +137 -0
  220. package/dist/src/transport/server-handshake.js.map +1 -0
  221. package/dist/src/transport/session-handler.d.ts +19 -0
  222. package/dist/src/transport/session-handler.js +134 -0
  223. package/dist/src/transport/session-handler.js.map +1 -0
  224. package/dist/src/transport/session-registry.d.ts +58 -0
  225. package/dist/src/transport/session-registry.js +91 -0
  226. package/dist/src/transport/session-registry.js.map +1 -0
  227. package/dist/src/transport/trust-store.d.ts +23 -0
  228. package/dist/src/transport/trust-store.js +33 -0
  229. package/dist/src/transport/trust-store.js.map +1 -0
  230. package/dist/src/transport/ws-adapter.d.ts +7 -0
  231. package/dist/src/transport/ws-adapter.js +16 -0
  232. package/dist/src/transport/ws-adapter.js.map +1 -0
  233. package/dist/src/version.d.ts +1 -0
  234. package/dist/src/version.js +13 -0
  235. package/dist/src/version.js.map +1 -0
  236. package/dist/src/workspace/browse-service.d.ts +10 -0
  237. package/dist/src/workspace/browse-service.js +97 -0
  238. package/dist/src/workspace/browse-service.js.map +1 -0
  239. package/dist/src/workspace/checkpoint-service.d.ts +21 -0
  240. package/dist/src/workspace/checkpoint-service.js +219 -0
  241. package/dist/src/workspace/checkpoint-service.js.map +1 -0
  242. package/dist/src/workspace/path-guard.d.ts +7 -0
  243. package/dist/src/workspace/path-guard.js +51 -0
  244. package/dist/src/workspace/path-guard.js.map +1 -0
  245. package/dist/src/workspace/workspace-service.d.ts +8 -0
  246. package/dist/src/workspace/workspace-service.js +111 -0
  247. package/dist/src/workspace/workspace-service.js.map +1 -0
  248. package/package.json +46 -0
  249. package/scripts/extract-gemini-hook.mjs +16 -0
  250. package/scripts/fake-approval-bridge.mjs +23 -0
  251. package/scripts/install-service-linux.sh +38 -0
  252. package/scripts/install-service-macos.sh +38 -0
  253. package/scripts/install-service-windows.ps1 +26 -0
  254. package/scripts/test-gemini-hook-e2e.mjs +168 -0
  255. package/scripts/write-gemini-settings.mjs +31 -0
@@ -0,0 +1,65 @@
1
+ /**
2
+ * uxnan-bridge — public API.
3
+ *
4
+ * `startBridge()` boots the daemon core (state, identity, JSON-RPC router).
5
+ * The live transport and agent runtimes are added in later increments.
6
+ */
7
+ export { startBridge } from './bridge.js';
8
+ export { BRIDGE_VERSION } from './version.js';
9
+ export { HandlerRouter } from './handler-router.js';
10
+ export { DaemonState, DAEMON_FILES } from './daemon-state.js';
11
+ export { DEFAULT_DAEMON_CONFIG, resolveDaemonConfig, } from './daemon-config.js';
12
+ export { ProjectRegistry, projectIdFor } from './projects/project-registry.js';
13
+ export { PushService, } from './push/push-service.js';
14
+ export { createBridgePushSender, defaultServiceAccountPath, } from './push/push-sender.js';
15
+ export { SessionHistoryReader, } from './conversation/session-history.js';
16
+ export { PairingCodeService, } from './pairing/pairing-code-service.js';
17
+ export { MdnsAdvertiser, parseQuestions, buildMessage, encodeName, } from './transport/mdns-advertiser.js';
18
+ export { SecureDeviceState } from './secure-device-state.js';
19
+ export { InMemorySecretStore } from './secret-store.js';
20
+ export { KeyringSecretStore, createDefaultSecretStore, loadNativeKeyringBackend, } from './keyring-secret-store.js';
21
+ export { SessionState } from './session-state.js';
22
+ export { LockFile, isProcessAlive } from './lock-file.js';
23
+ export { buildServicePlan, buildWindowsStartupPlan, installService, uninstallService, currentServiceEnv, isServicePlatformSupported, } from './service-installer.js';
24
+ export { buildBridgeStatus } from './bridge-status.js';
25
+ export { getAuthStatus } from './account-status.js';
26
+ export { generatePairingPayload, renderPairingQr } from './qr.js';
27
+ export { createLogger, createFileLogger, redactSecrets, logFileFor, } from './logger.js';
28
+ export { BaseAgentAdapter } from './adapters/base-adapter.js';
29
+ export { CodexAdapter, codexUsageTokens, parseCodexConfigModels, parseCodexModelList, parseCodexModelWindows, parseCodexReasoning, } from './adapters/codex-adapter.js';
30
+ export { resolveCodexBinary } from './adapters/resolve-codex.js';
31
+ export { PiAdapter, parsePiLine, parsePiModelList, parsePiUsageTokens, parsePiContextWindow, } from './adapters/pi-adapter.js';
32
+ export { resolvePiBinary } from './adapters/resolve-pi.js';
33
+ export { GeminiAdapter, parseGeminiLine, } from './adapters/gemini-adapter.js';
34
+ export { resolveGeminiBinary } from './adapters/resolve-gemini.js';
35
+ export { OpenCodeAdapter, parseOpenCodeLine, parseModelList, parseOpenCodeModelWindows, openCodeUsageTokens, } from './adapters/opencode-adapter.js';
36
+ export { resolveOpenCodeBinary } from './adapters/resolve-opencode.js';
37
+ export { defaultSpawn } from './adapters/spawn.js';
38
+ export { ClaudeCodeAdapter, claudeContextWindow, claudeUsageTokens, parseClaudeLine, } from './adapters/claude-adapter.js';
39
+ export { resolveClaudeBinary } from './adapters/resolve-claude.js';
40
+ export { EchoAgentAdapter } from './adapters/echo-agent-adapter.js';
41
+ export { ProcessAgentAdapter, } from './adapters/process-agent-adapter.js';
42
+ // Conversation engine
43
+ export { ThreadStore, } from './conversation/thread-store.js';
44
+ export { AgentManager, } from './agents/agent-manager.js';
45
+ // Transport (live E2EE)
46
+ export { MessageQueue, queueFor, createInMemoryIoPair, } from './transport/message-io.js';
47
+ export { generateEphemeralKeyPair, deriveSessionKey, randomHex, verifyEd25519, aesGcmEncrypt, aesGcmDecrypt, } from './transport/crypto.js';
48
+ export { BridgeSecureChannel, ReplayError } from './transport/secure-channel.js';
49
+ export { performServerHandshake, HandshakeError, } from './transport/server-handshake.js';
50
+ export { handleSecureConnection, } from './transport/session-handler.js';
51
+ export { FileTrustStore } from './transport/trust-store.js';
52
+ export { connectRelayAsMac, } from './transport/relay-client.js';
53
+ export { startLanServer, } from './transport/lan-server.js';
54
+ export { wsToMessageIO, rawDataToBuffer } from './transport/ws-adapter.js';
55
+ export { localHostPorts } from './transport/local-hosts.js';
56
+ export { OutboundLog } from './transport/outbound-log.js';
57
+ export { SessionRegistry } from './transport/session-registry.js';
58
+ // Git + workspace services
59
+ export { GitService } from './git/git-service.js';
60
+ export { runGit, GitCommandError, sanitizePaths } from './git/git-runner.js';
61
+ export { WorkspaceService } from './workspace/workspace-service.js';
62
+ export { BrowseService, browseRootIdFor } from './workspace/browse-service.js';
63
+ export { CheckpointService } from './workspace/checkpoint-service.js';
64
+ export { resolveWithinRoot, isSensitiveName } from './workspace/path-guard.js';
65
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * OS-keychain-backed {@link SecretStore} (Windows Credential Manager, macOS
3
+ * Keychain, Linux Secret Service) via the optional `@napi-rs/keyring` native
4
+ * module. The bridge's Ed25519 identity is a secret and (per AGENTS.md) must
5
+ * never be persisted in plaintext — this is how it survives restarts.
6
+ *
7
+ * The native module is optional and loaded lazily; if it is unavailable (e.g. a
8
+ * headless Linux box with no Secret Service), the caller falls back to an
9
+ * in-memory store via {@link createDefaultSecretStore} — the bridge still runs,
10
+ * but the identity is ephemeral (real pairing then requires the keychain).
11
+ */
12
+ import type { SecretStore } from './secret-store.js';
13
+ import type { Logger } from './logger.js';
14
+ /** Minimal synchronous keychain backend (one entry per service+account). */
15
+ export interface KeyringBackend {
16
+ getPassword(service: string, account: string): string | null;
17
+ setPassword(service: string, account: string, password: string): void;
18
+ deletePassword(service: string, account: string): boolean;
19
+ }
20
+ export declare class KeyringSecretStore implements SecretStore {
21
+ #private;
22
+ constructor(backend: KeyringBackend, service?: string);
23
+ get(key: string): Promise<string | null>;
24
+ set(key: string, value: string): Promise<void>;
25
+ delete(key: string): Promise<void>;
26
+ }
27
+ /**
28
+ * Lazily load `@napi-rs/keyring` and adapt it to {@link KeyringBackend}.
29
+ * Returns `null` if the module (or its native binary) is unavailable.
30
+ */
31
+ export declare function loadNativeKeyringBackend(): Promise<KeyringBackend | null>;
32
+ /**
33
+ * Build the default secret store: the OS keychain when available, otherwise a
34
+ * warned in-memory fallback.
35
+ */
36
+ export declare function createDefaultSecretStore(logger?: Logger): Promise<SecretStore>;
@@ -0,0 +1,70 @@
1
+ import { InMemorySecretStore } from './secret-store.js';
2
+ const SERVICE_NAME = 'uxnan-bridge';
3
+ export class KeyringSecretStore {
4
+ #backend;
5
+ #service;
6
+ constructor(backend, service = SERVICE_NAME) {
7
+ this.#backend = backend;
8
+ this.#service = service;
9
+ }
10
+ get(key) {
11
+ return Promise.resolve(this.#backend.getPassword(this.#service, key));
12
+ }
13
+ set(key, value) {
14
+ this.#backend.setPassword(this.#service, key, value);
15
+ return Promise.resolve();
16
+ }
17
+ delete(key) {
18
+ this.#backend.deletePassword(this.#service, key);
19
+ return Promise.resolve();
20
+ }
21
+ }
22
+ /**
23
+ * Lazily load `@napi-rs/keyring` and adapt it to {@link KeyringBackend}.
24
+ * Returns `null` if the module (or its native binary) is unavailable.
25
+ */
26
+ export async function loadNativeKeyringBackend() {
27
+ try {
28
+ const mod = (await import('@napi-rs/keyring'));
29
+ const { Entry } = mod;
30
+ return {
31
+ getPassword: (service, account) => {
32
+ try {
33
+ return new Entry(service, account).getPassword();
34
+ }
35
+ catch {
36
+ return null; // no entry / locked keychain
37
+ }
38
+ },
39
+ setPassword: (service, account, password) => {
40
+ new Entry(service, account).setPassword(password);
41
+ },
42
+ deletePassword: (service, account) => {
43
+ try {
44
+ return new Entry(service, account).deletePassword();
45
+ }
46
+ catch {
47
+ return false;
48
+ }
49
+ },
50
+ };
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ }
56
+ /**
57
+ * Build the default secret store: the OS keychain when available, otherwise a
58
+ * warned in-memory fallback.
59
+ */
60
+ export async function createDefaultSecretStore(logger) {
61
+ const backend = await loadNativeKeyringBackend();
62
+ if (backend) {
63
+ return new KeyringSecretStore(backend);
64
+ }
65
+ logger?.warn('OS keychain unavailable (@napi-rs/keyring not loaded); using an in-memory ' +
66
+ 'identity store. The bridge identity will NOT survive restarts — install a ' +
67
+ 'working keychain before real pairing. See bridge/FOR-DEV.md.');
68
+ return new InMemorySecretStore();
69
+ }
70
+ //# sourceMappingURL=keyring-secret-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyring-secret-store.js","sourceRoot":"","sources":["../../src/keyring-secret-store.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAGxD,MAAM,YAAY,GAAG,cAAc,CAAC;AASpC,MAAM,OAAO,kBAAkB;IACpB,QAAQ,CAAiB;IACzB,QAAQ,CAAS;IAE1B,YAAY,OAAuB,EAAE,UAAkB,YAAY;QACjE,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;IAC1B,CAAC;IAED,GAAG,CAAC,GAAW;QACb,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,KAAa;QAC5B,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QACrD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,MAAM,CAAC,GAAW;QAChB,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;QACjD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB;IAC5C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAS5C,CAAC;QACF,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC;QACtB,OAAO;YACL,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE;gBAChC,IAAI,CAAC;oBACH,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;gBACnD,CAAC;gBAAC,MAAM,CAAC;oBACP,OAAO,IAAI,CAAC,CAAC,6BAA6B;gBAC5C,CAAC;YACH,CAAC;YACD,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;gBAC1C,IAAI,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;YACpD,CAAC;YACD,cAAc,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE;gBACnC,IAAI,CAAC;oBACH,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,cAAc,EAAE,CAAC;gBACtD,CAAC;gBAAC,MAAM,CAAC;oBACP,OAAO,KAAK,CAAC;gBACf,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,MAAe;IAC5D,MAAM,OAAO,GAAG,MAAM,wBAAwB,EAAE,CAAC;IACjD,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IACD,MAAM,EAAE,IAAI,CACV,4EAA4E;QAC1E,4EAA4E;QAC5E,8DAA8D,CACjE,CAAC;IACF,OAAO,IAAI,mBAAmB,EAAE,CAAC;AACnC,CAAC"}
@@ -0,0 +1,18 @@
1
+ export interface LockInfo {
2
+ pid: number;
3
+ startedAt: number;
4
+ }
5
+ /** Whether a process with the given pid is currently alive. */
6
+ export declare function isProcessAlive(pid: number): boolean;
7
+ export declare class LockFile {
8
+ #private;
9
+ constructor(path: string);
10
+ read(): Promise<LockInfo | null>;
11
+ /**
12
+ * Try to acquire the lock. Returns `true` on success, or `false` if another
13
+ * live process already holds it. Stale locks are overwritten.
14
+ */
15
+ acquire(pid?: number, now?: number): Promise<boolean>;
16
+ /** Release the lock if it is owned by `pid` (no-op otherwise). */
17
+ release(pid?: number): Promise<void>;
18
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Single-instance lock for the daemon (`~/.uxnan/bridge.lock`).
3
+ *
4
+ * Prevents a standalone bridge and an embedded one (or two standalone daemons)
5
+ * from running at once and fighting over the relay identity and LAN port
6
+ * (architecture/02e-bridge-integration.md §7.3). A lock owned by a process that
7
+ * is no longer alive is considered stale and may be taken over.
8
+ */
9
+ import { readFile, unlink, writeFile } from 'node:fs/promises';
10
+ /** Whether a process with the given pid is currently alive. */
11
+ export function isProcessAlive(pid) {
12
+ try {
13
+ process.kill(pid, 0);
14
+ return true;
15
+ }
16
+ catch (err) {
17
+ // ESRCH → no such process; EPERM → exists but not signalable (alive).
18
+ return err.code === 'EPERM';
19
+ }
20
+ }
21
+ export class LockFile {
22
+ #path;
23
+ constructor(path) {
24
+ this.#path = path;
25
+ }
26
+ async read() {
27
+ try {
28
+ const raw = await readFile(this.#path, 'utf-8');
29
+ return JSON.parse(raw);
30
+ }
31
+ catch {
32
+ return null; // missing or corrupt → treat as no lock
33
+ }
34
+ }
35
+ /**
36
+ * Try to acquire the lock. Returns `true` on success, or `false` if another
37
+ * live process already holds it. Stale locks are overwritten.
38
+ */
39
+ async acquire(pid = process.pid, now = Date.now()) {
40
+ const existing = await this.read();
41
+ if (existing && existing.pid !== pid && isProcessAlive(existing.pid)) {
42
+ return false;
43
+ }
44
+ await writeFile(this.#path, JSON.stringify({ pid, startedAt: now }), 'utf-8');
45
+ return true;
46
+ }
47
+ /** Release the lock if it is owned by `pid` (no-op otherwise). */
48
+ async release(pid = process.pid) {
49
+ const existing = await this.read();
50
+ if (existing && existing.pid !== pid)
51
+ return;
52
+ try {
53
+ await unlink(this.#path);
54
+ }
55
+ catch {
56
+ // already gone
57
+ }
58
+ }
59
+ }
60
+ //# sourceMappingURL=lock-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock-file.js","sourceRoot":"","sources":["../../src/lock-file.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAO/D,+DAA+D;AAC/D,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,sEAAsE;QACtE,OAAQ,GAA6B,CAAC,IAAI,KAAK,OAAO,CAAC;IACzD,CAAC;AACH,CAAC;AAED,MAAM,OAAO,QAAQ;IACV,KAAK,CAAS;IAEvB,YAAY,IAAY;QACtB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YAChD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAa,CAAC;QACrC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC,CAAC,wCAAwC;QACvD,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE,MAAc,IAAI,CAAC,GAAG,EAAE;QAC/D,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACnC,IAAI,QAAQ,IAAI,QAAQ,CAAC,GAAG,KAAK,GAAG,IAAI,cAAc,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACrE,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;QAC9E,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kEAAkE;IAClE,KAAK,CAAC,OAAO,CAAC,MAAc,OAAO,CAAC,GAAG;QACrC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACnC,IAAI,QAAQ,IAAI,QAAQ,CAAC,GAAG,KAAK,GAAG;YAAE,OAAO;QAC7C,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,eAAe;QACjB,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,28 @@
1
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
2
+ export interface Logger {
3
+ debug(message: string, ...rest: unknown[]): void;
4
+ info(message: string, ...rest: unknown[]): void;
5
+ warn(message: string, ...rest: unknown[]): void;
6
+ error(message: string, ...rest: unknown[]): void;
7
+ }
8
+ /** Mask obvious secrets in a log line. Best-effort defense-in-depth. */
9
+ export declare function redactSecrets(text: string): string;
10
+ /** Console-only logger (stderr). */
11
+ export declare function createLogger(scope: string, minLevel?: LogLevel): Logger;
12
+ export interface FileLoggerOptions {
13
+ scope: string;
14
+ minLevel?: LogLevel;
15
+ /** Directory for daily log files (`bridge-YYYY-MM-DD.log`). */
16
+ logDir: string;
17
+ /** Also write to stderr (default true). */
18
+ toConsole?: boolean;
19
+ /** Injected clock for the timestamp + rotation filename (default `new Date()`). */
20
+ now?: () => Date;
21
+ }
22
+ /**
23
+ * Logger that writes to stderr and appends to a daily-rotated file. Logging never
24
+ * throws: file errors are swallowed so a logging failure can't crash the daemon.
25
+ */
26
+ export declare function createFileLogger(options: FileLoggerOptions): Logger;
27
+ /** `<dir>/bridge-YYYY-MM-DD.log` for the given date. */
28
+ export declare function logFileFor(dir: string, date: Date): string;
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Leveled logger for the bridge daemon, with optional file output (daily
3
+ * rotation under `~/.uxnan/logs/`) and a secret-redaction pass.
4
+ *
5
+ * Security (AGENTS.md): callers should not pass secrets to the logger, and
6
+ * {@link redactSecrets} is a defense-in-depth net that masks obvious secrets
7
+ * (JWTs, `key=…`/`token=…`/`secret=…` values, PEM key blocks) before they are
8
+ * written anywhere. All log output goes to stderr; stdout is reserved for IPC.
9
+ *
10
+ * Source: architecture/02a-system-architecture.md §5.8.3.
11
+ */
12
+ import { appendFileSync, mkdirSync } from 'node:fs';
13
+ import { join } from 'node:path';
14
+ const LEVEL_ORDER = { debug: 10, info: 20, warn: 30, error: 40 };
15
+ /** Mask obvious secrets in a log line. Best-effort defense-in-depth. */
16
+ export function redactSecrets(text) {
17
+ return (text
18
+ // JWT-like a.b.c
19
+ .replace(/\b[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g, '[REDACTED-JWT]')
20
+ // key=value / "token": "value" for secret-ish keys
21
+ .replace(/\b(token|secret|password|passwd|api[_-]?key|authorization|bearer|notificationSecret|privateKey|private_key)\b(["']?\s*[:=]\s*["']?)([^\s"',}]+)/gi, (_m, key, sep) => `${key}${sep}[REDACTED]`)
22
+ // PEM blocks
23
+ .replace(/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g, '[REDACTED-KEY]'));
24
+ }
25
+ function stringify(value) {
26
+ if (typeof value === 'string')
27
+ return value;
28
+ try {
29
+ return JSON.stringify(value);
30
+ }
31
+ catch {
32
+ return String(value);
33
+ }
34
+ }
35
+ function formatLine(input) {
36
+ const prefix = input.timestamp ? `[${input.timestamp}] ` : '';
37
+ const extra = input.rest.length > 0 ? ` ${input.rest.map(stringify).join(' ')}` : '';
38
+ return redactSecrets(`${prefix}[${input.level.toUpperCase()}] (${input.scope}) ${input.message}${extra}`);
39
+ }
40
+ /** Console-only logger (stderr). */
41
+ export function createLogger(scope, minLevel = 'info') {
42
+ const threshold = LEVEL_ORDER[minLevel];
43
+ const emit = (level, message, rest) => {
44
+ if (LEVEL_ORDER[level] < threshold)
45
+ return;
46
+ console.error(formatLine({ level, scope, message, rest }));
47
+ };
48
+ return {
49
+ debug: (m, ...r) => emit('debug', m, r),
50
+ info: (m, ...r) => emit('info', m, r),
51
+ warn: (m, ...r) => emit('warn', m, r),
52
+ error: (m, ...r) => emit('error', m, r),
53
+ };
54
+ }
55
+ /**
56
+ * Logger that writes to stderr and appends to a daily-rotated file. Logging never
57
+ * throws: file errors are swallowed so a logging failure can't crash the daemon.
58
+ */
59
+ export function createFileLogger(options) {
60
+ const threshold = LEVEL_ORDER[options.minLevel ?? 'info'];
61
+ const now = options.now ?? (() => new Date());
62
+ const toConsole = options.toConsole !== false;
63
+ let ensured = false;
64
+ const emit = (level, message, rest) => {
65
+ if (LEVEL_ORDER[level] < threshold)
66
+ return;
67
+ const date = now();
68
+ const line = formatLine({
69
+ level,
70
+ scope: options.scope,
71
+ message,
72
+ rest,
73
+ timestamp: date.toISOString(),
74
+ });
75
+ if (toConsole)
76
+ console.error(line);
77
+ try {
78
+ if (!ensured) {
79
+ mkdirSync(options.logDir, { recursive: true });
80
+ ensured = true;
81
+ }
82
+ appendFileSync(logFileFor(options.logDir, date), `${line}\n`);
83
+ }
84
+ catch {
85
+ // logging must never throw
86
+ }
87
+ };
88
+ return {
89
+ debug: (m, ...r) => emit('debug', m, r),
90
+ info: (m, ...r) => emit('info', m, r),
91
+ warn: (m, ...r) => emit('warn', m, r),
92
+ error: (m, ...r) => emit('error', m, r),
93
+ };
94
+ }
95
+ /** `<dir>/bridge-YYYY-MM-DD.log` for the given date. */
96
+ export function logFileFor(dir, date) {
97
+ return join(dir, `bridge-${date.toISOString().slice(0, 10)}.log`);
98
+ }
99
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC,MAAM,WAAW,GAA6B,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;AAS3F,wEAAwE;AACxE,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,CACL,IAAI;QACF,iBAAiB;SAChB,OAAO,CAAC,8DAA8D,EAAE,gBAAgB,CAAC;QAC1F,mDAAmD;SAClD,OAAO,CACN,mJAAmJ,EACnJ,CAAC,EAAE,EAAE,GAAW,EAAE,GAAW,EAAE,EAAE,CAAC,GAAG,GAAG,GAAG,GAAG,YAAY,CAC3D;QACD,aAAa;SACZ,OAAO,CACN,6EAA6E,EAC7E,gBAAgB,CACjB,CACJ,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;AACH,CAAC;AAUD,SAAS,UAAU,CAAC,KAAkB;IACpC,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACrF,OAAO,aAAa,CAClB,GAAG,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,MAAM,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,OAAO,GAAG,KAAK,EAAE,CACpF,CAAC;AACJ,CAAC;AAED,oCAAoC;AACpC,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,WAAqB,MAAM;IACrE,MAAM,SAAS,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,CAAC,KAAe,EAAE,OAAe,EAAE,IAAe,EAAQ,EAAE;QACvE,IAAI,WAAW,CAAC,KAAK,CAAC,GAAG,SAAS;YAAE,OAAO;QAC3C,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC7D,CAAC,CAAC;IACF,OAAO;QACL,KAAK,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;QACvC,IAAI,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;QACrC,IAAI,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;QACrC,KAAK,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;KACxC,CAAC;AACJ,CAAC;AAaD;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAA0B;IACzD,MAAM,SAAS,GAAG,WAAW,CAAC,OAAO,CAAC,QAAQ,IAAI,MAAM,CAAC,CAAC;IAC1D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAS,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,KAAK,KAAK,CAAC;IAC9C,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,MAAM,IAAI,GAAG,CAAC,KAAe,EAAE,OAAe,EAAE,IAAe,EAAQ,EAAE;QACvE,IAAI,WAAW,CAAC,KAAK,CAAC,GAAG,SAAS;YAAE,OAAO;QAC3C,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC;QACnB,MAAM,IAAI,GAAG,UAAU,CAAC;YACtB,KAAK;YACL,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,OAAO;YACP,IAAI;YACJ,SAAS,EAAE,IAAI,CAAC,WAAW,EAAE;SAC9B,CAAC,CAAC;QACH,IAAI,SAAS;YAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC;YACH,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC/C,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;YACD,cAAc,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC;QAChE,CAAC;QAAC,MAAM,CAAC;YACP,2BAA2B;QAC7B,CAAC;IACH,CAAC,CAAC;IAEF,OAAO;QACL,KAAK,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;QACvC,IAAI,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;QACrC,IAAI,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;QACrC,KAAK,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;KACxC,CAAC;AACJ,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,UAAU,CAAC,GAAW,EAAE,IAAU;IAChD,OAAO,IAAI,CAAC,GAAG,EAAE,UAAU,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC;AACpE,CAAC"}
@@ -0,0 +1,45 @@
1
+ import type { PairingPayload } from '@uxnan/shared';
2
+ export interface PairingCodeServiceOptions {
3
+ /** Builds the payload handed out on a valid code (the bridge's pairing data). */
4
+ buildPayload: () => PairingPayload;
5
+ /** Injected clock (epoch ms) for testability. */
6
+ now?: () => number;
7
+ /** Code lifetime before it rotates (default 10 min). */
8
+ ttlMs?: number;
9
+ /** Injected code generator (tests); defaults to an 8-char base32 code. */
10
+ generateCode?: () => string;
11
+ /** Per-IP resolve-attempt window + cap (anti-brute-force). */
12
+ rateWindowMs?: number;
13
+ rateMax?: number;
14
+ /**
15
+ * Absolute path to persist the current code+expiry (e.g.
16
+ * `~/.uxnan/pairing-code.json`). When set, the code is shared ACROSS processes
17
+ * so the running daemon that serves `/pair/resolve` and a separate `qr`/`code`
18
+ * command (or an autostarted, console-less daemon) hand out the SAME code.
19
+ * Omit for an in-memory-only instance (tests).
20
+ */
21
+ statePath?: string;
22
+ }
23
+ export declare class PairingCodeService {
24
+ #private;
25
+ constructor(options: PairingCodeServiceOptions);
26
+ /**
27
+ * The current pairing code, (re)issued if none exists or the previous one
28
+ * expired. Display this on the PC for the user to type on the phone. Returned
29
+ * grouped (`ABCD-EFGH`) for readability; {@link resolve} accepts either form.
30
+ */
31
+ currentCode(): string;
32
+ /** Force a fresh code now (e.g. the user asked to regenerate). */
33
+ rotate(): string;
34
+ /**
35
+ * Validate a code presented by a phone and, if it matches the active
36
+ * (unexpired) code, return the pairing payload. Comparison is constant-time and
37
+ * input is normalized (case, grouping, Crockford look-alikes).
38
+ */
39
+ resolve(code: string): PairingPayload | undefined;
40
+ /**
41
+ * Record a resolve attempt from `ip` and report whether it is now over the
42
+ * limit (the caller should answer 429 and NOT call {@link resolve}).
43
+ */
44
+ rateLimited(ip: string): boolean;
45
+ }
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Manual-code pairing (bridge-side; architecture §5.10.1 reframed for the
3
+ * bridge-first model — the relay's `/trusted-session/resolve` was the off-LAN
4
+ * equivalent).
5
+ *
6
+ * The bridge shows a short, rotating **pairing code** on the PC (CLI / desktop).
7
+ * A phone that has located the bridge on the LAN (via mDNS discovery — a later
8
+ * slice — or by typing the host) calls `GET /pair/resolve?code=<code>` on the LAN
9
+ * server; this service validates the code and hands back the full
10
+ * {@link PairingPayload} (the same data the QR carries), which the phone then runs
11
+ * through the normal E2EE handshake.
12
+ *
13
+ * The code is the **consent gate**: only someone who can read the PC screen learns
14
+ * it, so a random LAN device cannot pull the payload and pair. This is the same
15
+ * trust posture as the QR (whoever sees the screen can pair) — the code adds no
16
+ * new secret beyond what the QR already exposes. Brute force is bounded by the
17
+ * code entropy (40 bits), a short TTL, and per-IP rate limiting.
18
+ *
19
+ * Security note: the payload is not a secret that grants access on its own — the
20
+ * phone must still complete the identity-keyed E2EE handshake. See bridge/FOR-DEV.md.
21
+ */
22
+ import { randomBytes, timingSafeEqual } from 'node:crypto';
23
+ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
24
+ import { dirname } from 'node:path';
25
+ /** Crockford base32 alphabet (no I, L, O, U — unambiguous when read aloud/typed). */
26
+ const ALPHABET = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
27
+ const DEFAULT_TTL_MS = 10 * 60 * 1000; // 10 minutes
28
+ const DEFAULT_RATE_WINDOW_MS = 60 * 1000; // 1 minute
29
+ const DEFAULT_RATE_MAX = 10; // attempts per window per IP
30
+ export class PairingCodeService {
31
+ #buildPayload;
32
+ #now;
33
+ #ttlMs;
34
+ #generateCode;
35
+ #rateWindowMs;
36
+ #rateMax;
37
+ #rate = new Map();
38
+ #statePath;
39
+ #code;
40
+ #expiresAt = 0;
41
+ constructor(options) {
42
+ this.#buildPayload = options.buildPayload;
43
+ this.#now = options.now ?? (() => Date.now());
44
+ this.#ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
45
+ this.#generateCode = options.generateCode ?? defaultGenerateCode;
46
+ this.#rateWindowMs = options.rateWindowMs ?? DEFAULT_RATE_WINDOW_MS;
47
+ this.#rateMax = options.rateMax ?? DEFAULT_RATE_MAX;
48
+ this.#statePath = options.statePath;
49
+ }
50
+ /**
51
+ * The current pairing code, (re)issued if none exists or the previous one
52
+ * expired. Display this on the PC for the user to type on the phone. Returned
53
+ * grouped (`ABCD-EFGH`) for readability; {@link resolve} accepts either form.
54
+ */
55
+ currentCode() {
56
+ const now = this.#now();
57
+ this.#syncFromDisk(now);
58
+ if (!this.#code || now >= this.#expiresAt) {
59
+ this.#code = this.#generateCode();
60
+ this.#expiresAt = now + this.#ttlMs;
61
+ this.#persist();
62
+ }
63
+ return group(this.#code);
64
+ }
65
+ /** Force a fresh code now (e.g. the user asked to regenerate). */
66
+ rotate() {
67
+ this.#code = this.#generateCode();
68
+ this.#expiresAt = this.#now() + this.#ttlMs;
69
+ this.#persist();
70
+ return group(this.#code);
71
+ }
72
+ /**
73
+ * Validate a code presented by a phone and, if it matches the active
74
+ * (unexpired) code, return the pairing payload. Comparison is constant-time and
75
+ * input is normalized (case, grouping, Crockford look-alikes).
76
+ */
77
+ resolve(code) {
78
+ const now = this.#now();
79
+ // Re-read the shared code so a daemon serving `/pair/resolve` validates
80
+ // against the code another process (the `qr`/`code` command) may have issued.
81
+ this.#syncFromDisk(now);
82
+ if (!this.#code || now >= this.#expiresAt)
83
+ return undefined;
84
+ if (!constantTimeEqual(normalize(code), this.#code))
85
+ return undefined;
86
+ return this.#buildPayload();
87
+ }
88
+ /** Adopt the persisted (shared) code when present and still valid. */
89
+ #syncFromDisk(now) {
90
+ const persisted = this.#load();
91
+ if (persisted && now < persisted.expiresAt) {
92
+ this.#code = persisted.code;
93
+ this.#expiresAt = persisted.expiresAt;
94
+ }
95
+ }
96
+ /** Read the shared code from disk, or `undefined` (no path / missing / corrupt). */
97
+ #load() {
98
+ if (!this.#statePath)
99
+ return undefined;
100
+ try {
101
+ const parsed = JSON.parse(readFileSync(this.#statePath, 'utf-8'));
102
+ if (parsed &&
103
+ typeof parsed === 'object' &&
104
+ typeof parsed.code === 'string' &&
105
+ typeof parsed.expiresAt === 'number') {
106
+ return {
107
+ code: parsed.code,
108
+ expiresAt: parsed.expiresAt,
109
+ };
110
+ }
111
+ }
112
+ catch {
113
+ /* missing or corrupt → in-memory only */
114
+ }
115
+ return undefined;
116
+ }
117
+ /** Persist the current code so other processes hand out the same one. Best-effort. */
118
+ #persist() {
119
+ if (!this.#statePath || !this.#code)
120
+ return;
121
+ try {
122
+ mkdirSync(dirname(this.#statePath), { recursive: true });
123
+ writeFileSync(this.#statePath, JSON.stringify({ code: this.#code, expiresAt: this.#expiresAt }));
124
+ }
125
+ catch {
126
+ /* best-effort: persistence failure falls back to in-memory */
127
+ }
128
+ }
129
+ /**
130
+ * Record a resolve attempt from `ip` and report whether it is now over the
131
+ * limit (the caller should answer 429 and NOT call {@link resolve}).
132
+ */
133
+ rateLimited(ip) {
134
+ const now = this.#now();
135
+ const entry = this.#rate.get(ip);
136
+ if (!entry || now >= entry.resetAt) {
137
+ this.#rate.set(ip, { count: 1, resetAt: now + this.#rateWindowMs });
138
+ return false;
139
+ }
140
+ entry.count += 1;
141
+ return entry.count > this.#rateMax;
142
+ }
143
+ }
144
+ /** Generate an 8-char Crockford-base32 code from 40 bits of entropy. */
145
+ function defaultGenerateCode() {
146
+ const bytes = randomBytes(5); // 40 bits → 8 base32 chars
147
+ let bits = 0;
148
+ let value = 0;
149
+ let out = '';
150
+ for (const byte of bytes) {
151
+ value = (value << 8) | byte;
152
+ bits += 8;
153
+ while (bits >= 5) {
154
+ bits -= 5;
155
+ out += ALPHABET[(value >>> bits) & 31];
156
+ }
157
+ }
158
+ return out.slice(0, 8);
159
+ }
160
+ /** Group an 8-char code as `ABCD-EFGH` for display. */
161
+ function group(code) {
162
+ return code.length === 8 ? `${code.slice(0, 4)}-${code.slice(4)}` : code;
163
+ }
164
+ /**
165
+ * Normalize a user-typed code: uppercase, strip non-alphanumerics (dashes/spaces),
166
+ * and fold Crockford look-alikes (O→0, I/L→1) so a misread is still accepted.
167
+ */
168
+ function normalize(input) {
169
+ return input
170
+ .toUpperCase()
171
+ .replace(/[^0-9A-Z]/g, '')
172
+ .replace(/O/g, '0')
173
+ .replace(/[IL]/g, '1');
174
+ }
175
+ /** Constant-time string compare that never throws on length mismatch. */
176
+ function constantTimeEqual(a, b) {
177
+ const ba = Buffer.from(a, 'utf-8');
178
+ const bb = Buffer.from(b, 'utf-8');
179
+ if (ba.length !== bb.length)
180
+ return false;
181
+ return timingSafeEqual(ba, bb);
182
+ }
183
+ //# sourceMappingURL=pairing-code-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pairing-code-service.js","sourceRoot":"","sources":["../../../src/pairing/pairing-code-service.ts"],"names":[],"mappings":"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"}