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
package/lib/database.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const sqlite3 = require('sqlite3').verbose();
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const fs = require('fs');
|
|
4
|
-
const { getGitRoot } = require('./git-root');
|
|
4
|
+
const { getGitRoot, resetCache: resetGitRootCache } = require('./git-root');
|
|
5
5
|
const { runMigrations } = require('./migrations');
|
|
6
6
|
const { getSchemaSQL } = require('./schema');
|
|
7
7
|
|
|
@@ -12,6 +12,7 @@ let cachedDbPath = null;
|
|
|
12
12
|
let cachedGitRoot = null;
|
|
13
13
|
let isClosing = false;
|
|
14
14
|
let migrationPromise = null;
|
|
15
|
+
let schemaGeneration = 0;
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Get the root directory for jettypod operations
|
|
@@ -29,14 +30,19 @@ function getRepoRoot() {
|
|
|
29
30
|
return cachedGitRoot;
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
// Track which NODE_ENV was used to compute the cached path
|
|
34
|
+
let cachedNodeEnv = undefined;
|
|
35
|
+
|
|
32
36
|
// Dynamic getters for paths (always use main repo, not worktree)
|
|
33
37
|
function getJettypodDir() {
|
|
34
38
|
const root = getRepoRoot();
|
|
35
|
-
|
|
39
|
+
const currentNodeEnv = process.env.NODE_ENV;
|
|
40
|
+
if (!cachedJettypodDir || cachedJettypodDir !== path.join(root, '.jettypod') || cachedNodeEnv !== currentNodeEnv) {
|
|
36
41
|
cachedJettypodDir = path.join(root, '.jettypod');
|
|
37
42
|
// Use separate database for tests
|
|
38
|
-
const dbFileName =
|
|
43
|
+
const dbFileName = currentNodeEnv === 'test' ? 'test-work.db' : 'work.db';
|
|
39
44
|
cachedDbPath = path.join(cachedJettypodDir, dbFileName);
|
|
45
|
+
cachedNodeEnv = currentNodeEnv;
|
|
40
46
|
}
|
|
41
47
|
return cachedJettypodDir;
|
|
42
48
|
}
|
|
@@ -131,11 +137,18 @@ function getDb() {
|
|
|
131
137
|
* @throws {Error} If schema creation fails
|
|
132
138
|
*/
|
|
133
139
|
function initSchema() {
|
|
140
|
+
// Capture db reference and generation to prevent race conditions.
|
|
141
|
+
// Module-level getDb() calls (e.g. in work-tracking/index.js) start initSchema() async.
|
|
142
|
+
// If tests call resetDb() + getDb() before the old callback fires, the old callback
|
|
143
|
+
// would read the reassigned module-level `db` and run migrations on the wrong connection.
|
|
144
|
+
const localDb = db;
|
|
145
|
+
const myGeneration = ++schemaGeneration;
|
|
146
|
+
|
|
134
147
|
// Create a promise that resolves when migrations complete
|
|
135
148
|
migrationPromise = new Promise((resolve, reject) => {
|
|
136
|
-
|
|
149
|
+
localDb.serialize(() => {
|
|
137
150
|
// Create core tables using the shared schema module (single source of truth)
|
|
138
|
-
|
|
151
|
+
localDb.exec(getSchemaSQL(), (err) => {
|
|
139
152
|
if (err) {
|
|
140
153
|
throw new Error(`Failed to create core tables: ${err.message}`);
|
|
141
154
|
}
|
|
@@ -147,20 +160,25 @@ function initSchema() {
|
|
|
147
160
|
console.warn(`ALTER TABLE ADD COLUMN ${columnName} failed:`, err.message);
|
|
148
161
|
}
|
|
149
162
|
};
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
163
|
+
localDb.run(`ALTER TABLE work_items ADD COLUMN branch_name TEXT`, handleAlterError('branch_name'));
|
|
164
|
+
localDb.run(`ALTER TABLE work_items ADD COLUMN file_paths TEXT`, handleAlterError('file_paths'));
|
|
165
|
+
localDb.run(`ALTER TABLE work_items ADD COLUMN commit_sha TEXT`, handleAlterError('commit_sha'));
|
|
166
|
+
localDb.run(`ALTER TABLE work_items ADD COLUMN mode TEXT`, handleAlterError('mode'));
|
|
167
|
+
localDb.run(`ALTER TABLE work_items ADD COLUMN current INTEGER DEFAULT 0`, handleAlterError('current'));
|
|
168
|
+
localDb.run(`ALTER TABLE work_items ADD COLUMN needs_discovery INTEGER DEFAULT 0`, handleAlterError('needs_discovery'));
|
|
169
|
+
localDb.run(`ALTER TABLE work_items ADD COLUMN worktree_path TEXT`, handleAlterError('worktree_path'));
|
|
170
|
+
localDb.run(`ALTER TABLE work_items ADD COLUMN discovery_rationale TEXT`, handleAlterError('discovery_rationale'));
|
|
171
|
+
localDb.run(`ALTER TABLE work_items ADD COLUMN display_order INTEGER`, handleAlterError('display_order'));
|
|
172
|
+
localDb.run(`ALTER TABLE work_items ADD COLUMN architectural_decision TEXT`, async (err) => {
|
|
160
173
|
handleAlterError('architectural_decision')(err);
|
|
174
|
+
// If the singleton was reset since we started, abort — a new initSchema() owns migrations now
|
|
175
|
+
if (myGeneration !== schemaGeneration) {
|
|
176
|
+
resolve();
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
161
179
|
// Run data migrations after all schema operations complete
|
|
162
180
|
try {
|
|
163
|
-
await runMigrations(
|
|
181
|
+
await runMigrations(localDb);
|
|
164
182
|
resolve();
|
|
165
183
|
} catch (err) {
|
|
166
184
|
console.error('Migration failed:', err.message);
|
|
@@ -303,12 +321,14 @@ async function closeDb() {
|
|
|
303
321
|
* Forces the singleton to be recreated on next getDb() call
|
|
304
322
|
*/
|
|
305
323
|
function resetDb() {
|
|
324
|
+
schemaGeneration++; // Invalidate any in-flight initSchema callbacks
|
|
306
325
|
db = null;
|
|
307
326
|
cachedJettypodDir = null;
|
|
308
327
|
cachedDbPath = null;
|
|
309
328
|
cachedGitRoot = null;
|
|
310
329
|
isClosing = false;
|
|
311
330
|
migrationPromise = null;
|
|
331
|
+
resetGitRootCache();
|
|
312
332
|
}
|
|
313
333
|
|
|
314
334
|
/**
|
package/lib/db-watcher.js
CHANGED
package/lib/git-hooks/pre-commit
CHANGED
|
@@ -88,7 +88,7 @@ async function exportSnapshots() {
|
|
|
88
88
|
const paths = await exportAll();
|
|
89
89
|
|
|
90
90
|
// Stage the snapshot files
|
|
91
|
-
execSync(`git add "${paths.work}" "${paths.database}"`, {
|
|
91
|
+
execSync(`git add -f "${paths.work}" "${paths.database}"`, {
|
|
92
92
|
stdio: ['pipe', 'pipe', 'pipe']
|
|
93
93
|
});
|
|
94
94
|
|
package/lib/jettypod-backup.js
CHANGED
|
@@ -75,10 +75,33 @@ async function createBackup(gitRoot, reason = 'unknown', options = {}) {
|
|
|
75
75
|
const backupPath = path.join(backupBaseDir, backupName);
|
|
76
76
|
|
|
77
77
|
try {
|
|
78
|
-
// Copy
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
78
|
+
// Copy .jettypod directory structure
|
|
79
|
+
fs.mkdirSync(backupPath, { recursive: true });
|
|
80
|
+
|
|
81
|
+
// Copy all files, using sqlite3 .backup for .db files (WAL-safe)
|
|
82
|
+
// CRITICAL: fs.copyFileSync/cp does NOT work with SQLite WAL mode -
|
|
83
|
+
// it only copies the base .db file, missing data in the -wal file.
|
|
84
|
+
const entries = fs.readdirSync(jettypodPath);
|
|
85
|
+
for (const entry of entries) {
|
|
86
|
+
const srcFile = path.join(jettypodPath, entry);
|
|
87
|
+
const destFile = path.join(backupPath, entry);
|
|
88
|
+
const stat = fs.statSync(srcFile);
|
|
89
|
+
|
|
90
|
+
if (stat.isDirectory()) {
|
|
91
|
+
execSync(`cp -R "${srcFile}" "${destFile}"`, { stdio: 'pipe' });
|
|
92
|
+
} else if (entry.endsWith('.db') && !entry.endsWith('-shm') && !entry.endsWith('-wal')) {
|
|
93
|
+
// Use sqlite3 .backup for database files (handles WAL mode correctly)
|
|
94
|
+
try {
|
|
95
|
+
execSync(`sqlite3 "${srcFile}" ".backup '${destFile}'"`, { stdio: 'pipe' });
|
|
96
|
+
} catch (dbErr) {
|
|
97
|
+
// Fall back to file copy if sqlite3 fails (e.g., empty db)
|
|
98
|
+
fs.copyFileSync(srcFile, destFile);
|
|
99
|
+
}
|
|
100
|
+
} else if (!entry.endsWith('-shm') && !entry.endsWith('-wal')) {
|
|
101
|
+
// Copy non-WAL files normally (skip -shm/-wal as .backup handles them)
|
|
102
|
+
fs.copyFileSync(srcFile, destFile);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
82
105
|
|
|
83
106
|
// Verify backup was created
|
|
84
107
|
if (!fs.existsSync(backupPath)) {
|