@pixelbyte-software/pixcode 1.52.4 → 1.53.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 +9 -10
- package/README.tr.md +1 -1
- package/dist/api-automation.html +1 -1
- package/dist/assets/index-BO0lz8ZD.js +816 -0
- package/dist/assets/index-DnjqWG8B.css +32 -0
- package/dist/docs.html +10 -10
- package/dist/features.html +3 -3
- package/dist/index.html +2 -2
- package/dist/landing.html +16 -16
- package/dist/llms-full.txt +4 -4
- package/dist/llms.txt +3 -3
- package/dist/openapi.yaml +6 -6
- package/dist-server/server/index.js +8 -125
- 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/json-event.adapter.js +1 -1
- package/dist-server/server/modules/orchestration/a2a/adapters/json-event.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 +2 -5
- package/dist-server/server/modules/orchestration/a2a/routes.js.map +1 -1
- package/dist-server/server/modules/orchestration/a2a/task-dispatcher.js +295 -0
- package/dist-server/server/modules/orchestration/a2a/task-dispatcher.js.map +1 -0
- package/dist-server/server/modules/orchestration/index.js +0 -2
- package/dist-server/server/modules/orchestration/index.js.map +1 -1
- package/dist-server/server/modules/orchestration/tasks/orchestration-task-store.js +2 -2
- 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 +12 -0
- 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 +19 -26
- package/dist-server/server/modules/orchestration/tasks/orchestration-task.service.js.map +1 -1
- package/dist-server/server/modules/orchestration/workflows/workflow-runner.js +47 -58
- 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/routes/git.js +79 -21
- package/dist-server/server/routes/git.js.map +1 -1
- package/dist-server/server/services/public-api-manifest.js +0 -9
- package/dist-server/server/services/public-api-manifest.js.map +1 -1
- package/package.json +1 -1
- package/server/index.js +7 -134
- 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/json-event.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 +2 -6
- package/server/modules/orchestration/a2a/task-dispatcher.ts +345 -0
- package/server/modules/orchestration/index.ts +0 -2
- package/server/modules/orchestration/tasks/orchestration-task-store.ts +2 -2
- package/server/modules/orchestration/tasks/orchestration-task.routes.ts +17 -0
- package/server/modules/orchestration/tasks/orchestration-task.service.ts +19 -27
- package/server/modules/orchestration/tasks/orchestration-task.types.ts +2 -2
- package/server/modules/orchestration/tasks/task-run-graph.ts +1 -1
- package/server/modules/orchestration/workflows/workflow-runner.ts +52 -60
- package/server/modules/orchestration/workflows/workflow-trace.ts +2 -2
- package/server/modules/orchestration/workflows/workflow.types.ts +1 -1
- package/server/routes/git.js +89 -28
- package/server/services/public-api-manifest.js +0 -9
- package/dist/assets/index-CapxXSc7.js +0 -816
- package/dist/assets/index-DkT6-Pja.css +0 -32
- package/dist/hermes-agent.png +0 -0
- package/dist-server/server/modules/orchestration/hermes/hermes.routes.js +0 -577
- package/dist-server/server/modules/orchestration/hermes/hermes.routes.js.map +0 -1
- package/dist-server/server/services/hermes-gateway.js +0 -1467
- package/dist-server/server/services/hermes-gateway.js.map +0 -1
- package/dist-server/server/services/hermes-install-jobs.js +0 -676
- package/dist-server/server/services/hermes-install-jobs.js.map +0 -1
- package/scripts/hermes/configure-pixcode-mcp.mjs +0 -165
- package/scripts/hermes/pixcode-mcp-server.mjs +0 -1009
- package/scripts/smoke/hermes-api-install.mjs +0 -56
- package/scripts/smoke/hermes-gateway-persistence.mjs +0 -104
- package/scripts/smoke/hermes-mcp-pixcode-roundtrip.mjs +0 -426
- package/scripts/smoke/hermes-rest-chat-api.mjs +0 -162
- package/scripts/smoke/hermes-rest-chat-live.mjs +0 -45
- package/scripts/smoke/hermes-rest-codex-launch.mjs +0 -209
- package/scripts/smoke/hermes-rest-gateway.mjs +0 -79
- package/scripts/smoke/hermes-rest-live.mjs +0 -42
- package/scripts/smoke/hermes-roundtrip.mjs +0 -167
- package/scripts/smoke/hermes-settings-commands.mjs +0 -349
- package/scripts/smoke/hermes-smoke-launcher-guard.mjs +0 -34
- package/server/modules/orchestration/hermes/hermes.routes.ts +0 -642
- package/server/services/hermes-gateway.js +0 -1562
- package/server/services/hermes-install-jobs.js +0 -729
|
@@ -36,6 +36,11 @@ import {
|
|
|
36
36
|
} from '@/modules/orchestration/workflows/workspace-target.js';
|
|
37
37
|
import { workflowStore } from '@/modules/orchestration/workflows/workflow-store.js';
|
|
38
38
|
import { orchestrationTaskService } from '@/modules/orchestration/tasks/orchestration-task.service.js';
|
|
39
|
+
import {
|
|
40
|
+
cancelA2ATask,
|
|
41
|
+
getA2ATask,
|
|
42
|
+
submitA2ATask,
|
|
43
|
+
} from '@/modules/orchestration/a2a/task-dispatcher.js';
|
|
39
44
|
// @ts-ignore — plain-JS service
|
|
40
45
|
import {
|
|
41
46
|
getDefaultProviderModel,
|
|
@@ -101,10 +106,6 @@ function newId(prefix: string): string {
|
|
|
101
106
|
return `${prefix}_${crypto.randomBytes(8).toString('hex')}`;
|
|
102
107
|
}
|
|
103
108
|
|
|
104
|
-
function localHermesBaseUrl(): string {
|
|
105
|
-
return `http://127.0.0.1:${process.env.SERVER_PORT ?? process.env.PORT ?? '3001'}/hermes`;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
109
|
function validateWorkflow(workflow: Workflow): void {
|
|
109
110
|
if (workflow.nodes.length > 64) {
|
|
110
111
|
throw new Error('Workflow node limit exceeded.');
|
|
@@ -133,6 +134,7 @@ type TaskResult = {
|
|
|
133
134
|
};
|
|
134
135
|
|
|
135
136
|
type RawTask = {
|
|
137
|
+
id: string;
|
|
136
138
|
state?: string;
|
|
137
139
|
error?: { code?: string; message?: string };
|
|
138
140
|
history?: Array<{ role?: string; parts?: Array<{ kind?: string; text?: string; data?: Record<string, unknown> }> }>;
|
|
@@ -505,7 +507,7 @@ function handoffPrompt(agent: AgentAssignment, role: AgentRole): string {
|
|
|
505
507
|
return [
|
|
506
508
|
`You are ${agent.label} in a Pixcode CLI team.`,
|
|
507
509
|
`Your inferred stage is: ${role}.`,
|
|
508
|
-
'This is a bounded
|
|
510
|
+
'This is a bounded Pixcode handoff task, not the full implementation.',
|
|
509
511
|
'Read the original user goal and coordinator plan, then publish a compact contract for downstream agents.',
|
|
510
512
|
agent.instruction ? `Your explicit assignment from the user is: ${agent.instruction}` : '',
|
|
511
513
|
handoffArtifactInstructions('ready'),
|
|
@@ -1102,10 +1104,6 @@ function expandWorkflowForRun(workflow: Workflow, metadata?: Record<string, unkn
|
|
|
1102
1104
|
};
|
|
1103
1105
|
}
|
|
1104
1106
|
|
|
1105
|
-
async function cancelHermesTask(taskId: string): Promise<void> {
|
|
1106
|
-
await fetch(`${localHermesBaseUrl()}/tasks/${taskId}/cancel`, { method: 'POST' }).catch(() => undefined);
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
1107
|
function readTaskResult(task: RawTask): TaskResult {
|
|
1110
1108
|
const messages = (task.history ?? []).map((message) => ({
|
|
1111
1109
|
role: typeof message.role === 'string' ? message.role : 'agent',
|
|
@@ -1156,8 +1154,10 @@ async function waitForTask(
|
|
|
1156
1154
|
if (deadline && Date.now() >= deadline) {
|
|
1157
1155
|
throw new WorkflowNodeTimeoutError(timeout ?? 0);
|
|
1158
1156
|
}
|
|
1159
|
-
const
|
|
1160
|
-
|
|
1157
|
+
const task = getA2ATask(taskId);
|
|
1158
|
+
if (!task) {
|
|
1159
|
+
throw new Error(`Agent task ${taskId} was not found.`);
|
|
1160
|
+
}
|
|
1161
1161
|
const snapshot = readTaskResult(task);
|
|
1162
1162
|
onSnapshot?.(snapshot);
|
|
1163
1163
|
if (task.state && TERMINAL.has(task.state)) {
|
|
@@ -1307,13 +1307,13 @@ class WorkflowRunner {
|
|
|
1307
1307
|
|
|
1308
1308
|
this.cancelingRuns.add(run.id);
|
|
1309
1309
|
const taskIds = run.nodeRuns
|
|
1310
|
-
.filter((node) => node.
|
|
1311
|
-
.map((node) => node.
|
|
1310
|
+
.filter((node) => node.a2aTaskId && (node.status === 'running' || node.status === 'queued'))
|
|
1311
|
+
.map((node) => node.a2aTaskId as string);
|
|
1312
1312
|
|
|
1313
1313
|
this.markCanceled(run);
|
|
1314
1314
|
workflowStore.setRun(run);
|
|
1315
1315
|
|
|
1316
|
-
await Promise.all(taskIds.map((taskId) =>
|
|
1316
|
+
await Promise.all(taskIds.map((taskId) => cancelA2ATask(taskId)));
|
|
1317
1317
|
|
|
1318
1318
|
return workflowStore.getRun(run.id) ?? run;
|
|
1319
1319
|
}
|
|
@@ -1836,52 +1836,44 @@ class WorkflowRunner {
|
|
|
1836
1836
|
}
|
|
1837
1837
|
throw new Error(permissionDecision.message);
|
|
1838
1838
|
}
|
|
1839
|
-
let
|
|
1839
|
+
let submittedTask: RawTask;
|
|
1840
1840
|
try {
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1841
|
+
submittedTask = await submitA2ATask({
|
|
1842
|
+
adapterId: node.adapterId,
|
|
1843
|
+
contextId: run.contextId,
|
|
1844
|
+
message: {
|
|
1845
|
+
messageId: newId('msg'),
|
|
1846
|
+
role: 'user',
|
|
1847
|
+
parts: [{ kind: 'text', text: prompt }],
|
|
1848
|
+
},
|
|
1849
|
+
metadata: {
|
|
1850
|
+
workflowRunId: run.id,
|
|
1851
|
+
workflowNodeId: node.id,
|
|
1852
|
+
agentInstanceId: node.agentInstanceId,
|
|
1853
|
+
agentLabel: node.agentLabel,
|
|
1854
|
+
assignment: node.assignment,
|
|
1855
|
+
model: effectiveModel,
|
|
1856
|
+
permissionMode: effectivePermissionMode,
|
|
1857
|
+
permissionPolicy,
|
|
1858
|
+
permissionPolicyContext: {
|
|
1859
|
+
runId: run.id,
|
|
1860
|
+
nodeId: node.id,
|
|
1861
|
+
workflowId: run.workflowId,
|
|
1862
|
+
adapterId: node.adapterId,
|
|
1856
1863
|
agentLabel: node.agentLabel,
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
workflowId: run.workflowId,
|
|
1865
|
-
adapterId: node.adapterId,
|
|
1866
|
-
agentLabel: node.agentLabel,
|
|
1867
|
-
userId: readNotificationUserId(run.metadata),
|
|
1868
|
-
},
|
|
1869
|
-
toolsSettings: node.toolsSettings,
|
|
1864
|
+
userId: readNotificationUserId(run.metadata),
|
|
1865
|
+
},
|
|
1866
|
+
toolsSettings: node.toolsSettings,
|
|
1867
|
+
projectPath,
|
|
1868
|
+
workspaceTarget: workspaceTargetMetadata(workspaceTarget),
|
|
1869
|
+
workspace: {
|
|
1870
|
+
kind: isolation,
|
|
1870
1871
|
projectPath,
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
kind: isolation,
|
|
1874
|
-
projectPath,
|
|
1875
|
-
baseRef,
|
|
1876
|
-
keepAfterCompletion,
|
|
1877
|
-
},
|
|
1872
|
+
baseRef,
|
|
1873
|
+
keepAfterCompletion,
|
|
1878
1874
|
},
|
|
1879
|
-
}
|
|
1875
|
+
},
|
|
1880
1876
|
});
|
|
1881
|
-
body = await submit.json() as { id?: string; error?: { message?: string } };
|
|
1882
|
-
if (!submit.ok || !body.id) {
|
|
1883
|
-
throw new Error(body.error?.message ?? `Workflow node ${node.id} submit failed.`);
|
|
1884
|
-
}
|
|
1885
1877
|
} catch (error) {
|
|
1886
1878
|
nodeRun.finishedAt = Date.now();
|
|
1887
1879
|
nodeRun.status = 'failed';
|
|
@@ -1910,11 +1902,11 @@ class WorkflowRunner {
|
|
|
1910
1902
|
}
|
|
1911
1903
|
throw error;
|
|
1912
1904
|
}
|
|
1913
|
-
nodeRun.
|
|
1905
|
+
nodeRun.a2aTaskId = submittedTask.id;
|
|
1914
1906
|
workflowStore.setRun(run);
|
|
1915
1907
|
|
|
1916
1908
|
if (this.isCanceling(run.id)) {
|
|
1917
|
-
await
|
|
1909
|
+
await cancelA2ATask(submittedTask.id);
|
|
1918
1910
|
nodeRun.status = 'canceled';
|
|
1919
1911
|
nodeRun.finishedAt = Date.now();
|
|
1920
1912
|
workflowStore.setRun(run);
|
|
@@ -1924,7 +1916,7 @@ class WorkflowRunner {
|
|
|
1924
1916
|
let result: TaskResult;
|
|
1925
1917
|
try {
|
|
1926
1918
|
result = await waitForTask(
|
|
1927
|
-
|
|
1919
|
+
submittedTask.id,
|
|
1928
1920
|
() => this.isCanceling(run.id),
|
|
1929
1921
|
(snapshot) => {
|
|
1930
1922
|
nodeRun.outputText = snapshot.text || nodeRun.outputText;
|
|
@@ -1940,7 +1932,7 @@ class WorkflowRunner {
|
|
|
1940
1932
|
throw error;
|
|
1941
1933
|
}
|
|
1942
1934
|
|
|
1943
|
-
await
|
|
1935
|
+
await cancelA2ATask(submittedTask.id);
|
|
1944
1936
|
nodeRun.finishedAt = Date.now();
|
|
1945
1937
|
nodeRun.status = 'failed';
|
|
1946
1938
|
nodeRun.error = error.message;
|
|
@@ -2037,7 +2029,7 @@ class WorkflowRunner {
|
|
|
2037
2029
|
}
|
|
2038
2030
|
|
|
2039
2031
|
nodeRun.status = 'failed';
|
|
2040
|
-
nodeRun.error = result.error ?? `
|
|
2032
|
+
nodeRun.error = result.error ?? `Agent task ended with ${result.state}`;
|
|
2041
2033
|
workflowStore.setRun(run);
|
|
2042
2034
|
if (isExternalDirectoryPermissionError(`${nodeRun.error}\n${nodeRun.outputText ?? ''}`)) {
|
|
2043
2035
|
completeNodeWithPermissionFallback(nodeRun, node, outputs, completed, nodeRun.error, workspaceTarget);
|
|
@@ -333,7 +333,7 @@ export function buildWorkflowTrace(run: WorkflowRun): WorkflowTraceEvent[] {
|
|
|
333
333
|
id: traceId([run.id, node.nodeId, 'provider']),
|
|
334
334
|
type: 'provider',
|
|
335
335
|
severity: node.status === 'failed' ? 'error' : 'info',
|
|
336
|
-
status: node.
|
|
336
|
+
status: node.a2aTaskId ? 'submitted' : node.status,
|
|
337
337
|
timestamp: timestamp + 2,
|
|
338
338
|
durationMs: nodeDuration,
|
|
339
339
|
...base,
|
|
@@ -341,7 +341,7 @@ export function buildWorkflowTrace(run: WorkflowRun): WorkflowTraceEvent[] {
|
|
|
341
341
|
titleKey: 'workflow.trace.providerCall',
|
|
342
342
|
summary: [node.adapterId, node.model].filter(Boolean).join(' / '),
|
|
343
343
|
metadata: {
|
|
344
|
-
|
|
344
|
+
a2aTaskId: node.a2aTaskId,
|
|
345
345
|
permissionMode: node.permissionMode,
|
|
346
346
|
timeoutMs: node.timeoutMs,
|
|
347
347
|
},
|
|
@@ -58,7 +58,7 @@ export interface WorkflowNodeRun {
|
|
|
58
58
|
fallbackTrigger?: WorkflowFallbackTrigger;
|
|
59
59
|
fallbackSourceNodeId?: string;
|
|
60
60
|
status: WorkflowNodeStatus;
|
|
61
|
-
|
|
61
|
+
a2aTaskId?: string;
|
|
62
62
|
startedAt?: number;
|
|
63
63
|
finishedAt?: number;
|
|
64
64
|
error?: string;
|
package/server/routes/git.js
CHANGED
|
@@ -380,6 +380,27 @@ function normalizeRepositoryRelativeFilePath(filePath) {
|
|
|
380
380
|
.trim();
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
+
function normalizeRequestedRepositoryFilePath(repositoryRootPath, filePath) {
|
|
384
|
+
const rawFilePath = String(filePath || '').replace(/\\/g, '/').trim();
|
|
385
|
+
if (!rawFilePath) {
|
|
386
|
+
return '';
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (path.isAbsolute(rawFilePath)) {
|
|
390
|
+
const resolvedFilePath = path.resolve(rawFilePath);
|
|
391
|
+
const resolvedRepositoryRoot = path.resolve(repositoryRootPath);
|
|
392
|
+
const relativePath = path.relative(resolvedRepositoryRoot, resolvedFilePath);
|
|
393
|
+
|
|
394
|
+
if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
|
|
395
|
+
throw new Error('Invalid file path: outside repository');
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return normalizeRepositoryRelativeFilePath(relativePath);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return normalizeRepositoryRelativeFilePath(rawFilePath);
|
|
402
|
+
}
|
|
403
|
+
|
|
383
404
|
function parseStatusFilePaths(statusOutput) {
|
|
384
405
|
return statusOutput
|
|
385
406
|
.split('\n')
|
|
@@ -394,7 +415,7 @@ function parseStatusFilePaths(statusOutput) {
|
|
|
394
415
|
}
|
|
395
416
|
|
|
396
417
|
function buildFilePathCandidates(projectPath, repositoryRootPath, filePath) {
|
|
397
|
-
const normalizedFilePath =
|
|
418
|
+
const normalizedFilePath = normalizeRequestedRepositoryFilePath(repositoryRootPath, filePath);
|
|
398
419
|
const projectRelativePath = normalizeRepositoryRelativeFilePath(path.relative(repositoryRootPath, projectPath));
|
|
399
420
|
const candidates = [normalizedFilePath];
|
|
400
421
|
|
|
@@ -629,53 +650,93 @@ router.get('/file-with-diff', async (req, res) => {
|
|
|
629
650
|
repositoryRelativeFilePath,
|
|
630
651
|
} = await resolveRepositoryFilePath(projectPath, file);
|
|
631
652
|
|
|
653
|
+
const readFileFromIndex = async () => {
|
|
654
|
+
const { stdout } = await spawnAsync(
|
|
655
|
+
'git',
|
|
656
|
+
['show', `:${repositoryRelativeFilePath}`],
|
|
657
|
+
{ cwd: repositoryRootPath },
|
|
658
|
+
);
|
|
659
|
+
return stdout;
|
|
660
|
+
};
|
|
661
|
+
const readFileFromHead = async () => {
|
|
662
|
+
const { stdout } = await spawnAsync(
|
|
663
|
+
'git',
|
|
664
|
+
['show', `HEAD:${repositoryRelativeFilePath}`],
|
|
665
|
+
{ cwd: repositoryRootPath },
|
|
666
|
+
);
|
|
667
|
+
return stdout;
|
|
668
|
+
};
|
|
669
|
+
|
|
632
670
|
// Check file status
|
|
633
671
|
const { stdout: statusOutput } = await spawnAsync(
|
|
634
672
|
'git',
|
|
635
673
|
['status', '--porcelain', '--', repositoryRelativeFilePath],
|
|
636
674
|
{ cwd: repositoryRootPath },
|
|
637
675
|
);
|
|
638
|
-
const
|
|
639
|
-
const
|
|
676
|
+
const statusLine = statusOutput.split('\n').find((line) => line.trim()) || '';
|
|
677
|
+
const indexStatus = statusLine[0] || ' ';
|
|
678
|
+
const worktreeStatus = statusLine[1] || ' ';
|
|
679
|
+
const isUntracked = statusLine.startsWith('??');
|
|
680
|
+
const hasUnstagedChange = !isUntracked && worktreeStatus !== ' ';
|
|
681
|
+
const hasStagedChange = !isUntracked && indexStatus !== ' ';
|
|
682
|
+
const isDeleted = worktreeStatus === 'D' || (!hasUnstagedChange && indexStatus === 'D');
|
|
640
683
|
|
|
641
684
|
let currentContent = '';
|
|
642
685
|
let oldContent = '';
|
|
643
686
|
|
|
644
|
-
if (
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
687
|
+
if (hasUnstagedChange) {
|
|
688
|
+
try {
|
|
689
|
+
oldContent = await readFileFromIndex();
|
|
690
|
+
} catch {
|
|
691
|
+
oldContent = '';
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if (worktreeStatus === 'D') {
|
|
695
|
+
currentContent = '';
|
|
696
|
+
} else {
|
|
697
|
+
const filePath = path.join(repositoryRootPath, repositoryRelativeFilePath);
|
|
698
|
+
const stats = await fs.stat(filePath);
|
|
699
|
+
if (stats.isDirectory()) {
|
|
700
|
+
return res.status(400).json({ error: 'Cannot show diff for directories' });
|
|
701
|
+
}
|
|
702
|
+
currentContent = await fs.readFile(filePath, 'utf-8');
|
|
703
|
+
}
|
|
704
|
+
} else if (hasStagedChange) {
|
|
705
|
+
try {
|
|
706
|
+
oldContent = await readFileFromHead();
|
|
707
|
+
} catch {
|
|
708
|
+
// A staged add has no HEAD copy; showing the whole file as added is correct.
|
|
709
|
+
oldContent = '';
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if (indexStatus === 'D') {
|
|
713
|
+
currentContent = '';
|
|
714
|
+
} else {
|
|
715
|
+
try {
|
|
716
|
+
currentContent = await readFileFromIndex();
|
|
717
|
+
} catch {
|
|
718
|
+
currentContent = '';
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
} else if (isUntracked) {
|
|
655
722
|
const filePath = path.join(repositoryRootPath, repositoryRelativeFilePath);
|
|
656
723
|
const stats = await fs.stat(filePath);
|
|
657
724
|
|
|
658
725
|
if (stats.isDirectory()) {
|
|
659
|
-
// Cannot show content for directories
|
|
660
726
|
return res.status(400).json({ error: 'Cannot show diff for directories' });
|
|
661
727
|
}
|
|
662
728
|
|
|
663
729
|
currentContent = await fs.readFile(filePath, 'utf-8');
|
|
730
|
+
} else {
|
|
731
|
+
const filePath = path.join(repositoryRootPath, repositoryRelativeFilePath);
|
|
732
|
+
const stats = await fs.stat(filePath);
|
|
664
733
|
|
|
665
|
-
if (
|
|
666
|
-
|
|
667
|
-
try {
|
|
668
|
-
const { stdout: headContent } = await spawnAsync(
|
|
669
|
-
'git',
|
|
670
|
-
['show', `HEAD:${repositoryRelativeFilePath}`],
|
|
671
|
-
{ cwd: repositoryRootPath },
|
|
672
|
-
);
|
|
673
|
-
oldContent = headContent;
|
|
674
|
-
} catch (error) {
|
|
675
|
-
// File might be newly added to git (staged but not committed)
|
|
676
|
-
oldContent = '';
|
|
677
|
-
}
|
|
734
|
+
if (stats.isDirectory()) {
|
|
735
|
+
return res.status(400).json({ error: 'Cannot show diff for directories' });
|
|
678
736
|
}
|
|
737
|
+
|
|
738
|
+
currentContent = await fs.readFile(filePath, 'utf-8');
|
|
739
|
+
oldContent = currentContent;
|
|
679
740
|
}
|
|
680
741
|
|
|
681
742
|
res.json({
|
|
@@ -4,7 +4,6 @@ const API_GROUPS = [
|
|
|
4
4
|
{ id: 'sessions', title: 'Sessions and messages', basePath: '/api/sessions', scopes: ['sessions:read', 'sessions:write'] },
|
|
5
5
|
{ id: 'providers', title: 'CLI providers', basePath: '/api/providers', scopes: ['providers:read', 'providers:write'] },
|
|
6
6
|
{ id: 'terminal', title: 'Visible terminal sessions', basePath: '/api/shell/sessions', scopes: ['terminal:launch'] },
|
|
7
|
-
{ id: 'hermes', title: 'Hermes Agent control', basePath: '/api/orchestration/hermes', scopes: ['hermes:mcp', 'hermes:gateway', 'terminal:launch'] },
|
|
8
7
|
{ id: 'orchestration', title: 'Orchestration runs', basePath: '/api/orchestration', scopes: ['orchestration:read', 'orchestration:write'] },
|
|
9
8
|
{ id: 'notifications', title: 'Notifications', basePath: '/api/settings/notifications', scopes: ['notifications:read', 'notifications:write'] },
|
|
10
9
|
{ id: 'files', title: 'Files', basePath: '/api/projects/:projectName/files', scopes: ['files:read', 'files:write'] },
|
|
@@ -46,10 +45,6 @@ export function buildPublicApiManifest({ baseUrl = '' } = {}) {
|
|
|
46
45
|
title: 'Fetch diagnostics bundle',
|
|
47
46
|
curl: `curl -H "X-API-Key: px_your_key" ${origin || 'http://127.0.0.1:3001'}/api/diagnostics/bundle`,
|
|
48
47
|
},
|
|
49
|
-
{
|
|
50
|
-
title: 'Read Hermes control plane',
|
|
51
|
-
curl: `curl -H "X-API-Key: px_your_key" ${origin || 'http://127.0.0.1:3001'}/api/orchestration/hermes/control-plane`,
|
|
52
|
-
},
|
|
53
48
|
{
|
|
54
49
|
title: 'Read the mobile remote control room',
|
|
55
50
|
curl: `curl -H "X-API-Key: px_your_key" ${origin || 'http://127.0.0.1:3001'}/api/remote/control-room`,
|
|
@@ -139,10 +134,6 @@ export function buildCurlCookbook({ baseUrl = '' } = {}) {
|
|
|
139
134
|
title: 'List pending approvals',
|
|
140
135
|
command: `curl -H "X-API-Key: $PIXCODE_API_KEY" "$PIXCODE_URL/api/orchestration/workflows/approvals"`,
|
|
141
136
|
},
|
|
142
|
-
{
|
|
143
|
-
title: 'Read Hermes control plane',
|
|
144
|
-
command: `curl -H "X-API-Key: $PIXCODE_API_KEY" "$PIXCODE_URL/api/orchestration/hermes/control-plane"`,
|
|
145
|
-
},
|
|
146
137
|
{
|
|
147
138
|
title: 'Approve a pending action',
|
|
148
139
|
command: `curl -X POST -H "Content-Type: application/json" -H "X-API-Key: $PIXCODE_API_KEY" -d '{"allow":true,"source":"api"}' "$PIXCODE_URL/api/orchestration/workflows/approvals/approval_id"`,
|