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,123 @@
1
+ /**
2
+ * Daemon configuration shape and defaults.
3
+ *
4
+ * Source: uxnandesktop/architecture/02e-bridge-integration.md §6.1.
5
+ */
6
+ import { type AgentConfig, type AgentId } from '@uxnan/shared';
7
+ /**
8
+ * Headless permission posture for agents that gate tool use (e.g. Claude Code):
9
+ * - `default` → no flag (tools needing approval are auto-denied headless);
10
+ * - `acceptEdits` → file edits auto-apply, other tools stay gated;
11
+ * - `bypassPermissions` → all tools run without approval (full autonomy).
12
+ */
13
+ export type AgentPermissionMode = 'default' | 'acceptEdits' | 'bypassPermissions';
14
+ /**
15
+ * An explicit model to surface in the phone's model picker, declared in config.
16
+ *
17
+ * Use this to pin concrete, versioned models alongside an agent's own
18
+ * auto-updating aliases — e.g. for Claude Code, the `opus`/`sonnet`/`haiku`
19
+ * aliases always track the latest, and pinning `claude-opus-4-7` here adds an
20
+ * older-but-available version to the picker. `id` is passed verbatim to the
21
+ * CLI's `--model`/`-m` flag.
22
+ */
23
+ export interface AgentModelSpec {
24
+ /** Exact model id passed to the agent (e.g. `claude-opus-4-8`). */
25
+ id: string;
26
+ /** Human-facing label shown in the picker (defaults to `id`). */
27
+ displayName?: string;
28
+ /** Optional one-line description shown under the label. */
29
+ description?: string;
30
+ }
31
+ /** Per-agent overrides (binary location + default model + permissions). */
32
+ export interface AgentSettings {
33
+ /** Absolute path to the agent CLI/binary; resolved from PATH/standard locations when omitted. */
34
+ binaryPath?: string;
35
+ /** Default model the agent uses (e.g. `provider/model` for OpenCode). */
36
+ model?: string;
37
+ /**
38
+ * Extra explicit models to show in the picker **in addition** to the ones the
39
+ * agent reports itself. For Claude Code (which exposes only the moving
40
+ * `opus`/`sonnet`/`haiku` aliases), this is how you pin concrete versions
41
+ * (e.g. `claude-opus-4-7`) so users can deliberately select an older model.
42
+ * Entries may be a bare id string or an {@link AgentModelSpec}. Currently
43
+ * consumed by the Claude Code adapter; ignored by agents that enumerate their
44
+ * own models (OpenCode, Codex).
45
+ */
46
+ models?: (string | AgentModelSpec)[];
47
+ /**
48
+ * Headless permission posture for agents that support it (Claude Code).
49
+ * Defaults to `acceptEdits` when omitted. Ignored by agents that don't gate tools.
50
+ */
51
+ permissionMode?: AgentPermissionMode;
52
+ /**
53
+ * Opt-in interactive tool approvals (Claude Code only): inject a `PreToolUse`
54
+ * hook (via `--settings`) so every tool round-trips to the bridge and the user
55
+ * approves/rejects it on the phone (`turn/send { approvalResponse }`). Requires
56
+ * `lanEnabled` (the hook calls the bridge's local HTTP endpoint). Default false.
57
+ */
58
+ interactiveApprovals?: boolean;
59
+ }
60
+ export interface DaemonConfig {
61
+ relayUrl: string;
62
+ /**
63
+ * Use a relay as an off-LAN fallback. **Default `false`** — the bridge is
64
+ * LAN/Tailscale-direct out of the box (no hosting), and the pairing QR
65
+ * advertises only the direct `hosts` (see {@link lanEnabled}). The relay is
66
+ * **optional and self-hosted**: set `true` (and point {@link relayUrl} at your
67
+ * own relay) to also fall back through it for users who don't run a mesh VPN.
68
+ * See `docs/connectivity.md` and `relay/docs/deploy.md`.
69
+ */
70
+ relayEnabled: boolean;
71
+ lanEnabled: boolean;
72
+ lanPort: number;
73
+ /**
74
+ * Advertise the bridge on the LAN via mDNS/Bonjour (`_uxnan._tcp`) so the phone
75
+ * can discover it for manual-code pairing without typing the host. **Default
76
+ * `true`**; only effective when {@link lanEnabled}. Best-effort — a failed bind
77
+ * (port 5353 busy) degrades silently.
78
+ */
79
+ mdnsEnabled: boolean;
80
+ pushEnabled: boolean;
81
+ pushOnAgentDone: boolean;
82
+ pushOnAgentError: boolean;
83
+ autoReconnect: boolean;
84
+ maxConcurrentSessions: number;
85
+ sessionTimeoutMinutes: number;
86
+ /** Agent the bridge uses when a thread does not pick one. */
87
+ defaultAgent: AgentId;
88
+ /**
89
+ * Keep at most N newest workspace checkpoints per project (`cwd`); older ones
90
+ * are pruned (ref + metadata) on the next capture. `0` = unlimited.
91
+ */
92
+ checkpointMaxPerProject: number;
93
+ /** Delete workspace checkpoints older than N days on capture. `0` = no TTL. */
94
+ checkpointTtlDays: number;
95
+ /**
96
+ * Absolute project directories the phone may open. Empty → the bridge's own
97
+ * working directory is exposed as the single project.
98
+ */
99
+ workspaceRoots: string[];
100
+ /**
101
+ * Absolute base directories the phone may BROWSE under via `workspace/browseDirs`
102
+ * (descend into sub-folders, pick any directory as a thread's cwd) without
103
+ * escaping the root. Empty → falls back to {@link workspaceRoots}, then the
104
+ * user's home directory. Set this to e.g. your `Documents` folder.
105
+ */
106
+ browseRoots: string[];
107
+ /** Per-agent settings keyed by {@link AgentId}. */
108
+ agents: Partial<Record<AgentId, AgentSettings>>;
109
+ /**
110
+ * Per-project agent/model pins, identified by each entry's absolute `cwd`
111
+ * (the project directory). When a thread starts in a project (or browsed
112
+ * folder) whose path matches an entry and the phone did NOT pass an explicit
113
+ * `agentId`/`model`, the bridge uses the pinned `agentId` (and `model`, when it
114
+ * matches that agent). Lets a repo always open with e.g. Codex without the
115
+ * phone choosing each time. Reuses the shared {@link AgentConfig}; only
116
+ * `cwd`/`agentId`/`model` are consumed today (binaryPath/extraArgs are not yet
117
+ * wired — see FOR-DEV.md).
118
+ */
119
+ projectAgents: AgentConfig[];
120
+ }
121
+ export declare const DEFAULT_DAEMON_CONFIG: DaemonConfig;
122
+ /** Merge a partial (e.g. loaded from disk) over the defaults. */
123
+ export declare function resolveDaemonConfig(partial?: Partial<DaemonConfig> | null): DaemonConfig;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Daemon configuration shape and defaults.
3
+ *
4
+ * Source: uxnandesktop/architecture/02e-bridge-integration.md §6.1.
5
+ */
6
+ import { DEFAULT_LAN_PORT, DEFAULT_RELAY_URL } from '@uxnan/shared';
7
+ export const DEFAULT_DAEMON_CONFIG = {
8
+ relayUrl: DEFAULT_RELAY_URL,
9
+ // Relay is optional + self-hosted; off by default (LAN/Tailscale-direct).
10
+ relayEnabled: false,
11
+ lanEnabled: true,
12
+ lanPort: DEFAULT_LAN_PORT,
13
+ mdnsEnabled: true,
14
+ pushEnabled: true,
15
+ pushOnAgentDone: true,
16
+ pushOnAgentError: true,
17
+ autoReconnect: true,
18
+ maxConcurrentSessions: 1,
19
+ sessionTimeoutMinutes: 30,
20
+ defaultAgent: 'opencode',
21
+ checkpointMaxPerProject: 25,
22
+ checkpointTtlDays: 0,
23
+ workspaceRoots: [],
24
+ browseRoots: [],
25
+ projectAgents: [],
26
+ // Seed Claude Code with a few concrete, currently-available versions so the
27
+ // picker shows exact models out of the box, alongside the auto-updating
28
+ // `opus`/`sonnet`/`haiku` aliases. Curate this list as models are released or
29
+ // retired — the aliases always cover "latest" regardless. See docs/agents.md.
30
+ agents: {
31
+ 'claude-code': {
32
+ models: [
33
+ { id: 'claude-fable-5', displayName: 'Fable 5' },
34
+ { id: 'claude-opus-4-8', displayName: 'Opus 4.8' },
35
+ { id: 'claude-opus-4-7', displayName: 'Opus 4.7' },
36
+ { id: 'claude-sonnet-4-6', displayName: 'Sonnet 4.6' },
37
+ { id: 'claude-haiku-4-5', displayName: 'Haiku 4.5' },
38
+ ],
39
+ },
40
+ },
41
+ };
42
+ /** Merge a partial (e.g. loaded from disk) over the defaults. */
43
+ export function resolveDaemonConfig(partial) {
44
+ const merged = { ...DEFAULT_DAEMON_CONFIG, ...(partial ?? {}) };
45
+ // Deep-merge per-agent settings so a partial override (e.g. setting just
46
+ // `permissionMode` for one agent) preserves seeded defaults like Claude
47
+ // Code's `models` rather than wiping the whole agents map. Set an explicit
48
+ // empty value (e.g. `models: []`) to clear a seeded default.
49
+ const ids = new Set([
50
+ ...Object.keys(DEFAULT_DAEMON_CONFIG.agents),
51
+ ...Object.keys(partial?.agents ?? {}),
52
+ ]);
53
+ const agents = {};
54
+ for (const id of ids) {
55
+ const key = id;
56
+ agents[key] = {
57
+ ...DEFAULT_DAEMON_CONFIG.agents[key],
58
+ ...(partial?.agents?.[key] ?? {}),
59
+ };
60
+ }
61
+ merged.agents = agents;
62
+ return merged;
63
+ }
64
+ //# sourceMappingURL=daemon-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-config.js","sourceRoot":"","sources":["../../src/daemon-config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAkC,MAAM,eAAe,CAAC;AAwHpG,MAAM,CAAC,MAAM,qBAAqB,GAAiB;IACjD,QAAQ,EAAE,iBAAiB;IAC3B,0EAA0E;IAC1E,YAAY,EAAE,KAAK;IACnB,UAAU,EAAE,IAAI;IAChB,OAAO,EAAE,gBAAgB;IACzB,WAAW,EAAE,IAAI;IACjB,WAAW,EAAE,IAAI;IACjB,eAAe,EAAE,IAAI;IACrB,gBAAgB,EAAE,IAAI;IACtB,aAAa,EAAE,IAAI;IACnB,qBAAqB,EAAE,CAAC;IACxB,qBAAqB,EAAE,EAAE;IACzB,YAAY,EAAE,UAAU;IACxB,uBAAuB,EAAE,EAAE;IAC3B,iBAAiB,EAAE,CAAC;IACpB,cAAc,EAAE,EAAE;IAClB,WAAW,EAAE,EAAE;IACf,aAAa,EAAE,EAAE;IACjB,4EAA4E;IAC5E,wEAAwE;IACxE,8EAA8E;IAC9E,8EAA8E;IAC9E,MAAM,EAAE;QACN,aAAa,EAAE;YACb,MAAM,EAAE;gBACN,EAAE,EAAE,EAAE,gBAAgB,EAAE,WAAW,EAAE,SAAS,EAAE;gBAChD,EAAE,EAAE,EAAE,iBAAiB,EAAE,WAAW,EAAE,UAAU,EAAE;gBAClD,EAAE,EAAE,EAAE,iBAAiB,EAAE,WAAW,EAAE,UAAU,EAAE;gBAClD,EAAE,EAAE,EAAE,mBAAmB,EAAE,WAAW,EAAE,YAAY,EAAE;gBACtD,EAAE,EAAE,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,EAAE;aACrD;SACF;KACF;CACF,CAAC;AAEF,iEAAiE;AACjE,MAAM,UAAU,mBAAmB,CAAC,OAAsC;IACxE,MAAM,MAAM,GAAG,EAAE,GAAG,qBAAqB,EAAE,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC;IAChE,yEAAyE;IACzE,wEAAwE;IACxE,2EAA2E;IAC3E,6DAA6D;IAC7D,MAAM,GAAG,GAAG,IAAI,GAAG,CAAS;QAC1B,GAAG,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC;QAC5C,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,IAAI,EAAE,CAAC;KACtC,CAAC,CAAC;IACH,MAAM,MAAM,GAA4C,EAAE,CAAC;IAC3D,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,EAAa,CAAC;QAC1B,MAAM,CAAC,GAAG,CAAC,GAAG;YACZ,GAAG,qBAAqB,CAAC,MAAM,CAAC,GAAG,CAAC;YACpC,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;SAClC,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { type DaemonConfig } from './daemon-config.js';
2
+ export declare const DAEMON_FILES: {
3
+ readonly config: "daemon-config.json";
4
+ readonly status: "bridge-status.json";
5
+ readonly pairing: "pairing-session.json";
6
+ readonly pairingCode: "pairing-code.json";
7
+ readonly trustedPhones: "trusted-phones.json";
8
+ readonly managedWorktrees: "managed-worktrees.json";
9
+ readonly pushState: "push-state.json";
10
+ readonly lock: "bridge.lock";
11
+ readonly checkpoints: "checkpoints.json";
12
+ readonly threads: "threads.json";
13
+ };
14
+ export declare class DaemonState {
15
+ readonly baseDir: string;
16
+ constructor(baseDir?: string);
17
+ get logsDir(): string;
18
+ pathFor(file: string): string;
19
+ ensureDir(): Promise<void>;
20
+ readJson<T>(file: string): Promise<T | null>;
21
+ /** Atomically write JSON: write to a temp sibling, then rename over the target. */
22
+ writeJson(file: string, data: unknown): Promise<void>;
23
+ readConfig(): Promise<DaemonConfig>;
24
+ writeConfig(config: DaemonConfig): Promise<void>;
25
+ /** Write the default config if none exists yet; returns the effective config. */
26
+ initConfig(): Promise<DaemonConfig>;
27
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Persistent daemon state under `~/.uxnan/` (non-secret files only).
3
+ *
4
+ * Secrets (the Ed25519 private identity) live in a {@link SecretStore}, never in
5
+ * these JSON files. Writes are atomic (temp file + rename).
6
+ *
7
+ * Source: architecture/02a-system-architecture.md §5.8.3.
8
+ */
9
+ import { homedir } from 'node:os';
10
+ import { join } from 'node:path';
11
+ import { mkdir, readFile, rename, writeFile } from 'node:fs/promises';
12
+ import { randomUUID } from 'node:crypto';
13
+ import { DEFAULT_DAEMON_CONFIG, resolveDaemonConfig } from './daemon-config.js';
14
+ export const DAEMON_FILES = {
15
+ config: 'daemon-config.json',
16
+ status: 'bridge-status.json',
17
+ pairing: 'pairing-session.json',
18
+ pairingCode: 'pairing-code.json',
19
+ trustedPhones: 'trusted-phones.json',
20
+ managedWorktrees: 'managed-worktrees.json',
21
+ pushState: 'push-state.json',
22
+ lock: 'bridge.lock',
23
+ checkpoints: 'checkpoints.json',
24
+ threads: 'threads.json',
25
+ };
26
+ export class DaemonState {
27
+ baseDir;
28
+ constructor(baseDir = join(homedir(), '.uxnan')) {
29
+ this.baseDir = baseDir;
30
+ }
31
+ get logsDir() {
32
+ return join(this.baseDir, 'logs');
33
+ }
34
+ pathFor(file) {
35
+ return join(this.baseDir, file);
36
+ }
37
+ async ensureDir() {
38
+ await mkdir(this.baseDir, { recursive: true });
39
+ await mkdir(this.logsDir, { recursive: true });
40
+ }
41
+ async readJson(file) {
42
+ try {
43
+ const raw = await readFile(this.pathFor(file), 'utf-8');
44
+ return JSON.parse(raw);
45
+ }
46
+ catch (err) {
47
+ if (err.code === 'ENOENT')
48
+ return null;
49
+ throw err;
50
+ }
51
+ }
52
+ /** Atomically write JSON: write to a temp sibling, then rename over the target. */
53
+ async writeJson(file, data) {
54
+ await this.ensureDir();
55
+ const target = this.pathFor(file);
56
+ const tmp = `${target}.${randomUUID()}.tmp`;
57
+ await writeFile(tmp, JSON.stringify(data, null, 2), 'utf-8');
58
+ await rename(tmp, target);
59
+ }
60
+ async readConfig() {
61
+ const partial = await this.readJson(DAEMON_FILES.config);
62
+ return resolveDaemonConfig(partial);
63
+ }
64
+ async writeConfig(config) {
65
+ await this.writeJson(DAEMON_FILES.config, config);
66
+ }
67
+ /** Write the default config if none exists yet; returns the effective config. */
68
+ async initConfig() {
69
+ const existing = await this.readJson(DAEMON_FILES.config);
70
+ if (existing)
71
+ return resolveDaemonConfig(existing);
72
+ await this.writeConfig(DEFAULT_DAEMON_CONFIG);
73
+ return DEFAULT_DAEMON_CONFIG;
74
+ }
75
+ }
76
+ //# sourceMappingURL=daemon-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-state.js","sourceRoot":"","sources":["../../src/daemon-state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export declare class GitCommandError extends Error {
2
+ readonly stderr: string;
3
+ readonly code: number | null;
4
+ constructor(message: string, stderr: string, code: number | null);
5
+ }
6
+ export interface RunGitResult {
7
+ stdout: string;
8
+ stderr: string;
9
+ }
10
+ export declare function runGit(cwd: string, args: string[], options?: {
11
+ timeoutMs?: number;
12
+ env?: NodeJS.ProcessEnv;
13
+ }): Promise<RunGitResult>;
14
+ /**
15
+ * Runs the GitHub CLI (`gh`) the same safe way as git (no shell). Used for PR
16
+ * creation; surfaces a {@link GitCommandError} when `gh` is missing or fails
17
+ * (e.g. not authenticated), so the caller can show an actionable message.
18
+ */
19
+ export declare function runGh(cwd: string, args: string[], options?: {
20
+ timeoutMs?: number;
21
+ env?: NodeJS.ProcessEnv;
22
+ }): Promise<RunGitResult>;
23
+ /** Strip the project cwd and the user's home directory from text sent to the phone. */
24
+ export declare function sanitizePaths(text: string, cwd: string): string;
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Runs git locally via `child_process.execFile` (no shell → no command
3
+ * injection). Arguments are passed as an array; user-provided values are
4
+ * validated by the handlers (see `requireSafe`).
5
+ *
6
+ * Source: architecture/02a-system-architecture.md §5.8.6.
7
+ */
8
+ import { execFile } from 'node:child_process';
9
+ import { homedir } from 'node:os';
10
+ const DEFAULT_TIMEOUT_MS = 30_000;
11
+ const MAX_BUFFER = 16 * 1024 * 1024;
12
+ export class GitCommandError extends Error {
13
+ stderr;
14
+ code;
15
+ constructor(message, stderr, code) {
16
+ super(message);
17
+ this.name = 'GitCommandError';
18
+ this.stderr = stderr;
19
+ this.code = code;
20
+ }
21
+ }
22
+ export function runGit(cwd, args, options = {}) {
23
+ return runFile('git', cwd, args, options);
24
+ }
25
+ /**
26
+ * Runs the GitHub CLI (`gh`) the same safe way as git (no shell). Used for PR
27
+ * creation; surfaces a {@link GitCommandError} when `gh` is missing or fails
28
+ * (e.g. not authenticated), so the caller can show an actionable message.
29
+ */
30
+ export function runGh(cwd, args, options = {}) {
31
+ return runFile('gh', cwd, args, options);
32
+ }
33
+ function runFile(file, cwd, args, options = {}) {
34
+ return new Promise((resolve, reject) => {
35
+ execFile(file, args, {
36
+ cwd,
37
+ timeout: options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
38
+ maxBuffer: MAX_BUFFER,
39
+ windowsHide: true,
40
+ ...(options.env ? { env: options.env } : {}),
41
+ }, (error, stdout, stderr) => {
42
+ if (error) {
43
+ const rawCode = error.code;
44
+ const code = typeof rawCode === 'number' ? rawCode : null;
45
+ const detail = sanitizePaths(stderr || stdout || error.message, cwd);
46
+ reject(new GitCommandError(`${file} ${args[0] ?? ''} failed`, detail, code));
47
+ return;
48
+ }
49
+ resolve({ stdout, stderr });
50
+ });
51
+ });
52
+ }
53
+ /** Strip the project cwd and the user's home directory from text sent to the phone. */
54
+ export function sanitizePaths(text, cwd) {
55
+ let out = text;
56
+ if (cwd)
57
+ out = out.split(cwd).join('.');
58
+ const home = homedir();
59
+ if (home)
60
+ out = out.split(home).join('~');
61
+ return out.trim();
62
+ }
63
+ //# sourceMappingURL=git-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-runner.js","sourceRoot":"","sources":["../../../src/git/git-runner.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAClC,MAAM,UAAU,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAEpC,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,MAAM,CAAS;IACf,IAAI,CAAgB;IAE7B,YAAY,OAAe,EAAE,MAAc,EAAE,IAAmB;QAC9D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAOD,MAAM,UAAU,MAAM,CACpB,GAAW,EACX,IAAc,EACd,UAA2D,EAAE;IAE7D,OAAO,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,KAAK,CACnB,GAAW,EACX,IAAc,EACd,UAA2D,EAAE;IAE7D,OAAO,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAC3C,CAAC;AAED,SAAS,OAAO,CACd,IAAY,EACZ,GAAW,EACX,IAAc,EACd,UAA2D,EAAE;IAE7D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,QAAQ,CACN,IAAI,EACJ,IAAI,EACJ;YACE,GAAG;YACH,OAAO,EAAE,OAAO,CAAC,SAAS,IAAI,kBAAkB;YAChD,SAAS,EAAE,UAAU;YACrB,WAAW,EAAE,IAAI;YACjB,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC7C,EACD,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACxB,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,OAAO,GAAI,KAA+B,CAAC,IAAI,CAAC;gBACtD,MAAM,IAAI,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;gBAC1D,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,IAAI,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;gBACrE,MAAM,CAAC,IAAI,eAAe,CAAC,GAAG,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;gBAC7E,OAAO;YACT,CAAC;YACD,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QAC9B,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,GAAW;IACrD,IAAI,GAAG,GAAG,IAAI,CAAC;IACf,IAAI,GAAG;QAAE,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,IAAI,IAAI;QAAE,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1C,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC"}
@@ -0,0 +1,76 @@
1
+ import type { GitBranchList, GitBranchResult, GitCommitResult, GitDiff, GitPrResult, GitPullResult, GitPushResult, GitRepoStatus, GitWorktreeResult } from '@uxnan/shared';
2
+ export declare class GitService {
3
+ #private;
4
+ status(cwd: string): Promise<GitRepoStatus>;
5
+ /**
6
+ * Returns the working-tree diff. With `path`, returns just that file's diff —
7
+ * for an untracked file (no diff vs HEAD) it synthesises an all-additions
8
+ * unified diff from the file contents so the phone can render it.
9
+ */
10
+ diff(cwd: string, path?: string): Promise<GitDiff>;
11
+ commit(cwd: string, message: string, paths?: string[]): Promise<GitCommitResult>;
12
+ /**
13
+ * Undoes the most recent commit, keeping its changes in the working tree
14
+ * (`git reset --soft HEAD~1`) so the user can re-stage/re-commit before
15
+ * pushing. Non-destructive: no file content is lost.
16
+ */
17
+ undoCommit(cwd: string): Promise<void>;
18
+ /** Lists the current branch plus all local and remote branches. */
19
+ branches(cwd: string): Promise<GitBranchList>;
20
+ /** Stages the given paths (`git add`). */
21
+ stage(cwd: string, paths: string[]): Promise<void>;
22
+ /** Unstages the given paths, keeping working-tree changes (`git restore --staged`). */
23
+ unstage(cwd: string, paths: string[]): Promise<void>;
24
+ /**
25
+ * Discards working-tree changes for the given paths. Tracked files are
26
+ * restored from HEAD (index + worktree); untracked files are deleted. This is
27
+ * destructive and irreversible — callers must confirm first.
28
+ */
29
+ discard(cwd: string, paths: string[]): Promise<void>;
30
+ /**
31
+ * Opens a pull request via the GitHub CLI (`gh pr create`). Requires `gh` to
32
+ * be installed and authenticated; failures surface as {@link GitCommandError}
33
+ * with an actionable message. Returns the PR URL.
34
+ */
35
+ createPr(cwd: string, title: string, body?: string, base?: string, head?: string): Promise<GitPrResult>;
36
+ push(cwd: string, remote: string, branch: string): Promise<GitPushResult>;
37
+ pull(cwd: string, remote?: string, branch?: string): Promise<GitPullResult>;
38
+ checkout(cwd: string, branch: string): Promise<void>;
39
+ /**
40
+ * Switches to {@link target}, keeping each branch's work independent.
41
+ *
42
+ * - `carryChanges: true` → the working-tree changes follow you to the target
43
+ * (`git checkout` moves them; a conflict surfaces as an error).
44
+ * - `carryChanges: false` → the current branch's changes are stashed under a
45
+ * branch-tagged label so they stay put and are NOT lost. On switching back
46
+ * that branch's stash is automatically restored.
47
+ *
48
+ * Either way, any changes previously *left* on the target branch are restored
49
+ * after checkout.
50
+ */
51
+ switchBranch(cwd: string, target: string, carryChanges: boolean): Promise<void>;
52
+ createBranch(cwd: string, name: string): Promise<GitBranchResult>;
53
+ createWorktree(cwd: string, branch: string, path: string): Promise<GitWorktreeResult>;
54
+ /**
55
+ * Revert [commit] (e.g. `HEAD`, a sha) — creates a NEW commit that undoes it,
56
+ * preserving history (unlike `undoCommit`'s soft reset). `--no-edit` keeps the
57
+ * default revert message; fails (and surfaces) on a conflict.
58
+ */
59
+ revert(cwd: string, commit: string): Promise<void>;
60
+ /**
61
+ * Delete a local branch. With `force: false` this is `git branch -d`, which
62
+ * **refuses** a branch not fully merged (the safe default — the error is
63
+ * surfaced to the phone); `force: true` is `-D` (delete regardless).
64
+ */
65
+ deleteBranch(cwd: string, branch: string, force: boolean): Promise<void>;
66
+ /**
67
+ * Remove a worktree. With `force: false`, `git worktree remove` **refuses** a
68
+ * worktree with uncommitted/untracked changes (safe default — surfaced);
69
+ * `force: true` adds `--force`. Also prunes stale admin entries afterward.
70
+ *
71
+ * `git` refuses to remove the worktree you are *standing in*, so we run the
72
+ * removal from the repo's MAIN worktree (resolved via `worktree list`),
73
+ * letting the phone remove the very worktree backing the active thread.
74
+ */
75
+ removeWorktree(cwd: string, path: string, force: boolean): Promise<void>;
76
+ }