jettypod 4.4.120 ā 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 +2 -1
- 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 +54 -49
- package/apps/dashboard/app/demo/gates/page.tsx +3 -5
- package/apps/dashboard/app/design-system/page.tsx +1 -1
- package/apps/dashboard/app/globals.css +74 -2
- package/apps/dashboard/app/install-claude/page.tsx +3 -5
- package/apps/dashboard/app/login/page.tsx +17 -20
- package/apps/dashboard/app/page.tsx +101 -48
- package/apps/dashboard/app/settings/page.tsx +60 -12
- package/apps/dashboard/app/signup/page.tsx +14 -17
- 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 +12 -15
- package/apps/dashboard/app/work/[id]/page.tsx +90 -75
- package/apps/dashboard/app/work/[id]/proof/page.tsx +1489 -0
- package/apps/dashboard/components/AppShell.tsx +70 -61
- package/apps/dashboard/components/CardMenu.tsx +0 -1
- package/apps/dashboard/components/ClaudePanel.tsx +541 -283
- package/apps/dashboard/components/ClaudePanelInput.tsx +23 -4
- package/apps/dashboard/components/ConnectClaudeScreen.tsx +1 -5
- package/apps/dashboard/components/CopyableId.tsx +1 -2
- package/apps/dashboard/components/DetailReviewActions.tsx +11 -20
- package/apps/dashboard/components/DragContext.tsx +132 -62
- package/apps/dashboard/components/DraggableCard.tsx +3 -5
- package/apps/dashboard/components/DropZone.tsx +5 -6
- package/apps/dashboard/components/EditableDetailDescription.tsx +6 -12
- package/apps/dashboard/components/EditableDetailTitle.tsx +6 -13
- package/apps/dashboard/components/EditableTitle.tsx +0 -1
- package/apps/dashboard/components/ElapsedTimer.tsx +15 -3
- package/apps/dashboard/components/EpicGroup.tsx +100 -70
- package/apps/dashboard/components/GateCard.tsx +0 -1
- package/apps/dashboard/components/GateChoiceCard.tsx +1 -2
- package/apps/dashboard/components/InstallClaudeScreen.tsx +1 -5
- package/apps/dashboard/components/JettyLoader.tsx +0 -1
- package/apps/dashboard/components/KanbanBoard.tsx +319 -173
- package/apps/dashboard/components/KanbanCard.tsx +341 -107
- package/apps/dashboard/components/LazyCard.tsx +62 -0
- package/apps/dashboard/components/LazyMarkdown.tsx +0 -1
- package/apps/dashboard/components/MainNav.tsx +24 -25
- package/apps/dashboard/components/MessageBlock.tsx +93 -16
- package/apps/dashboard/components/ModeStartCard.tsx +0 -1
- package/apps/dashboard/components/OnboardingWelcome.tsx +0 -1
- package/apps/dashboard/components/PlaceholderCard.tsx +0 -1
- package/apps/dashboard/components/ProjectSwitcher.tsx +20 -20
- package/apps/dashboard/components/PrototypeTimeline.tsx +47 -26
- package/apps/dashboard/components/RealTimeKanbanWrapper.tsx +308 -223
- package/apps/dashboard/components/RealTimeTestsWrapper.tsx +303 -160
- package/apps/dashboard/components/ReviewFooter.tsx +12 -14
- package/apps/dashboard/components/SessionList.tsx +0 -1
- package/apps/dashboard/components/SubscribeContent.tsx +40 -11
- package/apps/dashboard/components/TestTree.tsx +1 -2
- package/apps/dashboard/components/TipCard.tsx +2 -4
- package/apps/dashboard/components/Toast.tsx +0 -1
- package/apps/dashboard/components/TypeIcon.tsx +7 -8
- package/apps/dashboard/components/ViewModeToolbar.tsx +104 -0
- package/apps/dashboard/components/WaveCompletionAnimation.tsx +5 -17
- package/apps/dashboard/components/WelcomeScreen.tsx +2 -6
- package/apps/dashboard/components/WorkItemHeader.tsx +0 -1
- package/apps/dashboard/components/WorkItemTree.tsx +2 -4
- package/apps/dashboard/components/settings/AccountSection.tsx +27 -13
- 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 +20 -73
- package/apps/dashboard/components/settings/GeneralSection.tsx +137 -26
- package/apps/dashboard/components/settings/ProjectStackSection.tsx +948 -0
- package/apps/dashboard/components/settings/SettingsLayout.tsx +0 -1
- package/apps/dashboard/components/ui/Button.tsx +1 -1
- package/apps/dashboard/components/ui/Input.tsx +1 -1
- package/apps/dashboard/components.json +1 -1
- package/apps/dashboard/contexts/ClaudeSessionContext.tsx +611 -358
- package/apps/dashboard/contexts/ConnectionStatusContext.tsx +0 -1
- package/apps/dashboard/contexts/UsageContext.tsx +62 -31
- package/apps/dashboard/dev.sh +35 -0
- package/apps/dashboard/eslint.config.mjs +9 -9
- package/apps/dashboard/hooks/useWebSocket.ts +138 -83
- package/apps/dashboard/index.html +73 -0
- package/apps/dashboard/lib/data-bridge.ts +722 -0
- package/apps/dashboard/lib/db.ts +69 -1302
- 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 +226 -26
- package/apps/dashboard/lib/proof-run.ts +495 -0
- package/apps/dashboard/lib/proof-scenario-runner.ts +346 -0
- 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 +253 -122
- 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 +3 -3
- package/apps/dashboard/next-env.d.ts +1 -1
- package/apps/dashboard/package.json +21 -33
- package/apps/dashboard/public/bug-icon.png +0 -0
- package/apps/dashboard/public/buoy-icon.png +0 -0
- package/apps/dashboard/public/in-flight-seagull.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/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 +167 -30
- 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/jettypod.js +96 -4
- package/lib/bdd-preflight.js +96 -0
- package/lib/merge-lock.js +111 -253
- package/lib/migrations/030-rejection-round-columns.js +54 -0
- package/lib/migrations/031-session-isolation-index.js +17 -0
- package/lib/work-commands/index.js +58 -16
- package/lib/work-tracking/index.js +108 -8
- package/package.json +1 -1
- package/skills-templates/bug-mode/SKILL.md +43 -1
- package/skills-templates/chore-mode/SKILL.md +40 -1
- package/skills-templates/design-system-selection/SKILL.md +273 -0
- package/skills-templates/epic-planning/SKILL.md +14 -0
- package/skills-templates/feature-planning/SKILL.md +90 -1
- package/skills-templates/production-mode/SKILL.md +20 -0
- package/skills-templates/simple-improvement/SKILL.md +39 -2
- package/skills-templates/speed-mode/SKILL.md +10 -15
- package/skills-templates/stable-mode/SKILL.md +47 -0
- package/apps/dashboard/README.md +0 -36
- package/apps/dashboard/app/api/claude/[workItemId]/message/route.ts +0 -446
- package/apps/dashboard/app/api/claude/[workItemId]/pin/route.ts +0 -24
- package/apps/dashboard/app/api/claude/[workItemId]/route.ts +0 -280
- 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 -525
- 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]/route.ts +0 -35
- package/apps/dashboard/app/api/work/[id]/status/route.ts +0 -63
- package/apps/dashboard/app/api/work/[id]/title/route.ts +0 -21
- package/apps/dashboard/app/layout.tsx +0 -55
- package/apps/dashboard/components/UpgradeBanner.tsx +0 -30
- package/apps/dashboard/electron/ipc-handlers.js +0 -1026
- package/apps/dashboard/electron/main.js +0 -2306
- package/apps/dashboard/electron/preload.js +0 -125
- package/apps/dashboard/electron/session-manager.js +0 -163
- package/apps/dashboard/electron-builder.config.js +0 -357
- package/apps/dashboard/hooks/useClaudeSessions.ts +0 -299
- package/apps/dashboard/lib/backlog-parser.ts +0 -50
- package/apps/dashboard/lib/claude-process-manager.ts +0 -529
- package/apps/dashboard/lib/db-bridge.ts +0 -283
- 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 -66
- package/apps/dashboard/postcss.config.mjs +0 -7
- package/apps/dashboard/public/bug-icon.svg +0 -9
- package/apps/dashboard/public/buoy-icon.svg +0 -9
- package/apps/dashboard/public/file.svg +0 -1
- package/apps/dashboard/public/globe.svg +0 -1
- package/apps/dashboard/public/in-flight-seagull.svg +0 -9
- package/apps/dashboard/public/next.svg +0 -1
- package/apps/dashboard/public/pier-icon.svg +0 -14
- package/apps/dashboard/public/star-icon.svg +0 -9
- package/apps/dashboard/public/vercel.svg +0 -1
- package/apps/dashboard/public/window.svg +0 -1
- package/apps/dashboard/public/wrench-icon.svg +0 -9
- package/apps/dashboard/scripts/download-node.js +0 -104
- package/apps/dashboard/scripts/upload-to-r2.js +0 -89
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// Tauri IPC bridge for auth, project, Claude CLI, and shell operations.
|
|
2
|
+
// Database operations are in data-bridge.ts.
|
|
3
|
+
// Low-level primitives (invoke, isTauri, openShell, openDialog) live in lib/tauri.ts.
|
|
4
|
+
|
|
5
|
+
import { invoke, isTauri, openShell, openDialog as tauriOpenDialog } from './tauri';
|
|
6
|
+
|
|
7
|
+
export { isTauri };
|
|
8
|
+
|
|
9
|
+
// Auth types
|
|
10
|
+
export interface AuthUser {
|
|
11
|
+
email: string;
|
|
12
|
+
plan: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface AuthStatus {
|
|
17
|
+
authenticated: boolean;
|
|
18
|
+
user: AuthUser | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Auth namespace
|
|
22
|
+
export const auth = {
|
|
23
|
+
getStatus: () => invoke<AuthStatus>('auth_get_status'),
|
|
24
|
+
getToken: () => invoke<string | null>('auth_get_token'),
|
|
25
|
+
saveToken: (token: string, user: AuthUser) => invoke<void>('auth_save_token', { token, user }),
|
|
26
|
+
logout: () => invoke<void>('auth_logout'),
|
|
27
|
+
hasLoggedInBefore: () => invoke<boolean>('auth_has_logged_in_before'),
|
|
28
|
+
getPostLoginPath: () => invoke<string | null>('auth_get_post_login_path'),
|
|
29
|
+
setPostLoginPath: (path: string | null) => invoke<void>('auth_set_post_login_path', { path }),
|
|
30
|
+
loginWithGoogle: () => invoke<void>('auth_login_with_google'),
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Project types
|
|
34
|
+
export interface RecentProject {
|
|
35
|
+
name: string;
|
|
36
|
+
path: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Project namespace
|
|
40
|
+
export const project = {
|
|
41
|
+
getRecent: () => invoke<RecentProject[]>('project_get_recent'),
|
|
42
|
+
addRecent: (name: string, path: string) => invoke<void>('project_add_recent', { name, path }),
|
|
43
|
+
async openDialog(): Promise<{ success: boolean; canceled?: boolean; error?: string }> {
|
|
44
|
+
let selected: string | string[] | null;
|
|
45
|
+
try {
|
|
46
|
+
selected = await tauriOpenDialog({ directory: true, title: 'Open JettyPod Project' });
|
|
47
|
+
} catch (err) {
|
|
48
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
49
|
+
console.error('Dialog open failed:', msg);
|
|
50
|
+
return { success: false, error: msg };
|
|
51
|
+
}
|
|
52
|
+
if (!selected || (Array.isArray(selected) && selected.length === 0)) {
|
|
53
|
+
return { success: false, canceled: true };
|
|
54
|
+
}
|
|
55
|
+
const projectPath = Array.isArray(selected) ? selected[0] : selected;
|
|
56
|
+
try {
|
|
57
|
+
await invoke('db_init', { projectPath });
|
|
58
|
+
const name = projectPath.split('/').pop() || projectPath;
|
|
59
|
+
await invoke('project_add_recent', { name, path: projectPath });
|
|
60
|
+
return { success: true };
|
|
61
|
+
} catch (err) {
|
|
62
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
63
|
+
console.error('Project init failed:', msg);
|
|
64
|
+
return { success: false, error: msg };
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
async newProject(): Promise<{ success: boolean; canceled?: boolean }> {
|
|
68
|
+
// Same as openDialog ā user picks folder, we initialize it
|
|
69
|
+
return project.openDialog();
|
|
70
|
+
},
|
|
71
|
+
async openRecent(path: string): Promise<{ success: boolean }> {
|
|
72
|
+
try {
|
|
73
|
+
await invoke('db_init', { projectPath: path });
|
|
74
|
+
const name = path.split('/').pop() || path;
|
|
75
|
+
await invoke('project_add_recent', { name, path });
|
|
76
|
+
return { success: true };
|
|
77
|
+
} catch {
|
|
78
|
+
return { success: false };
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Claude CLI types
|
|
84
|
+
export interface CliResult {
|
|
85
|
+
success: boolean;
|
|
86
|
+
error: string | null;
|
|
87
|
+
output: string | null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Claude CLI namespace
|
|
91
|
+
export const claudeCode = {
|
|
92
|
+
isAuthenticated: () => invoke<boolean>('claude_code_is_authenticated'),
|
|
93
|
+
login: () => invoke<CliResult>('claude_code_login'),
|
|
94
|
+
install: () => invoke<CliResult>('claude_code_install'),
|
|
95
|
+
update: () => invoke<CliResult>('claude_code_update'),
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Shell namespace
|
|
99
|
+
export const shell = {
|
|
100
|
+
openPath: (path: string) => openShell(path),
|
|
101
|
+
openUrl: (url: string) => openShell(url),
|
|
102
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical Tauri IPC bridge ā single source of truth for all __TAURI__ access.
|
|
3
|
+
*
|
|
4
|
+
* Every module that needs Tauri globals (invoke, listen, shell, dialog)
|
|
5
|
+
* should import from this file instead of touching window.__TAURI__ directly.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
interface TauriGlobal {
|
|
9
|
+
core: {
|
|
10
|
+
invoke<T>(cmd: string, args?: Record<string, unknown>): Promise<T>;
|
|
11
|
+
};
|
|
12
|
+
shell: {
|
|
13
|
+
open: (path: string) => Promise<void>;
|
|
14
|
+
};
|
|
15
|
+
dialog: {
|
|
16
|
+
open: (options?: {
|
|
17
|
+
directory?: boolean;
|
|
18
|
+
multiple?: boolean;
|
|
19
|
+
title?: string;
|
|
20
|
+
}) => Promise<string | string[] | null>;
|
|
21
|
+
};
|
|
22
|
+
event: {
|
|
23
|
+
listen: <T>(
|
|
24
|
+
event: string,
|
|
25
|
+
handler: (event: { payload: T }) => void
|
|
26
|
+
) => Promise<() => void>;
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function getTauri(): TauriGlobal {
|
|
31
|
+
return (window as any).__TAURI__;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Returns true when running inside a Tauri webview. */
|
|
35
|
+
export function isTauri(): boolean {
|
|
36
|
+
return !!(window as any).__TAURI__;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Invoke a Tauri IPC command. */
|
|
40
|
+
export async function invoke<T>(
|
|
41
|
+
cmd: string,
|
|
42
|
+
args?: Record<string, unknown>
|
|
43
|
+
): Promise<T> {
|
|
44
|
+
return getTauri().core.invoke(cmd, args);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Listen for a Tauri event. Returns an unlisten function. */
|
|
48
|
+
export async function listen<T>(
|
|
49
|
+
event: string,
|
|
50
|
+
handler: (event: { payload: T }) => void
|
|
51
|
+
): Promise<() => void> {
|
|
52
|
+
return getTauri().event.listen(event, handler);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Invoke a Tauri IPC command with a timeout. Rejects if the call doesn't resolve within timeoutMs. */
|
|
56
|
+
export async function invokeWithTimeout<T>(
|
|
57
|
+
cmd: string,
|
|
58
|
+
args?: Record<string, unknown>,
|
|
59
|
+
timeoutMs: number = 10000
|
|
60
|
+
): Promise<T> {
|
|
61
|
+
return Promise.race([
|
|
62
|
+
invoke<T>(cmd, args),
|
|
63
|
+
new Promise<never>((_, reject) =>
|
|
64
|
+
setTimeout(() => reject(new Error(`IPC timeout after ${timeoutMs}ms: ${cmd}`)), timeoutMs)
|
|
65
|
+
),
|
|
66
|
+
]);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// āāā Shell command result type āāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
70
|
+
|
|
71
|
+
export interface ShellCommandResult {
|
|
72
|
+
stdout: string;
|
|
73
|
+
stderr: string;
|
|
74
|
+
exit_code: number | null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Run a shell command and return its output.
|
|
79
|
+
* Used by service recovery for pre-flight checks and remedies.
|
|
80
|
+
*/
|
|
81
|
+
export async function runShellCommand(
|
|
82
|
+
command: string,
|
|
83
|
+
args: string[],
|
|
84
|
+
options?: { cwd?: string; timeoutMs?: number }
|
|
85
|
+
): Promise<ShellCommandResult> {
|
|
86
|
+
return invoke<ShellCommandResult>('run_shell_command', {
|
|
87
|
+
command,
|
|
88
|
+
args,
|
|
89
|
+
cwd: options?.cwd ?? null,
|
|
90
|
+
timeoutMs: options?.timeoutMs ?? null,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Open a path or URL via the system shell. */
|
|
95
|
+
export function openShell(path: string): Promise<void> {
|
|
96
|
+
return getTauri().shell.open(path);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Open a native file/directory dialog. */
|
|
100
|
+
export function openDialog(options?: {
|
|
101
|
+
directory?: boolean;
|
|
102
|
+
multiple?: boolean;
|
|
103
|
+
title?: string;
|
|
104
|
+
}): Promise<string | string[] | null> {
|
|
105
|
+
return getTauri().dialog.open(options);
|
|
106
|
+
}
|
|
@@ -5,8 +5,8 @@ export function cn(...inputs: ClassValue[]) {
|
|
|
5
5
|
return twMerge(clsx(inputs))
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
export const WS_PORT = 47808;
|
|
9
|
+
|
|
8
10
|
export function getWebSocketUrl(): string {
|
|
9
|
-
return
|
|
10
|
-
? `ws://${window.location.hostname}:47808`
|
|
11
|
-
: 'ws://localhost:47808';
|
|
11
|
+
return `ws://${window.location.hostname}:${WS_PORT}`;
|
|
12
12
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="next" />
|
|
2
2
|
/// <reference types="next/image-types/global" />
|
|
3
|
-
import "./.next/types/routes.d.ts";
|
|
3
|
+
import "./.next/dev/types/routes.d.ts";
|
|
4
4
|
|
|
5
5
|
// NOTE: This file should not be edited
|
|
6
6
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
|
@@ -1,59 +1,47 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dashboard",
|
|
3
|
-
"version": "4.4.
|
|
3
|
+
"version": "4.4.121",
|
|
4
4
|
"private": true,
|
|
5
|
-
"main": "electron/main.js",
|
|
6
5
|
"scripts": {
|
|
7
|
-
"dev": "
|
|
8
|
-
"build": "
|
|
9
|
-
"
|
|
6
|
+
"dev": "vite",
|
|
7
|
+
"build": "vite build",
|
|
8
|
+
"preview": "vite preview",
|
|
10
9
|
"lint": "eslint",
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"electron:build:win": "npm run build && npx electron-builder --config electron-builder.config.js --win",
|
|
20
|
-
"electron:build:linux": "npm run build && npx electron-builder --config electron-builder.config.js --linux",
|
|
21
|
-
"electron:pack": "npm run build && npx electron-builder --config electron-builder.config.js --dir",
|
|
22
|
-
"upload:r2": "node scripts/upload-to-r2.js",
|
|
23
|
-
"electron:release": "npm run electron:build:mac && npm run upload:r2"
|
|
10
|
+
"tauri:dev": "cargo tauri dev",
|
|
11
|
+
"tauri:build": "node scripts/tauri-build.js",
|
|
12
|
+
"tauri:build:release": "node scripts/tauri-build.js --release",
|
|
13
|
+
"tauri:build:arm64": "node scripts/tauri-build.js --release --target=arm64",
|
|
14
|
+
"tauri:build:x64": "node scripts/tauri-build.js --release --target=x64",
|
|
15
|
+
"tauri:build:universal": "node scripts/tauri-build.js --release --target=universal",
|
|
16
|
+
"tauri:release": "node scripts/tauri-build.js --release --upload",
|
|
17
|
+
"upload:tauri:r2": "node scripts/upload-tauri-to-r2.js"
|
|
24
18
|
},
|
|
25
19
|
"dependencies": {
|
|
26
20
|
"@dnd-kit/core": "^6.3.1",
|
|
27
21
|
"@dnd-kit/sortable": "^10.0.0",
|
|
28
|
-
"@
|
|
29
|
-
"@
|
|
30
|
-
"better-sqlite3": "^12.5.0",
|
|
22
|
+
"@fontsource-variable/geist-mono": "^5.2.7",
|
|
23
|
+
"@tanstack/react-virtual": "^3.13.19",
|
|
31
24
|
"class-variance-authority": "^0.7.1",
|
|
32
25
|
"clsx": "^2.1.1",
|
|
33
|
-
"
|
|
26
|
+
"eventemitter3": "^5.0.4",
|
|
34
27
|
"framer-motion": "^11.15.0",
|
|
35
|
-
"lucide-react": "^0.555.0",
|
|
36
|
-
"next": "16.0.6",
|
|
37
28
|
"react": "19.2.0",
|
|
38
29
|
"react-dom": "19.2.0",
|
|
39
30
|
"react-markdown": "^10.1.0",
|
|
31
|
+
"react-router-dom": "^7.13.0",
|
|
40
32
|
"remark-gfm": "^4.0.1",
|
|
41
|
-
"tailwind-merge": "^3.4.0"
|
|
42
|
-
"ws": "^8.19.0"
|
|
33
|
+
"tailwind-merge": "^3.4.0"
|
|
43
34
|
},
|
|
44
35
|
"devDependencies": {
|
|
45
|
-
"@tailwindcss/
|
|
46
|
-
"@types/better-sqlite3": "^7.6.13",
|
|
36
|
+
"@tailwindcss/vite": "^4.2.0",
|
|
47
37
|
"@types/node": "^20",
|
|
48
38
|
"@types/react": "^19",
|
|
49
39
|
"@types/react-dom": "^19",
|
|
50
|
-
"
|
|
51
|
-
"electron": "^32.0.0",
|
|
52
|
-
"electron-builder": "^26.4.0",
|
|
40
|
+
"@vitejs/plugin-react-swc": "^4.2.3",
|
|
53
41
|
"eslint": "^9",
|
|
54
|
-
"eslint-config-next": "16.0.6",
|
|
55
42
|
"tailwindcss": "^4",
|
|
56
43
|
"tw-animate-css": "^1.4.0",
|
|
57
|
-
"typescript": "5.9.3"
|
|
44
|
+
"typescript": "5.9.3",
|
|
45
|
+
"vite": "^7.3.1"
|
|
58
46
|
}
|
|
59
47
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tauri Build Script
|
|
5
|
+
*
|
|
6
|
+
* Orchestrates the Tauri release build:
|
|
7
|
+
* 1. Validates prerequisites (cargo-tauri, signing keys)
|
|
8
|
+
* 2. Builds the Vite frontend
|
|
9
|
+
* 3. Runs `cargo tauri build` with code signing
|
|
10
|
+
* 4. Optionally uploads to R2
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* node scripts/tauri-build.js [--release] [--upload] [--target arm64|x64|universal]
|
|
14
|
+
*
|
|
15
|
+
* Environment variables:
|
|
16
|
+
* APPLE_SIGNING_IDENTITY - Code signing identity (default: "-" for ad-hoc)
|
|
17
|
+
* APPLE_TEAM_ID - Apple Developer Team ID (enables notarization)
|
|
18
|
+
* APPLE_ID - Apple ID email (for notarization)
|
|
19
|
+
* APPLE_PASSWORD - App-specific password (for notarization)
|
|
20
|
+
* TAURI_SIGNING_PRIVATE_KEY - Ed25519 private key for update signing
|
|
21
|
+
* TAURI_SIGNING_PRIVATE_KEY_PASSWORD - Password for the signing key
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const { execSync, spawnSync } = require('child_process');
|
|
25
|
+
const fs = require('fs');
|
|
26
|
+
const path = require('path');
|
|
27
|
+
|
|
28
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
29
|
+
const TAURI_DIR = path.join(ROOT, 'src-tauri');
|
|
30
|
+
const TAURI_CONF = path.join(TAURI_DIR, 'tauri.conf.json');
|
|
31
|
+
const TAURI_PROD_CONF = path.join(TAURI_DIR, 'tauri.prod.conf.json');
|
|
32
|
+
// Cargo uses the workspace-level target/ directory (repo root), not src-tauri/target/
|
|
33
|
+
const WORKSPACE_ROOT = path.resolve(ROOT, '..', '..');
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// Argument parsing
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
const args = process.argv.slice(2);
|
|
40
|
+
const isRelease = args.includes('--release');
|
|
41
|
+
const doUpload = args.includes('--upload');
|
|
42
|
+
const targetArg = args.find(a => a.startsWith('--target='));
|
|
43
|
+
const target = targetArg ? targetArg.split('=')[1] : null;
|
|
44
|
+
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Validation
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
function check(label, fn) {
|
|
50
|
+
process.stdout.write(` ${label}... `);
|
|
51
|
+
try {
|
|
52
|
+
const result = fn();
|
|
53
|
+
console.log('ā
');
|
|
54
|
+
return result;
|
|
55
|
+
} catch (e) {
|
|
56
|
+
console.log('ā');
|
|
57
|
+
console.error(` ${e.message}`);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function validate() {
|
|
63
|
+
console.log('\nš Validating prerequisites:\n');
|
|
64
|
+
|
|
65
|
+
check('cargo-tauri CLI', () => {
|
|
66
|
+
try {
|
|
67
|
+
execSync('cargo tauri --version 2>/dev/null', { stdio: 'pipe', env: { ...process.env, PATH: `${process.env.HOME}/.cargo/bin:${process.env.PATH}` } });
|
|
68
|
+
} catch {
|
|
69
|
+
throw new Error('cargo-tauri not found. Install with: cargo install tauri-cli');
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
check('Rust toolchain', () => {
|
|
74
|
+
try {
|
|
75
|
+
execSync('rustc --version 2>/dev/null', { stdio: 'pipe', env: { ...process.env, PATH: `${process.env.HOME}/.cargo/bin:${process.env.PATH}` } });
|
|
76
|
+
} catch {
|
|
77
|
+
throw new Error('Rust not found. Install from https://rustup.rs');
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
check('tauri.conf.json', () => {
|
|
82
|
+
if (!fs.existsSync(TAURI_CONF)) {
|
|
83
|
+
throw new Error(`Missing ${TAURI_CONF}`);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (isRelease) {
|
|
88
|
+
check('Signing identity', () => {
|
|
89
|
+
const identity = process.env.APPLE_SIGNING_IDENTITY;
|
|
90
|
+
if (!identity || identity === '-') {
|
|
91
|
+
console.log('\n ā ļø Using ad-hoc signing. Set APPLE_SIGNING_IDENTITY for production.');
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
if (process.env.APPLE_TEAM_ID) {
|
|
96
|
+
check('Notarization credentials', () => {
|
|
97
|
+
if (!process.env.APPLE_ID) throw new Error('APPLE_ID required for notarization');
|
|
98
|
+
if (!process.env.APPLE_PASSWORD) throw new Error('APPLE_PASSWORD required for notarization');
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
check('Update signing key', () => {
|
|
103
|
+
if (!process.env.TAURI_SIGNING_PRIVATE_KEY) {
|
|
104
|
+
console.log('\n ā ļø No TAURI_SIGNING_PRIVATE_KEY set. Updates won\'t be signed.');
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
// Build steps
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
function runCommand(label, cmd, opts = {}) {
|
|
115
|
+
console.log(`\nš¦ ${label}...`);
|
|
116
|
+
const env = {
|
|
117
|
+
...process.env,
|
|
118
|
+
PATH: `${process.env.HOME}/.cargo/bin:${process.env.PATH}`,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const result = spawnSync('sh', ['-c', cmd], {
|
|
122
|
+
stdio: 'inherit',
|
|
123
|
+
cwd: opts.cwd || ROOT,
|
|
124
|
+
env,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (result.status !== 0) {
|
|
128
|
+
console.error(`\nā ${label} failed (exit code ${result.status})`);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function updateSigningIdentity() {
|
|
134
|
+
// Pass signing identity via APPLE_SIGNING_IDENTITY env var.
|
|
135
|
+
// Tauri reads this automatically ā no need to mutate tauri.conf.json.
|
|
136
|
+
const identity = process.env.APPLE_SIGNING_IDENTITY;
|
|
137
|
+
if (identity && identity !== '-') {
|
|
138
|
+
console.log(` Using signing identity: ${identity}`);
|
|
139
|
+
} else {
|
|
140
|
+
console.log(' Using ad-hoc signing (no APPLE_SIGNING_IDENTITY set)');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function buildTauri() {
|
|
145
|
+
let buildCmd = 'cargo tauri build';
|
|
146
|
+
|
|
147
|
+
if (target === 'universal') {
|
|
148
|
+
buildCmd += ' --target universal-apple-darwin';
|
|
149
|
+
} else if (target === 'arm64') {
|
|
150
|
+
buildCmd += ' --target aarch64-apple-darwin';
|
|
151
|
+
} else if (target === 'x64') {
|
|
152
|
+
buildCmd += ' --target x86_64-apple-darwin';
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Apply production config overlay for release builds
|
|
156
|
+
if (isRelease && fs.existsSync(TAURI_PROD_CONF)) {
|
|
157
|
+
buildCmd += ` --config "${TAURI_PROD_CONF}"`;
|
|
158
|
+
console.log(' Applying production config overlay (JettyPod name + clean icon)');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Pass signing key via environment
|
|
162
|
+
const extraEnv = [];
|
|
163
|
+
if (process.env.TAURI_SIGNING_PRIVATE_KEY) {
|
|
164
|
+
extraEnv.push('TAURI_SIGNING_PRIVATE_KEY');
|
|
165
|
+
}
|
|
166
|
+
if (process.env.TAURI_SIGNING_PRIVATE_KEY_PASSWORD) {
|
|
167
|
+
extraEnv.push('TAURI_SIGNING_PRIVATE_KEY_PASSWORD');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
runCommand('Building Tauri app', buildCmd, { cwd: TAURI_DIR });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function showArtifacts() {
|
|
174
|
+
const targetDir = path.join(WORKSPACE_ROOT, 'target');
|
|
175
|
+
// When cross-compiling, bundles go under target/{triple}/release/bundle/
|
|
176
|
+
const candidates = [
|
|
177
|
+
path.join(targetDir, 'release', 'bundle'),
|
|
178
|
+
path.join(targetDir, 'x86_64-apple-darwin', 'release', 'bundle'),
|
|
179
|
+
path.join(targetDir, 'aarch64-apple-darwin', 'release', 'bundle'),
|
|
180
|
+
path.join(targetDir, 'universal-apple-darwin', 'release', 'bundle'),
|
|
181
|
+
];
|
|
182
|
+
const bundleDir = candidates.find(d => fs.existsSync(d)) || path.join(targetDir, 'release', 'bundle');
|
|
183
|
+
|
|
184
|
+
console.log('\nā
Build complete! Artifacts:');
|
|
185
|
+
|
|
186
|
+
// Check for DMG files
|
|
187
|
+
const dmgDir = path.join(bundleDir, 'dmg');
|
|
188
|
+
if (fs.existsSync(dmgDir)) {
|
|
189
|
+
const dmgs = fs.readdirSync(dmgDir).filter(f => f.endsWith('.dmg'));
|
|
190
|
+
dmgs.forEach(f => {
|
|
191
|
+
const size = (fs.statSync(path.join(dmgDir, f)).size / 1024 / 1024).toFixed(1);
|
|
192
|
+
console.log(` š ${f} (${size} MB)`);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Check for app bundle
|
|
197
|
+
const macosDir = path.join(bundleDir, 'macos');
|
|
198
|
+
if (fs.existsSync(macosDir)) {
|
|
199
|
+
const apps = fs.readdirSync(macosDir).filter(f => f.endsWith('.app'));
|
|
200
|
+
apps.forEach(f => {
|
|
201
|
+
console.log(` š± ${f}`);
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
// Main
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
|
|
210
|
+
console.log('š JettyPod Tauri Build');
|
|
211
|
+
console.log(` Mode: ${isRelease ? 'RELEASE' : 'DEBUG'}`);
|
|
212
|
+
console.log(` Target: ${target || 'native'}`);
|
|
213
|
+
console.log(` Upload: ${doUpload ? 'yes' : 'no'}`);
|
|
214
|
+
|
|
215
|
+
validate();
|
|
216
|
+
|
|
217
|
+
if (isRelease) {
|
|
218
|
+
updateSigningIdentity();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
buildTauri();
|
|
222
|
+
showArtifacts();
|
|
223
|
+
|
|
224
|
+
if (doUpload) {
|
|
225
|
+
runCommand('Uploading to R2', 'node scripts/upload-tauri-to-r2.js');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
console.log('\nš Done!\n');
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Upload Tauri release artifacts to Cloudflare R2.
|
|
5
|
+
* Run after tauri build: node scripts/upload-tauri-to-r2.js
|
|
6
|
+
*
|
|
7
|
+
* Requires wrangler login or CLOUDFLARE_API_TOKEN env var.
|
|
8
|
+
*
|
|
9
|
+
* Uploads DMG and update signature files to R2 under the
|
|
10
|
+
* tauri/ prefix, organized by target/arch for the updater endpoint.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { execSync } = require('child_process');
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
const BUCKET_NAME = 'jettypod-releases';
|
|
18
|
+
// Cargo uses the workspace-level target/ directory (repo root), not src-tauri/target/
|
|
19
|
+
const WORKSPACE_ROOT = path.resolve(__dirname, '..', '..', '..');
|
|
20
|
+
const TARGET_DIR = path.join(WORKSPACE_ROOT, 'target');
|
|
21
|
+
|
|
22
|
+
// Tauri puts release bundles under target/release/bundle/
|
|
23
|
+
// or target/{triple}/release/bundle/ for cross-compilation
|
|
24
|
+
function findBundleDir() {
|
|
25
|
+
const candidates = [
|
|
26
|
+
path.join(TARGET_DIR, 'release', 'bundle'),
|
|
27
|
+
path.join(TARGET_DIR, 'aarch64-apple-darwin', 'release', 'bundle'),
|
|
28
|
+
path.join(TARGET_DIR, 'x86_64-apple-darwin', 'release', 'bundle'),
|
|
29
|
+
path.join(TARGET_DIR, 'universal-apple-darwin', 'release', 'bundle'),
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
for (const dir of candidates) {
|
|
33
|
+
if (fs.existsSync(dir)) return dir;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function findArtifacts(bundleDir) {
|
|
40
|
+
const artifacts = [];
|
|
41
|
+
|
|
42
|
+
// DMG files
|
|
43
|
+
const dmgDir = path.join(bundleDir, 'dmg');
|
|
44
|
+
if (fs.existsSync(dmgDir)) {
|
|
45
|
+
for (const f of fs.readdirSync(dmgDir)) {
|
|
46
|
+
if (f.endsWith('.dmg')) {
|
|
47
|
+
artifacts.push({ name: f, path: path.join(dmgDir, f), type: 'dmg' });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Update signature files (.tar.gz and .sig for Tauri updater)
|
|
53
|
+
const macosDir = path.join(bundleDir, 'macos');
|
|
54
|
+
if (fs.existsSync(macosDir)) {
|
|
55
|
+
for (const f of fs.readdirSync(macosDir)) {
|
|
56
|
+
if (f.endsWith('.tar.gz') || f.endsWith('.tar.gz.sig')) {
|
|
57
|
+
artifacts.push({ name: f, path: path.join(macosDir, f), type: 'update' });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return artifacts;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function uploadFile(artifact, r2Key) {
|
|
66
|
+
const stats = fs.statSync(artifact.path);
|
|
67
|
+
const sizeMB = (stats.size / (1024 * 1024)).toFixed(1);
|
|
68
|
+
|
|
69
|
+
process.stdout.write(` ${r2Key} (${sizeMB} MB)... `);
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
execSync(
|
|
73
|
+
`npx wrangler r2 object put "${BUCKET_NAME}/${r2Key}" --file="${artifact.path}" --remote`,
|
|
74
|
+
{ stdio: 'pipe' }
|
|
75
|
+
);
|
|
76
|
+
console.log('ā
');
|
|
77
|
+
return true;
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.log('ā');
|
|
80
|
+
console.error(` ${error.message}`);
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function main() {
|
|
86
|
+
console.log('š Uploading Tauri release artifacts to R2...\n');
|
|
87
|
+
|
|
88
|
+
const bundleDir = findBundleDir();
|
|
89
|
+
if (!bundleDir) {
|
|
90
|
+
console.error('ā No Tauri bundle directory found.');
|
|
91
|
+
console.error('Run `cargo tauri build` first.');
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
console.log(`Bundle dir: ${bundleDir}\n`);
|
|
96
|
+
|
|
97
|
+
const artifacts = findArtifacts(bundleDir);
|
|
98
|
+
if (artifacts.length === 0) {
|
|
99
|
+
console.error('ā No release artifacts found.');
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
console.log(`Found ${artifacts.length} artifact(s):\n`);
|
|
104
|
+
|
|
105
|
+
let success = 0;
|
|
106
|
+
let failed = 0;
|
|
107
|
+
|
|
108
|
+
for (const artifact of artifacts) {
|
|
109
|
+
// Upload under tauri/ prefix for organization
|
|
110
|
+
const r2Key = `tauri/${artifact.name}`;
|
|
111
|
+
if (uploadFile(artifact, r2Key)) {
|
|
112
|
+
success++;
|
|
113
|
+
} else {
|
|
114
|
+
failed++;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
console.log(`\nš¦ Upload complete: ${success} uploaded, ${failed} failed`);
|
|
119
|
+
|
|
120
|
+
if (failed > 0) {
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
main();
|