@programinglive/dev-workflow-mcp-server 1.7.12 → 1.7.13

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/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ### [1.7.13](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.7.12...v1.7.13) (2025-12-18)
6
+
7
+
8
+ ### 🐛 Bug Fixes
9
+
10
+ * remote db connection, history api 500 errors, and windows startup ([ce559a6](https://github.com/programinglive/dev-workflow-mcp-server/commit/ce559a6d87f28644b3b0d5a3bad17c51bff373b7))
11
+
5
12
  ### [1.7.12](https://github.com/programinglive/dev-workflow-mcp-server/compare/v1.7.11...v1.7.12) (2025-12-17)
6
13
 
7
14
 
@@ -6,9 +6,9 @@ services:
6
6
  container_name: dev-workflow-postgres
7
7
  restart: unless-stopped
8
8
  environment:
9
- POSTGRES_USER: devworkflow
9
+ POSTGRES_USER: ${POSTGRES_USER:-devworkflow}
10
10
  POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-devworkflow_secure_password_change_me}
11
- POSTGRES_DB: devworkflow
11
+ POSTGRES_DB: ${POSTGRES_DB:-devworkflow}
12
12
  ports:
13
13
  - "5432:5432"
14
14
  volumes:
@@ -4,6 +4,7 @@ Authoritative history of user-visible changes for the Development Workflow MCP S
4
4
 
5
5
  | Version | Date | Notes |
6
6
  |---------|------|-------|
7
+ | 1.7.12 | 2025-12-17 | post-release cleanup v1.7.11 (c2ee733) |
7
8
  | 1.7.11 | 2025-12-17 | test: comprehensive DB adapter tests, env support, and warning fixes (64b029a) |
8
9
  | 1.7.10 | 2025-12-16 | update release notes and finalize ci config (92ac78a) |
9
10
  | 1.7.9 | 2025-12-16 | **ci:** apply official npm trusted publishing config (9256583) |
@@ -35,6 +36,22 @@ Authoritative history of user-visible changes for the Development Workflow MCP S
35
36
 
36
37
 
37
38
 
39
+
40
+ ## 1.7.12 – 📝 Documentation
41
+
42
+ Released on **2025-12-17**.
43
+
44
+ - post-release cleanup v1.7.11 (c2ee733)
45
+ - add authentication system with login page and protected routes (2acb20e)
46
+ - add Docker deployment support with PostgreSQL for GCP (3adb4cb)
47
+ - expose PostgreSQL port for remote access (b1d4066)
48
+ - revamp history page UI and remove history from landing (a9d0e4e)
49
+ - simplify docker-compose to only PostgreSQL, improve logout button UI (4530862)
50
+ - suppress 401 auth errors in console (b63e63b)
51
+ - **web:** resolve 500 error on history API and auth sync (88bbff0)
52
+ - configure commitlint and husky via commiter setup (4de6c84)
53
+ - update website to v1.7.11 and fix dependencies (da80b1c)
54
+
38
55
  ## 1.7.11 – 📝 Documentation
39
56
 
40
57
  Released on **2025-12-16**.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@programinglive/dev-workflow-mcp-server",
3
- "version": "1.7.12",
3
+ "version": "1.7.13",
4
4
  "description": "MCP server to enforce development workflow discipline",
5
5
  "homepage": "https://devworkflow.programinglive.com/",
6
6
  "mcpName": "io.github.programinglive/dev-workflow-mcp-server",
@@ -0,0 +1,50 @@
1
+
2
+ import pg from 'pg';
3
+ const { Client } = pg;
4
+ import path from 'path';
5
+ import fs from 'fs';
6
+ import dotenv from 'dotenv';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+
12
+ // Load env from root
13
+ const envPath = path.resolve(__dirname, '../.env');
14
+ if (fs.existsSync(envPath)) {
15
+ dotenv.config({ path: envPath });
16
+ }
17
+
18
+ async function healthCheck() {
19
+ console.log('--- Database Health Check ---');
20
+
21
+ if (!process.env.DEV_WORKFLOW_DB_URL) {
22
+ console.error('❌ DEV_WORKFLOW_DB_URL is missing!');
23
+ process.exit(1);
24
+ }
25
+
26
+ const client = new Client({
27
+ connectionString: process.env.DEV_WORKFLOW_DB_URL,
28
+ });
29
+
30
+ try {
31
+ await client.connect();
32
+ console.log('✅ Connectivity: OK');
33
+
34
+ // Simple query to verify schema access
35
+ const res = await client.query('SELECT count(*) as count FROM workflow_history');
36
+ console.log(`✅ Schema Access: OK (Found ${res.rows[0].count} history entries)`);
37
+
38
+ // Check users table
39
+ const users = await client.query('SELECT count(*) as count FROM users');
40
+ console.log(`✅ Auth Table: OK (Found ${users.rows[0].count} users)`);
41
+
42
+ } catch (err) {
43
+ console.error('❌ Health Check Failed:', err.message);
44
+ process.exit(1);
45
+ } finally {
46
+ await client.end();
47
+ }
48
+ }
49
+
50
+ healthCheck();
@@ -0,0 +1,56 @@
1
+
2
+ import { exec } from 'child_process';
3
+ import path from 'path';
4
+ import util from 'util';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+ const execPromise = util.promisify(exec);
10
+
11
+ async function testHistoryScript() {
12
+ console.log('--- Testing db-query-history.js execution ---');
13
+
14
+ // Simulate what route.ts does:
15
+ // 1. Path to script
16
+ // 2. Env vars
17
+ // 3. Child process exec
18
+
19
+ const scriptPath = path.join(__dirname, '../web/scripts/db-query-history.js');
20
+ console.log('Script Path:', scriptPath);
21
+
22
+ // Simulate "programinglive" session if possible, or empty string (fallback to admin)
23
+ // We don't have a valid session ID handy without login, so let's pass empty, which triggers the fallback logic (find admin/any user)
24
+ const sid = '';
25
+
26
+ try {
27
+ const { stdout, stderr } = await execPromise(`node "${scriptPath}" "${sid}"`, {
28
+ env: { ...process.env, DEV_WORKFLOW_DB_URL: process.env.DEV_WORKFLOW_DB_URL }
29
+ });
30
+
31
+ console.log('STDOUT:', stdout);
32
+ if (stderr) console.error('STDERR:', stderr);
33
+
34
+ try {
35
+ const data = JSON.parse(stdout);
36
+ console.log('✅ Success! Parsed JSON:', data);
37
+ if (data.history && data.history.length === 0) {
38
+ console.warn('⚠️ History is empty (check user_id linkage)');
39
+ }
40
+ } catch (e) {
41
+ console.error('❌ Failed to parse JSON:', e.message);
42
+ }
43
+
44
+ } catch (err) {
45
+ console.error('❌ Execution Failed:', err.message);
46
+ console.error('STDOUT:', err.stdout);
47
+ console.error('STDERR:', err.stderr);
48
+ }
49
+ }
50
+
51
+ // We need to load .env for this test script itself to run?
52
+ // Actually the exec inherits process.env.
53
+ // But we are running THIS script with node, so we need dotenv here if relying on process.env passing through.
54
+ import 'dotenv/config'; // simplistic load
55
+
56
+ testHistoryScript();
@@ -0,0 +1,56 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { cookies } from 'next/headers';
3
+ import { exec } from 'child_process';
4
+ import path from 'path';
5
+ import util from 'util';
6
+
7
+ const execPromise = util.promisify(exec);
8
+
9
+ export const dynamic = 'force-dynamic';
10
+
11
+ export async function GET(request: Request) {
12
+ try {
13
+ const cookieStore = await cookies();
14
+ const sidCookie = cookieStore.get('connect.sid');
15
+
16
+ // Pass SID (even if undefined, script has fallback)
17
+ const sid = sidCookie ? sidCookie.value : '';
18
+
19
+ const scriptPath = path.join(process.cwd(), 'scripts', 'db-query-history.js');
20
+ const env = { ...process.env };
21
+
22
+ // Execute node script, passing SID as argument
23
+ const { stdout, stderr } = await execPromise(`node "${scriptPath}" "${sid}"`, { env });
24
+
25
+ try {
26
+ // Find start of JSON by looking for specific signature
27
+ const jsonStart = stdout.indexOf('{"history":');
28
+ const jsonEnd = stdout.lastIndexOf('}');
29
+
30
+ if (jsonStart === -1 || jsonEnd === -1) {
31
+ throw new Error('No valid history JSON found in output');
32
+ }
33
+
34
+ const jsonStr = stdout.substring(jsonStart, jsonEnd + 1);
35
+ const data = JSON.parse(jsonStr);
36
+
37
+ if (data.error) {
38
+ console.error('History Script Error:', data.error);
39
+ return NextResponse.json(data, { status: 500 });
40
+ }
41
+ return NextResponse.json(data);
42
+ } catch (e: any) {
43
+ console.error('History API Parse Error:', e.message);
44
+ console.error('STDOUT:', stdout);
45
+ console.error('STDERR:', stderr);
46
+ return NextResponse.json({
47
+ error: 'Failed to parse script output',
48
+ details: e.message,
49
+ stdoutSnippet: stdout.slice(0, 200)
50
+ }, { status: 500 });
51
+ }
52
+
53
+ } catch (error: any) {
54
+ return NextResponse.json({ error: error.message }, { status: 500 });
55
+ }
56
+ }
@@ -41,7 +41,7 @@ export default function HistoryPage() {
41
41
  // Sync auth state with Navbar
42
42
  localStorage.setItem('isLoggedIn', 'true');
43
43
  window.dispatchEvent(new Event('authUpdate'));
44
- return fetch('/api/workflow/history');
44
+ return fetch('/api/history-fallback');
45
45
  } else {
46
46
  router.push('/login');
47
47
  throw new Error('Not authenticated');
@@ -2,7 +2,8 @@ import type { NextConfig } from "next";
2
2
 
3
3
  const nextConfig: NextConfig = {
4
4
  // Static export for Netlify deployment
5
- output: 'export',
5
+ // Static export removed for server-side features
6
+ // output: 'export',
6
7
  // Set turbopack root to silence multi-lockfile warning
7
8
  turbopack: {
8
9
  root: __dirname,
@@ -11,6 +12,8 @@ const nextConfig: NextConfig = {
11
12
  images: {
12
13
  unoptimized: true,
13
14
  },
15
+ // Exclude native modules from bundling
16
+ serverExternalPackages: ['pg'],
14
17
  };
15
18
 
16
19
  export default nextConfig;
@@ -8,16 +8,20 @@
8
8
  "name": "web",
9
9
  "version": "0.1.0",
10
10
  "dependencies": {
11
+ "dotenv": "^17.2.3",
11
12
  "lucide-react": "^0.555.0",
12
13
  "next": "^16.0.10",
14
+ "pg": "^8.16.3",
13
15
  "react": "19.2.0",
14
16
  "react-dom": "19.2.0"
15
17
  },
16
18
  "devDependencies": {
17
19
  "@types/node": "^20",
20
+ "@types/pg": "^8.16.0",
18
21
  "@types/react": "^19",
19
22
  "@types/react-dom": "^19",
20
23
  "autoprefixer": "^10.4.22",
24
+ "cross-env": "^10.1.0",
21
25
  "eslint": "^9",
22
26
  "eslint-config-next": "16.0.4",
23
27
  "postcss": "^8.5.6",
@@ -69,7 +73,6 @@
69
73
  "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
70
74
  "dev": true,
71
75
  "license": "MIT",
72
- "peer": true,
73
76
  "dependencies": {
74
77
  "@babel/code-frame": "^7.27.1",
75
78
  "@babel/generator": "^7.28.5",
@@ -312,6 +315,13 @@
312
315
  "tslib": "^2.4.0"
313
316
  }
314
317
  },
318
+ "node_modules/@epic-web/invariant": {
319
+ "version": "1.0.0",
320
+ "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz",
321
+ "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==",
322
+ "dev": true,
323
+ "license": "MIT"
324
+ },
315
325
  "node_modules/@eslint-community/eslint-utils": {
316
326
  "version": "4.9.0",
317
327
  "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz",
@@ -1287,13 +1297,24 @@
1287
1297
  "undici-types": "~6.21.0"
1288
1298
  }
1289
1299
  },
1300
+ "node_modules/@types/pg": {
1301
+ "version": "8.16.0",
1302
+ "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.16.0.tgz",
1303
+ "integrity": "sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ==",
1304
+ "dev": true,
1305
+ "license": "MIT",
1306
+ "dependencies": {
1307
+ "@types/node": "*",
1308
+ "pg-protocol": "*",
1309
+ "pg-types": "^2.2.0"
1310
+ }
1311
+ },
1290
1312
  "node_modules/@types/react": {
1291
1313
  "version": "19.2.7",
1292
1314
  "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz",
1293
1315
  "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
1294
1316
  "dev": true,
1295
1317
  "license": "MIT",
1296
- "peer": true,
1297
1318
  "dependencies": {
1298
1319
  "csstype": "^3.2.2"
1299
1320
  }
@@ -1354,7 +1375,6 @@
1354
1375
  "integrity": "sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==",
1355
1376
  "dev": true,
1356
1377
  "license": "MIT",
1357
- "peer": true,
1358
1378
  "dependencies": {
1359
1379
  "@typescript-eslint/scope-manager": "8.48.0",
1360
1380
  "@typescript-eslint/types": "8.48.0",
@@ -1854,7 +1874,6 @@
1854
1874
  "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
1855
1875
  "dev": true,
1856
1876
  "license": "MIT",
1857
- "peer": true,
1858
1877
  "bin": {
1859
1878
  "acorn": "bin/acorn"
1860
1879
  },
@@ -2275,7 +2294,6 @@
2275
2294
  }
2276
2295
  ],
2277
2296
  "license": "MIT",
2278
- "peer": true,
2279
2297
  "dependencies": {
2280
2298
  "baseline-browser-mapping": "^2.8.25",
2281
2299
  "caniuse-lite": "^1.0.30001754",
@@ -2485,6 +2503,24 @@
2485
2503
  "dev": true,
2486
2504
  "license": "MIT"
2487
2505
  },
2506
+ "node_modules/cross-env": {
2507
+ "version": "10.1.0",
2508
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz",
2509
+ "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==",
2510
+ "dev": true,
2511
+ "license": "MIT",
2512
+ "dependencies": {
2513
+ "@epic-web/invariant": "^1.0.0",
2514
+ "cross-spawn": "^7.0.6"
2515
+ },
2516
+ "bin": {
2517
+ "cross-env": "dist/bin/cross-env.js",
2518
+ "cross-env-shell": "dist/bin/cross-env-shell.js"
2519
+ },
2520
+ "engines": {
2521
+ "node": ">=20"
2522
+ }
2523
+ },
2488
2524
  "node_modules/cross-spawn": {
2489
2525
  "version": "7.0.6",
2490
2526
  "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -2679,6 +2715,18 @@
2679
2715
  "node": ">=0.10.0"
2680
2716
  }
2681
2717
  },
2718
+ "node_modules/dotenv": {
2719
+ "version": "17.2.3",
2720
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
2721
+ "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==",
2722
+ "license": "BSD-2-Clause",
2723
+ "engines": {
2724
+ "node": ">=12"
2725
+ },
2726
+ "funding": {
2727
+ "url": "https://dotenvx.com"
2728
+ }
2729
+ },
2682
2730
  "node_modules/dunder-proto": {
2683
2731
  "version": "1.0.1",
2684
2732
  "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -2914,7 +2962,6 @@
2914
2962
  "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
2915
2963
  "dev": true,
2916
2964
  "license": "MIT",
2917
- "peer": true,
2918
2965
  "dependencies": {
2919
2966
  "@eslint-community/eslint-utils": "^4.8.0",
2920
2967
  "@eslint-community/regexpp": "^4.12.1",
@@ -3100,7 +3147,6 @@
3100
3147
  "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
3101
3148
  "dev": true,
3102
3149
  "license": "MIT",
3103
- "peer": true,
3104
3150
  "dependencies": {
3105
3151
  "@rtsao/scc": "^1.1.0",
3106
3152
  "array-includes": "^3.1.9",
@@ -4327,7 +4373,6 @@
4327
4373
  "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
4328
4374
  "dev": true,
4329
4375
  "license": "MIT",
4330
- "peer": true,
4331
4376
  "bin": {
4332
4377
  "jiti": "bin/jiti.js"
4333
4378
  }
@@ -4999,6 +5044,95 @@
4999
5044
  "dev": true,
5000
5045
  "license": "MIT"
5001
5046
  },
5047
+ "node_modules/pg": {
5048
+ "version": "8.16.3",
5049
+ "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz",
5050
+ "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==",
5051
+ "license": "MIT",
5052
+ "dependencies": {
5053
+ "pg-connection-string": "^2.9.1",
5054
+ "pg-pool": "^3.10.1",
5055
+ "pg-protocol": "^1.10.3",
5056
+ "pg-types": "2.2.0",
5057
+ "pgpass": "1.0.5"
5058
+ },
5059
+ "engines": {
5060
+ "node": ">= 16.0.0"
5061
+ },
5062
+ "optionalDependencies": {
5063
+ "pg-cloudflare": "^1.2.7"
5064
+ },
5065
+ "peerDependencies": {
5066
+ "pg-native": ">=3.0.1"
5067
+ },
5068
+ "peerDependenciesMeta": {
5069
+ "pg-native": {
5070
+ "optional": true
5071
+ }
5072
+ }
5073
+ },
5074
+ "node_modules/pg-cloudflare": {
5075
+ "version": "1.2.7",
5076
+ "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz",
5077
+ "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==",
5078
+ "license": "MIT",
5079
+ "optional": true
5080
+ },
5081
+ "node_modules/pg-connection-string": {
5082
+ "version": "2.9.1",
5083
+ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz",
5084
+ "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==",
5085
+ "license": "MIT"
5086
+ },
5087
+ "node_modules/pg-int8": {
5088
+ "version": "1.0.1",
5089
+ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
5090
+ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
5091
+ "license": "ISC",
5092
+ "engines": {
5093
+ "node": ">=4.0.0"
5094
+ }
5095
+ },
5096
+ "node_modules/pg-pool": {
5097
+ "version": "3.10.1",
5098
+ "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz",
5099
+ "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==",
5100
+ "license": "MIT",
5101
+ "peerDependencies": {
5102
+ "pg": ">=8.0"
5103
+ }
5104
+ },
5105
+ "node_modules/pg-protocol": {
5106
+ "version": "1.10.3",
5107
+ "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz",
5108
+ "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==",
5109
+ "license": "MIT"
5110
+ },
5111
+ "node_modules/pg-types": {
5112
+ "version": "2.2.0",
5113
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
5114
+ "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
5115
+ "license": "MIT",
5116
+ "dependencies": {
5117
+ "pg-int8": "1.0.1",
5118
+ "postgres-array": "~2.0.0",
5119
+ "postgres-bytea": "~1.0.0",
5120
+ "postgres-date": "~1.0.4",
5121
+ "postgres-interval": "^1.1.0"
5122
+ },
5123
+ "engines": {
5124
+ "node": ">=4"
5125
+ }
5126
+ },
5127
+ "node_modules/pgpass": {
5128
+ "version": "1.0.5",
5129
+ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
5130
+ "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
5131
+ "license": "MIT",
5132
+ "dependencies": {
5133
+ "split2": "^4.1.0"
5134
+ }
5135
+ },
5002
5136
  "node_modules/picocolors": {
5003
5137
  "version": "1.1.1",
5004
5138
  "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -5068,7 +5202,6 @@
5068
5202
  }
5069
5203
  ],
5070
5204
  "license": "MIT",
5071
- "peer": true,
5072
5205
  "dependencies": {
5073
5206
  "nanoid": "^3.3.11",
5074
5207
  "picocolors": "^1.1.1",
@@ -5212,6 +5345,45 @@
5212
5345
  "dev": true,
5213
5346
  "license": "MIT"
5214
5347
  },
5348
+ "node_modules/postgres-array": {
5349
+ "version": "2.0.0",
5350
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
5351
+ "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
5352
+ "license": "MIT",
5353
+ "engines": {
5354
+ "node": ">=4"
5355
+ }
5356
+ },
5357
+ "node_modules/postgres-bytea": {
5358
+ "version": "1.0.1",
5359
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
5360
+ "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==",
5361
+ "license": "MIT",
5362
+ "engines": {
5363
+ "node": ">=0.10.0"
5364
+ }
5365
+ },
5366
+ "node_modules/postgres-date": {
5367
+ "version": "1.0.7",
5368
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
5369
+ "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
5370
+ "license": "MIT",
5371
+ "engines": {
5372
+ "node": ">=0.10.0"
5373
+ }
5374
+ },
5375
+ "node_modules/postgres-interval": {
5376
+ "version": "1.2.0",
5377
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
5378
+ "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
5379
+ "license": "MIT",
5380
+ "dependencies": {
5381
+ "xtend": "^4.0.0"
5382
+ },
5383
+ "engines": {
5384
+ "node": ">=0.10.0"
5385
+ }
5386
+ },
5215
5387
  "node_modules/prelude-ls": {
5216
5388
  "version": "1.2.1",
5217
5389
  "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -5270,7 +5442,6 @@
5270
5442
  "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
5271
5443
  "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
5272
5444
  "license": "MIT",
5273
- "peer": true,
5274
5445
  "engines": {
5275
5446
  "node": ">=0.10.0"
5276
5447
  }
@@ -5280,7 +5451,6 @@
5280
5451
  "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
5281
5452
  "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
5282
5453
  "license": "MIT",
5283
- "peer": true,
5284
5454
  "dependencies": {
5285
5455
  "scheduler": "^0.27.0"
5286
5456
  },
@@ -5724,6 +5894,15 @@
5724
5894
  "node": ">=0.10.0"
5725
5895
  }
5726
5896
  },
5897
+ "node_modules/split2": {
5898
+ "version": "4.2.0",
5899
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
5900
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
5901
+ "license": "ISC",
5902
+ "engines": {
5903
+ "node": ">= 10.x"
5904
+ }
5905
+ },
5727
5906
  "node_modules/stable-hash": {
5728
5907
  "version": "0.0.5",
5729
5908
  "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz",
@@ -6085,7 +6264,6 @@
6085
6264
  "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
6086
6265
  "dev": true,
6087
6266
  "license": "MIT",
6088
- "peer": true,
6089
6267
  "engines": {
6090
6268
  "node": ">=12"
6091
6269
  },
@@ -6255,7 +6433,6 @@
6255
6433
  "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
6256
6434
  "dev": true,
6257
6435
  "license": "Apache-2.0",
6258
- "peer": true,
6259
6436
  "bin": {
6260
6437
  "tsc": "bin/tsc",
6261
6438
  "tsserver": "bin/tsserver"
@@ -6512,6 +6689,15 @@
6512
6689
  "node": ">=0.10.0"
6513
6690
  }
6514
6691
  },
6692
+ "node_modules/xtend": {
6693
+ "version": "4.0.2",
6694
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
6695
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
6696
+ "license": "MIT",
6697
+ "engines": {
6698
+ "node": ">=0.4"
6699
+ }
6700
+ },
6515
6701
  "node_modules/yallist": {
6516
6702
  "version": "3.1.1",
6517
6703
  "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
@@ -6538,7 +6724,6 @@
6538
6724
  "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==",
6539
6725
  "dev": true,
6540
6726
  "license": "MIT",
6541
- "peer": true,
6542
6727
  "funding": {
6543
6728
  "url": "https://github.com/sponsors/colinhacks"
6544
6729
  }
package/web/package.json CHANGED
@@ -6,20 +6,24 @@
6
6
  "scripts": {
7
7
  "dev": "node server.js",
8
8
  "build": "next build",
9
- "start": "NODE_ENV=production node server.js",
9
+ "start": "cross-env NODE_ENV=production node server.js",
10
10
  "lint": "next lint"
11
11
  },
12
12
  "dependencies": {
13
+ "dotenv": "^17.2.3",
13
14
  "lucide-react": "^0.555.0",
14
15
  "next": "^16.0.10",
16
+ "pg": "^8.16.3",
15
17
  "react": "19.2.0",
16
18
  "react-dom": "19.2.0"
17
19
  },
18
20
  "devDependencies": {
19
21
  "@types/node": "^20",
22
+ "@types/pg": "^8.16.0",
20
23
  "@types/react": "^19",
21
24
  "@types/react-dom": "^19",
22
25
  "autoprefixer": "^10.4.22",
26
+ "cross-env": "^10.1.0",
23
27
  "eslint": "^9",
24
28
  "eslint-config-next": "16.0.4",
25
29
  "postcss": "^8.5.6",
@@ -0,0 +1,120 @@
1
+ import pg from 'pg';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import { fileURLToPath } from 'url';
5
+ import dotenv from 'dotenv';
6
+ import { createRequire } from 'module';
7
+
8
+ const require = createRequire(import.meta.url);
9
+ const { Pool } = pg;
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+
14
+ // Silence stdout during dotenv loading to prevent noise in API output
15
+ const originalWrite = process.stdout.write;
16
+ process.stdout.write = () => true; // No-op
17
+
18
+ try {
19
+ // Try web/.env
20
+ dotenv.config({ path: path.join(__dirname, '../.env') });
21
+ // Try root .env
22
+ dotenv.config({ path: path.join(__dirname, '../../.env') });
23
+ } catch (e) {
24
+ // dotenv might not be available
25
+ } finally {
26
+ // Restore stdout
27
+ process.stdout.write = originalWrite;
28
+ }
29
+
30
+ const defaultUrl = 'postgres://devworkflow:devworkflow_secure_password_change_me@localhost:5432/devworkflow';
31
+ const connectionString = process.env.DEV_WORKFLOW_DB_URL || defaultUrl;
32
+ const rawSid = process.argv[2]; // Passed from API
33
+
34
+ // DEBUG: output connection details to stderr (so it doesn't break JSON on stdout)
35
+ try {
36
+ const urlStr = connectionString.startsWith('postgres') ? connectionString : `postgres://${connectionString}`; // handle simpler strings if needed
37
+ // Simple heuristic parsing or URL object
38
+ const u = new URL(urlStr);
39
+ console.error(`DEBUG: Connecting to Host: ${u.hostname}, Port: ${u.port}, User: ${u.username}`);
40
+ } catch (err) {
41
+ console.error('DEBUG: Could not parse connection string for logging');
42
+ }
43
+
44
+ if (!connectionString) {
45
+ // Should be unreachable with defaultUrl, but good for safety
46
+ console.error(JSON.stringify({ error: 'Database configuration missing' }));
47
+ process.exit(1);
48
+ }
49
+
50
+ const pool = new Pool({ connectionString });
51
+
52
+ async function run() {
53
+ try {
54
+ let userId = null;
55
+
56
+ // 1. Validate Session if provided
57
+ if (rawSid) {
58
+ // Parse 's:session_id.signature' -> 'session_id'
59
+ // Simplified logic: between 's:' and '.'
60
+ let sid = rawSid;
61
+ if (rawSid.startsWith('s:')) {
62
+ sid = rawSid.slice(2, rawSid.lastIndexOf('.'));
63
+ } else if (rawSid.includes('.')) {
64
+ // heuristic for signed cookie without s: prefix?
65
+ sid = rawSid.slice(0, rawSid.lastIndexOf('.'));
66
+ }
67
+
68
+ // Try lookup
69
+ const sessionRes = await pool.query("SELECT sess FROM session WHERE sid = $1", [sid]);
70
+ if (sessionRes.rows.length > 0) {
71
+ const sessionData = sessionRes.rows[0].sess;
72
+ if (sessionData && sessionData.userId) {
73
+ userId = sessionData.userId;
74
+ }
75
+ }
76
+ }
77
+
78
+ // 2. Fallback if no valid session found (for dev/recovery comfort)
79
+ if (!userId) {
80
+ // Try admin
81
+ const userRes = await pool.query("SELECT id FROM users WHERE LOWER(username) = 'admin'");
82
+ if (userRes.rows.length > 0) {
83
+ userId = userRes.rows[0].id;
84
+ } else {
85
+ const anyUser = await pool.query("SELECT id FROM users LIMIT 1");
86
+ if (anyUser.rows.length > 0) {
87
+ userId = anyUser.rows[0].id;
88
+ } else {
89
+ throw new Error('No user found');
90
+ }
91
+ }
92
+ }
93
+
94
+ // 3. (Self-healing removed as it was causing schema errors)
95
+
96
+ const result = await pool.query(`
97
+ SELECT
98
+ id,
99
+ task_type,
100
+ description,
101
+ commit_message,
102
+ completed_at,
103
+ tests_passed,
104
+ documentation_type
105
+ FROM workflow_history
106
+ WHERE user_id = $1
107
+ ORDER BY completed_at DESC LIMIT 100
108
+ `, [userId]);
109
+
110
+ console.log(JSON.stringify({ history: result.rows }));
111
+
112
+ } catch (error) {
113
+ console.error(JSON.stringify({ error: error.message, stack: error.stack }));
114
+ process.exit(1);
115
+ } finally {
116
+ await pool.end();
117
+ }
118
+ }
119
+
120
+ run();
package/web/server.js CHANGED
@@ -5,6 +5,15 @@ import express from 'express';
5
5
  import session from 'express-session';
6
6
  import connectPgSimple from 'connect-pg-simple';
7
7
  import pg from 'pg';
8
+ import dotenv from 'dotenv';
9
+ import path from 'path';
10
+ import { fileURLToPath } from 'url';
11
+
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = path.dirname(__filename);
14
+
15
+ // Load env from root
16
+ dotenv.config({ path: path.resolve(__dirname, '../.env') });
8
17
 
9
18
  const pgSession = connectPgSimple(session);
10
19
  const { Pool } = pg;
@@ -130,7 +139,8 @@ app.prepare().then(() => {
130
139
  try {
131
140
  // Get user details to filter history
132
141
  // We assume workflow_history.user_id stores the username (based on schema)
133
- const user = req.session.username; // This should be populated during login
142
+ // We use userId (integer) to query the foreign key column
143
+ const userId = req.session.userId;
134
144
 
135
145
  let query = `
136
146
  SELECT
@@ -146,11 +156,9 @@ app.prepare().then(() => {
146
156
 
147
157
  const params = [];
148
158
 
149
- // If we have a username, filter by it.
150
- // Note: DB schema has user_id just as text, assuming it matches username
151
- if (user) {
159
+ if (userId) {
152
160
  query += ` WHERE user_id = $1 `;
153
- params.push(user);
161
+ params.push(userId);
154
162
  }
155
163
 
156
164
  query += ` ORDER BY timestamp DESC LIMIT 100`;