@lattices/cli 0.4.13 → 0.5.0

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 (180) hide show
  1. package/README.md +5 -7
  2. package/apps/mac/Info.plist +2 -2
  3. package/apps/mac/Lattices.app/Contents/Info.plist +4 -12
  4. package/apps/mac/Lattices.app/Contents/MacOS/Lattices +0 -0
  5. package/bin/lattices-app.ts +110 -17
  6. package/bin/lattices-build +125 -0
  7. package/bin/lattices-dev +89 -16
  8. package/bin/lattices.ts +977 -16
  9. package/docs/agents.md +81 -4
  10. package/docs/ai-chat-ux-review.md +416 -0
  11. package/docs/api.md +135 -3
  12. package/docs/app.md +30 -8
  13. package/docs/config.md +4 -0
  14. package/docs/mouse-gestures.md +191 -63
  15. package/docs/proposals/LAT-004-interactive-overlay-actors.md +1 -1
  16. package/docs/proposals/LAT-005-action-runtime-product-spine.md +914 -0
  17. package/docs/proposals/LAT-006-mira-in-lattices.md +553 -0
  18. package/docs/reference/dewey.config.ts +2 -2
  19. package/docs/release.md +171 -0
  20. package/docs/repo-structure.md +4 -5
  21. package/docs/voice.md +11 -27
  22. package/package.json +9 -10
  23. package/apps/mac/Package.swift +0 -27
  24. package/apps/mac/Sources/AppShell/App.swift +0 -26
  25. package/apps/mac/Sources/AppShell/AppActivationCoordinator.swift +0 -27
  26. package/apps/mac/Sources/AppShell/AppDelegate.swift +0 -189
  27. package/apps/mac/Sources/AppShell/AppServicesBootstrap.swift +0 -25
  28. package/apps/mac/Sources/AppShell/AppShellView.swift +0 -171
  29. package/apps/mac/Sources/AppShell/AppUpdater.swift +0 -305
  30. package/apps/mac/Sources/AppShell/CliActionLauncher.swift +0 -50
  31. package/apps/mac/Sources/AppShell/HomeDashboardView.swift +0 -133
  32. package/apps/mac/Sources/AppShell/HotkeyBootstrap.swift +0 -87
  33. package/apps/mac/Sources/AppShell/KeyRecorderView.swift +0 -210
  34. package/apps/mac/Sources/AppShell/LatticesRuntime.swift +0 -104
  35. package/apps/mac/Sources/AppShell/MainView.swift +0 -847
  36. package/apps/mac/Sources/AppShell/MainWindow.swift +0 -83
  37. package/apps/mac/Sources/AppShell/MenuBarController.swift +0 -177
  38. package/apps/mac/Sources/AppShell/OnboardingView.swift +0 -483
  39. package/apps/mac/Sources/AppShell/PermissionsAssistantView.swift +0 -366
  40. package/apps/mac/Sources/AppShell/PermissionsAssistantWindow.swift +0 -70
  41. package/apps/mac/Sources/AppShell/Preferences.swift +0 -297
  42. package/apps/mac/Sources/AppShell/SettingsView.swift +0 -3163
  43. package/apps/mac/Sources/AppShell/SettingsWindow.swift +0 -34
  44. package/apps/mac/Sources/AppShell/WorkspaceInspectorPresenter.swift +0 -13
  45. package/apps/mac/Sources/Core/Actions/HotkeyManager.swift +0 -256
  46. package/apps/mac/Sources/Core/Actions/HotkeyStore.swift +0 -399
  47. package/apps/mac/Sources/Core/Actions/IntentEngine.swift +0 -988
  48. package/apps/mac/Sources/Core/Actions/IntentSchema.swift +0 -94
  49. package/apps/mac/Sources/Core/Actions/Intents/CreateLayerIntent.swift +0 -54
  50. package/apps/mac/Sources/Core/Actions/Intents/DistributeIntent.swift +0 -56
  51. package/apps/mac/Sources/Core/Actions/Intents/FocusIntent.swift +0 -69
  52. package/apps/mac/Sources/Core/Actions/Intents/HelpIntent.swift +0 -41
  53. package/apps/mac/Sources/Core/Actions/Intents/KillIntent.swift +0 -47
  54. package/apps/mac/Sources/Core/Actions/Intents/LatticeIntent.swift +0 -53
  55. package/apps/mac/Sources/Core/Actions/Intents/LaunchIntent.swift +0 -67
  56. package/apps/mac/Sources/Core/Actions/Intents/ListSessionsIntent.swift +0 -32
  57. package/apps/mac/Sources/Core/Actions/Intents/ListWindowsIntent.swift +0 -30
  58. package/apps/mac/Sources/Core/Actions/Intents/ScanIntent.swift +0 -52
  59. package/apps/mac/Sources/Core/Actions/Intents/SearchIntent.swift +0 -190
  60. package/apps/mac/Sources/Core/Actions/Intents/SwitchLayerIntent.swift +0 -50
  61. package/apps/mac/Sources/Core/Actions/Intents/TileIntent.swift +0 -61
  62. package/apps/mac/Sources/Core/Actions/PaletteCommand.swift +0 -439
  63. package/apps/mac/Sources/Core/Actions/VoiceIntentResolver.swift +0 -713
  64. package/apps/mac/Sources/Core/Companion/CompanionActivityLog.swift +0 -70
  65. package/apps/mac/Sources/Core/Companion/CompanionKeyboardController.swift +0 -141
  66. package/apps/mac/Sources/Core/Companion/LatticesCompanionBridgeServer.swift +0 -454
  67. package/apps/mac/Sources/Core/Companion/LatticesCompanionCockpit.swift +0 -555
  68. package/apps/mac/Sources/Core/Companion/LatticesCompanionSecurityCoordinator.swift +0 -629
  69. package/apps/mac/Sources/Core/Companion/LatticesCompanionTrackpadController.swift +0 -204
  70. package/apps/mac/Sources/Core/Companion/LatticesDeckHost.swift +0 -1463
  71. package/apps/mac/Sources/Core/Daemon/DaemonProtocol.swift +0 -114
  72. package/apps/mac/Sources/Core/Daemon/DaemonServer.swift +0 -427
  73. package/apps/mac/Sources/Core/Daemon/LatticesApi.swift +0 -2965
  74. package/apps/mac/Sources/Core/Desktop/AccessibilityTextExtractor.swift +0 -111
  75. package/apps/mac/Sources/Core/Desktop/AppTypeClassifier.swift +0 -106
  76. package/apps/mac/Sources/Core/Desktop/DesktopModel.swift +0 -331
  77. package/apps/mac/Sources/Core/Desktop/DesktopModelTypes.swift +0 -73
  78. package/apps/mac/Sources/Core/Desktop/InventoryManager.swift +0 -35
  79. package/apps/mac/Sources/Core/Desktop/InventoryPath.swift +0 -43
  80. package/apps/mac/Sources/Core/Desktop/MouseFinder.swift +0 -527
  81. package/apps/mac/Sources/Core/Desktop/OcrModel.swift +0 -467
  82. package/apps/mac/Sources/Core/Desktop/OcrStore.swift +0 -329
  83. package/apps/mac/Sources/Core/Desktop/PlacementSpec.swift +0 -195
  84. package/apps/mac/Sources/Core/Desktop/SessionWindowLocator.swift +0 -139
  85. package/apps/mac/Sources/Core/Desktop/TilePickerView.swift +0 -209
  86. package/apps/mac/Sources/Core/Desktop/WindowCapture.swift +0 -33
  87. package/apps/mac/Sources/Core/Desktop/WindowDragSnapController.swift +0 -429
  88. package/apps/mac/Sources/Core/Desktop/WindowPreviewCard.swift +0 -100
  89. package/apps/mac/Sources/Core/Desktop/WindowPreviewStore.swift +0 -112
  90. package/apps/mac/Sources/Core/Desktop/WindowSelectionStore.swift +0 -76
  91. package/apps/mac/Sources/Core/Desktop/WindowTiler.swift +0 -2222
  92. package/apps/mac/Sources/Core/Input/EventTapBreaker.swift +0 -124
  93. package/apps/mac/Sources/Core/Input/EventTapThread.swift +0 -54
  94. package/apps/mac/Sources/Core/Input/InputCaptureResetCenter.swift +0 -20
  95. package/apps/mac/Sources/Core/Input/KeyboardRemapConfig.swift +0 -69
  96. package/apps/mac/Sources/Core/Input/KeyboardRemapController.swift +0 -346
  97. package/apps/mac/Sources/Core/Input/KeyboardRemapStore.swift +0 -141
  98. package/apps/mac/Sources/Core/Input/MouseGestureConfig.swift +0 -499
  99. package/apps/mac/Sources/Core/Input/MouseGestureController.swift +0 -2271
  100. package/apps/mac/Sources/Core/Input/MouseInputDeviceStore.swift +0 -98
  101. package/apps/mac/Sources/Core/Input/MouseInputEventViewer.swift +0 -272
  102. package/apps/mac/Sources/Core/Input/MouseShortcutStore.swift +0 -170
  103. package/apps/mac/Sources/Core/Input/SecureEventInputMonitor.swift +0 -39
  104. package/apps/mac/Sources/Core/Input/ShapeRecognizer.swift +0 -624
  105. package/apps/mac/Sources/Core/Input/TapBudgetMeter.swift +0 -56
  106. package/apps/mac/Sources/Core/Overlays/AppWindowShell.swift +0 -63
  107. package/apps/mac/Sources/Core/Overlays/CommandMode/CommandModeState.swift +0 -1566
  108. package/apps/mac/Sources/Core/Overlays/CommandMode/CommandModeView.swift +0 -1927
  109. package/apps/mac/Sources/Core/Overlays/CommandMode/CommandModeWindow.swift +0 -196
  110. package/apps/mac/Sources/Core/Overlays/CommandPalette/CommandPaletteView.swift +0 -307
  111. package/apps/mac/Sources/Core/Overlays/CommandPalette/CommandPaletteWindow.swift +0 -67
  112. package/apps/mac/Sources/Core/Overlays/HUD/CheatSheetHUD.swift +0 -576
  113. package/apps/mac/Sources/Core/Overlays/HUD/HUDBottomBar.swift +0 -279
  114. package/apps/mac/Sources/Core/Overlays/HUD/HUDController.swift +0 -1158
  115. package/apps/mac/Sources/Core/Overlays/HUD/HUDLeftBar.swift +0 -849
  116. package/apps/mac/Sources/Core/Overlays/HUD/HUDMinimap.swift +0 -179
  117. package/apps/mac/Sources/Core/Overlays/HUD/HUDRightBar.swift +0 -596
  118. package/apps/mac/Sources/Core/Overlays/HUD/HUDState.swift +0 -367
  119. package/apps/mac/Sources/Core/Overlays/HUD/HUDTopBar.swift +0 -243
  120. package/apps/mac/Sources/Core/Overlays/HUD/LauncherHUD.swift +0 -334
  121. package/apps/mac/Sources/Core/Overlays/HUD/LayerBezel.swift +0 -203
  122. package/apps/mac/Sources/Core/Overlays/OmniSearch/OmniSearchState.swift +0 -280
  123. package/apps/mac/Sources/Core/Overlays/OmniSearch/OmniSearchView.swift +0 -422
  124. package/apps/mac/Sources/Core/Overlays/OmniSearch/OmniSearchWindow.swift +0 -94
  125. package/apps/mac/Sources/Core/Overlays/OverlayPanelShell.swift +0 -241
  126. package/apps/mac/Sources/Core/Overlays/ScreenMap/ScreenMapState.swift +0 -3135
  127. package/apps/mac/Sources/Core/Overlays/ScreenMap/ScreenMapView.swift +0 -3977
  128. package/apps/mac/Sources/Core/Overlays/ScreenMap/ScreenMapWindowController.swift +0 -119
  129. package/apps/mac/Sources/Core/Overlays/ScreenOverlayCanvasController.swift +0 -1217
  130. package/apps/mac/Sources/Core/Overlays/Voice/VoiceCommandWindow.swift +0 -1575
  131. package/apps/mac/Sources/Core/Pi/PiAuthNextStepCard.swift +0 -148
  132. package/apps/mac/Sources/Core/Pi/PiAuthPromptCard.swift +0 -90
  133. package/apps/mac/Sources/Core/Pi/PiChatDock.swift +0 -564
  134. package/apps/mac/Sources/Core/Pi/PiChatSession.swift +0 -1948
  135. package/apps/mac/Sources/Core/Pi/PiInstallCallout.swift +0 -86
  136. package/apps/mac/Sources/Core/Pi/PiProviderSetupCallout.swift +0 -99
  137. package/apps/mac/Sources/Core/Pi/PiWorkspaceView.swift +0 -510
  138. package/apps/mac/Sources/Core/System/Capability.swift +0 -79
  139. package/apps/mac/Sources/Core/System/DiagnosticLog.swift +0 -373
  140. package/apps/mac/Sources/Core/System/EventBus.swift +0 -31
  141. package/apps/mac/Sources/Core/System/PermissionChecker.swift +0 -224
  142. package/apps/mac/Sources/Core/System/ProcessModel.swift +0 -199
  143. package/apps/mac/Sources/Core/System/ProcessQuery.swift +0 -151
  144. package/apps/mac/Sources/Core/System/SystemTelemetryMonitor.swift +0 -273
  145. package/apps/mac/Sources/Core/Voice/AdvisorLearningStore.swift +0 -90
  146. package/apps/mac/Sources/Core/Voice/AgentSession.swift +0 -377
  147. package/apps/mac/Sources/Core/Voice/AudioProvider.swift +0 -555
  148. package/apps/mac/Sources/Core/Voice/HandsOffSession.swift +0 -839
  149. package/apps/mac/Sources/Core/Voice/VoiceChatView.swift +0 -192
  150. package/apps/mac/Sources/Core/Voice/VoxClient.swift +0 -454
  151. package/apps/mac/Sources/Core/Workspace/Project.swift +0 -28
  152. package/apps/mac/Sources/Core/Workspace/ProjectScanner.swift +0 -141
  153. package/apps/mac/Sources/Core/Workspace/SessionLayerStore.swift +0 -285
  154. package/apps/mac/Sources/Core/Workspace/SessionManager.swift +0 -75
  155. package/apps/mac/Sources/Core/Workspace/Terminal/Terminal.swift +0 -259
  156. package/apps/mac/Sources/Core/Workspace/Terminal/TerminalQuery.swift +0 -156
  157. package/apps/mac/Sources/Core/Workspace/Terminal/TerminalSynthesizer.swift +0 -200
  158. package/apps/mac/Sources/Core/Workspace/Tmux/TmuxModel.swift +0 -60
  159. package/apps/mac/Sources/Core/Workspace/Tmux/TmuxQuery.swift +0 -105
  160. package/apps/mac/Sources/Core/Workspace/WorkspaceManager.swift +0 -1027
  161. package/apps/mac/Sources/UI/ActionRow.swift +0 -78
  162. package/apps/mac/Sources/UI/OrphanRow.swift +0 -129
  163. package/apps/mac/Sources/UI/ProjectRow.swift +0 -368
  164. package/apps/mac/Sources/UI/TabGroupRow.swift +0 -178
  165. package/apps/mac/Sources/UI/Theme.swift +0 -164
  166. package/apps/mac/Tests/StageDragTests.swift +0 -333
  167. package/apps/mac/Tests/StageJoinTests.swift +0 -313
  168. package/apps/mac/Tests/StageManagerTests.swift +0 -280
  169. package/apps/mac/Tests/StageTileTests.swift +0 -353
  170. package/swift/Package.swift +0 -20
  171. package/swift/Sources/DeckKit/DeckAction.swift +0 -51
  172. package/swift/Sources/DeckKit/DeckBridgeSecurity.swift +0 -152
  173. package/swift/Sources/DeckKit/DeckCockpit.swift +0 -82
  174. package/swift/Sources/DeckKit/DeckHost.swift +0 -7
  175. package/swift/Sources/DeckKit/DeckManifest.swift +0 -145
  176. package/swift/Sources/DeckKit/DeckRuntimeSnapshot.swift +0 -533
  177. package/swift/Sources/DeckKit/DeckTrackpad.swift +0 -63
  178. package/swift/Sources/DeckKit/DeckValue.swift +0 -93
  179. package/swift/Sources/DeckKit/DeckVoiceError.swift +0 -88
  180. package/swift/Tests/DeckKitTests/DeckKitTests.swift +0 -286
