khal-os 1.260324.2

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 (408) hide show
  1. package/.env.example +23 -0
  2. package/.genie/mailbox/cli-sent.jsonl +3 -0
  3. package/.genie/mailbox/ds1-wave2-engineer-1.json +15 -0
  4. package/.genie/mailbox/ds1-wave2-engineer-2.json +15 -0
  5. package/.genie/mailbox/ds1-wave2-engineer-3.json +15 -0
  6. package/.genie/state/os-observability.json +39 -0
  7. package/.genie/state/tmux-control-mode-terminal.json +28 -0
  8. package/.genie/wishes/genieos-one-theme/WISH.md +417 -0
  9. package/.genie/wishes/workos-prod-rbac/WISH.md +345 -0
  10. package/.github/workflows/ci.yml +39 -0
  11. package/.github/workflows/release.yml +78 -0
  12. package/.github/workflows/version.yml +122 -0
  13. package/.husky/pre-commit +1 -0
  14. package/.pnpm-approve-builds.json +1 -0
  15. package/CLAUDE.md +117 -0
  16. package/LICENSE +21 -0
  17. package/README.md +38 -0
  18. package/biome.json +124 -0
  19. package/bun.lock +1249 -0
  20. package/docs/workos-setup.md +116 -0
  21. package/ecosystem.config.cjs +26 -0
  22. package/instrumentation.ts +8 -0
  23. package/knip.json +35 -0
  24. package/nats.conf +7 -0
  25. package/next.config.ts +25 -0
  26. package/package.json +78 -0
  27. package/packages/dev3000-app/components.ts +12 -0
  28. package/packages/dev3000-app/manifest.ts +19 -0
  29. package/packages/dev3000-app/package.json +23 -0
  30. package/packages/dev3000-app/views/dev3000/Dev3000App.tsx +758 -0
  31. package/packages/dev3000-app/views/dev3000/ErrorsPanel.tsx +160 -0
  32. package/packages/dev3000-app/views/dev3000/dev3000-context.tsx +21 -0
  33. package/packages/dev3000-app/views/dev3000/index.ts +4 -0
  34. package/packages/dev3000-app/views/dev3000/schema.ts +55 -0
  35. package/packages/dev3000-app/views/dev3000/service/index.ts +358 -0
  36. package/packages/dev3000-app/views/dev3000/service/runtime +1 -0
  37. package/packages/dev3000-app/views/dev3000/subjects.ts +9 -0
  38. package/packages/dev3000-app/views/dev3000/types.ts +77 -0
  39. package/packages/files-app/components.ts +12 -0
  40. package/packages/files-app/manifest.ts +19 -0
  41. package/packages/files-app/package.json +23 -0
  42. package/packages/files-app/views/files/ContextMenu.tsx +151 -0
  43. package/packages/files-app/views/files/DeleteConfirmDialog.tsx +39 -0
  44. package/packages/files-app/views/files/FileItem.tsx +128 -0
  45. package/packages/files-app/views/files/FilesApp.tsx +509 -0
  46. package/packages/files-app/views/files/FilesListView.tsx +201 -0
  47. package/packages/files-app/views/files/FilesToolbar.tsx +117 -0
  48. package/packages/files-app/views/files/GridView.tsx +90 -0
  49. package/packages/files-app/views/files/InlineInput.tsx +131 -0
  50. package/packages/files-app/views/files/UploadOverlay.tsx +61 -0
  51. package/packages/files-app/views/files/schema.ts +49 -0
  52. package/packages/files-app/views/files/service/index.ts +184 -0
  53. package/packages/files-app/views/files/service/runtime +1 -0
  54. package/packages/files-app/views/files/use-files.ts +201 -0
  55. package/packages/files-app/views/files/use-upload.ts +105 -0
  56. package/packages/genie-app/components.ts +12 -0
  57. package/packages/genie-app/lib/subjects.ts +87 -0
  58. package/packages/genie-app/manifest.ts +19 -0
  59. package/packages/genie-app/package.json +29 -0
  60. package/packages/genie-app/views/genie/service/agent-lifecycle.ts +136 -0
  61. package/packages/genie-app/views/genie/service/cli.ts +114 -0
  62. package/packages/genie-app/views/genie/service/comms.ts +141 -0
  63. package/packages/genie-app/views/genie/service/directory.ts +167 -0
  64. package/packages/genie-app/views/genie/service/index.ts +219 -0
  65. package/packages/genie-app/views/genie/service/system.ts +123 -0
  66. package/packages/genie-app/views/genie/service/teams.ts +191 -0
  67. package/packages/genie-app/views/genie/service/terminal-proxy.ts +184 -0
  68. package/packages/genie-app/views/genie/service/tmux-control.ts +318 -0
  69. package/packages/genie-app/views/genie/service/wishes.ts +270 -0
  70. package/packages/genie-app/views/genie/ui/GenieApp.tsx +5 -0
  71. package/packages/genie-app/views/genie/ui/PaneCard.tsx +307 -0
  72. package/packages/genie-app/views/genie/ui/Sidebar.tsx +212 -0
  73. package/packages/genie-app/views/genie/ui/TabBar.tsx +70 -0
  74. package/packages/genie-app/views/genie/ui/WorkspaceCanvas.tsx +343 -0
  75. package/packages/genie-app/views/genie/ui/XTermPane.tsx +306 -0
  76. package/packages/genie-app/views/genie/ui/hooks/useNatsAction.ts +54 -0
  77. package/packages/genie-app/views/genie/ui/hooks/useNatsRequest.ts +68 -0
  78. package/packages/genie-app/views/genie/ui/panels/AgentsPanel.tsx +399 -0
  79. package/packages/genie-app/views/genie/ui/panels/ChatPanel.tsx +351 -0
  80. package/packages/genie-app/views/genie/ui/panels/SystemPanel.tsx +195 -0
  81. package/packages/genie-app/views/genie/ui/panels/TeamsPanel.tsx +560 -0
  82. package/packages/genie-app/views/genie/ui/panels/WishesPanel.tsx +424 -0
  83. package/packages/nats-viewer-app/components.ts +12 -0
  84. package/packages/nats-viewer-app/manifest.ts +18 -0
  85. package/packages/nats-viewer-app/package.json +14 -0
  86. package/packages/nats-viewer-app/views/nats-viewer/ActiveSubs.tsx +34 -0
  87. package/packages/nats-viewer-app/views/nats-viewer/MessageLog.tsx +247 -0
  88. package/packages/nats-viewer-app/views/nats-viewer/NatsViewer.tsx +209 -0
  89. package/packages/nats-viewer-app/views/nats-viewer/PublishPanel.tsx +111 -0
  90. package/packages/nats-viewer-app/views/nats-viewer/RequestPanel.tsx +165 -0
  91. package/packages/nats-viewer-app/views/nats-viewer/Sidebar.tsx +59 -0
  92. package/packages/nats-viewer-app/views/nats-viewer/SubjectCatalog.tsx +63 -0
  93. package/packages/nats-viewer-app/views/nats-viewer/SubscribeInput.tsx +59 -0
  94. package/packages/nats-viewer-app/views/nats-viewer/index.ts +5 -0
  95. package/packages/nats-viewer-app/views/nats-viewer/nats-viewer-context.tsx +31 -0
  96. package/packages/nats-viewer-app/views/nats-viewer/types.ts +7 -0
  97. package/packages/nats-viewer-app/views/nats-viewer/use-message-buffer.ts +55 -0
  98. package/packages/os-cli/package.json +18 -0
  99. package/packages/os-cli/src/commands/events.ts +176 -0
  100. package/packages/os-cli/src/commands/logs.ts +96 -0
  101. package/packages/os-cli/src/commands/status.ts +53 -0
  102. package/packages/os-cli/src/commands/traces.ts +115 -0
  103. package/packages/os-cli/src/index.ts +15 -0
  104. package/packages/os-cli/src/lib/formatter.ts +123 -0
  105. package/packages/os-cli/src/lib/nats.ts +16 -0
  106. package/packages/os-cli/src/lib/trace-tree.ts +144 -0
  107. package/packages/os-cli/tsconfig.json +12 -0
  108. package/packages/os-sdk/package.json +27 -0
  109. package/packages/os-sdk/src/api/handler.ts +67 -0
  110. package/packages/os-sdk/src/config.ts +68 -0
  111. package/packages/os-sdk/src/db/factory.test.ts +42 -0
  112. package/packages/os-sdk/src/db/factory.ts +72 -0
  113. package/packages/os-sdk/src/db/migrate.ts +140 -0
  114. package/packages/os-sdk/src/db/provision.ts +44 -0
  115. package/packages/os-sdk/src/index.ts +36 -0
  116. package/packages/os-sdk/src/service/console-intercept.ts +60 -0
  117. package/packages/os-sdk/src/service/logger.ts +88 -0
  118. package/packages/os-sdk/src/service/o11y-streams.ts +88 -0
  119. package/packages/os-sdk/src/service/runtime.ts +259 -0
  120. package/packages/os-sdk/src/service/trace.ts +71 -0
  121. package/packages/os-sdk/tsconfig.json +16 -0
  122. package/packages/os-ui/package.json +13 -0
  123. package/packages/os-ui/src/index.ts +29 -0
  124. package/packages/os-ui/src/server.ts +4 -0
  125. package/packages/os-ui/tsconfig.json +19 -0
  126. package/packages/settings-app/components.ts +12 -0
  127. package/packages/settings-app/manifest.ts +18 -0
  128. package/packages/settings-app/package.json +14 -0
  129. package/packages/settings-app/views/settings/Settings.tsx +492 -0
  130. package/packages/terminal-app/components.ts +12 -0
  131. package/packages/terminal-app/manifest.ts +20 -0
  132. package/packages/terminal-app/package.json +23 -0
  133. package/packages/terminal-app/views/terminal/schema.ts +82 -0
  134. package/packages/terminal-app/views/terminal/service/index.ts +133 -0
  135. package/packages/terminal-app/views/terminal/service/runtime +1 -0
  136. package/packages/terminal-app/views/terminal/service/session.ts +290 -0
  137. package/packages/terminal-app/views/terminal/service/shell-hooks/bashrc-hook.sh +21 -0
  138. package/packages/terminal-app/views/terminal/types.ts +26 -0
  139. package/packages/terminal-app/views/terminal/ui/MultiTerminalApp.tsx +615 -0
  140. package/packages/terminal-app/views/terminal/ui/SplitDragHandle.tsx +91 -0
  141. package/packages/terminal-app/views/terminal/ui/SplitPaneRenderer.tsx +112 -0
  142. package/packages/terminal-app/views/terminal/ui/TerminalPane.tsx +478 -0
  143. package/packages/terminal-app/views/terminal/ui/TerminalTabBar.tsx +131 -0
  144. package/pnpm-workspace.yaml +9 -0
  145. package/postcss.config.mjs +7 -0
  146. package/public/file.svg +1 -0
  147. package/public/globe.svg +1 -0
  148. package/public/icons/code-server.svg +6 -0
  149. package/public/icons/default.svg +5 -0
  150. package/public/icons/dusk/1password.svg +1 -0
  151. package/public/icons/dusk/activity_monitor.svg +1 -0
  152. package/public/icons/dusk/app_store.svg +1 -0
  153. package/public/icons/dusk/atom.svg +1 -0
  154. package/public/icons/dusk/brave.svg +1 -0
  155. package/public/icons/dusk/calculator.svg +1 -0
  156. package/public/icons/dusk/calendar.svg +1 -0
  157. package/public/icons/dusk/chrome.svg +1 -0
  158. package/public/icons/dusk/chrome2.svg +1 -0
  159. package/public/icons/dusk/dashboard.svg +13 -0
  160. package/public/icons/dusk/discord.svg +1 -0
  161. package/public/icons/dusk/dropbox.svg +1 -0
  162. package/public/icons/dusk/electron.svg +1 -0
  163. package/public/icons/dusk/figma.svg +1 -0
  164. package/public/icons/dusk/finder.svg +1 -0
  165. package/public/icons/dusk/finder2.svg +1 -0
  166. package/public/icons/dusk/finder3.svg +1 -0
  167. package/public/icons/dusk/firefox.svg +1 -0
  168. package/public/icons/dusk/framer.svg +1 -0
  169. package/public/icons/dusk/gimp.svg +1 -0
  170. package/public/icons/dusk/github_desktop.svg +1 -0
  171. package/public/icons/dusk/hyper.svg +1 -0
  172. package/public/icons/dusk/hyper3.svg +1 -0
  173. package/public/icons/dusk/intellij.svg +1 -0
  174. package/public/icons/dusk/iterm2.svg +1 -0
  175. package/public/icons/dusk/itunes.svg +1 -0
  176. package/public/icons/dusk/mail.svg +1 -0
  177. package/public/icons/dusk/messenger.svg +1 -0
  178. package/public/icons/dusk/mongodb.svg +1 -0
  179. package/public/icons/dusk/notes.svg +1 -0
  180. package/public/icons/dusk/notion.svg +1 -0
  181. package/public/icons/dusk/obs.svg +1 -0
  182. package/public/icons/dusk/pages.svg +1 -0
  183. package/public/icons/dusk/photos.svg +1 -0
  184. package/public/icons/dusk/postman.svg +1 -0
  185. package/public/icons/dusk/preview.svg +1 -0
  186. package/public/icons/dusk/reminders.svg +1 -0
  187. package/public/icons/dusk/safari.svg +1 -0
  188. package/public/icons/dusk/sequel_pro.svg +1 -0
  189. package/public/icons/dusk/sketch.svg +1 -0
  190. package/public/icons/dusk/skype.svg +1 -0
  191. package/public/icons/dusk/slack.svg +1 -0
  192. package/public/icons/dusk/slack2.svg +1 -0
  193. package/public/icons/dusk/spotify.svg +1 -0
  194. package/public/icons/dusk/steam.svg +1 -0
  195. package/public/icons/dusk/system_preferences.svg +1 -0
  196. package/public/icons/dusk/tableplus.svg +1 -0
  197. package/public/icons/dusk/teams.svg +1 -0
  198. package/public/icons/dusk/telegram.svg +1 -0
  199. package/public/icons/dusk/terminal.svg +1 -0
  200. package/public/icons/dusk/todoist.svg +1 -0
  201. package/public/icons/dusk/trash.svg +1 -0
  202. package/public/icons/dusk/trello.svg +1 -0
  203. package/public/icons/dusk/vivaldi.svg +1 -0
  204. package/public/icons/dusk/vlc.svg +1 -0
  205. package/public/icons/dusk/vscode.svg +1 -0
  206. package/public/icons/dusk/whatsapp.svg +1 -0
  207. package/public/icons/dusk/xeyes.svg +1 -0
  208. package/public/icons/dusk/zoom.svg +1 -0
  209. package/public/icons/files.svg +5 -0
  210. package/public/icons/pwa/icon-192.png +0 -0
  211. package/public/icons/pwa/icon-512.png +0 -0
  212. package/public/icons/settings.svg +14 -0
  213. package/public/icons/terminal.svg +5 -0
  214. package/public/icons/text-editor.svg +7 -0
  215. package/public/manifest.json +38 -0
  216. package/public/next.svg +1 -0
  217. package/public/sw.js +41 -0
  218. package/public/vercel.svg +1 -0
  219. package/public/wallpapers/default.svg +10 -0
  220. package/public/window.svg +1 -0
  221. package/scripts/generate-pwa-icons.mjs +33 -0
  222. package/scripts/install-nats.sh +37 -0
  223. package/sentry.client.config.ts +21 -0
  224. package/sentry.edge.config.ts +12 -0
  225. package/sentry.server.config.ts +12 -0
  226. package/src/app/api/files/download/route.ts +81 -0
  227. package/src/app/api/files/download-zip/route.ts +102 -0
  228. package/src/app/api/files/upload/route.ts +58 -0
  229. package/src/app/api/webhooks/workos/route.ts +98 -0
  230. package/src/app/auth/callback/route.ts +16 -0
  231. package/src/app/auth/logout/route.ts +15 -0
  232. package/src/app/desktop/desktop-shell.tsx +110 -0
  233. package/src/app/desktop/layout.tsx +8 -0
  234. package/src/app/desktop/page.tsx +24 -0
  235. package/src/app/favicon.ico +0 -0
  236. package/src/app/globals.css +7 -0
  237. package/src/app/layout.tsx +64 -0
  238. package/src/app/offline/page.tsx +83 -0
  239. package/src/app/page.tsx +5 -0
  240. package/src/app/standalone/[appId]/page.tsx +28 -0
  241. package/src/app/standalone/layout.tsx +10 -0
  242. package/src/components/app-icon.tsx +55 -0
  243. package/src/components/apps/_echo/schema.ts +14 -0
  244. package/src/components/apps/_echo/service/index.ts +42 -0
  245. package/src/components/apps/app-manifest.ts +97 -0
  246. package/src/components/apps/app-registry.ts +55 -0
  247. package/src/components/apps/dev3000/Dev3000App.tsx +224 -0
  248. package/src/components/apps/dev3000/ErrorsPanel.tsx +160 -0
  249. package/src/components/apps/dev3000/Sidebar.tsx +41 -0
  250. package/src/components/apps/dev3000/TimelineLog.tsx +173 -0
  251. package/src/components/apps/dev3000/dev3000-context.tsx +29 -0
  252. package/src/components/apps/dev3000/index.ts +4 -0
  253. package/src/components/apps/dev3000/schema.ts +48 -0
  254. package/src/components/apps/dev3000/service/index.ts +520 -0
  255. package/src/components/apps/dev3000/service/runtime +1 -0
  256. package/src/components/apps/dev3000/types.ts +15 -0
  257. package/src/components/apps/dev3000/use-message-buffer.ts +46 -0
  258. package/src/components/apps/files/ContextMenu.tsx +151 -0
  259. package/src/components/apps/files/DeleteConfirmDialog.tsx +78 -0
  260. package/src/components/apps/files/FileItem.tsx +128 -0
  261. package/src/components/apps/files/FilesApp.tsx +509 -0
  262. package/src/components/apps/files/FilesListView.tsx +201 -0
  263. package/src/components/apps/files/FilesToolbar.tsx +117 -0
  264. package/src/components/apps/files/GridView.tsx +90 -0
  265. package/src/components/apps/files/InlineInput.tsx +131 -0
  266. package/src/components/apps/files/UploadOverlay.tsx +61 -0
  267. package/src/components/apps/files/schema.ts +49 -0
  268. package/src/components/apps/files/service/index.ts +227 -0
  269. package/src/components/apps/files/service/runtime +1 -0
  270. package/src/components/apps/files/use-files.ts +201 -0
  271. package/src/components/apps/files/use-upload.ts +105 -0
  272. package/src/components/apps/nats-viewer/ActiveSubs.tsx +34 -0
  273. package/src/components/apps/nats-viewer/MessageLog.tsx +247 -0
  274. package/src/components/apps/nats-viewer/NatsViewer.tsx +209 -0
  275. package/src/components/apps/nats-viewer/PublishPanel.tsx +113 -0
  276. package/src/components/apps/nats-viewer/RequestPanel.tsx +167 -0
  277. package/src/components/apps/nats-viewer/Sidebar.tsx +62 -0
  278. package/src/components/apps/nats-viewer/SubjectCatalog.tsx +64 -0
  279. package/src/components/apps/nats-viewer/SubscribeInput.tsx +59 -0
  280. package/src/components/apps/nats-viewer/index.ts +5 -0
  281. package/src/components/apps/nats-viewer/nats-viewer-context.tsx +31 -0
  282. package/src/components/apps/nats-viewer/types.ts +7 -0
  283. package/src/components/apps/nats-viewer/use-message-buffer.ts +55 -0
  284. package/src/components/apps/settings/Settings.tsx +492 -0
  285. package/src/components/apps/terminal/schema.ts +82 -0
  286. package/src/components/apps/terminal/service/index.ts +189 -0
  287. package/src/components/apps/terminal/service/runtime +1 -0
  288. package/src/components/apps/terminal/service/session.ts +296 -0
  289. package/src/components/apps/terminal/service/shell-hooks/bashrc-hook.sh +21 -0
  290. package/src/components/apps/terminal/types.ts +26 -0
  291. package/src/components/apps/terminal/ui/MultiTerminalApp.tsx +617 -0
  292. package/src/components/apps/terminal/ui/SplitDragHandle.tsx +91 -0
  293. package/src/components/apps/terminal/ui/SplitPaneRenderer.tsx +112 -0
  294. package/src/components/apps/terminal/ui/TerminalPane.tsx +476 -0
  295. package/src/components/apps/terminal/ui/TerminalTabBar.tsx +131 -0
  296. package/src/components/desktop/AnimatedBackground.tsx +69 -0
  297. package/src/components/desktop/Desktop.tsx +79 -0
  298. package/src/components/desktop/DesktopBackground.tsx +16 -0
  299. package/src/components/desktop/DesktopIcon.tsx +49 -0
  300. package/src/components/desktop/ShortcutViewer.tsx +136 -0
  301. package/src/components/desktop/WindowRenderer.tsx +34 -0
  302. package/src/components/desktop/WindowSwitcher.tsx +42 -0
  303. package/src/components/notifications/NotificationCenter.tsx +153 -0
  304. package/src/components/notifications/NotificationToasts.tsx +66 -0
  305. package/src/components/notifications/OrphanSessionToast.tsx +293 -0
  306. package/src/components/os-primitives/collapsible-sidebar.tsx +226 -0
  307. package/src/components/os-primitives/dialog.tsx +76 -0
  308. package/src/components/os-primitives/empty-state.tsx +43 -0
  309. package/src/components/os-primitives/index.ts +21 -0
  310. package/src/components/os-primitives/list-view.tsx +155 -0
  311. package/src/components/os-primitives/property-panel.tsx +108 -0
  312. package/src/components/os-primitives/section-header.tsx +19 -0
  313. package/src/components/os-primitives/sidebar-nav.tsx +110 -0
  314. package/src/components/os-primitives/split-pane.tsx +146 -0
  315. package/src/components/os-primitives/status-badge.tsx +10 -0
  316. package/src/components/os-primitives/status-bar.tsx +100 -0
  317. package/src/components/os-primitives/toolbar.tsx +152 -0
  318. package/src/components/taskbar/AppLauncher.tsx +114 -0
  319. package/src/components/taskbar/RunningApps.tsx +71 -0
  320. package/src/components/taskbar/SystemTray.tsx +134 -0
  321. package/src/components/taskbar/Taskbar.tsx +45 -0
  322. package/src/components/taskbar/UserMenu.tsx +138 -0
  323. package/src/components/taskbar/WorkspaceSwitcher.tsx +9 -0
  324. package/src/components/ui/ContextMenu.tsx +130 -0
  325. package/src/components/ui/badge.tsx +39 -0
  326. package/src/components/ui/button.tsx +102 -0
  327. package/src/components/ui/command.tsx +165 -0
  328. package/src/components/ui/dropdown-menu.tsx +233 -0
  329. package/src/components/ui/input.tsx +48 -0
  330. package/src/components/ui/note.tsx +55 -0
  331. package/src/components/ui/separator.tsx +25 -0
  332. package/src/components/ui/spinner.tsx +42 -0
  333. package/src/components/ui/switch.tsx +36 -0
  334. package/src/components/ui/theme-provider.tsx +24 -0
  335. package/src/components/ui/theme-switcher.tsx +51 -0
  336. package/src/components/ui/tooltip.tsx +62 -0
  337. package/src/components/window/MobileWindowStack.tsx +218 -0
  338. package/src/components/window/SnapPreview.tsx +37 -0
  339. package/src/components/window/StandaloneFrame.tsx +170 -0
  340. package/src/components/window/Window.tsx +423 -0
  341. package/src/components/window/WindowContent.tsx +14 -0
  342. package/src/components/window/WindowControlsOverlay.tsx +89 -0
  343. package/src/components/window/WindowFrame.tsx +124 -0
  344. package/src/lib/auth/index.ts +27 -0
  345. package/src/lib/auth/roles.ts +50 -0
  346. package/src/lib/auth/types.ts +32 -0
  347. package/src/lib/auth/use-auth.ts +53 -0
  348. package/src/lib/auth/webhook-handler.ts +87 -0
  349. package/src/lib/auth/workos.ts +67 -0
  350. package/src/lib/constants.ts +1 -0
  351. package/src/lib/desktop/dedup.ts +57 -0
  352. package/src/lib/desktop/schema.ts +55 -0
  353. package/src/lib/files/filename-validation.ts +41 -0
  354. package/src/lib/files/safe-path.ts +49 -0
  355. package/src/lib/hooks/use-desktop-nats.ts +438 -0
  356. package/src/lib/hooks/use-is-mobile.ts +23 -0
  357. package/src/lib/hooks/use-launch-app.ts +79 -0
  358. package/src/lib/hooks/use-nats-notifications.ts +84 -0
  359. package/src/lib/hooks/use-nats.ts +60 -0
  360. package/src/lib/hooks/use-visual-viewport.ts +72 -0
  361. package/src/lib/icons/resolve-window-icon.ts +10 -0
  362. package/src/lib/keyboard/defaults.ts +146 -0
  363. package/src/lib/keyboard/types.ts +52 -0
  364. package/src/lib/keyboard/use-global-keybinds.ts +231 -0
  365. package/src/lib/nats-client.ts +255 -0
  366. package/src/lib/nats.ts +35 -0
  367. package/src/lib/notifications/schema.ts +12 -0
  368. package/src/lib/service-loader.ts +171 -0
  369. package/src/lib/subjects.ts +64 -0
  370. package/src/lib/utils.ts +6 -0
  371. package/src/lib/ws-bridge.ts +288 -0
  372. package/src/lib/ws-protocol.ts +53 -0
  373. package/src/lib/ws-server.ts +167 -0
  374. package/src/middleware.ts +57 -0
  375. package/src/stores/desktop-store.ts +112 -0
  376. package/src/stores/keybind-store.ts +66 -0
  377. package/src/stores/notification-store.ts +271 -0
  378. package/src/stores/theme-store.ts +25 -0
  379. package/src/stores/window-store.ts +294 -0
  380. package/src/theme/animations.css +68 -0
  381. package/src/theme/base.css +123 -0
  382. package/src/theme/controls.css +35 -0
  383. package/src/theme/design-tokens.css +276 -0
  384. package/src/theme/index.css +23 -0
  385. package/src/theme/menus.css +45 -0
  386. package/src/theme/status.css +41 -0
  387. package/src/theme/surfaces.css +94 -0
  388. package/src/theme/tailwind-map.css +138 -0
  389. package/src/theme/taskbar.css +25 -0
  390. package/src/theme/terminal.css +55 -0
  391. package/src/theme/typography.css +26 -0
  392. package/src/theme/utilities.css +156 -0
  393. package/src/theme/window.css +103 -0
  394. package/src/types/desktop-entry.ts +12 -0
  395. package/src/types/use-descendants.d.ts +13 -0
  396. package/src/types/window.ts +28 -0
  397. package/src/types.d.ts +9 -0
  398. package/tauri/Cargo.lock +5464 -0
  399. package/tauri/Cargo.toml +19 -0
  400. package/tauri/build.rs +3 -0
  401. package/tauri/capabilities/default.json +36 -0
  402. package/tauri/icons/128x128.png +0 -0
  403. package/tauri/icons/128x128@2x.png +0 -0
  404. package/tauri/icons/32x32.png +0 -0
  405. package/tauri/icons/icon.png +0 -0
  406. package/tauri/src/main.rs +396 -0
  407. package/tauri/tauri.conf.json +23 -0
  408. package/tsconfig.json +43 -0
