@programinglive/dev-workflow-mcp-server 1.7.13 → 1.7.16
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 +70 -0
- package/README.md +69 -1
- package/cloudflared-config.yml +3 -0
- package/db/adapters/postgres.js +85 -28
- package/db/adapters/sqlite.js +7 -1
- package/docker-compose.yml +17 -0
- package/docs/product/PRD.md +4 -2
- package/docs/release-notes/RELEASE_NOTES.md +61 -0
- package/index.js +12 -2
- package/netlify.toml +7 -0
- package/nginx-api.conf +27 -0
- package/package.json +1 -1
- package/scripts/test-auth-flow.js +75 -0
- package/tests/build.test.js +1 -1
- package/tests/startup-resilience.test.js +73 -0
- package/web/Dockerfile.api +25 -0
- package/web/__tests__/api/workflow-history.test.js +14 -12
- package/web/api-server.js +211 -0
- package/web/app/docs/page.tsx +1 -1
- package/web/app/history/page.tsx +25 -18
- package/web/app/login/page.tsx +12 -14
- package/web/components/Hero.tsx +1 -1
- package/web/components/History.tsx +4 -4
- package/web/components/Navbar.tsx +11 -12
- package/web/lib/api-client.ts +123 -0
- package/web/netlify.toml +5 -0
- package/web/next.config.ts +2 -2
- package/web/package-lock.json +6407 -2241
- package/web/package.json +11 -2
- package/workflow-state.js +3 -2
- package/scripts/add-user.js +0 -52
- package/scripts/health-check.js +0 -50
- package/scripts/test-history.js +0 -56
- package/web/app/api/history-fallback/route.ts +0 -56
package/web/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "web",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.7.15",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -10,7 +10,13 @@
|
|
|
10
10
|
"lint": "next lint"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
+
"bcrypt": "^6.0.0",
|
|
14
|
+
"connect-pg-simple": "^10.0.0",
|
|
15
|
+
"cors": "^2.8.5",
|
|
13
16
|
"dotenv": "^17.2.3",
|
|
17
|
+
"express": "^5.2.1",
|
|
18
|
+
"express-session": "^1.18.2",
|
|
19
|
+
"jsonwebtoken": "^9.0.3",
|
|
14
20
|
"lucide-react": "^0.555.0",
|
|
15
21
|
"next": "^16.0.10",
|
|
16
22
|
"pg": "^8.16.3",
|
|
@@ -18,6 +24,7 @@
|
|
|
18
24
|
"react-dom": "19.2.0"
|
|
19
25
|
},
|
|
20
26
|
"devDependencies": {
|
|
27
|
+
"@netlify/plugin-nextjs": "^5.15.2",
|
|
21
28
|
"@types/node": "^20",
|
|
22
29
|
"@types/pg": "^8.16.0",
|
|
23
30
|
"@types/react": "^19",
|
|
@@ -26,8 +33,10 @@
|
|
|
26
33
|
"cross-env": "^10.1.0",
|
|
27
34
|
"eslint": "^9",
|
|
28
35
|
"eslint-config-next": "16.0.4",
|
|
36
|
+
"jest": "^30.2.0",
|
|
29
37
|
"postcss": "^8.5.6",
|
|
38
|
+
"supertest": "^7.1.4",
|
|
30
39
|
"tailwindcss": "^3.4.18",
|
|
31
40
|
"typescript": "^5"
|
|
32
41
|
}
|
|
33
|
-
}
|
|
42
|
+
}
|
package/workflow-state.js
CHANGED
|
@@ -460,7 +460,8 @@ export class WorkflowState {
|
|
|
460
460
|
...parsed,
|
|
461
461
|
};
|
|
462
462
|
// Ensure compatibility mirrors exist whenever the primary file is present
|
|
463
|
-
|
|
463
|
+
// Run this as non-blocking to speed up startup
|
|
464
|
+
ensureCompatibilityLinks(this.stateFile).catch(() => { });
|
|
464
465
|
if (typeof this.state.testsCreated !== "boolean") {
|
|
465
466
|
this.state.testsCreated = false;
|
|
466
467
|
}
|
|
@@ -512,7 +513,7 @@ export class WorkflowState {
|
|
|
512
513
|
|
|
513
514
|
// Always ensure a fresh project summary so dependent tooling can read it immediately.
|
|
514
515
|
await this.updateProjectSummary();
|
|
515
|
-
|
|
516
|
+
ensureCompatibilityLinks(this.stateFile).catch(() => { });
|
|
516
517
|
|
|
517
518
|
return created;
|
|
518
519
|
}
|
package/scripts/add-user.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import 'dotenv/config';
|
|
3
|
-
import pg from 'pg';
|
|
4
|
-
import { createUser } from '../web/lib/auth.js';
|
|
5
|
-
|
|
6
|
-
const { Pool } = pg;
|
|
7
|
-
|
|
8
|
-
// Get database configuration from environment
|
|
9
|
-
const dbUrl = process.env.DEV_WORKFLOW_DB_URL;
|
|
10
|
-
|
|
11
|
-
if (!dbUrl) {
|
|
12
|
-
console.error('Error: DEV_WORKFLOW_DB_URL environment variable is not set');
|
|
13
|
-
process.exit(1);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const pool = new Pool({ connectionString: dbUrl });
|
|
17
|
-
|
|
18
|
-
async function addUser() {
|
|
19
|
-
const username = process.argv[2];
|
|
20
|
-
const password = process.argv[3];
|
|
21
|
-
const email = process.argv[4];
|
|
22
|
-
|
|
23
|
-
if (!username || !password) {
|
|
24
|
-
console.error('Usage: node scripts/add-user.js <username> <password> [email]');
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
// Check if user already exists
|
|
30
|
-
const existing = await pool.query('SELECT id FROM users WHERE username = $1', [username]);
|
|
31
|
-
|
|
32
|
-
if (existing.rows.length > 0) {
|
|
33
|
-
console.log(`✗ User '${username}' already exists`);
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Create user
|
|
38
|
-
const user = await createUser(pool, username, password, email);
|
|
39
|
-
console.log(`✓ Created user: ${user.username}`);
|
|
40
|
-
if (user.email) {
|
|
41
|
-
console.log(` Email: ${user.email}`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
} catch (error) {
|
|
45
|
-
console.error('Error creating user:', error.message);
|
|
46
|
-
process.exit(1);
|
|
47
|
-
} finally {
|
|
48
|
-
await pool.end();
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
addUser();
|
package/scripts/health-check.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
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();
|
package/scripts/test-history.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
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();
|
|
@@ -1,56 +0,0 @@
|
|
|
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
|
-
}
|