jettypod 4.4.118 → 4.4.121
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/.env +4 -3
- package/Cargo.lock +6450 -0
- package/Cargo.toml +35 -0
- package/README.md +5 -1
- package/TAURI-MIGRATION-PLAN.md +840 -0
- package/apps/dashboard/app/connect-claude/page.tsx +5 -6
- package/apps/dashboard/app/decision/[id]/page.tsx +63 -58
- package/apps/dashboard/app/demo/gates/page.tsx +43 -45
- package/apps/dashboard/app/design-system/page.tsx +868 -0
- package/apps/dashboard/app/globals.css +80 -4
- package/apps/dashboard/app/install-claude/page.tsx +4 -6
- package/apps/dashboard/app/login/page.tsx +72 -54
- package/apps/dashboard/app/page.tsx +101 -48
- package/apps/dashboard/app/settings/page.tsx +61 -13
- package/apps/dashboard/app/signup/page.tsx +242 -0
- package/apps/dashboard/app/subscribe/page.tsx +0 -2
- package/apps/dashboard/app/tests/page.tsx +37 -4
- package/apps/dashboard/app/welcome/page.tsx +13 -16
- package/apps/dashboard/app/work/[id]/page.tsx +117 -118
- package/apps/dashboard/app/work/[id]/proof/page.tsx +1489 -0
- package/apps/dashboard/components/AppShell.tsx +92 -85
- package/apps/dashboard/components/CardMenu.tsx +45 -12
- package/apps/dashboard/components/ClaudePanel.tsx +771 -850
- package/apps/dashboard/components/ClaudePanelInput.tsx +43 -15
- package/apps/dashboard/components/ConnectClaudeScreen.tsx +17 -34
- package/apps/dashboard/components/CopyableId.tsx +3 -4
- package/apps/dashboard/components/DetailReviewActions.tsx +100 -0
- package/apps/dashboard/components/DragContext.tsx +134 -63
- package/apps/dashboard/components/DraggableCard.tsx +3 -5
- package/apps/dashboard/components/DropZone.tsx +6 -7
- package/apps/dashboard/components/EditableDetailDescription.tsx +7 -13
- package/apps/dashboard/components/EditableDetailTitle.tsx +6 -13
- package/apps/dashboard/components/EditableTitle.tsx +26 -7
- package/apps/dashboard/components/ElapsedTimer.tsx +66 -0
- package/apps/dashboard/components/EpicGroup.tsx +359 -0
- package/apps/dashboard/components/GateCard.tsx +79 -17
- package/apps/dashboard/components/GateChoiceCard.tsx +15 -18
- package/apps/dashboard/components/InstallClaudeScreen.tsx +15 -32
- package/apps/dashboard/components/JettyLoader.tsx +37 -0
- package/apps/dashboard/components/KanbanBoard.tsx +368 -958
- package/apps/dashboard/components/KanbanCard.tsx +740 -0
- package/apps/dashboard/components/LazyCard.tsx +62 -0
- package/apps/dashboard/components/LazyMarkdown.tsx +11 -0
- package/apps/dashboard/components/MainNav.tsx +38 -73
- package/apps/dashboard/components/MessageBlock.tsx +468 -0
- package/apps/dashboard/components/ModeStartCard.tsx +15 -16
- package/apps/dashboard/components/OnboardingWelcome.tsx +213 -0
- package/apps/dashboard/components/PlaceholderCard.tsx +3 -4
- package/apps/dashboard/components/ProjectSwitcher.tsx +30 -30
- package/apps/dashboard/components/PrototypeTimeline.tsx +72 -51
- package/apps/dashboard/components/RealTimeKanbanWrapper.tsx +406 -388
- package/apps/dashboard/components/RealTimeTestsWrapper.tsx +373 -235
- package/apps/dashboard/components/ReviewFooter.tsx +139 -0
- package/apps/dashboard/components/SessionList.tsx +19 -19
- package/apps/dashboard/components/SubscribeContent.tsx +91 -47
- package/apps/dashboard/components/TestTree.tsx +16 -16
- package/apps/dashboard/components/TipCard.tsx +16 -17
- package/apps/dashboard/components/Toast.tsx +5 -6
- package/apps/dashboard/components/TypeIcon.tsx +55 -0
- package/apps/dashboard/components/ViewModeToolbar.tsx +104 -0
- package/apps/dashboard/components/WaveCompletionAnimation.tsx +52 -65
- package/apps/dashboard/components/WelcomeScreen.tsx +19 -35
- package/apps/dashboard/components/WorkItemHeader.tsx +4 -5
- package/apps/dashboard/components/WorkItemTree.tsx +11 -32
- package/apps/dashboard/components/settings/AccountSection.tsx +55 -35
- package/apps/dashboard/components/settings/AiContextSection.tsx +89 -0
- package/apps/dashboard/components/settings/ContextDocumentsSection.tsx +317 -0
- package/apps/dashboard/components/settings/EnvVarsSection.tsx +74 -152
- package/apps/dashboard/components/settings/GeneralSection.tsx +162 -56
- package/apps/dashboard/components/settings/ProjectStackSection.tsx +948 -0
- package/apps/dashboard/components/settings/SettingsLayout.tsx +4 -5
- package/apps/dashboard/components/ui/Button.tsx +104 -0
- package/apps/dashboard/components/ui/Input.tsx +78 -0
- package/apps/dashboard/components.json +1 -1
- package/apps/dashboard/contexts/ClaudeSessionContext.tsx +711 -418
- package/apps/dashboard/contexts/ConnectionStatusContext.tsx +25 -5
- package/apps/dashboard/contexts/UsageContext.tsx +87 -32
- package/apps/dashboard/dev.sh +35 -0
- package/apps/dashboard/eslint.config.mjs +9 -9
- package/apps/dashboard/hooks/useKanbanAnimation.ts +29 -0
- package/apps/dashboard/hooks/useKanbanUndo.ts +83 -0
- package/apps/dashboard/hooks/useWebSocket.ts +138 -83
- package/apps/dashboard/index.html +73 -0
- package/apps/dashboard/lib/constants.ts +43 -0
- package/apps/dashboard/lib/data-bridge.ts +722 -0
- package/apps/dashboard/lib/db.ts +69 -1265
- package/apps/dashboard/lib/environment-config.ts +173 -0
- package/apps/dashboard/lib/environment-verification.ts +119 -0
- package/apps/dashboard/lib/kanban-utils.ts +270 -0
- package/apps/dashboard/lib/proof-run.ts +495 -0
- package/apps/dashboard/lib/proof-scenario-runner.ts +346 -0
- package/apps/dashboard/lib/run-migrations.js +27 -2
- package/apps/dashboard/lib/service-recovery.ts +326 -0
- package/apps/dashboard/lib/session-state-machine.ts +1 -0
- package/apps/dashboard/lib/session-state-utils.ts +0 -164
- package/apps/dashboard/lib/session-stream-manager.ts +308 -134
- package/apps/dashboard/lib/shadows.ts +7 -0
- package/apps/dashboard/lib/stream-manager-registry.ts +46 -6
- package/apps/dashboard/lib/tauri-bridge.ts +102 -0
- package/apps/dashboard/lib/tauri.ts +106 -0
- package/apps/dashboard/lib/utils.ts +6 -0
- package/apps/dashboard/next-env.d.ts +1 -1
- package/apps/dashboard/package.json +21 -32
- package/apps/dashboard/public/bug-icon.png +0 -0
- package/apps/dashboard/public/buoy-icon.png +0 -0
- package/apps/dashboard/public/fonts/Satoshi-Variable.woff2 +0 -0
- package/apps/dashboard/public/fonts/Satoshi-VariableItalic.woff2 +0 -0
- package/apps/dashboard/public/in-flight-seagull.png +0 -0
- package/apps/dashboard/public/jetty-icon-loading-alt.svg +11 -0
- package/apps/dashboard/public/jetty-icon-loading.svg +11 -0
- package/apps/dashboard/public/jettypod_logo.png +0 -0
- package/apps/dashboard/public/pier-icon.png +0 -0
- package/apps/dashboard/public/star-icon.png +0 -0
- package/apps/dashboard/public/wrench-icon.png +0 -0
- package/apps/dashboard/scripts/tauri-build.js +228 -0
- package/apps/dashboard/scripts/upload-tauri-to-r2.js +125 -0
- package/apps/dashboard/scripts/ws-server.js +191 -0
- package/apps/dashboard/src/main.tsx +12 -0
- package/apps/dashboard/src/router.tsx +107 -0
- package/apps/dashboard/src/vite-env.d.ts +1 -0
- package/apps/dashboard/tsconfig.json +7 -12
- package/apps/dashboard/tsconfig.tsbuildinfo +1 -1
- package/apps/dashboard/vite.config.ts +33 -0
- package/apps/update-server/src/index.ts +228 -80
- package/claude-hooks/global-guardrails.js +14 -13
- package/crates/jettypod-cli/Cargo.toml +19 -0
- package/crates/jettypod-cli/src/commands.rs +1249 -0
- package/crates/jettypod-cli/src/main.rs +595 -0
- package/crates/jettypod-core/Cargo.toml +26 -0
- package/crates/jettypod-core/build.rs +98 -0
- package/crates/jettypod-core/migrations/V1__baseline.sql +197 -0
- package/crates/jettypod-core/migrations/V2__work_items_indexes.sql +6 -0
- package/crates/jettypod-core/migrations/V3__qa_steps.sql +2 -0
- package/crates/jettypod-core/src/auth.rs +294 -0
- package/crates/jettypod-core/src/config.rs +397 -0
- package/crates/jettypod-core/src/db/mod.rs +507 -0
- package/crates/jettypod-core/src/db/recovery.rs +114 -0
- package/crates/jettypod-core/src/db/startup.rs +101 -0
- package/crates/jettypod-core/src/db/validate.rs +149 -0
- package/crates/jettypod-core/src/error.rs +76 -0
- package/crates/jettypod-core/src/git.rs +458 -0
- package/crates/jettypod-core/src/lib.rs +20 -0
- package/crates/jettypod-core/src/sessions.rs +625 -0
- package/crates/jettypod-core/src/skills.rs +556 -0
- package/crates/jettypod-core/src/work.rs +1086 -0
- package/crates/jettypod-core/src/worktree.rs +628 -0
- package/crates/jettypod-core/src/ws.rs +767 -0
- package/cucumber-test.cjs +6 -0
- package/cucumber.js +9 -3
- package/docs/COMMAND_REFERENCE.md +34 -0
- package/hooks/post-checkout +32 -75
- package/hooks/post-merge +111 -10
- package/jest.setup.js +1 -0
- package/jettypod.js +145 -116
- package/lib/bdd-preflight.js +96 -0
- package/lib/chore-taxonomy.js +33 -10
- package/lib/database.js +36 -16
- package/lib/db-watcher.js +1 -1
- package/lib/git-hooks/pre-commit +1 -1
- package/lib/jettypod-backup.js +27 -4
- package/lib/merge-lock.js +111 -253
- package/lib/migrations/027-plan-at-creation-column.js +3 -1
- package/lib/migrations/029-remove-autoincrement.js +307 -0
- package/lib/migrations/029-rename-corrupted-to-cleaned.js +149 -0
- package/lib/migrations/030-rejection-round-columns.js +54 -0
- package/lib/migrations/031-session-isolation-index.js +17 -0
- package/lib/migrations/index.js +47 -4
- package/lib/schema.js +10 -5
- package/lib/seed-onboarding.js +1 -1
- package/lib/update-command/index.js +9 -175
- package/lib/work-commands/index.js +144 -19
- package/lib/work-tracking/index.js +148 -27
- package/lib/worktree-diagnostics.js +16 -16
- package/lib/worktree-facade.js +1 -1
- package/lib/worktree-manager.js +8 -8
- package/lib/worktree-reconciler.js +5 -5
- package/package.json +9 -2
- package/scripts/ndjson-to-cucumber-json.js +152 -0
- package/scripts/postinstall.js +25 -0
- package/skills-templates/bug-mode/SKILL.md +79 -20
- package/skills-templates/bug-planning/SKILL.md +25 -29
- package/skills-templates/chore-mode/SKILL.md +171 -69
- package/skills-templates/chore-mode/verification.js +51 -10
- package/skills-templates/chore-planning/SKILL.md +47 -18
- package/skills-templates/design-system-selection/SKILL.md +273 -0
- package/skills-templates/epic-planning/SKILL.md +82 -48
- package/skills-templates/external-transition/SKILL.md +47 -47
- package/skills-templates/feature-planning/SKILL.md +173 -74
- package/skills-templates/production-mode/SKILL.md +69 -49
- package/skills-templates/request-routing/SKILL.md +4 -4
- package/skills-templates/simple-improvement/SKILL.md +74 -29
- package/skills-templates/speed-mode/SKILL.md +217 -141
- package/skills-templates/stable-mode/SKILL.md +148 -89
- package/apps/dashboard/README.md +0 -36
- package/apps/dashboard/app/api/claude/[workItemId]/message/route.ts +0 -386
- package/apps/dashboard/app/api/claude/[workItemId]/pin/route.ts +0 -24
- package/apps/dashboard/app/api/claude/[workItemId]/route.ts +0 -167
- package/apps/dashboard/app/api/claude/sessions/[sessionId]/content/route.ts +0 -52
- package/apps/dashboard/app/api/claude/sessions/[sessionId]/message/route.ts +0 -378
- package/apps/dashboard/app/api/claude/sessions/[sessionId]/pin/route.ts +0 -24
- package/apps/dashboard/app/api/claude/sessions/cleanup/route.ts +0 -34
- package/apps/dashboard/app/api/claude/sessions/route.ts +0 -184
- package/apps/dashboard/app/api/decisions/[id]/route.ts +0 -25
- package/apps/dashboard/app/api/internal/set-project/route.ts +0 -17
- package/apps/dashboard/app/api/kanban/route.ts +0 -15
- package/apps/dashboard/app/api/settings/env-vars/route.ts +0 -125
- package/apps/dashboard/app/api/settings/general/route.ts +0 -21
- package/apps/dashboard/app/api/tests/route.ts +0 -9
- package/apps/dashboard/app/api/tests/run/route.ts +0 -82
- package/apps/dashboard/app/api/tests/run/stream/route.ts +0 -71
- package/apps/dashboard/app/api/tests/undefined/route.ts +0 -9
- package/apps/dashboard/app/api/usage/route.ts +0 -17
- package/apps/dashboard/app/api/work/[id]/description/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/epic/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/order/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/status/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/title/route.ts +0 -21
- package/apps/dashboard/app/layout.tsx +0 -43
- package/apps/dashboard/components/UpgradeBanner.tsx +0 -29
- package/apps/dashboard/electron/ipc-handlers.js +0 -1028
- package/apps/dashboard/electron/main.js +0 -2124
- package/apps/dashboard/electron/preload.js +0 -123
- package/apps/dashboard/electron/session-manager.js +0 -141
- package/apps/dashboard/electron-builder.config.js +0 -357
- package/apps/dashboard/hooks/useClaudeSessions.ts +0 -299
- package/apps/dashboard/lib/claude-process-manager.ts +0 -492
- package/apps/dashboard/lib/db-bridge.ts +0 -282
- package/apps/dashboard/lib/prototypes.ts +0 -202
- package/apps/dashboard/lib/test-results-db.ts +0 -307
- package/apps/dashboard/lib/tests.ts +0 -282
- package/apps/dashboard/next.config.js +0 -50
- package/apps/dashboard/postcss.config.mjs +0 -7
- package/apps/dashboard/public/file.svg +0 -1
- package/apps/dashboard/public/globe.svg +0 -1
- package/apps/dashboard/public/next.svg +0 -1
- package/apps/dashboard/public/vercel.svg +0 -1
- package/apps/dashboard/public/window.svg +0 -1
- package/apps/dashboard/scripts/download-node.js +0 -104
- package/apps/dashboard/scripts/upload-to-r2.js +0 -89
- package/docs/bdd-guidance.md +0 -390
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
const { contextBridge, ipcRenderer } = require('electron');
|
|
2
|
-
|
|
3
|
-
// Expose IPC methods to renderer process via window.electronAPI
|
|
4
|
-
contextBridge.exposeInMainWorld('electronAPI', {
|
|
5
|
-
// Database operations
|
|
6
|
-
db: {
|
|
7
|
-
// Kanban
|
|
8
|
-
getKanbanData: (doneLimit) => ipcRenderer.invoke('db:getKanbanData', doneLimit),
|
|
9
|
-
|
|
10
|
-
// Work items
|
|
11
|
-
getAllWorkItems: () => ipcRenderer.invoke('db:getAllWorkItems'),
|
|
12
|
-
getWorkItem: (id) => ipcRenderer.invoke('db:getWorkItem', id),
|
|
13
|
-
getChildWorkItems: (parentId) => ipcRenderer.invoke('db:getChildWorkItems', parentId),
|
|
14
|
-
updateWorkItemTitle: (id, title) => ipcRenderer.invoke('db:updateWorkItemTitle', id, title),
|
|
15
|
-
updateWorkItemStatus: (id, status) => ipcRenderer.invoke('db:updateWorkItemStatus', id, status),
|
|
16
|
-
updateWorkItemOrder: (id, displayOrder) => ipcRenderer.invoke('db:updateWorkItemOrder', id, displayOrder),
|
|
17
|
-
updateWorkItemEpic: (id, epicId) => ipcRenderer.invoke('db:updateWorkItemEpic', id, epicId),
|
|
18
|
-
|
|
19
|
-
// Decisions
|
|
20
|
-
getDecision: (id) => ipcRenderer.invoke('db:getDecision', id),
|
|
21
|
-
getDecisionsForWorkItem: (workItemId) => ipcRenderer.invoke('db:getDecisionsForWorkItem', workItemId),
|
|
22
|
-
|
|
23
|
-
// Sessions
|
|
24
|
-
listSessions: () => ipcRenderer.invoke('db:listSessions'),
|
|
25
|
-
createSession: (title) => ipcRenderer.invoke('db:createSession', title),
|
|
26
|
-
getSession: (sessionId) => ipcRenderer.invoke('db:getSession', sessionId),
|
|
27
|
-
closeSession: (sessionId) => ipcRenderer.invoke('db:closeSession', sessionId),
|
|
28
|
-
closeSessionByWorkItem: (workItemId) => ipcRenderer.invoke('db:closeSessionByWorkItem', workItemId),
|
|
29
|
-
linkSession: (sessionId, workItemId) => ipcRenderer.invoke('db:linkSession', sessionId, workItemId),
|
|
30
|
-
isLinkableWorkItem: (workItemId) => ipcRenderer.invoke('db:isLinkableWorkItem', workItemId),
|
|
31
|
-
getActiveSessionByWorkItem: (workItemId) => ipcRenderer.invoke('db:getActiveSessionByWorkItem', workItemId),
|
|
32
|
-
getOrCreateSessionForWorkItem: (workItemId) => ipcRenderer.invoke('db:getOrCreateSessionForWorkItem', workItemId),
|
|
33
|
-
countActiveSessions: () => ipcRenderer.invoke('db:countActiveSessions'),
|
|
34
|
-
cleanupStaleSessions: (retentionDays) => ipcRenderer.invoke('db:cleanupStaleSessions', retentionDays),
|
|
35
|
-
|
|
36
|
-
// Session content
|
|
37
|
-
getSessionContent: (sessionId) => ipcRenderer.invoke('db:getSessionContent', sessionId),
|
|
38
|
-
getSessionContentByWorkItem: (workItemId) => ipcRenderer.invoke('db:getSessionContentByWorkItem', workItemId),
|
|
39
|
-
appendSessionContent: (sessionId, turn) => ipcRenderer.invoke('db:appendSessionContent', sessionId, turn),
|
|
40
|
-
appendSessionContentByWorkItem: (workItemId, turn) => ipcRenderer.invoke('db:appendSessionContentByWorkItem', workItemId, turn),
|
|
41
|
-
|
|
42
|
-
// Environment variables
|
|
43
|
-
getEnvVars: () => ipcRenderer.invoke('db:getEnvVars'),
|
|
44
|
-
setEnvVar: (name, value) => ipcRenderer.invoke('db:setEnvVar', name, value),
|
|
45
|
-
deleteEnvVar: (name) => ipcRenderer.invoke('db:deleteEnvVar', name),
|
|
46
|
-
|
|
47
|
-
// Project info
|
|
48
|
-
getProjectName: () => ipcRenderer.invoke('db:getProjectName'),
|
|
49
|
-
|
|
50
|
-
},
|
|
51
|
-
|
|
52
|
-
// Claude subprocess management
|
|
53
|
-
claude: {
|
|
54
|
-
spawn: (sessionId, cwd) => ipcRenderer.invoke('claude:spawn', sessionId, cwd),
|
|
55
|
-
write: (sessionId, text) => ipcRenderer.invoke('claude:write', sessionId, text),
|
|
56
|
-
kill: (sessionId) => ipcRenderer.invoke('claude:kill', sessionId),
|
|
57
|
-
onOutput: (callback) => {
|
|
58
|
-
ipcRenderer.on('claude:output', (event, data) => callback(data));
|
|
59
|
-
// Return cleanup function
|
|
60
|
-
return () => ipcRenderer.removeAllListeners('claude:output');
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
// Dev server management for app preview
|
|
65
|
-
devServer: {
|
|
66
|
-
spawn: (projectPath, command, port) => ipcRenderer.invoke('devServer:spawn', projectPath, command, port),
|
|
67
|
-
kill: (projectPath) => ipcRenderer.invoke('devServer:kill', projectPath),
|
|
68
|
-
status: (projectPath) => ipcRenderer.invoke('devServer:status', projectPath),
|
|
69
|
-
list: () => ipcRenderer.invoke('devServer:list'),
|
|
70
|
-
onOutput: (callback) => {
|
|
71
|
-
ipcRenderer.on('devServer:output', (event, data) => callback(data));
|
|
72
|
-
// Return cleanup function
|
|
73
|
-
return () => ipcRenderer.removeAllListeners('devServer:output');
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
// Project operations
|
|
78
|
-
project: {
|
|
79
|
-
openDialog: () => ipcRenderer.invoke('dialog:openProject'),
|
|
80
|
-
newProject: () => ipcRenderer.invoke('dialog:newProject'),
|
|
81
|
-
getRecent: () => ipcRenderer.invoke('projects:getRecent'),
|
|
82
|
-
addRecent: (path) => ipcRenderer.invoke('projects:addRecent', path),
|
|
83
|
-
openRecent: (path) => ipcRenderer.invoke('projects:openRecent', path),
|
|
84
|
-
},
|
|
85
|
-
|
|
86
|
-
// Claude Code operations
|
|
87
|
-
claudeCode: {
|
|
88
|
-
install: () => ipcRenderer.invoke('claudeCode:install'),
|
|
89
|
-
isInstalled: () => ipcRenderer.invoke('claudeCode:isInstalled'),
|
|
90
|
-
isAuthenticated: () => ipcRenderer.invoke('claudeCode:isAuthenticated'),
|
|
91
|
-
login: () => ipcRenderer.invoke('claudeCode:login'),
|
|
92
|
-
update: () => ipcRenderer.invoke('claudeCode:update'),
|
|
93
|
-
},
|
|
94
|
-
|
|
95
|
-
// Subscription gating (legacy)
|
|
96
|
-
subscription: {
|
|
97
|
-
createCheckout: (plan) => ipcRenderer.invoke('subscription:createCheckout', plan),
|
|
98
|
-
activate: (customerId) => ipcRenderer.invoke('subscription:activate', customerId),
|
|
99
|
-
getStatus: () => ipcRenderer.invoke('subscription:getStatus'),
|
|
100
|
-
},
|
|
101
|
-
|
|
102
|
-
// Billing
|
|
103
|
-
billing: {
|
|
104
|
-
openCustomerPortal: () => ipcRenderer.invoke('billing:openCustomerPortal'),
|
|
105
|
-
},
|
|
106
|
-
|
|
107
|
-
// Auth (JWT-based)
|
|
108
|
-
auth: {
|
|
109
|
-
loginWithGoogle: () => ipcRenderer.invoke('auth:loginWithGoogle'),
|
|
110
|
-
saveToken: (token, user) => ipcRenderer.invoke('auth:saveToken', token, user),
|
|
111
|
-
getStatus: () => ipcRenderer.invoke('auth:getStatus'),
|
|
112
|
-
getToken: () => ipcRenderer.invoke('auth:getToken'),
|
|
113
|
-
logout: () => ipcRenderer.invoke('auth:logout'),
|
|
114
|
-
},
|
|
115
|
-
|
|
116
|
-
// Shell operations
|
|
117
|
-
shell: {
|
|
118
|
-
openPath: (filePath) => ipcRenderer.invoke('shell:openPath', filePath),
|
|
119
|
-
},
|
|
120
|
-
|
|
121
|
-
// Check if running in Electron
|
|
122
|
-
isElectron: true,
|
|
123
|
-
});
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const { app } = require('electron');
|
|
4
|
-
|
|
5
|
-
const UPDATE_SERVER_URL = 'https://jettypod-update-server.spangbaryn2.workers.dev';
|
|
6
|
-
const HEARTBEAT_INTERVAL_MS = 4 * 60 * 60 * 1000; // 4 hours
|
|
7
|
-
|
|
8
|
-
let heartbeatInterval = null;
|
|
9
|
-
let updaterHeaders = {};
|
|
10
|
-
let log = console.log;
|
|
11
|
-
|
|
12
|
-
function setLogger(logFn) {
|
|
13
|
-
log = logFn;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function getAuthPath() {
|
|
17
|
-
return path.join(app.getPath('userData'), 'auth.json');
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function readAuth() {
|
|
21
|
-
const authPath = getAuthPath();
|
|
22
|
-
if (!fs.existsSync(authPath)) return null;
|
|
23
|
-
try {
|
|
24
|
-
return JSON.parse(fs.readFileSync(authPath, 'utf-8'));
|
|
25
|
-
} catch {
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function saveAuth(data) {
|
|
31
|
-
const authPath = getAuthPath();
|
|
32
|
-
const dir = path.dirname(authPath);
|
|
33
|
-
if (!fs.existsSync(dir)) {
|
|
34
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
35
|
-
}
|
|
36
|
-
fs.writeFileSync(authPath, JSON.stringify(data, null, 2));
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function decodeJWT(token) {
|
|
40
|
-
const parts = token.split('.');
|
|
41
|
-
if (parts.length !== 3) return null;
|
|
42
|
-
try {
|
|
43
|
-
const payload = parts[1].replace(/-/g, '+').replace(/_/g, '/');
|
|
44
|
-
return JSON.parse(Buffer.from(payload, 'base64').toString('utf-8'));
|
|
45
|
-
} catch {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function updateAutoUpdaterHeaders() {
|
|
51
|
-
const auth = readAuth();
|
|
52
|
-
if (auth && auth.token) {
|
|
53
|
-
updaterHeaders = { 'Authorization': `Bearer ${auth.token}` };
|
|
54
|
-
} else {
|
|
55
|
-
updaterHeaders = {};
|
|
56
|
-
}
|
|
57
|
-
return updaterHeaders;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async function heartbeat() {
|
|
61
|
-
const auth = readAuth();
|
|
62
|
-
if (!auth || !auth.token) {
|
|
63
|
-
log('[Session] No auth token for heartbeat');
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
log('[Session] Heartbeat: checking /auth/me...');
|
|
68
|
-
try {
|
|
69
|
-
const response = await fetch(`${UPDATE_SERVER_URL}/auth/me`, {
|
|
70
|
-
headers: { 'Authorization': `Bearer ${auth.token}` },
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
if (!response.ok) {
|
|
74
|
-
log(`[Session] Heartbeat failed: ${response.status}`);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const data = await response.json();
|
|
79
|
-
|
|
80
|
-
// If server returned a new token (plan changed), save it
|
|
81
|
-
if (data.token) {
|
|
82
|
-
const payload = decodeJWT(data.token);
|
|
83
|
-
const user = payload
|
|
84
|
-
? { id: payload.sub, email: payload.email, plan: payload.plan }
|
|
85
|
-
: auth.user;
|
|
86
|
-
saveAuth({ token: data.token, user, savedAt: new Date().toISOString() });
|
|
87
|
-
log('[Session] Token refreshed from server');
|
|
88
|
-
} else if (data.user && data.user.plan !== auth.user?.plan) {
|
|
89
|
-
// Plan changed but no new token — update user info
|
|
90
|
-
saveAuth({ ...auth, user: data.user, savedAt: new Date().toISOString() });
|
|
91
|
-
log(`[Session] Plan updated: ${auth.user?.plan} → ${data.user.plan}`);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
updateAutoUpdaterHeaders();
|
|
95
|
-
} catch (error) {
|
|
96
|
-
log(`[Session] Heartbeat error: ${error.message}`);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function start() {
|
|
101
|
-
log('[Session] Starting session manager...');
|
|
102
|
-
updateAutoUpdaterHeaders();
|
|
103
|
-
|
|
104
|
-
// Run first heartbeat after a short delay (don't block startup)
|
|
105
|
-
setTimeout(heartbeat, 5000);
|
|
106
|
-
|
|
107
|
-
// Schedule periodic heartbeats
|
|
108
|
-
heartbeatInterval = setInterval(heartbeat, HEARTBEAT_INTERVAL_MS);
|
|
109
|
-
|
|
110
|
-
log('[Session] Session manager started (heartbeat every 4 hours)');
|
|
111
|
-
return true;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
function stop() {
|
|
115
|
-
if (heartbeatInterval) {
|
|
116
|
-
clearInterval(heartbeatInterval);
|
|
117
|
-
heartbeatInterval = null;
|
|
118
|
-
}
|
|
119
|
-
log('[Session] Session manager stopped');
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function isRunning() {
|
|
123
|
-
return heartbeatInterval !== null;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function getUpdaterHeaders() {
|
|
127
|
-
return updaterHeaders;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
module.exports = {
|
|
131
|
-
start,
|
|
132
|
-
stop,
|
|
133
|
-
heartbeat,
|
|
134
|
-
readAuth,
|
|
135
|
-
saveAuth,
|
|
136
|
-
updateAutoUpdaterHeaders,
|
|
137
|
-
isRunning,
|
|
138
|
-
getUpdaterHeaders,
|
|
139
|
-
setLogger,
|
|
140
|
-
getAuthPath,
|
|
141
|
-
};
|
|
@@ -1,357 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const os = require('os');
|
|
4
|
-
const { execSync } = require('child_process');
|
|
5
|
-
|
|
6
|
-
// Minimum disk space required for build (2GB in bytes)
|
|
7
|
-
const MIN_DISK_SPACE_GB = 2;
|
|
8
|
-
const MIN_DISK_SPACE_BYTES = MIN_DISK_SPACE_GB * 1024 * 1024 * 1024;
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Get available disk space in bytes for the given path
|
|
12
|
-
* @param {string} dirPath - Directory to check
|
|
13
|
-
* @returns {number} Available space in bytes
|
|
14
|
-
*/
|
|
15
|
-
function getAvailableDiskSpace(dirPath) {
|
|
16
|
-
try {
|
|
17
|
-
// Use df command to get disk space (works on macOS and Linux)
|
|
18
|
-
const output = execSync(`df -k "${dirPath}"`, { encoding: 'utf-8' });
|
|
19
|
-
const lines = output.trim().split('\n');
|
|
20
|
-
if (lines.length < 2) return Infinity; // Can't parse, assume OK
|
|
21
|
-
|
|
22
|
-
// Parse the output (second line, fourth column is available space in KB)
|
|
23
|
-
const parts = lines[1].split(/\s+/);
|
|
24
|
-
const availableKB = parseInt(parts[3], 10);
|
|
25
|
-
if (isNaN(availableKB)) return Infinity; // Can't parse, assume OK
|
|
26
|
-
|
|
27
|
-
return availableKB * 1024; // Convert to bytes
|
|
28
|
-
} catch (error) {
|
|
29
|
-
// If we can't check, don't block the build
|
|
30
|
-
console.warn('⚠️ Could not check disk space:', error.message);
|
|
31
|
-
return Infinity;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Clean up partial build artifacts
|
|
37
|
-
* @param {string} distDir - Distribution directory to clean
|
|
38
|
-
*/
|
|
39
|
-
function cleanupPartialArtifacts(distDir) {
|
|
40
|
-
try {
|
|
41
|
-
if (fs.existsSync(distDir)) {
|
|
42
|
-
const files = fs.readdirSync(distDir);
|
|
43
|
-
for (const file of files) {
|
|
44
|
-
if (file.includes('.tmp') || file.includes('.partial') || file.endsWith('.blockmap')) {
|
|
45
|
-
const filePath = path.join(distDir, file);
|
|
46
|
-
fs.unlinkSync(filePath);
|
|
47
|
-
console.log(` Cleaned up: ${file}`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
} catch (error) {
|
|
52
|
-
console.warn('⚠️ Could not clean up artifacts:', error.message);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* electron-builder configuration
|
|
58
|
-
* @type {import('electron-builder').Configuration}
|
|
59
|
-
*/
|
|
60
|
-
module.exports = {
|
|
61
|
-
// Pre-build validation hook
|
|
62
|
-
beforeBuild: async (context) => {
|
|
63
|
-
// Check disk space for all platforms
|
|
64
|
-
const projectDir = __dirname;
|
|
65
|
-
const distDir = path.join(projectDir, 'dist');
|
|
66
|
-
const availableSpace = getAvailableDiskSpace(projectDir);
|
|
67
|
-
const availableGB = (availableSpace / (1024 * 1024 * 1024)).toFixed(2);
|
|
68
|
-
|
|
69
|
-
if (availableSpace < MIN_DISK_SPACE_BYTES) {
|
|
70
|
-
console.error('\n❌ ERROR: Insufficient disk space');
|
|
71
|
-
console.error('');
|
|
72
|
-
console.error(`Available: ${availableGB} GB`);
|
|
73
|
-
console.error(`Required: ${MIN_DISK_SPACE_GB} GB minimum`);
|
|
74
|
-
console.error('');
|
|
75
|
-
console.error('Electron builds require significant disk space for:');
|
|
76
|
-
console.error(' - Node modules packaging');
|
|
77
|
-
console.error(' - Native module compilation');
|
|
78
|
-
console.error(' - DMG/installer creation');
|
|
79
|
-
console.error('');
|
|
80
|
-
console.error('Free up disk space and try again.');
|
|
81
|
-
|
|
82
|
-
// Clean up any partial artifacts
|
|
83
|
-
console.error('\nCleaning up partial build artifacts...');
|
|
84
|
-
cleanupPartialArtifacts(distDir);
|
|
85
|
-
|
|
86
|
-
process.exit(1);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Only check macOS-specific requirements for mac builds
|
|
90
|
-
if (context.platform.name === 'mac') {
|
|
91
|
-
// Check for signing certificate (skip if CSC_IDENTITY_AUTO_DISCOVERY=false for local dev)
|
|
92
|
-
const skipSigning = process.env.CSC_IDENTITY_AUTO_DISCOVERY === 'false';
|
|
93
|
-
|
|
94
|
-
if (skipSigning) {
|
|
95
|
-
console.warn('\n⚠️ WARNING: Code signing disabled (CSC_IDENTITY_AUTO_DISCOVERY=false)');
|
|
96
|
-
console.warn('Build will be unsigned - for local development only.\n');
|
|
97
|
-
} else if (!process.env.CSC_LINK && !process.env.CSC_NAME) {
|
|
98
|
-
console.error('\n❌ ERROR: Code signing certificate not configured');
|
|
99
|
-
console.error('');
|
|
100
|
-
console.error('To sign macOS builds, you need to set one of:');
|
|
101
|
-
console.error(' CSC_LINK - Path to .p12 certificate file (or base64 encoded)');
|
|
102
|
-
console.error(' CSC_NAME - Name of certificate in Keychain');
|
|
103
|
-
console.error('');
|
|
104
|
-
console.error('And if using CSC_LINK:');
|
|
105
|
-
console.error(' CSC_KEY_PASSWORD - Password for the .p12 certificate');
|
|
106
|
-
console.error('');
|
|
107
|
-
console.error('Unsigned builds are not allowed. Aborting.');
|
|
108
|
-
process.exit(1);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// If CSC_LINK is set, also need CSC_KEY_PASSWORD (skip if signing disabled)
|
|
112
|
-
if (!skipSigning && process.env.CSC_LINK && !process.env.CSC_KEY_PASSWORD) {
|
|
113
|
-
console.error('\n❌ ERROR: CSC_KEY_PASSWORD not set');
|
|
114
|
-
console.error('');
|
|
115
|
-
console.error('CSC_LINK is configured but CSC_KEY_PASSWORD is missing.');
|
|
116
|
-
console.error('Set CSC_KEY_PASSWORD to the password for your .p12 certificate.');
|
|
117
|
-
console.error('');
|
|
118
|
-
console.error('Aborting build.');
|
|
119
|
-
process.exit(1);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Warn if notarization credentials are missing (skip if signing disabled)
|
|
123
|
-
if (!skipSigning && !process.env.APPLE_TEAM_ID) {
|
|
124
|
-
console.warn('\n⚠️ WARNING: APPLE_TEAM_ID not set');
|
|
125
|
-
console.warn('');
|
|
126
|
-
console.warn('Notarization will be SKIPPED. The DMG will be signed but not notarized.');
|
|
127
|
-
console.warn('Users may see "unidentified developer" warnings on first launch.');
|
|
128
|
-
console.warn('');
|
|
129
|
-
console.warn('To enable notarization, set these environment variables:');
|
|
130
|
-
console.warn(' APPLE_TEAM_ID - Your Apple Developer Team ID');
|
|
131
|
-
console.warn(' APPLE_ID - Your Apple ID email (for notarytool)');
|
|
132
|
-
console.warn(' APPLE_APP_SPECIFIC_PASSWORD - App-specific password');
|
|
133
|
-
console.warn('');
|
|
134
|
-
console.warn('Continuing with build (without notarization)...\n');
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Check for entitlements file
|
|
138
|
-
const entitlementsPath = path.join(__dirname, 'build-resources/entitlements.mac.plist');
|
|
139
|
-
if (!fs.existsSync(entitlementsPath)) {
|
|
140
|
-
console.error('\n❌ ERROR: Entitlements file not found');
|
|
141
|
-
console.error('');
|
|
142
|
-
console.error(`Expected file at: ${entitlementsPath}`);
|
|
143
|
-
console.error('');
|
|
144
|
-
console.error('The entitlements file is required for hardened runtime signing.');
|
|
145
|
-
console.error('Create build-resources/entitlements.mac.plist with the required permissions.');
|
|
146
|
-
console.error('');
|
|
147
|
-
console.error('Aborting build.');
|
|
148
|
-
process.exit(1);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
},
|
|
152
|
-
|
|
153
|
-
appId: 'com.jettypod.dashboard',
|
|
154
|
-
productName: 'JettyPod',
|
|
155
|
-
|
|
156
|
-
// Directories
|
|
157
|
-
directories: {
|
|
158
|
-
output: 'dist',
|
|
159
|
-
buildResources: 'build-resources',
|
|
160
|
-
},
|
|
161
|
-
|
|
162
|
-
// Files to include in the app
|
|
163
|
-
// Note: node_modules is handled via extraFiles (with filter to exclude better-sqlite3)
|
|
164
|
-
// and extraResources (for better-sqlite3 with proper Electron rebuild)
|
|
165
|
-
files: [
|
|
166
|
-
'electron/**/*',
|
|
167
|
-
'lib/run-migrations.js',
|
|
168
|
-
'public/**/*',
|
|
169
|
-
'package.json',
|
|
170
|
-
],
|
|
171
|
-
|
|
172
|
-
// Extra files to copy to app directory (for dotfiles like .next)
|
|
173
|
-
extraFiles: [
|
|
174
|
-
{
|
|
175
|
-
from: '.next',
|
|
176
|
-
to: 'Resources/app/.next',
|
|
177
|
-
filter: [
|
|
178
|
-
'**/*',
|
|
179
|
-
// Exclude dev-only caches (not needed for production, breaks codesigning)
|
|
180
|
-
'!dev/**',
|
|
181
|
-
'!cache/**',
|
|
182
|
-
],
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
from: 'node_modules',
|
|
186
|
-
to: 'Resources/app/node_modules',
|
|
187
|
-
filter: [
|
|
188
|
-
'**/*',
|
|
189
|
-
// Exclude electron/build tooling (better-sqlite3 stays - rebuilt by npmRebuild)
|
|
190
|
-
'!electron/**',
|
|
191
|
-
'!electron-builder/**',
|
|
192
|
-
'!.bin/**',
|
|
193
|
-
// Exclude cross-platform binaries that break codesigning
|
|
194
|
-
'!7zip-bin/**',
|
|
195
|
-
'!app-builder-bin/**',
|
|
196
|
-
'!electron-winstaller/**',
|
|
197
|
-
'!dmg-builder/**',
|
|
198
|
-
// Exclude wrong-architecture native modules (Linux)
|
|
199
|
-
'!@next/swc-linux-*/**',
|
|
200
|
-
'!@img/sharp-linux-*/**',
|
|
201
|
-
'!@tailwindcss/oxide-linux-*/**',
|
|
202
|
-
'!@unrs/resolver-binding-linux-*/**',
|
|
203
|
-
'!lightningcss-linux-*/**',
|
|
204
|
-
// Exclude wrong-architecture native modules (Windows)
|
|
205
|
-
'!@next/swc-win32-*/**',
|
|
206
|
-
'!@img/sharp-win32-*/**',
|
|
207
|
-
'!@tailwindcss/oxide-win32-*/**',
|
|
208
|
-
'!@unrs/resolver-binding-win32-*/**',
|
|
209
|
-
'!lightningcss-win32-*/**',
|
|
210
|
-
],
|
|
211
|
-
},
|
|
212
|
-
],
|
|
213
|
-
|
|
214
|
-
// Extra resources (not bundled into asar)
|
|
215
|
-
extraResources: [
|
|
216
|
-
// Bundled Node.js for Claude Code installation (architecture-specific)
|
|
217
|
-
{
|
|
218
|
-
from: 'build-resources/node/${arch}',
|
|
219
|
-
to: 'node',
|
|
220
|
-
filter: ['**/*'],
|
|
221
|
-
},
|
|
222
|
-
// JettyPod CLI - bundled for PATH setup
|
|
223
|
-
{
|
|
224
|
-
from: '../../bin/jettypod',
|
|
225
|
-
to: 'bin/jettypod',
|
|
226
|
-
},
|
|
227
|
-
{
|
|
228
|
-
from: '../../jettypod.js',
|
|
229
|
-
to: 'bin/jettypod.js',
|
|
230
|
-
},
|
|
231
|
-
{
|
|
232
|
-
from: '../../lib',
|
|
233
|
-
to: 'bin/lib',
|
|
234
|
-
filter: ['**/*'],
|
|
235
|
-
},
|
|
236
|
-
{
|
|
237
|
-
from: '../../package.json',
|
|
238
|
-
to: 'bin/package.json',
|
|
239
|
-
},
|
|
240
|
-
// JettyPod skills - bundled for auto-sync on launch
|
|
241
|
-
{
|
|
242
|
-
from: path.join(os.homedir(), '.claude', 'skills'),
|
|
243
|
-
to: 'skills',
|
|
244
|
-
filter: ['**/*'],
|
|
245
|
-
},
|
|
246
|
-
],
|
|
247
|
-
|
|
248
|
-
// Don't pack into asar (native modules need filesystem access)
|
|
249
|
-
asar: false,
|
|
250
|
-
|
|
251
|
-
// App icon (electron-builder auto-converts PNG to platform formats)
|
|
252
|
-
icon: 'build-resources/icon.png',
|
|
253
|
-
|
|
254
|
-
// macOS configuration
|
|
255
|
-
mac: {
|
|
256
|
-
target: [
|
|
257
|
-
{
|
|
258
|
-
target: 'dmg',
|
|
259
|
-
// Allow BUILD_ARCH env var to override (for local dev builds)
|
|
260
|
-
arch: process.env.BUILD_ARCH ? [process.env.BUILD_ARCH] : ['arm64', 'x64'],
|
|
261
|
-
},
|
|
262
|
-
],
|
|
263
|
-
category: 'public.app-category.developer-tools',
|
|
264
|
-
hardenedRuntime: true,
|
|
265
|
-
gatekeeperAssess: false,
|
|
266
|
-
icon: 'build-resources/icon.png',
|
|
267
|
-
|
|
268
|
-
// Entitlements for hardened runtime
|
|
269
|
-
entitlements: 'build-resources/entitlements.mac.plist',
|
|
270
|
-
entitlementsInherit: 'build-resources/entitlements.mac.plist',
|
|
271
|
-
|
|
272
|
-
// Notarization - enabled when APPLE_TEAM_ID, APPLE_ID, and APPLE_APP_SPECIFIC_PASSWORD are set
|
|
273
|
-
// electron-builder reads these env vars automatically
|
|
274
|
-
notarize: !!process.env.APPLE_TEAM_ID,
|
|
275
|
-
},
|
|
276
|
-
|
|
277
|
-
// DMG configuration
|
|
278
|
-
dmg: {
|
|
279
|
-
contents: [
|
|
280
|
-
{
|
|
281
|
-
x: 130,
|
|
282
|
-
y: 220,
|
|
283
|
-
},
|
|
284
|
-
{
|
|
285
|
-
x: 410,
|
|
286
|
-
y: 220,
|
|
287
|
-
type: 'link',
|
|
288
|
-
path: '/Applications',
|
|
289
|
-
},
|
|
290
|
-
],
|
|
291
|
-
},
|
|
292
|
-
|
|
293
|
-
// Windows configuration
|
|
294
|
-
win: {
|
|
295
|
-
target: [
|
|
296
|
-
{
|
|
297
|
-
target: 'nsis',
|
|
298
|
-
arch: ['x64'],
|
|
299
|
-
},
|
|
300
|
-
],
|
|
301
|
-
},
|
|
302
|
-
|
|
303
|
-
// Linux configuration
|
|
304
|
-
linux: {
|
|
305
|
-
target: [
|
|
306
|
-
{
|
|
307
|
-
target: 'AppImage',
|
|
308
|
-
arch: ['x64'],
|
|
309
|
-
},
|
|
310
|
-
],
|
|
311
|
-
category: 'Development',
|
|
312
|
-
},
|
|
313
|
-
|
|
314
|
-
// Rebuild native modules
|
|
315
|
-
npmRebuild: true,
|
|
316
|
-
|
|
317
|
-
// Auto-update configuration - generic provider pointing to update server
|
|
318
|
-
publish: {
|
|
319
|
-
provider: 'generic',
|
|
320
|
-
url: 'https://jettypod-update-server.spangbaryn2.workers.dev/updates',
|
|
321
|
-
},
|
|
322
|
-
|
|
323
|
-
// Hooks for native module handling
|
|
324
|
-
afterPack: async (context) => {
|
|
325
|
-
const { rebuild } = require('@electron/rebuild');
|
|
326
|
-
const { Arch } = require('builder-util');
|
|
327
|
-
|
|
328
|
-
// Convert numeric arch enum to string (Arch.arm64=3, Arch.x64=1)
|
|
329
|
-
const archString = Arch[context.arch]; // "arm64", "x64", etc.
|
|
330
|
-
|
|
331
|
-
// macOS: appOutDir is dist/mac-arm64, app is at JettyPod.app/Contents/Resources/app
|
|
332
|
-
// context.packager.appInfo.productFilename gives us "JettyPod"
|
|
333
|
-
const appName = context.packager.appInfo.productFilename;
|
|
334
|
-
const resourcesPath = context.electronPlatformName === 'darwin'
|
|
335
|
-
? path.join(context.appOutDir, `${appName}.app`, 'Contents', 'Resources', 'app')
|
|
336
|
-
: path.join(context.appOutDir, 'resources', 'app');
|
|
337
|
-
|
|
338
|
-
const betterSqlitePath = path.join(resourcesPath, 'node_modules', 'better-sqlite3');
|
|
339
|
-
|
|
340
|
-
// Only rebuild if better-sqlite3 exists in the output
|
|
341
|
-
if (fs.existsSync(betterSqlitePath)) {
|
|
342
|
-
console.log(`Rebuilding better-sqlite3 for ${archString} at ${resourcesPath}...`);
|
|
343
|
-
|
|
344
|
-
await rebuild({
|
|
345
|
-
buildPath: resourcesPath,
|
|
346
|
-
electronVersion: '32.3.3',
|
|
347
|
-
arch: archString,
|
|
348
|
-
onlyModules: ['better-sqlite3'],
|
|
349
|
-
force: true,
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
console.log('better-sqlite3 rebuilt successfully');
|
|
353
|
-
} else {
|
|
354
|
-
console.log(`better-sqlite3 not found at ${betterSqlitePath}, skipping rebuild`);
|
|
355
|
-
}
|
|
356
|
-
},
|
|
357
|
-
};
|