@pixelbyte-software/pixcode 1.34.0 → 1.35.0
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/dist/api-docs.html +162 -9
- package/dist/assets/index-B8w57E1r.css +32 -0
- package/dist/assets/index-Djuh0wHV.js +854 -0
- package/dist/favicon.svg +8 -8
- package/dist/icons/icon-128x128.svg +9 -9
- package/dist/icons/icon-144x144.svg +9 -9
- package/dist/icons/icon-152x152.svg +9 -9
- package/dist/icons/icon-192x192.svg +9 -9
- package/dist/icons/icon-384x384.svg +9 -9
- package/dist/icons/icon-512x512.svg +9 -9
- package/dist/icons/icon-72x72.svg +9 -9
- package/dist/icons/icon-96x96.svg +9 -9
- package/dist/icons/icon-template.svg +9 -9
- package/dist/index.html +2 -2
- package/dist/logo.svg +12 -12
- package/dist/openapi.yaml +383 -1
- package/dist-server/server/claude-sdk.js +38 -7
- package/dist-server/server/claude-sdk.js.map +1 -1
- package/dist-server/server/cli.js +12 -17
- package/dist-server/server/cli.js.map +1 -1
- package/dist-server/server/daemon-manager.js +98 -51
- package/dist-server/server/daemon-manager.js.map +1 -1
- package/dist-server/server/database/json-store.js +8 -5
- package/dist-server/server/database/json-store.js.map +1 -1
- package/dist-server/server/index.js +31 -10
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapter-registry.js +45 -19
- package/dist-server/server/modules/orchestration/a2a/adapter-registry.js.map +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapters/abstract-a2a.adapter.js.map +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapters/claude-code.adapter.js +1 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/claude-code.adapter.js.map +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapters/codex.adapter.js +202 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/codex.adapter.js.map +1 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/cursor.adapter.js +205 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/cursor.adapter.js.map +1 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/gemini.adapter.js +205 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/gemini.adapter.js.map +1 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/opencode.adapter.js +205 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/opencode.adapter.js.map +1 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/qwen.adapter.js +205 -0
- package/dist-server/server/modules/orchestration/a2a/adapters/qwen.adapter.js.map +1 -0
- package/dist-server/server/modules/orchestration/a2a/routes.js +298 -34
- package/dist-server/server/modules/orchestration/a2a/routes.js.map +1 -1
- package/dist-server/server/modules/orchestration/a2a/task-store.js +144 -0
- package/dist-server/server/modules/orchestration/a2a/task-store.js.map +1 -0
- package/dist-server/server/modules/orchestration/a2a/validator.js +16 -0
- package/dist-server/server/modules/orchestration/a2a/validator.js.map +1 -1
- package/dist-server/server/modules/orchestration/index.js +14 -0
- package/dist-server/server/modules/orchestration/index.js.map +1 -1
- package/dist-server/server/modules/orchestration/preview/port-watcher.js +90 -0
- package/dist-server/server/modules/orchestration/preview/port-watcher.js.map +1 -0
- package/dist-server/server/modules/orchestration/preview/preview-proxy.js +58 -0
- package/dist-server/server/modules/orchestration/preview/preview-proxy.js.map +1 -0
- package/dist-server/server/modules/orchestration/preview/types.js +2 -0
- package/dist-server/server/modules/orchestration/preview/types.js.map +1 -0
- package/dist-server/server/modules/orchestration/tasks/orchestration-task-store.js +37 -0
- package/dist-server/server/modules/orchestration/tasks/orchestration-task-store.js.map +1 -0
- package/dist-server/server/modules/orchestration/tasks/orchestration-task.routes.js +68 -0
- package/dist-server/server/modules/orchestration/tasks/orchestration-task.routes.js.map +1 -0
- package/dist-server/server/modules/orchestration/tasks/orchestration-task.service.js +128 -0
- package/dist-server/server/modules/orchestration/tasks/orchestration-task.service.js.map +1 -0
- package/dist-server/server/modules/orchestration/tasks/orchestration-task.types.js +2 -0
- package/dist-server/server/modules/orchestration/tasks/orchestration-task.types.js.map +1 -0
- package/dist-server/server/modules/orchestration/workflows/built-in-workflows.js +126 -0
- package/dist-server/server/modules/orchestration/workflows/built-in-workflows.js.map +1 -0
- package/dist-server/server/modules/orchestration/workflows/workflow-runner.js +1047 -0
- package/dist-server/server/modules/orchestration/workflows/workflow-runner.js.map +1 -0
- package/dist-server/server/modules/orchestration/workflows/workflow-store.js +76 -0
- package/dist-server/server/modules/orchestration/workflows/workflow-store.js.map +1 -0
- package/dist-server/server/modules/orchestration/workflows/workflow.routes.js +151 -0
- package/dist-server/server/modules/orchestration/workflows/workflow.routes.js.map +1 -0
- package/dist-server/server/modules/orchestration/workflows/workflow.types.js +2 -0
- package/dist-server/server/modules/orchestration/workflows/workflow.types.js.map +1 -0
- package/dist-server/server/modules/orchestration/workflows/workspace-target.js +98 -0
- package/dist-server/server/modules/orchestration/workflows/workspace-target.js.map +1 -0
- package/dist-server/server/modules/orchestration/workspace/docker-workspace.js +122 -0
- package/dist-server/server/modules/orchestration/workspace/docker-workspace.js.map +1 -0
- package/dist-server/server/modules/orchestration/workspace/path-safety.js +48 -0
- package/dist-server/server/modules/orchestration/workspace/path-safety.js.map +1 -0
- package/dist-server/server/modules/orchestration/workspace/types.js +11 -0
- package/dist-server/server/modules/orchestration/workspace/types.js.map +1 -0
- package/dist-server/server/modules/orchestration/workspace/workspace-manager.js +80 -0
- package/dist-server/server/modules/orchestration/workspace/workspace-manager.js.map +1 -0
- package/dist-server/server/modules/orchestration/workspace/worktree-workspace.js +96 -0
- package/dist-server/server/modules/orchestration/workspace/worktree-workspace.js.map +1 -0
- package/dist-server/server/modules/providers/index.js +3 -0
- package/dist-server/server/modules/providers/index.js.map +1 -0
- package/dist-server/server/openai-codex.js +35 -4
- package/dist-server/server/openai-codex.js.map +1 -1
- package/dist-server/server/routes/taskmaster.js +106 -89
- package/dist-server/server/routes/taskmaster.js.map +1 -1
- package/package.json +3 -1
- package/scripts/smoke/a2a-roundtrip.mjs +167 -98
- package/scripts/smoke/orchestration-api.mjs +172 -0
- package/scripts/smoke/orchestration-live-run.mjs +176 -0
- package/server/claude-sdk.js +48 -7
- package/server/cli.js +12 -17
- package/server/daemon-manager.js +90 -51
- package/server/database/db.js +794 -794
- package/server/database/json-store.js +8 -5
- package/server/index.js +40 -9
- package/server/modules/orchestration/a2a/adapter-registry.ts +108 -58
- package/server/modules/orchestration/a2a/adapters/abstract-a2a.adapter.ts +55 -49
- package/server/modules/orchestration/a2a/adapters/claude-code.adapter.ts +284 -283
- package/server/modules/orchestration/a2a/adapters/codex.adapter.ts +244 -0
- package/server/modules/orchestration/a2a/adapters/cursor.adapter.ts +249 -0
- package/server/modules/orchestration/a2a/adapters/gemini.adapter.ts +248 -0
- package/server/modules/orchestration/a2a/adapters/opencode.adapter.ts +248 -0
- package/server/modules/orchestration/a2a/adapters/qwen.adapter.ts +248 -0
- package/server/modules/orchestration/a2a/agent-card.ts +55 -55
- package/server/modules/orchestration/a2a/auth.middleware.ts +29 -29
- package/server/modules/orchestration/a2a/bus.ts +46 -46
- package/server/modules/orchestration/a2a/routes.ts +577 -264
- package/server/modules/orchestration/a2a/task-store.ts +178 -0
- package/server/modules/orchestration/a2a/types.ts +125 -111
- package/server/modules/orchestration/a2a/validator.ts +113 -90
- package/server/modules/orchestration/index.ts +66 -26
- package/server/modules/orchestration/preview/port-watcher.ts +112 -0
- package/server/modules/orchestration/preview/preview-proxy.ts +60 -0
- package/server/modules/orchestration/preview/types.ts +19 -0
- package/server/modules/orchestration/tasks/orchestration-task-store.ts +45 -0
- package/server/modules/orchestration/tasks/orchestration-task.routes.ts +73 -0
- package/server/modules/orchestration/tasks/orchestration-task.service.ts +145 -0
- package/server/modules/orchestration/tasks/orchestration-task.types.ts +29 -0
- package/server/modules/orchestration/workflows/built-in-workflows.ts +127 -0
- package/server/modules/orchestration/workflows/workflow-runner.ts +1206 -0
- package/server/modules/orchestration/workflows/workflow-store.ts +97 -0
- package/server/modules/orchestration/workflows/workflow.routes.ts +169 -0
- package/server/modules/orchestration/workflows/workflow.types.ts +70 -0
- package/server/modules/orchestration/workflows/workspace-target.ts +120 -0
- package/server/modules/orchestration/workspace/docker-workspace.ts +135 -0
- package/server/modules/orchestration/workspace/path-safety.ts +55 -0
- package/server/modules/orchestration/workspace/types.ts +52 -0
- package/server/modules/orchestration/workspace/workspace-manager.ts +97 -0
- package/server/modules/orchestration/workspace/worktree-workspace.ts +125 -0
- package/server/modules/providers/index.ts +2 -0
- package/server/modules/providers/list/opencode/opencode-auth.provider.ts +130 -130
- package/server/modules/providers/list/opencode/opencode-mcp.provider.ts +126 -126
- package/server/modules/providers/list/opencode/opencode.provider.ts +29 -29
- package/server/modules/providers/list/qwen/qwen-auth.provider.ts +145 -145
- package/server/modules/providers/list/qwen/qwen-mcp.provider.ts +114 -114
- package/server/modules/providers/list/qwen/qwen.provider.ts +21 -21
- package/server/modules/providers/shared/provider-configs.ts +142 -142
- package/server/openai-codex.js +40 -4
- package/server/qwen-code-cli.js +395 -395
- package/server/qwen-response-handler.js +73 -73
- package/server/routes/qwen.js +27 -27
- package/server/routes/taskmaster.js +116 -91
- package/server/services/external-access.js +171 -171
- package/server/services/provider-models.js +381 -381
- package/server/services/telegram/telegram-http-client.js +130 -130
- package/server/services/vapid-keys.js +36 -36
- package/server/utils/port-access.js +209 -209
- package/dist/assets/index-B1ghfb4w.css +0 -32
- package/dist/assets/index-BvClqlMf.js +0 -852
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
// Qwen Code Response Handler — stream-json parser.
|
|
2
|
-
// Qwen Code is a fork of Gemini CLI and emits the same NDJSON stream-json
|
|
3
|
-
// event shape (type: init | message | tool_use | tool_result | result | error).
|
|
4
|
-
// This handler is intentionally a structural twin of gemini-response-handler;
|
|
5
|
-
// the split keeps provider normalization clean and lets the two evolve
|
|
6
|
-
// independently if Qwen's protocol ever diverges.
|
|
7
|
-
import { sessionsService } from './modules/providers/services/sessions.service.js';
|
|
8
|
-
|
|
9
|
-
class QwenResponseHandler {
|
|
10
|
-
constructor(ws, options = {}) {
|
|
11
|
-
this.ws = ws;
|
|
12
|
-
this.buffer = '';
|
|
13
|
-
this.onContentFragment = options.onContentFragment || null;
|
|
14
|
-
this.onInit = options.onInit || null;
|
|
15
|
-
this.onToolUse = options.onToolUse || null;
|
|
16
|
-
this.onToolResult = options.onToolResult || null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
processData(data) {
|
|
20
|
-
this.buffer += data;
|
|
21
|
-
|
|
22
|
-
const lines = this.buffer.split('\n');
|
|
23
|
-
this.buffer = lines.pop() || '';
|
|
24
|
-
|
|
25
|
-
for (const line of lines) {
|
|
26
|
-
if (!line.trim()) continue;
|
|
27
|
-
try {
|
|
28
|
-
const event = JSON.parse(line);
|
|
29
|
-
this.handleEvent(event);
|
|
30
|
-
} catch {
|
|
31
|
-
// Not a JSON line — debug output or CLI warning, ignore.
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
handleEvent(event) {
|
|
37
|
-
const sid = typeof this.ws.getSessionId === 'function' ? this.ws.getSessionId() : null;
|
|
38
|
-
|
|
39
|
-
if (event.type === 'init') {
|
|
40
|
-
if (this.onInit) this.onInit(event);
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (event.type === 'message' && event.role === 'assistant') {
|
|
45
|
-
const content = event.content || '';
|
|
46
|
-
if (this.onContentFragment && content) this.onContentFragment(content);
|
|
47
|
-
} else if (event.type === 'tool_use' && this.onToolUse) {
|
|
48
|
-
this.onToolUse(event);
|
|
49
|
-
} else if (event.type === 'tool_result' && this.onToolResult) {
|
|
50
|
-
this.onToolResult(event);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const normalized = sessionsService.normalizeMessage('qwen', event, sid);
|
|
54
|
-
for (const msg of normalized) {
|
|
55
|
-
this.ws.send(msg);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
forceFlush() {
|
|
60
|
-
if (this.buffer.trim()) {
|
|
61
|
-
try {
|
|
62
|
-
const event = JSON.parse(this.buffer);
|
|
63
|
-
this.handleEvent(event);
|
|
64
|
-
} catch { /* ignore */ }
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
destroy() {
|
|
69
|
-
this.buffer = '';
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export default QwenResponseHandler;
|
|
1
|
+
// Qwen Code Response Handler — stream-json parser.
|
|
2
|
+
// Qwen Code is a fork of Gemini CLI and emits the same NDJSON stream-json
|
|
3
|
+
// event shape (type: init | message | tool_use | tool_result | result | error).
|
|
4
|
+
// This handler is intentionally a structural twin of gemini-response-handler;
|
|
5
|
+
// the split keeps provider normalization clean and lets the two evolve
|
|
6
|
+
// independently if Qwen's protocol ever diverges.
|
|
7
|
+
import { sessionsService } from './modules/providers/services/sessions.service.js';
|
|
8
|
+
|
|
9
|
+
class QwenResponseHandler {
|
|
10
|
+
constructor(ws, options = {}) {
|
|
11
|
+
this.ws = ws;
|
|
12
|
+
this.buffer = '';
|
|
13
|
+
this.onContentFragment = options.onContentFragment || null;
|
|
14
|
+
this.onInit = options.onInit || null;
|
|
15
|
+
this.onToolUse = options.onToolUse || null;
|
|
16
|
+
this.onToolResult = options.onToolResult || null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
processData(data) {
|
|
20
|
+
this.buffer += data;
|
|
21
|
+
|
|
22
|
+
const lines = this.buffer.split('\n');
|
|
23
|
+
this.buffer = lines.pop() || '';
|
|
24
|
+
|
|
25
|
+
for (const line of lines) {
|
|
26
|
+
if (!line.trim()) continue;
|
|
27
|
+
try {
|
|
28
|
+
const event = JSON.parse(line);
|
|
29
|
+
this.handleEvent(event);
|
|
30
|
+
} catch {
|
|
31
|
+
// Not a JSON line — debug output or CLI warning, ignore.
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
handleEvent(event) {
|
|
37
|
+
const sid = typeof this.ws.getSessionId === 'function' ? this.ws.getSessionId() : null;
|
|
38
|
+
|
|
39
|
+
if (event.type === 'init') {
|
|
40
|
+
if (this.onInit) this.onInit(event);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (event.type === 'message' && event.role === 'assistant') {
|
|
45
|
+
const content = event.content || '';
|
|
46
|
+
if (this.onContentFragment && content) this.onContentFragment(content);
|
|
47
|
+
} else if (event.type === 'tool_use' && this.onToolUse) {
|
|
48
|
+
this.onToolUse(event);
|
|
49
|
+
} else if (event.type === 'tool_result' && this.onToolResult) {
|
|
50
|
+
this.onToolResult(event);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const normalized = sessionsService.normalizeMessage('qwen', event, sid);
|
|
54
|
+
for (const msg of normalized) {
|
|
55
|
+
this.ws.send(msg);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
forceFlush() {
|
|
60
|
+
if (this.buffer.trim()) {
|
|
61
|
+
try {
|
|
62
|
+
const event = JSON.parse(this.buffer);
|
|
63
|
+
this.handleEvent(event);
|
|
64
|
+
} catch { /* ignore */ }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
destroy() {
|
|
69
|
+
this.buffer = '';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export default QwenResponseHandler;
|
package/server/routes/qwen.js
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
|
-
|
|
3
|
-
import sessionManager from '../sessionManager.js';
|
|
4
|
-
import { sessionNamesDb } from '../database/db.js';
|
|
5
|
-
|
|
6
|
-
const router = express.Router();
|
|
7
|
-
|
|
8
|
-
// Delete a Qwen Code session — mirrors the Gemini/Codex routes so the UI's
|
|
9
|
-
// unified delete flow works identically regardless of the active provider.
|
|
10
|
-
router.delete('/sessions/:sessionId', async (req, res) => {
|
|
11
|
-
try {
|
|
12
|
-
const { sessionId } = req.params;
|
|
13
|
-
|
|
14
|
-
if (!sessionId || typeof sessionId !== 'string' || !/^[a-zA-Z0-9_.-]{1,100}$/.test(sessionId)) {
|
|
15
|
-
return res.status(400).json({ success: false, error: 'Invalid session ID format' });
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
await sessionManager.deleteSession(sessionId);
|
|
19
|
-
sessionNamesDb.deleteName(sessionId, 'qwen');
|
|
20
|
-
res.json({ success: true });
|
|
21
|
-
} catch (error) {
|
|
22
|
-
console.error(`Error deleting Qwen session ${req.params.sessionId}:`, error);
|
|
23
|
-
res.status(500).json({ success: false, error: error.message });
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
export default router;
|
|
1
|
+
import express from 'express';
|
|
2
|
+
|
|
3
|
+
import sessionManager from '../sessionManager.js';
|
|
4
|
+
import { sessionNamesDb } from '../database/db.js';
|
|
5
|
+
|
|
6
|
+
const router = express.Router();
|
|
7
|
+
|
|
8
|
+
// Delete a Qwen Code session — mirrors the Gemini/Codex routes so the UI's
|
|
9
|
+
// unified delete flow works identically regardless of the active provider.
|
|
10
|
+
router.delete('/sessions/:sessionId', async (req, res) => {
|
|
11
|
+
try {
|
|
12
|
+
const { sessionId } = req.params;
|
|
13
|
+
|
|
14
|
+
if (!sessionId || typeof sessionId !== 'string' || !/^[a-zA-Z0-9_.-]{1,100}$/.test(sessionId)) {
|
|
15
|
+
return res.status(400).json({ success: false, error: 'Invalid session ID format' });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
await sessionManager.deleteSession(sessionId);
|
|
19
|
+
sessionNamesDb.deleteName(sessionId, 'qwen');
|
|
20
|
+
res.json({ success: true });
|
|
21
|
+
} catch (error) {
|
|
22
|
+
console.error(`Error deleting Qwen session ${req.params.sessionId}:`, error);
|
|
23
|
+
res.status(500).json({ success: false, error: error.message });
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export default router;
|
|
@@ -16,6 +16,7 @@ import { spawn } from 'child_process';
|
|
|
16
16
|
import { extractProjectDirectory } from '../projects.js';
|
|
17
17
|
import { detectTaskMasterMCPServer } from '../utils/mcp-detector.js';
|
|
18
18
|
import { broadcastTaskMasterProjectUpdate, broadcastTaskMasterTasksUpdate } from '../utils/taskmaster-websocket.js';
|
|
19
|
+
import { orchestrationTaskService } from '@/modules/orchestration/tasks/orchestration-task.service.js';
|
|
19
20
|
|
|
20
21
|
const router = express.Router();
|
|
21
22
|
|
|
@@ -94,6 +95,53 @@ async function checkTaskMasterInstallation() {
|
|
|
94
95
|
});
|
|
95
96
|
}
|
|
96
97
|
|
|
98
|
+
|
|
99
|
+
async function readTaskMasterTasks(projectName) {
|
|
100
|
+
const projectPath = await extractProjectDirectory(projectName);
|
|
101
|
+
const tasksFilePath = path.join(projectPath, '.taskmaster', 'tasks', 'tasks.json');
|
|
102
|
+
|
|
103
|
+
await fsPromises.access(tasksFilePath);
|
|
104
|
+
const tasksContent = await fsPromises.readFile(tasksFilePath, 'utf8');
|
|
105
|
+
const tasksData = JSON.parse(tasksContent);
|
|
106
|
+
|
|
107
|
+
let tasks = [];
|
|
108
|
+
let currentTag = 'master';
|
|
109
|
+
|
|
110
|
+
if (Array.isArray(tasksData)) {
|
|
111
|
+
tasks = tasksData;
|
|
112
|
+
} else if (tasksData.tasks) {
|
|
113
|
+
tasks = tasksData.tasks;
|
|
114
|
+
} else if (tasksData[currentTag] && tasksData[currentTag].tasks) {
|
|
115
|
+
tasks = tasksData[currentTag].tasks;
|
|
116
|
+
} else if (tasksData.master && tasksData.master.tasks) {
|
|
117
|
+
tasks = tasksData.master.tasks;
|
|
118
|
+
} else {
|
|
119
|
+
const firstTag = Object.keys(tasksData).find((key) =>
|
|
120
|
+
tasksData[key].tasks && Array.isArray(tasksData[key].tasks)
|
|
121
|
+
);
|
|
122
|
+
if (firstTag) {
|
|
123
|
+
tasks = tasksData[firstTag].tasks;
|
|
124
|
+
currentTag = firstTag;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const transformedTasks = tasks.map((task) => ({
|
|
129
|
+
id: task.id,
|
|
130
|
+
title: task.title || 'Untitled Task',
|
|
131
|
+
description: task.description || '',
|
|
132
|
+
status: task.status || 'pending',
|
|
133
|
+
priority: task.priority || 'medium',
|
|
134
|
+
dependencies: task.dependencies || [],
|
|
135
|
+
createdAt: task.createdAt || task.created || new Date().toISOString(),
|
|
136
|
+
updatedAt: task.updatedAt || task.updated || new Date().toISOString(),
|
|
137
|
+
details: task.details || '',
|
|
138
|
+
testStrategy: task.testStrategy || task.test_strategy || '',
|
|
139
|
+
subtasks: task.subtasks || []
|
|
140
|
+
}));
|
|
141
|
+
|
|
142
|
+
return { projectPath, transformedTasks, currentTag };
|
|
143
|
+
}
|
|
144
|
+
|
|
97
145
|
// API Routes
|
|
98
146
|
|
|
99
147
|
/**
|
|
@@ -138,106 +186,40 @@ router.get('/installation-status', async (req, res) => {
|
|
|
138
186
|
router.get('/tasks/:projectName', async (req, res) => {
|
|
139
187
|
try {
|
|
140
188
|
const { projectName } = req.params;
|
|
141
|
-
|
|
142
|
-
// Get project path
|
|
143
|
-
let projectPath;
|
|
144
|
-
try {
|
|
145
|
-
projectPath = await extractProjectDirectory(projectName);
|
|
146
|
-
} catch (error) {
|
|
147
|
-
return res.status(404).json({
|
|
148
|
-
error: 'Project not found',
|
|
149
|
-
message: `Project "${projectName}" does not exist`
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
189
|
|
|
153
|
-
const
|
|
154
|
-
const tasksFilePath = path.join(taskMasterPath, 'tasks', 'tasks.json');
|
|
190
|
+
const { projectPath, transformedTasks, currentTag } = await readTaskMasterTasks(projectName);
|
|
155
191
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
192
|
+
res.json({
|
|
193
|
+
projectName,
|
|
194
|
+
projectPath,
|
|
195
|
+
tasks: transformedTasks,
|
|
196
|
+
currentTag,
|
|
197
|
+
totalTasks: transformedTasks.length,
|
|
198
|
+
tasksByStatus: {
|
|
199
|
+
pending: transformedTasks.filter(t => t.status === 'pending').length,
|
|
200
|
+
'in-progress': transformedTasks.filter(t => t.status === 'in-progress').length,
|
|
201
|
+
done: transformedTasks.filter(t => t.status === 'done').length,
|
|
202
|
+
review: transformedTasks.filter(t => t.status === 'review').length,
|
|
203
|
+
deferred: transformedTasks.filter(t => t.status === 'deferred').length,
|
|
204
|
+
cancelled: transformedTasks.filter(t => t.status === 'cancelled').length
|
|
205
|
+
},
|
|
206
|
+
timestamp: new Date().toISOString()
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
} catch (error) {
|
|
210
|
+
if (error?.code === 'ENOENT') {
|
|
160
211
|
return res.json({
|
|
161
|
-
projectName,
|
|
212
|
+
projectName: req.params.projectName,
|
|
162
213
|
tasks: [],
|
|
163
214
|
message: 'No tasks.json file found'
|
|
164
215
|
});
|
|
165
216
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const tasksData = JSON.parse(tasksContent);
|
|
171
|
-
|
|
172
|
-
let tasks = [];
|
|
173
|
-
let currentTag = 'master';
|
|
174
|
-
|
|
175
|
-
// Handle both tagged and legacy formats
|
|
176
|
-
if (Array.isArray(tasksData)) {
|
|
177
|
-
// Legacy format
|
|
178
|
-
tasks = tasksData;
|
|
179
|
-
} else if (tasksData.tasks) {
|
|
180
|
-
// Simple format with tasks array
|
|
181
|
-
tasks = tasksData.tasks;
|
|
182
|
-
} else {
|
|
183
|
-
// Tagged format - get tasks from current tag or master
|
|
184
|
-
if (tasksData[currentTag] && tasksData[currentTag].tasks) {
|
|
185
|
-
tasks = tasksData[currentTag].tasks;
|
|
186
|
-
} else if (tasksData.master && tasksData.master.tasks) {
|
|
187
|
-
tasks = tasksData.master.tasks;
|
|
188
|
-
} else {
|
|
189
|
-
// Get tasks from first available tag
|
|
190
|
-
const firstTag = Object.keys(tasksData).find(key =>
|
|
191
|
-
tasksData[key].tasks && Array.isArray(tasksData[key].tasks)
|
|
192
|
-
);
|
|
193
|
-
if (firstTag) {
|
|
194
|
-
tasks = tasksData[firstTag].tasks;
|
|
195
|
-
currentTag = firstTag;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Transform tasks to ensure all have required fields
|
|
201
|
-
const transformedTasks = tasks.map(task => ({
|
|
202
|
-
id: task.id,
|
|
203
|
-
title: task.title || 'Untitled Task',
|
|
204
|
-
description: task.description || '',
|
|
205
|
-
status: task.status || 'pending',
|
|
206
|
-
priority: task.priority || 'medium',
|
|
207
|
-
dependencies: task.dependencies || [],
|
|
208
|
-
createdAt: task.createdAt || task.created || new Date().toISOString(),
|
|
209
|
-
updatedAt: task.updatedAt || task.updated || new Date().toISOString(),
|
|
210
|
-
details: task.details || '',
|
|
211
|
-
testStrategy: task.testStrategy || task.test_strategy || '',
|
|
212
|
-
subtasks: task.subtasks || []
|
|
213
|
-
}));
|
|
214
|
-
|
|
215
|
-
res.json({
|
|
216
|
-
projectName,
|
|
217
|
-
projectPath,
|
|
218
|
-
tasks: transformedTasks,
|
|
219
|
-
currentTag,
|
|
220
|
-
totalTasks: transformedTasks.length,
|
|
221
|
-
tasksByStatus: {
|
|
222
|
-
pending: transformedTasks.filter(t => t.status === 'pending').length,
|
|
223
|
-
'in-progress': transformedTasks.filter(t => t.status === 'in-progress').length,
|
|
224
|
-
done: transformedTasks.filter(t => t.status === 'done').length,
|
|
225
|
-
review: transformedTasks.filter(t => t.status === 'review').length,
|
|
226
|
-
deferred: transformedTasks.filter(t => t.status === 'deferred').length,
|
|
227
|
-
cancelled: transformedTasks.filter(t => t.status === 'cancelled').length
|
|
228
|
-
},
|
|
229
|
-
timestamp: new Date().toISOString()
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
} catch (parseError) {
|
|
233
|
-
console.error('Failed to parse tasks.json:', parseError);
|
|
234
|
-
return res.status(500).json({
|
|
235
|
-
error: 'Failed to parse tasks file',
|
|
236
|
-
message: parseError.message
|
|
217
|
+
if (String(error?.message || '').includes('does not exist')) {
|
|
218
|
+
return res.status(404).json({
|
|
219
|
+
error: 'Project not found',
|
|
220
|
+
message: `Project "${req.params.projectName}" does not exist`
|
|
237
221
|
});
|
|
238
222
|
}
|
|
239
|
-
|
|
240
|
-
} catch (error) {
|
|
241
223
|
console.error('TaskMaster tasks loading error:', error);
|
|
242
224
|
res.status(500).json({
|
|
243
225
|
error: 'Failed to load TaskMaster tasks',
|
|
@@ -246,6 +228,49 @@ router.get('/tasks/:projectName', async (req, res) => {
|
|
|
246
228
|
}
|
|
247
229
|
});
|
|
248
230
|
|
|
231
|
+
/**
|
|
232
|
+
* POST /api/taskmaster/sync-orchestration/:projectName
|
|
233
|
+
* One-way sync: TaskMaster -> Orchestration tasks
|
|
234
|
+
*/
|
|
235
|
+
router.post('/sync-orchestration/:projectName', async (req, res) => {
|
|
236
|
+
try {
|
|
237
|
+
const { projectName } = req.params;
|
|
238
|
+
const { transformedTasks } = await readTaskMasterTasks(projectName);
|
|
239
|
+
|
|
240
|
+
const projectId = typeof req.body?.projectId === 'string' && req.body.projectId.trim()
|
|
241
|
+
? req.body.projectId.trim()
|
|
242
|
+
: projectName;
|
|
243
|
+
|
|
244
|
+
const syncedTasks = transformedTasks.map((task) =>
|
|
245
|
+
orchestrationTaskService.upsertFromTaskMaster({
|
|
246
|
+
projectId,
|
|
247
|
+
taskmasterId: String(task.id),
|
|
248
|
+
title: task.title,
|
|
249
|
+
description: task.description,
|
|
250
|
+
})
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
res.json({
|
|
254
|
+
success: true,
|
|
255
|
+
projectName,
|
|
256
|
+
projectId,
|
|
257
|
+
synced: syncedTasks.length,
|
|
258
|
+
tasks: syncedTasks,
|
|
259
|
+
timestamp: new Date().toISOString(),
|
|
260
|
+
});
|
|
261
|
+
} catch (error) {
|
|
262
|
+
if (error?.code === 'ENOENT') {
|
|
263
|
+
return res.json({ success: true, projectName: req.params.projectName, synced: 0, tasks: [] });
|
|
264
|
+
}
|
|
265
|
+
console.error('TaskMaster orchestration sync error:', error);
|
|
266
|
+
res.status(500).json({
|
|
267
|
+
success: false,
|
|
268
|
+
error: 'Failed to sync TaskMaster tasks to orchestration',
|
|
269
|
+
message: error.message,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
|
|
249
274
|
/**
|
|
250
275
|
* GET /api/taskmaster/prd/:projectName
|
|
251
276
|
* List all PRD files in the project's .taskmaster/docs directory
|