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.
- package/README.md +150 -0
- package/dist/src/account-status.d.ts +13 -0
- package/dist/src/account-status.js +78 -0
- package/dist/src/account-status.js.map +1 -0
- package/dist/src/adapters/base-adapter.d.ts +18 -0
- package/dist/src/adapters/base-adapter.js +15 -0
- package/dist/src/adapters/base-adapter.js.map +1 -0
- package/dist/src/adapters/claude-adapter.d.ts +102 -0
- package/dist/src/adapters/claude-adapter.js +486 -0
- package/dist/src/adapters/claude-adapter.js.map +1 -0
- package/dist/src/adapters/claude-tools.d.ts +25 -0
- package/dist/src/adapters/claude-tools.js +146 -0
- package/dist/src/adapters/claude-tools.js.map +1 -0
- package/dist/src/adapters/codex-adapter.d.ts +116 -0
- package/dist/src/adapters/codex-adapter.js +912 -0
- package/dist/src/adapters/codex-adapter.js.map +1 -0
- package/dist/src/adapters/codex-app-server.d.ts +74 -0
- package/dist/src/adapters/codex-app-server.js +225 -0
- package/dist/src/adapters/codex-app-server.js.map +1 -0
- package/dist/src/adapters/codex-approval.d.ts +88 -0
- package/dist/src/adapters/codex-approval.js +160 -0
- package/dist/src/adapters/codex-approval.js.map +1 -0
- package/dist/src/adapters/codex-tools.d.ts +18 -0
- package/dist/src/adapters/codex-tools.js +106 -0
- package/dist/src/adapters/codex-tools.js.map +1 -0
- package/dist/src/adapters/content-blocks.d.ts +68 -0
- package/dist/src/adapters/content-blocks.js +205 -0
- package/dist/src/adapters/content-blocks.js.map +1 -0
- package/dist/src/adapters/echo-agent-adapter.d.ts +23 -0
- package/dist/src/adapters/echo-agent-adapter.js +72 -0
- package/dist/src/adapters/echo-agent-adapter.js.map +1 -0
- package/dist/src/adapters/gemini-adapter.d.ts +87 -0
- package/dist/src/adapters/gemini-adapter.js +594 -0
- package/dist/src/adapters/gemini-adapter.js.map +1 -0
- package/dist/src/adapters/gemini-tools.d.ts +4 -0
- package/dist/src/adapters/gemini-tools.js +48 -0
- package/dist/src/adapters/gemini-tools.js.map +1 -0
- package/dist/src/adapters/opencode-adapter.d.ts +74 -0
- package/dist/src/adapters/opencode-adapter.js +418 -0
- package/dist/src/adapters/opencode-adapter.js.map +1 -0
- package/dist/src/adapters/opencode-tools.d.ts +2 -0
- package/dist/src/adapters/opencode-tools.js +41 -0
- package/dist/src/adapters/opencode-tools.js.map +1 -0
- package/dist/src/adapters/pi-adapter.d.ts +92 -0
- package/dist/src/adapters/pi-adapter.js +467 -0
- package/dist/src/adapters/pi-adapter.js.map +1 -0
- package/dist/src/adapters/pi-tools.d.ts +10 -0
- package/dist/src/adapters/pi-tools.js +72 -0
- package/dist/src/adapters/pi-tools.js.map +1 -0
- package/dist/src/adapters/process-agent-adapter.d.ts +24 -0
- package/dist/src/adapters/process-agent-adapter.js +111 -0
- package/dist/src/adapters/process-agent-adapter.js.map +1 -0
- package/dist/src/adapters/resolve-claude.d.ts +13 -0
- package/dist/src/adapters/resolve-claude.js +57 -0
- package/dist/src/adapters/resolve-claude.js.map +1 -0
- package/dist/src/adapters/resolve-codex.d.ts +13 -0
- package/dist/src/adapters/resolve-codex.js +48 -0
- package/dist/src/adapters/resolve-codex.js.map +1 -0
- package/dist/src/adapters/resolve-gemini.d.ts +13 -0
- package/dist/src/adapters/resolve-gemini.js +47 -0
- package/dist/src/adapters/resolve-gemini.js.map +1 -0
- package/dist/src/adapters/resolve-opencode.d.ts +11 -0
- package/dist/src/adapters/resolve-opencode.js +49 -0
- package/dist/src/adapters/resolve-opencode.js.map +1 -0
- package/dist/src/adapters/resolve-pi.d.ts +13 -0
- package/dist/src/adapters/resolve-pi.js +46 -0
- package/dist/src/adapters/resolve-pi.js.map +1 -0
- package/dist/src/adapters/run-options.d.ts +22 -0
- package/dist/src/adapters/run-options.js +48 -0
- package/dist/src/adapters/run-options.js.map +1 -0
- package/dist/src/adapters/spawn.d.ts +20 -0
- package/dist/src/adapters/spawn.js +16 -0
- package/dist/src/adapters/spawn.js.map +1 -0
- package/dist/src/agents/agent-manager.d.ts +98 -0
- package/dist/src/agents/agent-manager.js +433 -0
- package/dist/src/agents/agent-manager.js.map +1 -0
- package/dist/src/agents/attachments.d.ts +28 -0
- package/dist/src/agents/attachments.js +121 -0
- package/dist/src/agents/attachments.js.map +1 -0
- package/dist/src/bridge-context.d.ts +45 -0
- package/dist/src/bridge-context.js +2 -0
- package/dist/src/bridge-context.js.map +1 -0
- package/dist/src/bridge-status.d.ts +12 -0
- package/dist/src/bridge-status.js +17 -0
- package/dist/src/bridge-status.js.map +1 -0
- package/dist/src/bridge.d.ts +37 -0
- package/dist/src/bridge.js +446 -0
- package/dist/src/bridge.js.map +1 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +194 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/conversation/session-history.d.ts +27 -0
- package/dist/src/conversation/session-history.js +1082 -0
- package/dist/src/conversation/session-history.js.map +1 -0
- package/dist/src/conversation/thread-store.d.ts +74 -0
- package/dist/src/conversation/thread-store.js +366 -0
- package/dist/src/conversation/thread-store.js.map +1 -0
- package/dist/src/daemon-config.d.ts +123 -0
- package/dist/src/daemon-config.js +64 -0
- package/dist/src/daemon-config.js.map +1 -0
- package/dist/src/daemon-state.d.ts +27 -0
- package/dist/src/daemon-state.js +76 -0
- package/dist/src/daemon-state.js.map +1 -0
- package/dist/src/git/git-runner.d.ts +24 -0
- package/dist/src/git/git-runner.js +63 -0
- package/dist/src/git/git-runner.js.map +1 -0
- package/dist/src/git/git-service.d.ts +76 -0
- package/dist/src/git/git-service.js +435 -0
- package/dist/src/git/git-service.js.map +1 -0
- package/dist/src/handler-router.d.ts +34 -0
- package/dist/src/handler-router.js +67 -0
- package/dist/src/handler-router.js.map +1 -0
- package/dist/src/handlers/account-handler.d.ts +4 -0
- package/dist/src/handlers/account-handler.js +27 -0
- package/dist/src/handlers/account-handler.js.map +1 -0
- package/dist/src/handlers/agent-handler.d.ts +2 -0
- package/dist/src/handlers/agent-handler.js +8 -0
- package/dist/src/handlers/agent-handler.js.map +1 -0
- package/dist/src/handlers/bridge-control-handler.d.ts +2 -0
- package/dist/src/handlers/bridge-control-handler.js +64 -0
- package/dist/src/handlers/bridge-control-handler.js.map +1 -0
- package/dist/src/handlers/desktop-handler.d.ts +12 -0
- package/dist/src/handlers/desktop-handler.js +5 -0
- package/dist/src/handlers/desktop-handler.js.map +1 -0
- package/dist/src/handlers/git-handler.d.ts +2 -0
- package/dist/src/handlers/git-handler.js +82 -0
- package/dist/src/handlers/git-handler.js.map +1 -0
- package/dist/src/handlers/index.d.ts +8 -0
- package/dist/src/handlers/index.js +22 -0
- package/dist/src/handlers/index.js.map +1 -0
- package/dist/src/handlers/not-implemented.d.ts +10 -0
- package/dist/src/handlers/not-implemented.js +21 -0
- package/dist/src/handlers/not-implemented.js.map +1 -0
- package/dist/src/handlers/notifications-handler.d.ts +2 -0
- package/dist/src/handlers/notifications-handler.js +62 -0
- package/dist/src/handlers/notifications-handler.js.map +1 -0
- package/dist/src/handlers/params.d.ts +11 -0
- package/dist/src/handlers/params.js +72 -0
- package/dist/src/handlers/params.js.map +1 -0
- package/dist/src/handlers/project-handler.d.ts +2 -0
- package/dist/src/handlers/project-handler.js +6 -0
- package/dist/src/handlers/project-handler.js.map +1 -0
- package/dist/src/handlers/thread-context-handler.d.ts +2 -0
- package/dist/src/handlers/thread-context-handler.js +211 -0
- package/dist/src/handlers/thread-context-handler.js.map +1 -0
- package/dist/src/handlers/workspace-handler.d.ts +2 -0
- package/dist/src/handlers/workspace-handler.js +101 -0
- package/dist/src/handlers/workspace-handler.js.map +1 -0
- package/dist/src/hooks/claude-approval-hook.d.ts +7 -0
- package/dist/src/hooks/claude-approval-hook.js +95 -0
- package/dist/src/hooks/claude-approval-hook.js.map +1 -0
- package/dist/src/hooks/gemini-approval-hook.d.ts +7 -0
- package/dist/src/hooks/gemini-approval-hook.js +113 -0
- package/dist/src/hooks/gemini-approval-hook.js.map +1 -0
- package/dist/src/index.d.ts +62 -0
- package/dist/src/index.js +65 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/keyring-secret-store.d.ts +36 -0
- package/dist/src/keyring-secret-store.js +70 -0
- package/dist/src/keyring-secret-store.js.map +1 -0
- package/dist/src/lock-file.d.ts +18 -0
- package/dist/src/lock-file.js +60 -0
- package/dist/src/lock-file.js.map +1 -0
- package/dist/src/logger.d.ts +28 -0
- package/dist/src/logger.js +99 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/pairing/pairing-code-service.d.ts +45 -0
- package/dist/src/pairing/pairing-code-service.js +183 -0
- package/dist/src/pairing/pairing-code-service.js.map +1 -0
- package/dist/src/projects/project-registry.d.ts +14 -0
- package/dist/src/projects/project-registry.js +60 -0
- package/dist/src/projects/project-registry.js.map +1 -0
- package/dist/src/push/push-sender.d.ts +21 -0
- package/dist/src/push/push-sender.js +96 -0
- package/dist/src/push/push-sender.js.map +1 -0
- package/dist/src/push/push-service.d.ts +122 -0
- package/dist/src/push/push-service.js +260 -0
- package/dist/src/push/push-service.js.map +1 -0
- package/dist/src/qr.d.ts +17 -0
- package/dist/src/qr.js +31 -0
- package/dist/src/qr.js.map +1 -0
- package/dist/src/secret-store.d.ts +23 -0
- package/dist/src/secret-store.js +27 -0
- package/dist/src/secret-store.js.map +1 -0
- package/dist/src/secure-device-state.d.ts +16 -0
- package/dist/src/secure-device-state.js +63 -0
- package/dist/src/secure-device-state.js.map +1 -0
- package/dist/src/service-installer.d.ts +57 -0
- package/dist/src/service-installer.js +254 -0
- package/dist/src/service-installer.js.map +1 -0
- package/dist/src/session-state.d.ts +14 -0
- package/dist/src/session-state.js +19 -0
- package/dist/src/session-state.js.map +1 -0
- package/dist/src/transport/crypto.d.ts +43 -0
- package/dist/src/transport/crypto.js +78 -0
- package/dist/src/transport/crypto.js.map +1 -0
- package/dist/src/transport/lan-server.d.ts +33 -0
- package/dist/src/transport/lan-server.js +105 -0
- package/dist/src/transport/lan-server.js.map +1 -0
- package/dist/src/transport/local-hosts.d.ts +17 -0
- package/dist/src/transport/local-hosts.js +30 -0
- package/dist/src/transport/local-hosts.js.map +1 -0
- package/dist/src/transport/mdns-advertiser.d.ts +83 -0
- package/dist/src/transport/mdns-advertiser.js +282 -0
- package/dist/src/transport/mdns-advertiser.js.map +1 -0
- package/dist/src/transport/message-io.d.ts +33 -0
- package/dist/src/transport/message-io.js +87 -0
- package/dist/src/transport/message-io.js.map +1 -0
- package/dist/src/transport/outbound-log.d.ts +24 -0
- package/dist/src/transport/outbound-log.js +78 -0
- package/dist/src/transport/outbound-log.js.map +1 -0
- package/dist/src/transport/relay-client.d.ts +19 -0
- package/dist/src/transport/relay-client.js +27 -0
- package/dist/src/transport/relay-client.js.map +1 -0
- package/dist/src/transport/secure-channel.d.ts +33 -0
- package/dist/src/transport/secure-channel.js +81 -0
- package/dist/src/transport/secure-channel.js.map +1 -0
- package/dist/src/transport/server-handshake.d.ts +49 -0
- package/dist/src/transport/server-handshake.js +137 -0
- package/dist/src/transport/server-handshake.js.map +1 -0
- package/dist/src/transport/session-handler.d.ts +19 -0
- package/dist/src/transport/session-handler.js +134 -0
- package/dist/src/transport/session-handler.js.map +1 -0
- package/dist/src/transport/session-registry.d.ts +58 -0
- package/dist/src/transport/session-registry.js +91 -0
- package/dist/src/transport/session-registry.js.map +1 -0
- package/dist/src/transport/trust-store.d.ts +23 -0
- package/dist/src/transport/trust-store.js +33 -0
- package/dist/src/transport/trust-store.js.map +1 -0
- package/dist/src/transport/ws-adapter.d.ts +7 -0
- package/dist/src/transport/ws-adapter.js +16 -0
- package/dist/src/transport/ws-adapter.js.map +1 -0
- package/dist/src/version.d.ts +1 -0
- package/dist/src/version.js +13 -0
- package/dist/src/version.js.map +1 -0
- package/dist/src/workspace/browse-service.d.ts +10 -0
- package/dist/src/workspace/browse-service.js +97 -0
- package/dist/src/workspace/browse-service.js.map +1 -0
- package/dist/src/workspace/checkpoint-service.d.ts +21 -0
- package/dist/src/workspace/checkpoint-service.js +219 -0
- package/dist/src/workspace/checkpoint-service.js.map +1 -0
- package/dist/src/workspace/path-guard.d.ts +7 -0
- package/dist/src/workspace/path-guard.js +51 -0
- package/dist/src/workspace/path-guard.js.map +1 -0
- package/dist/src/workspace/workspace-service.d.ts +8 -0
- package/dist/src/workspace/workspace-service.js +111 -0
- package/dist/src/workspace/workspace-service.js.map +1 -0
- package/package.json +46 -0
- package/scripts/extract-gemini-hook.mjs +16 -0
- package/scripts/fake-approval-bridge.mjs +23 -0
- package/scripts/install-service-linux.sh +38 -0
- package/scripts/install-service-macos.sh +38 -0
- package/scripts/install-service-windows.ps1 +26 -0
- package/scripts/test-gemini-hook-e2e.mjs +168 -0
- 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":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAqB,MAAM,oBAAoB,CAAC;AAEnG,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,MAAM,EAAE,oBAAoB;IAC5B,MAAM,EAAE,oBAAoB;IAC5B,OAAO,EAAE,sBAAsB;IAC/B,WAAW,EAAE,mBAAmB;IAChC,aAAa,EAAE,qBAAqB;IACpC,gBAAgB,EAAE,wBAAwB;IAC1C,SAAS,EAAE,iBAAiB;IAC5B,IAAI,EAAE,aAAa;IACnB,WAAW,EAAE,kBAAkB;IAC/B,OAAO,EAAE,cAAc;CACf,CAAC;AAEX,MAAM,OAAO,WAAW;IACb,OAAO,CAAS;IAEzB,YAAY,UAAkB,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC;QACrD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,CAAC,IAAY;QAClB,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,SAAS;QACb,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/C,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;IAED,KAAK,CAAC,QAAQ,CAAI,IAAY;QAC5B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;YACxD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAM,CAAC;QAC9B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;gBAAE,OAAO,IAAI,CAAC;YAClE,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAED,mFAAmF;IACnF,KAAK,CAAC,SAAS,CAAC,IAAY,EAAE,IAAa;QACzC,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,GAAG,GAAG,GAAG,MAAM,IAAI,UAAU,EAAE,MAAM,CAAC;QAC5C,MAAM,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;QAC7D,MAAM,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAwB,YAAY,CAAC,MAAM,CAAC,CAAC;QAChF,OAAO,mBAAmB,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,MAAoB;QACpC,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpD,CAAC;IAED,iFAAiF;IACjF,KAAK,CAAC,UAAU;QACd,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAwB,YAAY,CAAC,MAAM,CAAC,CAAC;QACjF,IAAI,QAAQ;YAAE,OAAO,mBAAmB,CAAC,QAAQ,CAAC,CAAC;QACnD,MAAM,IAAI,CAAC,WAAW,CAAC,qBAAqB,CAAC,CAAC;QAC9C,OAAO,qBAAqB,CAAC;IAC/B,CAAC;CACF"}
|
|
@@ -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
|
+
}
|