package/README.md CHANGED
@@ -42,15 +42,11 @@ The CLI and app work independently — use either or both.
42
42
  git clone https://github.com/arach/lattices.git
43
43
  cd lattices
44
44
 
45
- # Build the menu bar app (requires Swift 6.2 / Xcode 26+)
46
- cd apps/mac && swift build -c release && cd ../..
47
-
48
45
  # Install CLI dependencies
49
46
  npm install
50
47
 
51
- # Launch
52
- bun bin/lattices-app.ts build # bundle the .app
53
- bun bin/lattices-app.ts # launch it
48
+ # Build/install/relaunch the dev app at the stable permission target
49
+ ./run.sh
54
50
  ```
55
51
 
56
52
  To build a signed, notarized DMG for distribution:
@@ -59,10 +55,12 @@ To build a signed, notarized DMG for distribution:
59
55
  # Requires a Developer ID certificate and notarytool keychain profile
60
56
  ./tools/release/build-dmg.sh
61
57
 
62
- # Update v<package.json version> and upload the DMG to GitHub Releases
58
+ # Create/update v<package.json version> and upload DMG assets
63
59
  ./tools/release/ship.sh
64
60
  ```
65
61
 
62
+ See [Release](docs/release.md) for the CI and local maintainer workflows.
63
+
66
64
  ## Quick start