@@ -0,0 +1,345 @@
1
+ # Wish: WorkOS Production + RBAC Enforcement (Phase 1)
2
+
3
+ | Field | Value |
4
+ |-------|-------|
5
+ | **Status** | DRAFT |
6
+ | **Slug** | `workos-prod-rbac` |
7
+ | **Date** | 2026-03-23 |
8
+ | **GitHub Issue** | [namastexlabs/genie-os#13](https://github.com/namastexlabs/genie-os/issues/13) |
9
+ | **Architecture** | [namastexlabs/genie-os#16](https://github.com/namastexlabs/genie-os/issues/16) |
10
+
11
+ ## Summary
12
+
13
+ Phase 1 of the multi-tenant Genie OS platform. Flip WorkOS from staging to production, remove the TEMP BYPASS that grants all users admin, and enforce role-based access control using WorkOS's native RBAC. This is the auth foundation that all subsequent phases (multi-org, fleet management, FGA, marketplace) build on.
14
+
15
+ ## Context: Multi-Tenant Vision
16
+
17
+ This wish is Phase 1 of a 6-phase platform build (see [#16](https://github.com/namastexlabs/genie-os/issues/16)):
18
+
19
+ ```
20
+ Phase 1: WorkOS prod + RBAC enforcement ← THIS WISH
21
+ Phase 2: Multi-org + client isolation
22
+ Phase 3: Instance fleet management
23
+ Phase 4: FGA per-app permissions
24
+ Phase 5: Enterprise (Admin Portal, SCIM, Audit Logs)
25
+ Phase 6: App marketplace
26
+ ```
27
+
28
+ **Full hierarchy (future):**
29
+ | Scope | Role | Access |
30
+ |-------|------|--------|
31
+ | Platform | `platform-owner` | Everything across all clients |
32
+ | Platform | `platform-admin` | All instances + monitoring |
33
+ | Platform | `platform-dev` | Assigned instances + dev tools |
34
+ | Client | `client-owner` | All apps deployed to their org |
35
+ | Client | `client-admin` | Apps + manage employees |
36
+ | Client | `client-member` | Only assigned apps |
37
+
38
+ **Phase 1 implements the platform scope only** (NamasteX org, our roles). Client scope comes in Phase 2.
39
+
40
+ ## Scope
41
+
42
+ ### IN
43
+ - **WorkOS production environment**: Swap staging API key, client ID, redirect URI, cookie password for production values
44
+ - **Remove TEMP BYPASS**: In `src/lib/auth/use-auth.ts`, replace hardcoded admin grant with actual session role
45
+ - **Server→client role plumbing**: Create a server component that calls `withAuth()` and passes role/permissions to client components (the `useAuth()` client hook doesn't expose role directly)
46
+ - **WorkOS Dashboard config**: Document exact roles, permissions, and org setup needed
47
+ - **Permission enforcement**: Return real role + derived permissions so `minRole` app gates work
48
+ - **Webhook endpoint**: Add `/api/webhooks/workos` route for role/membership change events
49
+ - **Preserve HeadlessChrome bypass**: Machine auth for d3k must keep working
50
+ - **Forward-compatible role slugs**: Use `platform-owner`, `platform-admin`, `platform-dev`, `member` — slugs that won't collide when client-scoped roles are added in Phase 2
51
+
52
+ ### OUT
53
+ - **Multi-org / client organizations** — Phase 2
54
+ - **Instance fleet management** — Phase 3
55
+ - **FGA / per-app sharing** — Phase 4
56
+ - **Admin Portal, SCIM, Audit Logs** — Phase 5
57
+ - **App Directory / Marketplace** — Phase 6
58
+ - **Permissions management app in Genie OS** — Phase 2 (needs multi-org first)
59
+ - **Custom WorkOS UI / Widgets** — use hosted AuthKit for now
60
+ - **Feature flags** — available free but not needed yet
61
+
62
+ ## Decisions
63
+
64
+ | Decision | Rationale |
65
+ |----------|-----------|
66
+ | Use WorkOS hosted AuthKit UI, not custom | Zero frontend auth code. Free up to 1M MAUs. |
67
+ | Server component for role plumbing | `useAuth()` client hook returns `{ user, loading }` only. `withAuth()` server-side returns `{ user, role, permissions }`. Use a server component wrapper that calls `withAuth()` and passes role to client context. |
68
+ | Role slugs: `platform-owner`, `platform-admin`, `platform-dev`, `member` | Forward-compatible with Phase 2 multi-org. WorkOS auto-prefixes org roles with `org-`, so platform roles need distinct slugs. `member` is WorkOS's default role for new memberships. |
69
+ | Single org for Phase 1 | Our NamasteX org only. Multi-org in Phase 2. |
70
+ | Webhook logs events + broadcasts via NATS | On role change, broadcast to connected clients via NATS so UI updates without full page reload. No server-side session store to invalidate — AuthKit manages cookies. |
71
+ | `ROLE_DEFAULTS` as fallback | If JWT permissions are empty (edge case during migration), fall back to role-based defaults. |
72
+
73
+ ## Success Criteria
74
+
75
+ - [ ] WorkOS production environment active with production API key and client ID
76
+ - [ ] `NEXT_PUBLIC_WORKOS_REDIRECT_URI` points to production domain
77
+ - [ ] TEMP BYPASS removed from `use-auth.ts` — users get their actual role
78
+ - [ ] Server component passes role from `withAuth()` to client-side auth context
79
+ - [ ] A `member` role user can only access the Files app
80
+ - [ ] A `platform-dev` role user can access Terminal, Files, Settings, NATS Viewer
81
+ - [ ] A `platform-admin`/`platform-owner` role user can access all apps
82
+ - [ ] HeadlessChrome bypass still works for d3k
83
+ - [ ] Login/logout flow works end-to-end on production domain
84
+ - [ ] `/api/webhooks/workos` receives and processes role change events
85
+ - [ ] `WORKOS_WEBHOOK_SECRET` in `.env.example`
86
+ - [ ] `bun biome check .` and `bunx tsc --noEmit` pass
87
+
88
+ ## Execution Strategy
89
+
90
+ ### Wave 1 (foundation — docs + server-side role plumbing)
91
+ | Group | Agent | Description |
92
+ |-------|-------|-------------|
93
+ | 1 | engineer | WorkOS Dashboard setup guide + env config + server-side role provider |
94
+
95
+ ### Wave 2 (parallel — client enforcement + webhooks)
96
+ | Group | Agent | Description |
97
+ |-------|-------|-------------|
98
+ | 2 | engineer | Remove TEMP BYPASS + wire role enforcement across UI |
99
+ | 3 | engineer | Webhook endpoint + NATS role-change broadcast |
100
+
101
+ ### Wave 3 (quality gate)
102
+ | Group | Agent | Description |
103
+ |-------|-------|-------------|
104
+ | review | reviewer | Review all groups, run validation |
105
+
106
+ ## Execution Groups
107
+
108
+ ### Group 1: WorkOS Config + Server-Side Role Provider
109
+
110
+ **Goal:** Document dashboard setup and create the server→client role bridge that the review identified as missing.
111
+
112
+ **Deliverables:**
113
+
114
+ 1. `docs/workos-setup.md` — Step-by-step guide for WorkOS dashboard:
115
+ - Create production environment (or switch from staging)
116
+ - Create NamasteX organization
117
+ - Define environment roles with these slugs:
118
+ - `platform-owner`: all permissions (NamasteX founders)
119
+ - `platform-admin`: `apps:access`, `settings:manage`, `members:manage` (NamasteX senior)
120
+ - `platform-dev`: `apps:access` (NamasteX builders)
121
+ - `member`: (WorkOS default — base access, maps to viewer)
122
+ - Define permissions using `resource:action` pattern:
123
+ - `apps:access` — base app access
124
+ - `apps:install` — install from marketplace (future)
125
+ - `settings:manage` — OS settings
126
+ - `members:manage` — invite/remove users
127
+ - `billing:manage` — billing/subscription (future)
128
+ - `instances:manage` — fleet management (future)
129
+ - Configure redirect URI, email verification, session config
130
+ - Configure webhook endpoint URL + signing secret
131
+
132
+ 2. `.env.example` — Update with all WorkOS env vars:
133
+ ```
134
+ WORKOS_API_KEY=sk_live_...
135
+ WORKOS_CLIENT_ID=client_...
136
+ NEXT_PUBLIC_WORKOS_REDIRECT_URI=https://<prod-domain>/auth/callback
137
+ WORKOS_COOKIE_PASSWORD=<32+ char random string>
138
+ WORKOS_WEBHOOK_SECRET=whsec_...
139
+ ```
140
+
141
+ 3. `src/app/desktop/page.tsx` (or existing server component) — Server-side role injection:
142
+ - Call `withAuth()` to get `{ user, role, permissions, organizationId }`
143
+ - Pass `role` and `permissions` as props to the client-side desktop shell
144
+ - This solves the architectural gap: server `withAuth()` → prop drilling → client context
145
+
146
+ 4. `src/lib/auth/roles.ts` — Update role hierarchy and aliases:
147
+ - Role hierarchy: `['member', 'platform-dev', 'platform-admin', 'platform-owner']`
148
+ - Aliases: `admin` → `platform-admin`, `developer` → `platform-dev`, `owner` → `platform-owner`, `viewer` → `member`, `user` → `member`
149
+
150
+ 5. `src/components/apps/app-manifest.ts` — Update `ROLE_HIERARCHY`:
151
+ - `['member', 'platform-dev', 'platform-admin', 'platform-owner'] as const`
152
+ - Update `Role` type accordingly
153
+
154
+ **Acceptance Criteria:**
155
+ - [ ] `docs/workos-setup.md` exists with complete dashboard instructions
156
+ - [ ] `.env.example` has `WORKOS_WEBHOOK_SECRET`
157
+ - [ ] Server component calls `withAuth()` and passes role to client
158
+ - [ ] `ROLE_HIERARCHY` uses forward-compatible slugs
159
+ - [ ] `normalizeRole()` maps legacy slugs (`admin` → `platform-admin`, etc.)
160
+ - [ ] `bunx tsc --noEmit` passes
161
+
162
+ **Validation:**
163
+ ```bash
164
+ test -f docs/workos-setup.md && \
165
+ grep -q "platform-owner" docs/workos-setup.md && \
166
+ grep -q "WORKOS_WEBHOOK_SECRET" .env.example && \
167
+ grep -q "platform-owner" src/components/apps/app-manifest.ts && \
168
+ grep -q "withAuth" src/app/desktop/page.tsx && \
169
+ bunx tsc --noEmit && \
170
+ echo "PASS" || echo "FAIL"
171
+ ```
172
+
173
+ **depends-on:** none
174
+
175
+ ---
176
+
177
+ ### Group 2: Remove TEMP BYPASS + Enforce Roles
178
+
179
+ **Goal:** Wire the actual WorkOS session role into the permission system so app gates work.
180
+
181
+ **Deliverables:**
182
+
183
+ 1. `src/lib/auth/use-auth.ts` — Remove TEMP BYPASS:
184
+ - Accept role from server component props (passed via React context or prop drilling)
185
+ - Remove hardcoded `effectiveRole = 'admin'` (lines 42-44)
186
+ - Map server-provided role to internal role via `normalizeRole()`
187
+ - Derive permissions from role using `getRolePermissions()`
188
+ - Keep HeadlessChrome bypass intact — use `platform-owner` permissions (new top of hierarchy)
189
+
190
+ 2. `src/lib/auth/workos.ts` — Align `ROLE_DEFAULTS` with new role slugs:
191
+ - `platform-owner`: `{ apps: ['*'], manageMembers: true }`
192
+ - `platform-admin`: `{ apps: ['*'], manageMembers: true }`
193
+ - `platform-dev`: `{ apps: ['terminal', 'files', 'settings', 'nats-viewer'], manageMembers: false }`
194
+ - `member`: `{ apps: ['files'], manageMembers: false }`
195
+
196
+ 3. `src/components/taskbar/UserMenu.tsx` — Update role colors:
197
+ - Add `platform-owner` color (distinct from admin)
198
+ - Add `platform-admin` color
199
+ - Add `platform-dev` color
200
+ - `member` uses muted color (existing viewer color)
201
+
202
+ 4. Update any component that checks role strings to use new slugs
203
+
204
+ **Acceptance Criteria:**
205
+ - [ ] TEMP BYPASS removed — no hardcoded `'admin'` assignment for all users
206
+ - [ ] `member` role user sees only Files app in desktop
207
+ - [ ] `platform-dev` role user sees Terminal, Files, Settings, NATS Viewer
208
+ - [ ] `platform-admin`/`platform-owner` sees all apps
209
+ - [ ] HeadlessChrome bypass returns `platform-owner`-level access
210
+ - [ ] `ROLE_COLORS` includes all 4 role slugs
211
+ - [ ] `bunx tsc --noEmit` passes
212
+ - [ ] `bun biome check .` passes
213
+
214
+ **Validation:**
215
+ ```bash
216
+ # No TEMP BYPASS remaining
217
+ ! grep -q "TEMP BYPASS" src/lib/auth/use-auth.ts && \
218
+ # New role slugs in use
219
+ grep -q "platform-owner" src/lib/auth/workos.ts && \
220
+ grep -q "platform-owner" src/components/taskbar/UserMenu.tsx && \
221
+ # Typecheck + lint pass
222
+ bunx tsc --noEmit && \
223
+ bun biome check . && \
224
+ echo "PASS" || echo "FAIL"
225
+ ```
226
+
227
+ **depends-on:** Group 1
228
+
229
+ ---
230
+
231
+ ### Group 3: Webhook Endpoint + NATS Role Broadcast
232
+
233
+ **Goal:** Receive WorkOS webhook events and broadcast role changes to connected clients via NATS.
234
+
235
+ **Deliverables:**
236
+
237
+ 1. `src/app/api/webhooks/workos/route.ts` — Webhook handler:
238
+ - Verify webhook signature using WorkOS SDK (`constructEvent()` or manual HMAC)
239
+ - Handle `organization_membership.updated` — extract new role, broadcast via NATS
240
+ - Handle `organization_membership.deleted` — broadcast membership revocation
241
+ - Handle `user.updated` — broadcast user metadata changes
242
+ - Return 200 OK on success, 400 on signature failure
243
+ - Route is already covered by `unauthenticatedPaths: ['/api/webhooks/:path*']`
244
+
245
+ 2. `src/lib/auth/webhook-handler.ts` — Event processing + NATS broadcast:
246
+ - Parse WorkOS webhook payload by event type
247
+ - On role change: publish to NATS subject `os.auth.role-changed` with `{ userId, newRole, orgId }`
248
+ - On membership deleted: publish to `os.auth.membership-revoked` with `{ userId, orgId }`
249
+ - Connected WS clients subscribed to these subjects can force re-auth
250
+ - Log events for debugging (console OK in service files)
251
+
252
+ 3. Clarification on session invalidation approach:
253
+ - AuthKit manages sessions via encrypted cookies — no server-side session store
254
+ - On `membership.deleted`: broadcast via NATS; client-side listener forces logout
255
+ - On `membership.updated` (role change): broadcast via NATS; client-side listener refreshes role from server
256
+ - Sessions expire naturally per WorkOS session config; webhook provides faster feedback
257
+
258
+ **Acceptance Criteria:**
259
+ - [ ] `POST /api/webhooks/workos` returns 200 for valid signed payloads
260
+ - [ ] `POST /api/webhooks/workos` returns 400 for invalid signatures
261
+ - [ ] NATS subjects `os.auth.role-changed` and `os.auth.membership-revoked` documented
262
+ - [ ] `bunx tsc --noEmit` passes
263
+
264
+ **Validation:**
265
+ ```bash
266
+ test -f src/app/api/webhooks/workos/route.ts && \
267
+ grep -q "organization_membership" src/lib/auth/webhook-handler.ts && \
268
+ grep -q "os.auth" src/lib/auth/webhook-handler.ts && \
269
+ bunx tsc --noEmit && \
270
+ echo "PASS" || echo "FAIL"
271
+ ```
272
+
273
+ **depends-on:** Group 1
274
+
275
+ ---
276
+
277
+ ## QA Criteria
278
+
279
+ _What must be verified on dev after merge._
280
+
281
+ - [ ] Login with WorkOS production credentials works end-to-end
282
+ - [ ] User with `member` role sees only Files app on desktop
283
+ - [ ] User with `platform-dev` role sees Terminal, Files, Settings, NATS Viewer
284
+ - [ ] User with `platform-admin` role sees all apps + can manage members
285
+ - [ ] Role change in WorkOS dashboard reflects in app after webhook + NATS broadcast
286
+ - [ ] HeadlessChrome d3k bypass still renders desktop without login
287
+ - [ ] Logout clears session and redirects to login
288
+ - [ ] Invalid/expired sessions redirect to login
289
+ - [ ] Role slugs are forward-compatible (no refactoring needed for Phase 2 multi-org)
290
+
291
+ ## Assumptions / Risks
292
+
293
+ | Risk | Severity | Mitigation |
294
+ |------|----------|------------|
295
+ | WorkOS staging→prod migration breaks existing sessions | Medium | Schedule during low-traffic; all users re-login once |
296
+ | `platform-owner` slug not yet in WorkOS dashboard | Low | Create in dashboard before deploying code. `normalizeRole()` fallback to `platform-admin`. |
297
+ | JWT permissions empty for existing users | Low | `ROLE_DEFAULTS` fallback handles this |
298
+ | Webhook signature verification needs shared secret | Low | `WORKOS_WEBHOOK_SECRET` in `.env` |
299
+ | Renaming roles from `admin`→`platform-admin` breaks existing code | Medium | `normalizeRole()` maps old→new. Grep for hardcoded role strings in all files. |
300
+ | `withAuth()` not available in all server component contexts | Low | Use Next.js middleware or layout-level server component. AuthKit middleware already runs on all protected routes. |
301
+
302
+ ## Review Results
303
+
304
+ ### Review 1 — Plan Review (pre-fix)
305
+
306
+ **Verdict:** FIX-FIRST
307
+
308
+ | # | Severity | Gap | Resolution |
309
+ |---|----------|-----|------------|
310
+ | 1 | HIGH | Client-side role access: `useAuth()` doesn't expose role | **Fixed:** Added server component deliverable in Group 1 that calls `withAuth()` and passes role as props |
311
+ | 2 | MEDIUM | `WORKOS_WEBHOOK_SECRET` missing from `.env.example` | **Fixed:** Added to Group 1, deliverable 2 |
312
+ | 3 | MEDIUM | Webhook session invalidation unspecified | **Fixed:** Added NATS broadcast approach in Group 3, deliverable 3 |
313
+ | 4 | LOW | `ROLE_COLORS` missing `owner` | **Fixed:** Incorporated in Group 2, deliverable 3 |
314
+ | 5 | LOW | HeadlessChrome should use top-of-hierarchy | **Fixed:** Group 2 specifies `platform-owner` permissions |
315
+
316
+ ---
317
+
318
+ ## Files to Create/Modify
319
+
320
+ ```
321
+ # Create
322
+ docs/workos-setup.md — Dashboard setup guide (roles, permissions, session config)
323
+ src/app/api/webhooks/workos/route.ts — Webhook endpoint
324
+ src/lib/auth/webhook-handler.ts — Webhook event processing + NATS broadcast
325
+
326
+ # Modify
327
+ .env.example — Add WORKOS_WEBHOOK_SECRET
328
+ src/app/desktop/page.tsx — Server-side withAuth() → pass role to client
329
+ src/lib/auth/use-auth.ts — Remove TEMP BYPASS, accept role from server
330
+ src/lib/auth/workos.ts — Update ROLE_DEFAULTS to new slugs
331
+ src/lib/auth/roles.ts — New hierarchy + slug aliases
332
+ src/components/apps/app-manifest.ts — New ROLE_HIERARCHY with platform-* slugs
333
+ src/components/taskbar/UserMenu.tsx — ROLE_COLORS for all 4 slugs
334
+ ```
335
+
336
+ ## Future Wishes (Pre-Drafted)
337
+
338
+ | Phase | Wish | GitHub Issue | What |
339
+ |-------|------|-------------|------|
340
+ | 2 | Multi-Org + Client Isolation | [#16](https://github.com/namastexlabs/genie-os/issues/16) | WorkOS Organizations per client, org-level roles, org switcher, permissions management app |
341
+ | 3 | Instance Fleet Management | [#16](https://github.com/namastexlabs/genie-os/issues/16) | Instance registry, central admin panel, FGA instance resources |
342
+ | 4 | FGA App Permissions | [#14](https://github.com/namastexlabs/genie-os/issues/14) | Per-app per-instance per-user permissions, "share like Google Docs" |
343
+ | 5 | Enterprise Features | TBD | Admin Portal embed, Directory Sync, Audit Logs, Domain Verification |
344
+ | 6 | App Marketplace | [#14](https://github.com/namastexlabs/genie-os/issues/14) | Browse, publish, upvote apps; success/cost metrics; creator attribution |
345
+ | — | Braindump / Idea Pool | [#15](https://github.com/namastexlabs/genie-os/issues/15) | Non-disruptive idea capture in Genie OS |
@@ -0,0 +1,39 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, dev]
6
+ pull_request:
7
+ branches: [main, dev]
8
+
9
+ concurrency:
10
+ group: ci-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ quality-gate:
15
+ name: Quality Gate
16
+ runs-on: ubuntu-latest
17
+ timeout-minutes: 15
18
+
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+
22
+ - uses: pnpm/action-setup@v4
23
+
24
+ - uses: actions/setup-node@v4
25
+ with:
26
+ node-version: 22
27
+ cache: pnpm
28
+
29
+ - name: Install dependencies
30
+ run: pnpm install --frozen-lockfile
31
+
32
+ - name: Typecheck
33
+ run: pnpm exec tsc --noEmit
34
+
35
+ - name: Lint
36
+ run: pnpm lint
37
+
38
+ - name: Build
39
+ run: pnpm build
@@ -0,0 +1,78 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: write
10
+
11
+ jobs:
12
+ release:
13
+ name: Create Release
14
+ if: "!startsWith(github.event.head_commit.message, '[skip ci]')"
15
+ runs-on: ubuntu-latest
16
+ timeout-minutes: 10
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ with:
20
+ fetch-depth: 0
21
+
22
+ - name: Resolve version
23
+ id: ver
24
+ env:
25
+ GH_TOKEN: ${{ github.token }}
26
+ run: |
27
+ VERSION=$(node -p "require('./package.json').version")
28
+ TAG="v${VERSION}"
29
+
30
+ if gh release view "${TAG}" > /dev/null 2>&1; then
31
+ echo "Release ${TAG} already exists — skipping"
32
+ echo "skip=true" >> "$GITHUB_OUTPUT"
33
+ else
34
+ echo "skip=false" >> "$GITHUB_OUTPUT"
35
+ fi
36
+
37
+ echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
38
+ echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
39
+
40
+ - name: Find previous release tag
41
+ id: prev
42
+ if: steps.ver.outputs.skip == 'false'
43
+ env:
44
+ GH_TOKEN: ${{ github.token }}
45
+ run: |
46
+ TAG="${{ steps.ver.outputs.tag }}"
47
+ PREV=$(gh release list --limit 50 --json tagName -q '.[].tagName' | grep -v "^${TAG}$" | head -1)
48
+ if [ -n "$PREV" ] && git merge-base --is-ancestor "$PREV" HEAD 2>/dev/null; then
49
+ echo "tag=${PREV}" >> "$GITHUB_OUTPUT"
50
+ else
51
+ echo "tag=" >> "$GITHUB_OUTPUT"
52
+ fi
53
+
54
+ - name: Generate changelog
55
+ id: changelog
56
+ if: steps.ver.outputs.skip == 'false' && steps.prev.outputs.tag != ''
57
+ run: |
58
+ PREV="${{ steps.prev.outputs.tag }}"
59
+ NOTES=$(git log --oneline "${PREV}..HEAD" --pretty="- %s" | head -50)
60
+ echo "notes<<EOF" >> "$GITHUB_OUTPUT"
61
+ echo "$NOTES" >> "$GITHUB_OUTPUT"
62
+ echo "EOF" >> "$GITHUB_OUTPUT"
63
+
64
+ - name: Create release
65
+ if: steps.ver.outputs.skip == 'false'
66
+ env:
67
+ GH_TOKEN: ${{ github.token }}
68
+ run: |
69
+ TAG="${{ steps.ver.outputs.tag }}"
70
+ NOTES="${{ steps.changelog.outputs.notes }}"
71
+ if [ -z "$NOTES" ]; then
72
+ NOTES="Genie OS release ${TAG}"
73
+ fi
74
+ printf '%s' "$NOTES" > /tmp/release-notes.md
75
+ gh release create "${TAG}" \
76
+ --target "${{ github.sha }}" \
77
+ --title "${TAG}" \
78
+ --notes-file /tmp/release-notes.md
@@ -0,0 +1,122 @@
1
+ name: Version
2
+
3
+ on:
4
+ workflow_run:
5
+ workflows: ["CI"]
6
+ types: [completed]
7
+ branches: [main, dev]
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ contents: write
12
+
13
+ concurrency:
14
+ group: version-${{ github.event.workflow_run.head_branch || 'manual' }}
15
+ cancel-in-progress: false
16
+
17
+ jobs:
18
+ auto-version:
19
+ name: Auto Version
20
+ runs-on: ubuntu-latest
21
+ timeout-minutes: 10
22
+ if: >-
23
+ (github.event_name == 'workflow_dispatch') ||
24
+ (github.event.workflow_run.conclusion == 'success' &&
25
+ github.event.workflow_run.event == 'push' &&
26
+ !contains(github.event.workflow_run.head_commit.message, '[skip ci]') &&
27
+ (
28
+ (github.event.workflow_run.head_branch == 'main' &&
29
+ startsWith(github.event.workflow_run.head_commit.message, 'Merge pull request') &&
30
+ contains(github.event.workflow_run.head_commit.message, '/dev'))
31
+ ||
32
+ (github.event.workflow_run.head_branch == 'dev')
33
+ ))
34
+
35
+ steps:
36
+ - name: Determine branch and version prefix
37
+ id: context
38
+ run: |
39
+ BRANCH="${{ github.event.workflow_run.head_branch || 'dev' }}"
40
+ if [ "$BRANCH" = "main" ]; then
41
+ echo "branch=dev" >> "$GITHUB_OUTPUT"
42
+ echo "prefix=1" >> "$GITHUB_OUTPUT"
43
+ echo "npm_tag=latest" >> "$GITHUB_OUTPUT"
44
+ else
45
+ echo "branch=dev" >> "$GITHUB_OUTPUT"
46
+ echo "prefix=1" >> "$GITHUB_OUTPUT"
47
+ echo "npm_tag=next" >> "$GITHUB_OUTPUT"
48
+ fi
49
+ echo "Resolved: branch=dev, npm_tag=$([ "$BRANCH" = "main" ] && echo latest || echo next)"
50
+
51
+ - uses: actions/checkout@v4
52
+ with:
53
+ ref: ${{ steps.context.outputs.branch }}
54
+ fetch-depth: 0
55
+ token: ${{ secrets.GITHUB_TOKEN }}
56
+
57
+ - uses: pnpm/action-setup@v4
58
+
59
+ - uses: actions/setup-node@v4
60
+ with:
61
+ node-version: 22
62
+ cache: pnpm
63
+
64
+ - name: Install dependencies
65
+ run: pnpm install
66
+
67
+ - name: Configure git
68
+ run: |
69
+ git config user.name "github-actions[bot]"
70
+ git config user.email "github-actions[bot]@users.noreply.github.com"
71
+
72
+ - name: Derive version
73
+ id: version
74
+ run: |
75
+ PREFIX="${{ steps.context.outputs.prefix }}"
76
+ TODAY=$(date -u +%y%m%d)
77
+ EXISTING=$(git tag --list "v${PREFIX}.${TODAY}.*" | wc -l)
78
+ BUILD_NUMBER=$((EXISTING + 1))
79
+ VERSION="${PREFIX}.${TODAY}.${BUILD_NUMBER}"
80
+ echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
81
+ echo "build_number=${BUILD_NUMBER}" >> "$GITHUB_OUTPUT"
82
+ echo "Derived version: ${VERSION}"
83
+
84
+ - name: Update package.json version
85
+ run: |
86
+ VERSION="${{ steps.version.outputs.version }}"
87
+ node -e "
88
+ const pkg = require('./package.json');
89
+ pkg.version = '${VERSION}';
90
+ require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, '\t') + '\n');
91
+ "
92
+
93
+ - name: Commit and tag
94
+ run: |
95
+ VERSION="${{ steps.version.outputs.version }}"
96
+ BRANCH="${{ steps.context.outputs.branch }}"
97
+
98
+ git add package.json
99
+ if git diff --cached --quiet; then
100
+ echo "No version changes to commit"
101
+ else
102
+ git commit -m "chore(version): bump to ${VERSION} [skip ci]"
103
+ fi
104
+
105
+ git tag "v${VERSION}"
106
+ git push --atomic origin "HEAD:refs/heads/${BRANCH}" "refs/tags/v${VERSION}"
107
+
108
+ - name: Build
109
+ run: pnpm build
110
+
111
+ - name: Publish to npm
112
+ env:
113
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
114
+ NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
115
+ HUSKY: "0"
116
+ run: |
117
+ if [ -z "$NPM_TOKEN" ]; then
118
+ echo "⚠️ NPM_TOKEN not set — skipping publish"
119
+ exit 0
120
+ fi
121
+ echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
122
+ npm publish --access public --tag ${{ steps.context.outputs.npm_tag }} || echo "Publish failed — version may already exist"
@@ -0,0 +1 @@
1
+ pnpm lint
@@ -0,0 +1 @@
1
+ { "node-pty@1.1.0": true }