@pixelbyte-software/pixcode 1.47.5 → 1.48.1
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/README.md +16 -30
- package/README.tr.md +1 -1
- package/dist/api-automation.html +1 -1
- package/dist/assets/index-By2TVdoL.css +32 -0
- package/dist/assets/index-rGiqd0D-.js +818 -0
- package/dist/assets/{vendor-codemirror-CzSp4P1a.js → vendor-codemirror-CIYNS698.js} +8 -8
- package/dist/docs.html +10 -10
- package/dist/features.html +3 -3
- package/dist/index.html +3 -3
- package/dist/landing.html +21 -23
- package/dist/llms-full.txt +4 -8
- package/dist/llms.txt +5 -6
- package/dist/openapi.yaml +7 -7
- package/dist-server/server/index.js +4 -6
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapters/claude-code.adapter.js +1 -1
- 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 +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapters/codex.adapter.js.map +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapters/cursor.adapter.js +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapters/cursor.adapter.js.map +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapters/gemini.adapter.js +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapters/gemini.adapter.js.map +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapters/opencode.adapter.js +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapters/opencode.adapter.js.map +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapters/qwen.adapter.js +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapters/qwen.adapter.js.map +1 -1
- package/dist-server/server/modules/orchestration/a2a/agent-card.js +4 -4
- package/dist-server/server/modules/orchestration/a2a/agent-card.js.map +1 -1
- package/dist-server/server/modules/orchestration/a2a/routes.js +6 -2
- package/dist-server/server/modules/orchestration/a2a/routes.js.map +1 -1
- package/dist-server/server/modules/orchestration/hermes/hermes.routes.js +62 -0
- package/dist-server/server/modules/orchestration/hermes/hermes.routes.js.map +1 -0
- package/dist-server/server/modules/orchestration/index.js +2 -1
- package/dist-server/server/modules/orchestration/index.js.map +1 -1
- package/dist-server/server/modules/orchestration/tasks/orchestration-task-store.js +2 -5
- package/dist-server/server/modules/orchestration/tasks/orchestration-task-store.js.map +1 -1
- package/dist-server/server/modules/orchestration/tasks/orchestration-task.routes.js +1 -20
- package/dist-server/server/modules/orchestration/tasks/orchestration-task.routes.js.map +1 -1
- package/dist-server/server/modules/orchestration/tasks/orchestration-task.service.js +4 -37
- package/dist-server/server/modules/orchestration/tasks/orchestration-task.service.js.map +1 -1
- package/dist-server/server/modules/orchestration/tasks/task-run-graph.js +4 -62
- package/dist-server/server/modules/orchestration/tasks/task-run-graph.js.map +1 -1
- package/dist-server/server/modules/orchestration/workflows/workflow-runner.js +14 -14
- package/dist-server/server/modules/orchestration/workflows/workflow-runner.js.map +1 -1
- package/dist-server/server/modules/orchestration/workflows/workflow-trace.js +2 -2
- package/dist-server/server/modules/orchestration/workflows/workflow-trace.js.map +1 -1
- package/dist-server/server/projects.js +0 -160
- package/dist-server/server/projects.js.map +1 -1
- package/dist-server/server/routes/mcp-utils.js +0 -18
- package/dist-server/server/routes/mcp-utils.js.map +1 -1
- package/dist-server/server/services/public-api-manifest.js +0 -5
- package/dist-server/server/services/public-api-manifest.js.map +1 -1
- package/dist-server/server/services/telegram/control-center.js +2 -144
- package/dist-server/server/services/telegram/control-center.js.map +1 -1
- package/dist-server/server/services/telegram/translations.js +2 -14
- package/dist-server/server/services/telegram/translations.js.map +1 -1
- package/package.json +1 -2
- package/scripts/smoke/default-landing-routing.mjs +7 -16
- package/scripts/smoke/{a2a-roundtrip.mjs → hermes-roundtrip.mjs} +23 -23
- package/scripts/smoke/mac-desktop-runtime.mjs +1 -7
- package/scripts/smoke/orchestration-live-run.mjs +1 -1
- package/scripts/smoke/orchestration-model-sync.mjs +2 -2
- package/scripts/smoke/orchestration-user-facing-output.mjs +2 -2
- package/scripts/smoke/pixcode-workbench-1-48.mjs +55 -0
- package/scripts/smoke/taskmaster-config.mjs +21 -56
- package/scripts/smoke/taskmaster-execution-telegram.mjs +1 -50
- package/scripts/smoke/taskmaster-onboarding.mjs +1 -50
- package/scripts/smoke/taskmaster-run-graph.mjs +1 -53
- package/scripts/smoke/vscode-workbench-layout.mjs +25 -62
- package/scripts/smoke/vscode-workbench-polish.mjs +77 -5
- package/server/index.js +5 -7
- package/server/modules/orchestration/a2a/adapters/claude-code.adapter.ts +1 -1
- package/server/modules/orchestration/a2a/adapters/codex.adapter.ts +1 -1
- package/server/modules/orchestration/a2a/adapters/cursor.adapter.ts +1 -1
- package/server/modules/orchestration/a2a/adapters/gemini.adapter.ts +1 -1
- package/server/modules/orchestration/a2a/adapters/opencode.adapter.ts +1 -1
- package/server/modules/orchestration/a2a/adapters/qwen.adapter.ts +1 -1
- package/server/modules/orchestration/a2a/agent-card.ts +4 -4
- package/server/modules/orchestration/a2a/routes.ts +7 -2
- package/server/modules/orchestration/hermes/hermes.routes.ts +69 -0
- package/server/modules/orchestration/index.ts +2 -1
- package/server/modules/orchestration/tasks/orchestration-task-store.ts +2 -6
- package/server/modules/orchestration/tasks/orchestration-task.routes.ts +1 -20
- package/server/modules/orchestration/tasks/orchestration-task.service.ts +4 -41
- package/server/modules/orchestration/tasks/orchestration-task.types.ts +2 -4
- package/server/modules/orchestration/tasks/task-run-graph.ts +6 -70
- package/server/modules/orchestration/workflows/workflow-runner.ts +14 -14
- package/server/modules/orchestration/workflows/workflow-trace.ts +2 -2
- package/server/modules/orchestration/workflows/workflow.types.ts +1 -1
- package/server/projects.js +0 -170
- package/server/routes/mcp-utils.js +0 -19
- package/server/services/public-api-manifest.js +0 -5
- package/server/services/telegram/control-center.js +2 -153
- package/server/services/telegram/translations.js +2 -14
- package/dist/assets/index-BBdWwJi6.css +0 -32
- package/dist/assets/index-DfqcgNYJ.js +0 -889
- package/dist-server/server/routes/taskmaster.js +0 -1793
- package/dist-server/server/routes/taskmaster.js.map +0 -1
- package/dist-server/server/services/taskmaster-config.js +0 -128
- package/dist-server/server/services/taskmaster-config.js.map +0 -1
- package/dist-server/server/utils/mcp-detector.js +0 -134
- package/dist-server/server/utils/mcp-detector.js.map +0 -1
- package/dist-server/server/utils/taskmaster-websocket.js +0 -118
- package/dist-server/server/utils/taskmaster-websocket.js.map +0 -1
- package/server/routes/taskmaster.js +0 -1918
- package/server/services/taskmaster-config.js +0 -146
- package/server/utils/mcp-detector.js +0 -147
- package/server/utils/taskmaster-websocket.js +0 -129
|
@@ -41,6 +41,76 @@ assert.match(
|
|
|
41
41
|
'Projects activity should use a dedicated project-directory panel.',
|
|
42
42
|
);
|
|
43
43
|
|
|
44
|
+
assert.match(
|
|
45
|
+
workbench,
|
|
46
|
+
/function WorkbenchProjectLanding/,
|
|
47
|
+
'Workbench center should show a project landing page instead of a blank editor when no project is selected.',
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
assert.match(
|
|
51
|
+
workbench,
|
|
52
|
+
/function WorkbenchCliPanel/,
|
|
53
|
+
'Right workbench pane should use a terminal-only CLI panel.',
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
assert.match(
|
|
57
|
+
workbench,
|
|
58
|
+
/function WorkbenchWorkspaceTabs/,
|
|
59
|
+
'Workbench should render Chrome-style workspace tabs directly under the menu bar.',
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
assert.match(
|
|
63
|
+
workbench,
|
|
64
|
+
/WORKBENCH_WORKSPACE_TABS_STORAGE_KEY/,
|
|
65
|
+
'Workspace tabs should persist names, stars, and open tabs across reloads.',
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
assert.doesNotMatch(
|
|
69
|
+
workbench,
|
|
70
|
+
/workspaceSlots/,
|
|
71
|
+
'Workspace controls should no longer occupy space inside the Explorer panel.',
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
assert.match(
|
|
75
|
+
workbench,
|
|
76
|
+
/editorTabStripRef/,
|
|
77
|
+
'Editor tabs should have a scrollable strip instead of shrinking every tab.',
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
assert.match(
|
|
81
|
+
workbench,
|
|
82
|
+
/handleEditorTabContextMenu/,
|
|
83
|
+
'Editor tabs should expose a right-click context menu.',
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
for (const token of ['closeAllTabs', 'copyPath', 'splitRight', 'splitMoveRight']) {
|
|
87
|
+
assert.match(workbench, new RegExp(token), `Editor tab context menu should support ${token}.`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
assert.match(
|
|
91
|
+
workbench,
|
|
92
|
+
/SessionProviderLogo/,
|
|
93
|
+
'CLI provider picker should keep provider icons visible.',
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
assert.match(
|
|
97
|
+
workbench,
|
|
98
|
+
/useProviderAuthStatus/,
|
|
99
|
+
'CLI provider picker should show install/update status without opening Settings.',
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
assert.match(
|
|
103
|
+
workbench,
|
|
104
|
+
/autoConnect=\{canAutoConnect\}/,
|
|
105
|
+
'Right CLI terminal should auto-connect when the selected provider can run.',
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
assert.match(
|
|
109
|
+
workbench,
|
|
110
|
+
/function WorkbenchSessionHistory/,
|
|
111
|
+
'CLI history should be integrated as a polished project-scoped panel.',
|
|
112
|
+
);
|
|
113
|
+
|
|
44
114
|
assert.doesNotMatch(
|
|
45
115
|
workbench,
|
|
46
116
|
/return <Sidebar \{\.\.\.sidebarProps\} isMobile=\{false\} \/>/,
|
|
@@ -59,6 +129,12 @@ assert.match(
|
|
|
59
129
|
'Projects panel should show file counts.',
|
|
60
130
|
);
|
|
61
131
|
|
|
132
|
+
assert.match(
|
|
133
|
+
workbench,
|
|
134
|
+
/Work in this folder/,
|
|
135
|
+
'Project cards should make the folder binding explicit.',
|
|
136
|
+
);
|
|
137
|
+
|
|
62
138
|
assert.match(projectType, /fileCount\?: number/, 'Project type should expose optional fileCount metadata.');
|
|
63
139
|
assert.match(projectsServer, /async function countProjectFiles/, 'Backend should count project files for the workbench project list.');
|
|
64
140
|
|
|
@@ -74,11 +150,7 @@ assert.doesNotMatch(
|
|
|
74
150
|
'Workbench center panel should be allowed to shrink with narrow three-pane layouts.',
|
|
75
151
|
);
|
|
76
152
|
|
|
77
|
-
assert.
|
|
78
|
-
workbench,
|
|
79
|
-
/compactComposer/,
|
|
80
|
-
'Workbench should request compact composer behavior in the right CLI pane.',
|
|
81
|
-
);
|
|
153
|
+
assert.doesNotMatch(workbench, /<ChatInterface/, 'Workbench should not embed the chat composer in the right CLI pane.');
|
|
82
154
|
|
|
83
155
|
assert.match(
|
|
84
156
|
workbench,
|
package/server/index.js
CHANGED
|
@@ -58,7 +58,6 @@ import sessionManager from './sessionManager.js';
|
|
|
58
58
|
import gitRoutes from './routes/git.js';
|
|
59
59
|
import authRoutes from './routes/auth.js';
|
|
60
60
|
import cursorRoutes from './routes/cursor.js';
|
|
61
|
-
import taskmasterRoutes from './routes/taskmaster.js';
|
|
62
61
|
import mcpUtilsRoutes from './routes/mcp-utils.js';
|
|
63
62
|
import commandsRoutes from './routes/commands.js';
|
|
64
63
|
import settingsRoutes from './routes/settings.js';
|
|
@@ -84,7 +83,7 @@ import platformizationRoutes from './routes/platformization.js';
|
|
|
84
83
|
import liveViewRoutes, { createLiveViewPublicRouter } from './routes/live-view.js';
|
|
85
84
|
import providerRoutes from './modules/providers/provider.routes.js';
|
|
86
85
|
import {
|
|
87
|
-
|
|
86
|
+
createHermesTaskRouter,
|
|
88
87
|
adapterRegistry,
|
|
89
88
|
ClaudeCodeA2AAdapter,
|
|
90
89
|
CodexA2AAdapter,
|
|
@@ -94,6 +93,7 @@ import {
|
|
|
94
93
|
QwenA2AAdapter,
|
|
95
94
|
createPreviewProxyRouter,
|
|
96
95
|
createOrchestrationTaskRouter,
|
|
96
|
+
createHermesRouter,
|
|
97
97
|
createWorkflowRouter,
|
|
98
98
|
} from './modules/orchestration/index.js';
|
|
99
99
|
import networkRoutes from './routes/network.js';
|
|
@@ -370,9 +370,6 @@ app.use('/api/git', authenticateToken, gitRoutes);
|
|
|
370
370
|
// Cursor API Routes (protected)
|
|
371
371
|
app.use('/api/cursor', authenticateToken, cursorRoutes);
|
|
372
372
|
|
|
373
|
-
// TaskMaster API Routes (protected)
|
|
374
|
-
app.use('/api/taskmaster', authenticateToken, taskmasterRoutes);
|
|
375
|
-
|
|
376
373
|
// MCP utilities
|
|
377
374
|
app.use('/api/mcp-utils', authenticateToken, mcpUtilsRoutes);
|
|
378
375
|
|
|
@@ -424,16 +421,17 @@ app.use('/api/live-view', authenticateToken, liveViewRoutes);
|
|
|
424
421
|
// Unified provider MCP routes (protected)
|
|
425
422
|
app.use('/api/providers', authenticateToken, providerRoutes);
|
|
426
423
|
|
|
427
|
-
//
|
|
424
|
+
// Hermes internal task router has its own localhost/auth middleware; do not wrap with authenticateToken.
|
|
428
425
|
adapterRegistry.register(new ClaudeCodeA2AAdapter());
|
|
429
426
|
adapterRegistry.register(new CodexA2AAdapter());
|
|
430
427
|
adapterRegistry.register(new CursorA2AAdapter());
|
|
431
428
|
adapterRegistry.register(new GeminiA2AAdapter());
|
|
432
429
|
adapterRegistry.register(new QwenA2AAdapter());
|
|
433
430
|
adapterRegistry.register(new OpenCodeA2AAdapter());
|
|
434
|
-
app.use('/
|
|
431
|
+
app.use('/hermes', createHermesTaskRouter());
|
|
435
432
|
app.use('/preview', authenticateToken, createPreviewProxyRouter());
|
|
436
433
|
app.use('/api/orchestration', authenticateToken, createOrchestrationTaskRouter());
|
|
434
|
+
app.use('/api/orchestration/hermes', authenticateToken, createHermesRouter());
|
|
437
435
|
app.use('/api/orchestration', authenticateToken, createWorkflowRouter());
|
|
438
436
|
app.use('/live', createLiveViewPublicRouter());
|
|
439
437
|
|
|
@@ -49,7 +49,7 @@ export class ClaudeCodeA2AAdapter extends AbstractA2AAdapter {
|
|
|
49
49
|
readonly agentCard: AgentCard = {
|
|
50
50
|
name: 'pixcode-claude-code',
|
|
51
51
|
description: 'Anthropic Claude Code, accessed via Pixcode',
|
|
52
|
-
url: '/
|
|
52
|
+
url: '/hermes/agents/claude-code',
|
|
53
53
|
version: '1.0.0',
|
|
54
54
|
capabilities: ['streaming', 'fileEdit', 'commandExec', 'mcp'],
|
|
55
55
|
skills: [
|
|
@@ -40,7 +40,7 @@ export class CodexA2AAdapter extends AbstractA2AAdapter {
|
|
|
40
40
|
readonly agentCard: AgentCard = {
|
|
41
41
|
name: 'pixcode-codex',
|
|
42
42
|
description: 'OpenAI Codex, accessed via Pixcode',
|
|
43
|
-
url: '/
|
|
43
|
+
url: '/hermes/agents/codex',
|
|
44
44
|
version: '1.0.0',
|
|
45
45
|
capabilities: ['streaming', 'fileEdit', 'commandExec', 'webSearch', 'mcp'],
|
|
46
46
|
skills: [
|
|
@@ -40,7 +40,7 @@ export class CursorA2AAdapter extends AbstractA2AAdapter {
|
|
|
40
40
|
readonly agentCard: AgentCard = {
|
|
41
41
|
name: 'pixcode-cursor',
|
|
42
42
|
description: 'Cursor CLI, accessed via Pixcode',
|
|
43
|
-
url: '/
|
|
43
|
+
url: '/hermes/agents/cursor',
|
|
44
44
|
version: '1.0.0',
|
|
45
45
|
capabilities: ['streaming', 'fileEdit', 'commandExec'],
|
|
46
46
|
skills: [
|
|
@@ -40,7 +40,7 @@ export class GeminiA2AAdapter extends AbstractA2AAdapter {
|
|
|
40
40
|
readonly agentCard: AgentCard = {
|
|
41
41
|
name: 'pixcode-gemini',
|
|
42
42
|
description: 'Google Gemini CLI, accessed via Pixcode',
|
|
43
|
-
url: '/
|
|
43
|
+
url: '/hermes/agents/gemini',
|
|
44
44
|
version: '1.0.0',
|
|
45
45
|
capabilities: ['streaming', 'fileEdit', 'commandExec', 'mcp'],
|
|
46
46
|
skills: [
|
|
@@ -40,7 +40,7 @@ export class OpenCodeA2AAdapter extends AbstractA2AAdapter {
|
|
|
40
40
|
readonly agentCard: AgentCard = {
|
|
41
41
|
name: 'pixcode-opencode',
|
|
42
42
|
description: 'OpenCode CLI, accessed via Pixcode',
|
|
43
|
-
url: '/
|
|
43
|
+
url: '/hermes/agents/opencode',
|
|
44
44
|
version: '1.0.0',
|
|
45
45
|
capabilities: ['streaming', 'fileEdit', 'commandExec', 'multiProvider'],
|
|
46
46
|
skills: [
|
|
@@ -40,7 +40,7 @@ export class QwenA2AAdapter extends AbstractA2AAdapter {
|
|
|
40
40
|
readonly agentCard: AgentCard = {
|
|
41
41
|
name: 'pixcode-qwen',
|
|
42
42
|
description: 'Qwen Code CLI, accessed via Pixcode',
|
|
43
|
-
url: '/
|
|
43
|
+
url: '/hermes/agents/qwen',
|
|
44
44
|
version: '1.0.0',
|
|
45
45
|
capabilities: ['streaming', 'fileEdit', 'commandExec', 'mcp'],
|
|
46
46
|
skills: [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// server/modules/orchestration/a2a/agent-card.ts
|
|
2
|
-
// Pixcode advertises
|
|
3
|
-
// Per-CLI adapters publish their own cards under /
|
|
2
|
+
// Pixcode advertises Hermes as one local control agent at /hermes/.well-known/agent-card.json.
|
|
3
|
+
// Per-CLI adapters publish their own cards under /hermes/agents/:id/agent-card.
|
|
4
4
|
|
|
5
5
|
import { readFileSync } from 'node:fs';
|
|
6
6
|
import { dirname, resolve } from 'node:path';
|
|
@@ -44,9 +44,9 @@ export function buildPixcodeAgentCard(baseUrl: string): AgentCard {
|
|
|
44
44
|
return {
|
|
45
45
|
name: 'pixcode',
|
|
46
46
|
description:
|
|
47
|
-
'Pixcode multi-CLI orchestration platform. Routes
|
|
47
|
+
'Pixcode Hermes multi-CLI orchestration platform. Routes Hermes tasks to ' +
|
|
48
48
|
'Claude Code, Codex, Cursor, Gemini, Qwen, or OpenCode adapters.',
|
|
49
|
-
url: `${baseUrl.replace(/\/$/, '')}/
|
|
49
|
+
url: `${baseUrl.replace(/\/$/, '')}/hermes`,
|
|
50
50
|
version: VERSION,
|
|
51
51
|
capabilities: ['streaming', 'taskRouting'],
|
|
52
52
|
skills,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// server/modules/orchestration/a2a/routes.ts
|
|
2
|
-
//
|
|
2
|
+
// Internal Hermes task router. The adapter primitives still live in this legacy
|
|
3
|
+
// folder, but the public mount is /hermes.
|
|
3
4
|
|
|
4
5
|
import crypto from 'node:crypto';
|
|
5
6
|
|
|
@@ -257,7 +258,7 @@ for (const task of taskStore.values()) {
|
|
|
257
258
|
}
|
|
258
259
|
}
|
|
259
260
|
|
|
260
|
-
|
|
261
|
+
function createA2ARouter(): Router {
|
|
261
262
|
const router: Router = express.Router();
|
|
262
263
|
|
|
263
264
|
router.use(express.json({ limit: '5mb' }));
|
|
@@ -583,3 +584,7 @@ export function createA2ARouter(): Router {
|
|
|
583
584
|
|
|
584
585
|
return router;
|
|
585
586
|
}
|
|
587
|
+
|
|
588
|
+
export function createHermesTaskRouter(): Router {
|
|
589
|
+
return createA2ARouter();
|
|
590
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
|
|
3
|
+
import express, { type Router } from 'express';
|
|
4
|
+
|
|
5
|
+
import { adapterRegistry } from '@/modules/orchestration/a2a/adapter-registry.js';
|
|
6
|
+
import { a2aTaskStore as hermesTaskStore } from '@/modules/orchestration/a2a/task-store.js';
|
|
7
|
+
|
|
8
|
+
export function createHermesRouter(): Router {
|
|
9
|
+
const router = express.Router();
|
|
10
|
+
|
|
11
|
+
router.get('/status', (_req, res) => {
|
|
12
|
+
res.json({
|
|
13
|
+
id: 'hermes',
|
|
14
|
+
name: 'Hermes Agent',
|
|
15
|
+
product: 'Pixcode',
|
|
16
|
+
runtime: {
|
|
17
|
+
mode: process.env.PIXCODE_MANAGED_PLATFORM === '1' ? 'managed' : 'local',
|
|
18
|
+
host: os.hostname(),
|
|
19
|
+
node: process.version,
|
|
20
|
+
pid: process.pid,
|
|
21
|
+
},
|
|
22
|
+
capabilities: [
|
|
23
|
+
'project-aware CLI orchestration',
|
|
24
|
+
'background checks and tests',
|
|
25
|
+
'workspace-scoped terminal operations',
|
|
26
|
+
'run history and artifact review',
|
|
27
|
+
],
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
router.get('/context', (req, res) => {
|
|
32
|
+
const project = typeof req.query.project === 'string' ? req.query.project : null;
|
|
33
|
+
const cwd = typeof req.query.cwd === 'string' ? req.query.cwd : process.cwd();
|
|
34
|
+
res.json({
|
|
35
|
+
agent: 'Hermes Agent',
|
|
36
|
+
product: 'Pixcode',
|
|
37
|
+
project,
|
|
38
|
+
cwd,
|
|
39
|
+
local: true,
|
|
40
|
+
message: project
|
|
41
|
+
? `Hermes is operating inside Pixcode for ${project}.`
|
|
42
|
+
: 'Hermes is operating inside Pixcode.',
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
router.get('/agents', (_req, res) => {
|
|
47
|
+
res.json({
|
|
48
|
+
agent: 'hermes',
|
|
49
|
+
agents: adapterRegistry.agentCards().map((agent) => ({
|
|
50
|
+
...agent,
|
|
51
|
+
coordinator: 'hermes',
|
|
52
|
+
})),
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
router.get('/tasks/:id', (req, res) => {
|
|
57
|
+
const task = hermesTaskStore.get(req.params.id);
|
|
58
|
+
if (!task) {
|
|
59
|
+
res.status(404).json({ error: { code: 'HERMES_TASK_NOT_FOUND', message: req.params.id } });
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
res.json({
|
|
63
|
+
...task,
|
|
64
|
+
hermesTaskId: task.id,
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return router;
|
|
69
|
+
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// All cross-module consumers must import from here per
|
|
4
4
|
// eslint.config.js boundaries rules.
|
|
5
5
|
|
|
6
|
-
export {
|
|
6
|
+
export { createHermesTaskRouter } from './a2a/routes.js';
|
|
7
7
|
export { adapterRegistry } from './a2a/adapter-registry.js';
|
|
8
8
|
export { ClaudeCodeA2AAdapter } from './a2a/adapters/claude-code.adapter.js';
|
|
9
9
|
export { CodexA2AAdapter } from './a2a/adapters/codex.adapter.js';
|
|
@@ -30,6 +30,7 @@ export {
|
|
|
30
30
|
} from './security/permission-policy.js';
|
|
31
31
|
export { createOrchestrationTaskRouter } from './tasks/orchestration-task.routes.js';
|
|
32
32
|
export { orchestrationTaskService } from './tasks/orchestration-task.service.js';
|
|
33
|
+
export { createHermesRouter } from './hermes/hermes.routes.js';
|
|
33
34
|
export { createWorkflowRouter } from './workflows/workflow.routes.js';
|
|
34
35
|
export {
|
|
35
36
|
listPendingApprovals,
|
|
@@ -20,12 +20,8 @@ export class OrchestrationTaskStore {
|
|
|
20
20
|
return this.store.findWhere('orchestration_tasks', (r: OrchestrationTask) => r.id === id) ?? undefined;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
return this.store.findWhere('orchestration_tasks', (r: OrchestrationTask) => r.
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
getByTaskMasterId(taskmasterId: string): OrchestrationTask | undefined {
|
|
28
|
-
return this.store.findWhere('orchestration_tasks', (r: OrchestrationTask) => r.taskmasterId === taskmasterId) ?? undefined;
|
|
23
|
+
getByHermesTaskId(hermesTaskId: string): OrchestrationTask | undefined {
|
|
24
|
+
return this.store.findWhere('orchestration_tasks', (r: OrchestrationTask) => r.hermesTaskId === hermesTaskId) ?? undefined;
|
|
29
25
|
}
|
|
30
26
|
|
|
31
27
|
list(projectId?: string): OrchestrationTask[] {
|
|
@@ -15,35 +15,16 @@ export function createOrchestrationTaskRouter(): Router {
|
|
|
15
15
|
const projectId = typeof req.body?.projectId === 'string' ? req.body.projectId : 'default';
|
|
16
16
|
const title = typeof req.body?.title === 'string' ? req.body.title.trim() : '';
|
|
17
17
|
const description = typeof req.body?.description === 'string' ? req.body.description : undefined;
|
|
18
|
-
const taskmasterId = typeof req.body?.taskmasterId === 'string' ? req.body.taskmasterId : undefined;
|
|
19
18
|
|
|
20
19
|
if (!title) {
|
|
21
20
|
res.status(400).json({ error: { code: 'TITLE_REQUIRED', message: 'title is required' } });
|
|
22
21
|
return;
|
|
23
22
|
}
|
|
24
23
|
|
|
25
|
-
const task = orchestrationTaskService.create({ projectId, title, description
|
|
24
|
+
const task = orchestrationTaskService.create({ projectId, title, description });
|
|
26
25
|
res.status(201).json(task);
|
|
27
26
|
});
|
|
28
27
|
|
|
29
|
-
router.post('/tasks/import-taskmaster', (req, res) => {
|
|
30
|
-
const projectId = typeof req.body?.projectId === 'string' ? req.body.projectId : 'default';
|
|
31
|
-
const entries = Array.isArray(req.body?.tasks) ? req.body.tasks : [];
|
|
32
|
-
const imported = entries
|
|
33
|
-
.map((entry: unknown) => {
|
|
34
|
-
if (!entry || typeof entry !== 'object') return null;
|
|
35
|
-
const record = entry as Record<string, unknown>;
|
|
36
|
-
const taskmasterId = typeof record.id === 'string' ? record.id : undefined;
|
|
37
|
-
const title = typeof record.title === 'string' ? record.title : undefined;
|
|
38
|
-
const description = typeof record.description === 'string' ? record.description : undefined;
|
|
39
|
-
if (!taskmasterId || !title) return null;
|
|
40
|
-
return orchestrationTaskService.upsertFromTaskMaster({ projectId, title, description, taskmasterId });
|
|
41
|
-
})
|
|
42
|
-
.filter(Boolean);
|
|
43
|
-
|
|
44
|
-
res.json({ tasks: imported, count: imported.length });
|
|
45
|
-
});
|
|
46
|
-
|
|
47
28
|
router.post('/tasks/:id/dispatch', async (req, res) => {
|
|
48
29
|
try {
|
|
49
30
|
const adapterId = typeof req.body?.adapterId === 'string' ? req.body.adapterId : '';
|
|
@@ -79,7 +79,6 @@ class OrchestrationTaskService {
|
|
|
79
79
|
projectId: input.projectId,
|
|
80
80
|
title: input.title,
|
|
81
81
|
description: input.description,
|
|
82
|
-
taskmasterId: input.taskmasterId,
|
|
83
82
|
acceptanceCriteria: input.acceptanceCriteria,
|
|
84
83
|
changedFiles: input.changedFiles,
|
|
85
84
|
state: 'todo',
|
|
@@ -90,27 +89,11 @@ class OrchestrationTaskService {
|
|
|
90
89
|
return task;
|
|
91
90
|
}
|
|
92
91
|
|
|
93
|
-
upsertFromTaskMaster(input: CreateOrchestrationTaskInput): OrchestrationTask {
|
|
94
|
-
const existing = this.store.list(input.projectId).find((task) =>
|
|
95
|
-
task.taskmasterId === input.taskmasterId,
|
|
96
|
-
);
|
|
97
|
-
if (existing) {
|
|
98
|
-
existing.title = input.title;
|
|
99
|
-
existing.description = input.description;
|
|
100
|
-
existing.acceptanceCriteria = input.acceptanceCriteria ?? existing.acceptanceCriteria;
|
|
101
|
-
existing.changedFiles = uniqueStrings([...(existing.changedFiles ?? []), ...(input.changedFiles ?? [])]);
|
|
102
|
-
existing.updatedAt = Date.now();
|
|
103
|
-
this.store.set(existing);
|
|
104
|
-
return existing;
|
|
105
|
-
}
|
|
106
|
-
return this.create(input);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
92
|
async dispatch(taskId: string, input: DispatchOrchestrationTaskInput): Promise<OrchestrationTask> {
|
|
110
93
|
const task = this.store.get(taskId);
|
|
111
94
|
if (!task) throw new Error('TASK_NOT_FOUND');
|
|
112
95
|
|
|
113
|
-
const a2aResponse = await fetch(`http://127.0.0.1:${process.env.SERVER_PORT ?? '3001'}/
|
|
96
|
+
const a2aResponse = await fetch(`http://127.0.0.1:${process.env.SERVER_PORT ?? '3001'}/hermes/tasks`, {
|
|
114
97
|
method: 'POST',
|
|
115
98
|
headers: { 'Content-Type': 'application/json' },
|
|
116
99
|
body: JSON.stringify({
|
|
@@ -129,7 +112,6 @@ class OrchestrationTaskService {
|
|
|
129
112
|
projectPath: input.projectPath,
|
|
130
113
|
},
|
|
131
114
|
orchestrationTaskId: task.id,
|
|
132
|
-
taskmasterId: task.taskmasterId,
|
|
133
115
|
},
|
|
134
116
|
}),
|
|
135
117
|
});
|
|
@@ -139,7 +121,7 @@ class OrchestrationTaskService {
|
|
|
139
121
|
throw new Error(body?.error?.message ?? 'DISPATCH_FAILED');
|
|
140
122
|
}
|
|
141
123
|
|
|
142
|
-
task.
|
|
124
|
+
task.hermesTaskId = body.id;
|
|
143
125
|
task.adapterId = input.adapterId;
|
|
144
126
|
task.adapterSelector = input.adapterId;
|
|
145
127
|
task.workspaceKind = input.isolation ?? 'worktree';
|
|
@@ -163,12 +145,7 @@ class OrchestrationTaskService {
|
|
|
163
145
|
updateFromWorkflowRun(run: WorkflowRun): OrchestrationTask | undefined {
|
|
164
146
|
const metadata = run.metadata ?? {};
|
|
165
147
|
const taskId = readString(metadata.orchestrationTaskId);
|
|
166
|
-
const
|
|
167
|
-
const task = taskId
|
|
168
|
-
? this.store.get(taskId)
|
|
169
|
-
: taskmasterId
|
|
170
|
-
? this.store.list(readString(metadata.projectId)).find((candidate) => candidate.taskmasterId === taskmasterId)
|
|
171
|
-
: undefined;
|
|
148
|
+
const task = taskId ? this.store.get(taskId) : undefined;
|
|
172
149
|
if (!task) return undefined;
|
|
173
150
|
|
|
174
151
|
const changedFiles = changedFilesFromWorkflowRun(run);
|
|
@@ -187,9 +164,6 @@ class OrchestrationTaskService {
|
|
|
187
164
|
task.updatedAt = Date.now();
|
|
188
165
|
this.store.set(task);
|
|
189
166
|
|
|
190
|
-
if (task.taskmasterId && task.state === 'done') {
|
|
191
|
-
this.syncTaskMasterStatus(task.taskmasterId, 'done');
|
|
192
|
-
}
|
|
193
167
|
return task;
|
|
194
168
|
}
|
|
195
169
|
|
|
@@ -207,7 +181,7 @@ class OrchestrationTaskService {
|
|
|
207
181
|
if (event.kind !== 'task-state') return;
|
|
208
182
|
if (!TERMINAL_A2A_STATES.includes(event.state)) return;
|
|
209
183
|
|
|
210
|
-
const orchTask = this.store.
|
|
184
|
+
const orchTask = this.store.getByHermesTaskId(event.taskId);
|
|
211
185
|
if (!orchTask) return;
|
|
212
186
|
if (orchTask.state === 'done' || orchTask.state === 'failed' || orchTask.state === 'canceled') return;
|
|
213
187
|
|
|
@@ -219,20 +193,9 @@ class OrchestrationTaskService {
|
|
|
219
193
|
orchTask.updatedAt = Date.now();
|
|
220
194
|
this.store.set(orchTask);
|
|
221
195
|
|
|
222
|
-
if (orchTask.taskmasterId && mapped === 'done') {
|
|
223
|
-
this.syncTaskMasterStatus(orchTask.taskmasterId, 'done');
|
|
224
|
-
}
|
|
225
196
|
});
|
|
226
197
|
}
|
|
227
198
|
|
|
228
|
-
private syncTaskMasterStatus(taskmasterId: string, status: string): void {
|
|
229
|
-
const { spawn } = require('node:child_process') as typeof import('node:child_process');
|
|
230
|
-
spawn('task-master', ['set-status', '--id', taskmasterId, '--status', status], {
|
|
231
|
-
stdio: 'ignore',
|
|
232
|
-
detached: true,
|
|
233
|
-
}).unref();
|
|
234
|
-
}
|
|
235
|
-
|
|
236
199
|
cancel(taskId: string): OrchestrationTask {
|
|
237
200
|
const task = this.store.get(taskId);
|
|
238
201
|
if (!task) throw new Error('TASK_NOT_FOUND');
|
|
@@ -2,9 +2,8 @@ export type OrchestrationTaskState = 'todo' | 'in_progress' | 'in_review' | 'don
|
|
|
2
2
|
|
|
3
3
|
export interface OrchestrationTask {
|
|
4
4
|
id: string;
|
|
5
|
-
|
|
5
|
+
hermesTaskId?: string;
|
|
6
6
|
workflowRunIds?: string[];
|
|
7
|
-
taskmasterId?: string;
|
|
8
7
|
projectId: string;
|
|
9
8
|
title: string;
|
|
10
9
|
description?: string;
|
|
@@ -13,7 +12,7 @@ export interface OrchestrationTask {
|
|
|
13
12
|
id: string;
|
|
14
13
|
label: string;
|
|
15
14
|
status: 'pending' | 'passed' | 'failed';
|
|
16
|
-
source: '
|
|
15
|
+
source: 'workflow' | 'hermes';
|
|
17
16
|
}>;
|
|
18
17
|
changedFiles?: string[];
|
|
19
18
|
adapterId?: string;
|
|
@@ -28,7 +27,6 @@ export interface CreateOrchestrationTaskInput {
|
|
|
28
27
|
projectId: string;
|
|
29
28
|
title: string;
|
|
30
29
|
description?: string;
|
|
31
|
-
taskmasterId?: string;
|
|
32
30
|
acceptanceCriteria?: OrchestrationTask['acceptanceCriteria'];
|
|
33
31
|
changedFiles?: string[];
|
|
34
32
|
}
|