67
65
 
68
66
  ```sh
@@ -3,7 +3,7 @@
3
3
  <plist version="1.0">
4
4
  <dict>
5
5
  <key>CFBundleIdentifier</key>
6
- <string>com.arach.lattices</string>
6
+ <string>dev.lattices.app</string>
7
7
  <key>CFBundleName</key>
8
8
  <string>Lattices</string>
9
9
  <key>CFBundleDisplayName</key>
@@ -18,7 +18,7 @@
18
18
  <array>
19
19
  <dict>
20
20
  <key>CFBundleURLName</key>
21
- <string>com.arach.lattices</string>
21
+ <string>dev.lattices.app</string>
22
22
  <key>CFBundleURLSchemes</key>
23
23
  <array>
24
24
  <string>lattices</string>
@@ -3,7 +3,7 @@
3
3
  <plist version="1.0">
4
4
  <dict>
5
5
  <key>CFBundleIdentifier</key>
6
- <string>com.arach.lattices</string>
6
+ <string>dev.lattices.app</string>
7
7
  <key>CFBundleName</key>
8
8
  <string>Lattices</string>
9
9
  <key>CFBundleDisplayName</key>
@@ -18,7 +18,7 @@
18
18
  <array>
19
19
  <dict>
20
20
  <key>CFBundleURLName</key>
21
- <string>com.arach.lattices</string>
21
+ <string>dev.lattices.app</string>
22
22
  <key>CFBundleURLSchemes</key>
23
23
  <array>
24
24
  <string>lattices</string>
@@ -26,17 +26,9 @@
26
26
  </dict>
27
27
  </array>
28
28
  <key>CFBundleVersion</key>
29
- <string>0.4.13</string>
29
+ <string>0.5.0</string>
30
30
  <key>CFBundleShortVersionString</key>
31
- <string>0.4.13</string>
32
- <key>LatticesBuildChannel</key>
33
- <string>dev</string>
34
- <key>LatticesBuildTrack</key>
35
- <string>latest</string>
36
- <key>LatticesBuildRevision</key>
37
- <string>42928c7</string>
38
- <key>LatticesBuildTimestamp</key>
39
- <string>2026-05-04T17:39:23Z</string>
31
+ <string>0.5.0</string>
40
32
  <key>LSMinimumSystemVersion</key>
41
33
  <string>13.0</string>
42
34
  <key>LSUIElement</key>
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
- import { execSync, spawn } from "node:child_process";
3
+ import { execFileSync, execSync, spawn } from "node:child_process";
4
4
  import { existsSync, mkdirSync, chmodSync, createWriteStream, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
5
5
  import { tmpdir } from "node:os";
6
6
  import { join, resolve } from "node:path";
@@ -23,6 +23,8 @@ const RELEASE_APP_ASSET_NAMES = ["Lattices.dmg"];
23
23
  const RELEASE_BINARY_ASSET_NAMES = ["Lattices-macos-arm64", "LatticeApp-macos-arm64"];
24
24
  type ReleaseAsset = { name: string; browser_download_url: string };
25
25
  const selfScriptPath = resolve(__dirname, "lattices-app.ts");
26
+ const CAPS_LOCK_HID_USAGE = 0x700000039;
27
+ const F18_HID_USAGE = 0x70000006D;
26
28
 
27
29
  // ── Helpers ──────────────────────────────────────────────────────────
28
30
 
@@ -35,21 +37,112 @@ function isRunning(): boolean {
35
37
  }
36
38
  }
37
39
 
38
- function quit(): boolean {
40
+ function sleep(ms: number): void {
41
+ execFileSync("/bin/sleep", [(ms / 1000).toString()], { stdio: "ignore" });
42
+ }
43
+
44
+ function waitForExit(timeoutMs: number): boolean {
45
+ const deadline = Date.now() + timeoutMs;
46
+ while (Date.now() < deadline) {
47
+ if (!isRunning()) return true;
48
+ sleep(100);
49
+ }
50
+ return !isRunning();
51
+ }
52
+
53
+ type HIDKeyboardMapping = { src: number; dst: number };
54
+
55
+ function hasOwnedCapsLockTransportMapping(): boolean {
39
56
  try {
40
- execSync("pkill -x Lattices", { stdio: "pipe" });
41
- // Wait briefly for process to exit
42
- try { execSync("sleep 0.5", { stdio: "pipe" }); } catch {}
43
- // Force kill if still running
44
- if (isRunning()) {
45
- execSync("pkill -9 -x Lattices", { stdio: "pipe" });
46
- }
47
- return true;
57
+ return execFileSync(
58
+ "/usr/bin/defaults",
59
+ ["read", "dev.lattices.app", "keyboardRemaps.capsLockHIDTransportOwned"],
60
+ { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
61
+ ).trim() === "1";
48
62
  } catch {
49
63
  return false;
50
64
  }
51
65
  }
52
66
 
67
+ function readHIDKeyboardMappings(): HIDKeyboardMapping[] {
68
+ try {
69
+ const output = execFileSync(
70
+ "/usr/bin/hidutil",
71
+ ["property", "--get", "UserKeyMapping"],
72
+ { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
73
+ );
74
+ return Array.from(output.matchAll(/HIDKeyboardModifierMappingDst\s*=\s*(\d+);\s*HIDKeyboardModifierMappingSrc\s*=\s*(\d+);/g))
75
+ .map((match) => ({ dst: Number(match[1]), src: Number(match[2]) }));
76
+ } catch {
77
+ return [];
78
+ }
79
+ }
80
+
81
+ function writeHIDKeyboardMappings(mappings: HIDKeyboardMapping[]): void {
82
+ const pairs = mappings.map((mapping) => ({
83
+ HIDKeyboardModifierMappingSrc: mapping.src,
84
+ HIDKeyboardModifierMappingDst: mapping.dst,
85
+ }));
86
+ execFileSync(
87
+ "/usr/bin/hidutil",
88
+ ["property", "--set", JSON.stringify({ UserKeyMapping: pairs })],
89
+ { stdio: "ignore" }
90
+ );
91
+ }
92
+
93
+ function clearOwnedCapsLockTransportMapping(): void {
94
+ if (!hasOwnedCapsLockTransportMapping()) return;
95
+
96
+ const mappings = readHIDKeyboardMappings();
97
+ const filtered = mappings.filter((mapping) => (
98
+ mapping.src !== CAPS_LOCK_HID_USAGE || mapping.dst !== F18_HID_USAGE
99
+ ));
100
+
101
+ if (filtered.length !== mappings.length) {
102
+ try {
103
+ writeHIDKeyboardMappings(filtered);
104
+ } catch {
105
+ console.log("Warning: failed to clear Caps Lock HID transport mapping.");
106
+ return;
107
+ }
108
+ }
109
+
110
+ try {
111
+ execFileSync("/usr/bin/defaults", ["delete", "dev.lattices.app", "keyboardRemaps.capsLockHIDTransportOwned"], { stdio: "ignore" });
112
+ } catch {}
113
+ try {
114
+ execFileSync("/usr/bin/defaults", ["delete", "dev.lattices.app", "keyboardRemaps.capsLockHIDTransportOriginalMappings"], { stdio: "ignore" });
115
+ } catch {}
116
+ }
117
+
118
+ function quit(): boolean {
119
+ if (!isRunning()) {
120
+ clearOwnedCapsLockTransportMapping();
121
+ return false;
122
+ }
123
+
124
+ try {
125
+ execFileSync(
126
+ "/usr/bin/osascript",
127
+ ["-e", 'tell application id "dev.lattices.app" to quit'],
128
+ { stdio: "ignore" }
129
+ );
130
+ } catch {}
131
+
132
+ if (!waitForExit(2_000)) {
133
+ try { execSync("pkill -x Lattices", { stdio: "pipe" }); } catch {}
134
+ waitForExit(1_000);
135
+ }
136
+
137
+ if (isRunning()) {
138
+ try { execSync("pkill -9 -x Lattices", { stdio: "pipe" }); } catch {}
139
+ waitForExit(500);
140
+ }
141
+
142
+ clearOwnedCapsLockTransportMapping();
143
+ return !isRunning();
144
+ }
145
+
53
146
  function hasSwift(): boolean {
54
147
  try {
55
148
  execSync("which swift", { stdio: "pipe" });
@@ -112,8 +205,8 @@ function relaunchIfNeeded(shouldLaunch: boolean, extraArgs: string[] = []): void
112
205
  function resolveSigningIdentity(): string | null {
113
206
  try {
114
207
  const identities = execSync("security find-identity -v -p codesigning", { stdio: "pipe" }).toString();
115
- return identities.match(/"(Developer ID Application:[^"]+)"/)?.[1]
116
- || identities.match(/"(Apple Development:[^"]+)"/)?.[1]
208
+ return identities.match(/^\s*\d+\)\s+([A-F0-9]{40})\s+"Developer ID Application:[^"]+"/m)?.[1]
209
+ || identities.match(/^\s*\d+\)\s+([A-F0-9]{40})\s+"Apple Development:[^"]+"/m)?.[1]
117
210
  || null;
118
211
  } catch {
119
212
  return null;
@@ -133,7 +226,7 @@ function signBundle(): void {
133
226
  console.log(`Signing with: ${identity}`);
134
227
  try {
135
228
  execSync(
136
- `codesign --force --sign '${identity}'${entFlag} --identifier com.arach.lattices '${bundlePath}'`,
229
+ `codesign --force --options runtime --deep --sign '${identity}'${entFlag} --identifier dev.lattices.app '${bundlePath}'`,
137
230
  { stdio: "pipe" }
138
231
  );
139
232
  return;
@@ -145,7 +238,7 @@ function signBundle(): void {
145
238
  }
146
239
 
147
240
  execSync(
148
- `codesign --force --sign -${entFlag} --identifier com.arach.lattices '${bundlePath}'`,
241
+ `codesign --force --options runtime --deep --sign -${entFlag} --identifier dev.lattices.app '${bundlePath}'`,
149
242
  { stdio: "pipe" }
150
243
  );
151
244
 
@@ -188,7 +281,7 @@ function writeInfoPlist(metadata: BundleBuildMetadata = {}): void {
188
281
  <plist version="1.0">
189
282
  <dict>
190
283
  <key>CFBundleIdentifier</key>
191
- <string>com.arach.lattices</string>
284
+ <string>dev.lattices.app</string>
192
285
  <key>CFBundleName</key>
193
286
  <string>Lattices</string>
194
287
  <key>CFBundleDisplayName</key>
@@ -203,7 +296,7 @@ function writeInfoPlist(metadata: BundleBuildMetadata = {}): void {
203
296
  <array>
204
297
  <dict>
205
298
  <key>CFBundleURLName</key>
206
- <string>com.arach.lattices</string>
299
+ <string>dev.lattices.app</string>
207
300
  <key>CFBundleURLSchemes</key>
208
301
  <array>
209
302
  <string>lattices</string>
@@ -256,7 +349,7 @@ function buildFromSource(): boolean {
256
349
 
257
350
  mkdirSync(binaryDir, { recursive: true });
258
351
  execSync(`cp '${builtPath}' '${binaryPath}'`);
259
- writeInfoPlist({ channel: "dev", track: "latest" });
352
+ writeInfoPlist();
260
353
  syncBundleResources();
261
354
 
262
355
  // Re-sign the bundle so macOS TCC recognizes a stable identity across rebuilds.
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env bash
2
+ # lattices-build - explicit dev/package/dist build entry points
3
+
4
+ set -euo pipefail
5
+
6
+ SCRIPT_PATH="$(readlink -f "$0" 2>/dev/null || python3 -c "import os,sys; print(os.path.realpath(sys.argv[1]))" "$0")"
7
+ ROOT="$(cd "$(dirname "$SCRIPT_PATH")/.." && pwd)"
8
+
9
+ DEV_APPS_DIR="${LATTICES_DEV_APPS_DIR:-$HOME/Applications/dev/Lattices}"
10
+ DEV_BUNDLE="${LATTICES_DEV_APP:-$DEV_APPS_DIR/Lattices.app}"
11
+ DEV_BUNDLE_ID="${LATTICES_DEV_BUNDLE_ID:-dev.lattices.app.dev}"
12
+ RELEASE_BUNDLE="$ROOT/apps/mac/Lattices.app"
13
+ RELEASE_BUNDLE_ID="dev.lattices.app"
14
+ DIST_BUNDLE="$ROOT/dist/Lattices.app"
15
+ DIST_DMG="$ROOT/dist/Lattices.dmg"
16
+ PROD_INSTALL_BUNDLE="/Applications/Lattices.app"
17
+
18
+ green() { printf "\033[32m%s\033[0m\n" "$*"; }
19
+ dim() { printf "\033[2m%s\033[0m\n" "$*"; }
20
+ red() { printf "\033[31m%s\033[0m\n" "$*"; }
21
+
22
+ usage() {
23
+ cat <<EOF
24
+ lattices-build - build Lattices without mixing dev and release identities
25
+
26
+ Usage:
27
+ bin/lattices-build dev Build/install dev app
28
+ bin/lattices-build dev:restart Build/install/relaunch dev app
29
+ bin/lattices-build package Build repo app bundle for npm packaging
30
+ bin/lattices-build dist Build signed/notarized release DMG
31
+ bin/lattices-build dist:local Build local release DMG without notarizing
32
+ bin/lattices-build where Show canonical paths and bundle ids
33
+
34
+ Channels:
35
+ dev $DEV_BUNDLE_ID -> $DEV_BUNDLE
36
+ release $RELEASE_BUNDLE_ID -> $PROD_INSTALL_BUNDLE via $DIST_DMG
37
+ EOF
38
+ }
39
+
40
+ plist_value() {
41
+ local app="$1"
42
+ local key="$2"
43
+ [ -d "$app" ] || return 0
44
+ /usr/bin/defaults read "$app/Contents/Info.plist" "$key" 2>/dev/null || true
45
+ }
46
+
47
+ show_app() {
48
+ local label="$1"
49
+ local app="$2"
50
+
51
+ echo "$label"
52
+ echo " path: $app"
53
+ if [ -d "$app" ]; then
54
+ echo " bundle id: $(plist_value "$app" CFBundleIdentifier)"
55
+ echo " channel: $(plist_value "$app" LatticesBuildChannel)"
56
+ echo " built at: $(plist_value "$app" LatticesBuildTimestamp)"
57
+ else
58
+ echo " status: not built"
59
+ fi
60
+ }
61
+
62
+ cmd_dev() {
63
+ "$ROOT/bin/lattices-dev" build
64
+ show_app "Dev app" "$DEV_BUNDLE"
65
+ green "Dev permission target: $DEV_BUNDLE"
66
+ }
67
+
68
+ cmd_dev_restart() {
69
+ "$ROOT/bin/lattices-dev" restart
70
+ show_app "Running dev app" "$DEV_BUNDLE"
71
+ }
72
+
73
+ cmd_package() {
74
+ if ! command -v bun >/dev/null 2>&1; then
75
+ red "bun is required for package builds."
76
+ exit 1
77
+ fi
78
+
79
+ bun "$ROOT/bin/lattices-app.ts" build
80
+ show_app "Package app bundle" "$RELEASE_BUNDLE"
81
+ green "Package bundle ready: $RELEASE_BUNDLE"
82
+ }
83
+
84
+ cmd_dist() {
85
+ "$ROOT/tools/release/build-dmg.sh" "$@"
86
+ show_app "Release app artifact" "$DIST_BUNDLE"
87
+ [ -f "$DIST_DMG" ] && ls -lh "$DIST_DMG"
88
+ }
89
+
90
+ cmd_dist_local() {
91
+ LATTICES_SKIP_NOTARIZE=1 "$ROOT/tools/release/build-dmg.sh" "$@"
92
+ show_app "Local release app artifact" "$DIST_BUNDLE"
93
+ [ -f "$DIST_DMG" ] && ls -lh "$DIST_DMG"
94
+ }
95
+
96
+ cmd_where() {
97
+ show_app "Dev app" "$DEV_BUNDLE"
98
+ echo " expected bundle id: $DEV_BUNDLE_ID"
99
+ echo
100
+ show_app "Package app bundle" "$RELEASE_BUNDLE"
101
+ echo " expected bundle id: $RELEASE_BUNDLE_ID"
102
+ echo
103
+ show_app "Release app artifact" "$DIST_BUNDLE"
104
+ echo " expected install target: $PROD_INSTALL_BUNDLE"
105
+ [ -f "$DIST_DMG" ] && echo " dmg: $DIST_DMG"
106
+ }
107
+
108
+ cmd="${1:-help}"
109
+ shift || true
110
+
111
+ case "$cmd" in
112
+ dev) cmd_dev ;;
113
+ dev:restart|restart) cmd_dev_restart ;;
114
+ package) cmd_package ;;
115
+ dist) cmd_dist "$@" ;;
116
+ dist:local) cmd_dist_local "$@" ;;
117
+ where) cmd_where ;;
118
+ help|-h|--help) usage ;;
119
+ *)
120
+ red "Unknown command: $cmd"
121
+ echo
122
+ usage
123
+ exit 1
124
+ ;;
125
+ esac
package/bin/lattices-dev CHANGED
@@ -8,10 +8,14 @@ APP_DIR="$(cd "$(dirname "$SCRIPT_PATH")/../apps/mac" && pwd)"
8
8
  ROOT="$(cd "$(dirname "$SCRIPT_PATH")/.." && pwd)"
9
9
  LOG_FILE="$HOME/.lattices/lattices.log"
10
10
  BINARY="$APP_DIR/.build/release/Lattices"
11
- BUNDLE="$APP_DIR/Lattices.app"
11
+ BUILD_BASE="$ROOT/build/macos/Lattices"
12
+ BUNDLE="$BUILD_BASE/Build/Products/Debug/Lattices.app"
13
+ DEV_APPS_DIR="${LATTICES_DEV_APPS_DIR:-$HOME/Applications/dev/Lattices}"
14
+ INSTALL_BUNDLE="${LATTICES_DEV_APP:-$DEV_APPS_DIR/Lattices.app}"
12
15
  BUNDLE_BIN="$BUNDLE/Contents/MacOS/Lattices"
13
16
  RESOURCES_DIR="$BUNDLE/Contents/Resources"
14
17
  ENTITLEMENTS="$APP_DIR/Lattices.entitlements"
18
+ BUNDLE_ID="${LATTICES_DEV_BUNDLE_ID:-dev.lattices.app.dev}"
15
19
  ICON="$ROOT/assets/AppIcon.icns"
16
20
  TAP_SOUND="$APP_DIR/Resources/tap.wav"
17
21
  VERSION="$(node -p "require('$ROOT/package.json').version" 2>/dev/null || echo '0.1.0')"
@@ -25,9 +29,14 @@ dim() { printf "\033[2m%s\033[0m\n" "$*"; }
25
29
  select_sign_identity() {
26
30
  local identities identity=""
27
31
  identities="$(security find-identity -v -p codesigning 2>/dev/null || true)"
28
- identity="$(printf '%s\n' "$identities" | sed -n 's/.*"\(Developer ID Application:[^"]*\)".*/\1/p' | head -n 1)"
32
+ # Prefer Developer ID Application: its designated requirement pins to the stable
33
+ # team OU, so TCC permissions survive rebuilds and Apple Development cert rotations.
34
+ # Apple Development is only a fallback (its DR pins a specific cert that Xcode rotates,
35
+ # which silently resets every granted permission). Keep this order in sync with
36
+ # resolveSigningIdentity() in bin/lattices-app.ts.
37
+ identity="$(printf '%s\n' "$identities" | sed -n 's/^[[:space:]]*[0-9]*)[[:space:]]*\([A-F0-9]\{40\}\)[[:space:]]*"Developer ID Application:[^"]*".*/\1/p' | head -n 1)"
29
38
  if [ -z "$identity" ]; then
30
- identity="$(printf '%s\n' "$identities" | sed -n 's/.*"\(Apple Development:[^"]*\)".*/\1/p' | head -n 1)"
39
+ identity="$(printf '%s\n' "$identities" | sed -n 's/^[[:space:]]*[0-9]*)[[:space:]]*\([A-F0-9]\{40\}\)[[:space:]]*"Apple Development:[^"]*".*/\1/p' | head -n 1)"
31
40
  fi
32
41
  printf '%s' "$identity"
33
42
  }
@@ -45,7 +54,7 @@ sign_bundle() {
45
54
  identity="$(select_sign_identity)"
46
55
  if [ -n "$identity" ]; then
47
56
  dim "Signing with: $identity"
48
- if ! codesign --force --sign "$identity" "${ent_flags[@]}" --identifier com.arach.lattices "$BUNDLE"; then
57
+ if ! codesign --force --options runtime --deep --sign "$identity" "${ent_flags[@]}" --identifier "$BUNDLE_ID" "$BUNDLE"; then
49
58
  red "Signing with '$identity' failed. Falling back to ad-hoc."
50
59
  sign_status=1
51
60
  fi
@@ -55,12 +64,62 @@ sign_bundle() {
55
64
 
56
65
  if [ "$sign_status" -ne 0 ]; then
57
66
  dim "No usable signing identity found. Using ad-hoc signature."
58
- codesign --force --sign - "${ent_flags[@]}" --identifier com.arach.lattices "$BUNDLE"
67
+ codesign --force --options runtime --deep --sign - "${ent_flags[@]}" --identifier "$BUNDLE_ID" "$BUNDLE"
59
68
  fi
60
69
 
61
70
  rm -f "$BUNDLE_BIN".cstemp
62
71
  }
63
72
 
73
+ sync_bundle_copy() {
74
+ local destination="$1"
75
+
76
+ mkdir -p "$(dirname "$destination")"
77
+ rm -rf "$destination"
78
+ ditto "$BUNDLE" "$destination"
79
+ xattr -dr com.apple.quarantine "$destination" 2>/dev/null || true
80
+ touch "$destination" "$destination/Contents"
81
+ }
82
+
83
+ ensure_install_bundle() {
84
+ if [ -d "$INSTALL_BUNDLE" ]; then
85
+ return
86
+ fi
87
+
88
+ if [ -d "$BUNDLE" ]; then
89
+ sync_bundle_copy "$INSTALL_BUNDLE"
90
+ return
91
+ fi
92
+
93
+ cmd_build
94
+ }
95
+
96
+ clear_lattices_drag_cache() {
97
+ local cache_dir
98
+
99
+ for cache_dir in "$HOME"/Library/Caches/com.apple.SwiftUI.Drag-*; do
100
+ [ -d "$cache_dir" ] || continue
101
+ if [ -d "$cache_dir/Lattices.app" ]; then
102
+ rm -rf "$cache_dir/Lattices.app"
103
+ rmdir "$cache_dir" 2>/dev/null || true
104
+ fi
105
+ done
106
+ }
107
+
108
+ is_install_bundle_running() {
109
+ local target="$INSTALL_BUNDLE/Contents/MacOS/Lattices"
110
+ local pid args
111
+
112
+ while IFS= read -r pid; do
113
+ [ -n "$pid" ] || continue
114
+ args="$(ps -p "$pid" -o args= 2>/dev/null || true)"
115
+ case "$args" in
116
+ "$target"|"${target} "*) return 0 ;;
117
+ esac
118
+ done < <(pgrep -x Lattices 2>/dev/null || true)
119
+
120
+ return 1
121
+ }
122
+
64
123
  write_info_plist() {
65
124
  mkdir -p "$BUNDLE/Contents"
66
125
  cat > "$BUNDLE/Contents/Info.plist" <<PLIST
@@ -69,7 +128,7 @@ write_info_plist() {
69
128
  <plist version="1.0">
70
129
  <dict>
71
130
  <key>CFBundleIdentifier</key>
72
- <string>com.arach.lattices</string>
131
+ <string>$BUNDLE_ID</string>
73
132
  <key>CFBundleName</key>
74
133
  <string>Lattices</string>
75
134
  <key>CFBundleDisplayName</key>
@@ -84,7 +143,7 @@ write_info_plist() {
84
143
  <array>
85
144
  <dict>
86
145
  <key>CFBundleURLName</key>
87
- <string>com.arach.lattices</string>
146
+ <string>$BUNDLE_ID</string>
88
147
  <key>CFBundleURLSchemes</key>
89
148
  <array>
90
149
  <string>lattices</string>
@@ -117,10 +176,11 @@ PLIST
117
176
  }
118
177
 
119
178
  cmd_build() {
120
- echo "Building release..."
179
+ echo "Building dev app..."
121
180
  cd "$APP_DIR" && swift build -c release
122
- # Refresh the bundle so dev builds and published bundles are complete.
123
- rm -rf "$BUNDLE/Contents/MacOS" "$RESOURCES_DIR"
181
+ # Build into a repo-local dev artifact, then install to a stable app path
182
+ # that macOS permissions can trust across rebuilds.
183
+ rm -rf "$BUNDLE"
124
184
  mkdir -p "$(dirname "$BUNDLE_BIN")" "$RESOURCES_DIR"
125
185
  cp "$BINARY" "$BUNDLE_BIN"
126
186
  if [ -f "$ICON" ]; then
@@ -132,18 +192,26 @@ cmd_build() {
132
192
  write_info_plist
133
193
  # Re-sign so TCC permissions persist across rebuilds
134
194
  sign_bundle
195
+ touch "$BUNDLE" "$BUNDLE/Contents"
196
+ sync_bundle_copy "$INSTALL_BUNDLE"
197
+ clear_lattices_drag_cache
135
198
  green "Build complete."
199
+ dim "Installed: $INSTALL_BUNDLE"
136
200
  }
137
201
 
138
202
  cmd_restart() {
139
203
  echo "Restarting Lattices..."
204
+ /usr/bin/osascript -e 'tell application id "dev.lattices.app.dev" to quit' >/dev/null 2>&1 || true
205
+ /usr/bin/osascript -e 'tell application id "dev.lattices.app" to quit' >/dev/null 2>&1 || true
140
206
  pkill -x Lattices 2>/dev/null && sleep 1 || true
141
207
  cmd_build
142
- open "$BUNDLE"
208
+ open "$INSTALL_BUNDLE" --args --lattices-cli-root "$ROOT" "$@"
143
209
  green "Lattices restarted."
144
210
  }
145
211
 
146
212
  cmd_quit() {
213
+ /usr/bin/osascript -e 'tell application id "dev.lattices.app.dev" to quit' >/dev/null 2>&1 || true
214
+ /usr/bin/osascript -e 'tell application id "dev.lattices.app" to quit' >/dev/null 2>&1 || true
147
215
  if pkill -x Lattices 2>/dev/null; then
148
216
  green "Lattices stopped."
149
217
  else
@@ -152,10 +220,15 @@ cmd_quit() {
152
220
  }
153
221
 
154
222
  cmd_launch() {
155
- if pgrep -x Lattices >/dev/null 2>&1; then
156
- dim "Lattices is already running."
223
+ if is_install_bundle_running; then
224
+ dim "Lattices dev app is already running."
157
225
  else
158
- open "$BUNDLE"
226
+ if pgrep -x Lattices >/dev/null 2>&1; then
227
+ dim "A different Lattices bundle is running. Switching to the dev permission target."
228
+ cmd_quit
229
+ fi
230
+ ensure_install_bundle
231
+ open "$INSTALL_BUNDLE" --args --lattices-cli-root "$ROOT" "$@"
159
232
  green "Lattices launched."
160
233
  fi
161
234
  }
@@ -249,10 +322,10 @@ cmd_help() {
249
322
  case "${1:-help}" in
250
323
  link) cmd_link ;;
251
324
  unlink) cmd_unlink ;;
252
- restart) cmd_restart ;;
325
+ restart) cmd_restart "${@:2}" ;;
253
326
  build) cmd_build ;;
254
327
  quit|stop) cmd_quit ;;
255
- launch|start) cmd_launch ;;
328
+ launch|start) cmd_launch "${@:2}" ;;
256
329
  logs) cmd_logs ;;
257
330
  log) cmd_log "${2:-30}" ;;
258
331
  clear-logs) cmd_clear_logs ;;