diff --git a/public/locales/de.json b/public/locales/de.json index 9fa10a940..f75cdb672 100644 --- a/public/locales/de.json +++ b/public/locales/de.json @@ -555,6 +555,9 @@ "manual_run": "Manuelle Ausführung", "scheduled_run": "Geplante Ausführung", "api": "API", + "sdk": "SDK", + "mcp": "MCP", + "cli": "CLI", "unknown_run_type": "Unbekannter Ausführungstyp" }, "run_status_chips": { diff --git a/public/locales/en.json b/public/locales/en.json index 7ca9830cc..ff42d7b07 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -556,6 +556,8 @@ "scheduled_run": "Scheduled", "api": "API", "sdk": "SDK", + "mcp": "MCP", + "cli": "CLI", "unknown_run_type": "Unknown Run Type" }, "run_status_chips": { diff --git a/public/locales/es.json b/public/locales/es.json index 0b0fabbb3..5421856aa 100644 --- a/public/locales/es.json +++ b/public/locales/es.json @@ -555,6 +555,9 @@ "manual_run": "Ejecución Manual", "scheduled_run": "Ejecución Programada", "api": "API", + "sdk": "SDK", + "mcp": "MCP", + "cli": "CLI", "unknown_run_type": "Tipo de Ejecución Desconocido" }, "run_status_chips": { diff --git a/public/locales/ja.json b/public/locales/ja.json index 825606bb5..1c0486fbd 100644 --- a/public/locales/ja.json +++ b/public/locales/ja.json @@ -556,6 +556,9 @@ "manual_run": "手動実行", "scheduled_run": "スケジュール実行", "api": "API", + "sdk": "SDK", + "mcp": "MCP", + "cli": "CLI", "unknown_run_type": "不明な実行タイプ" }, "run_status_chips": { diff --git a/public/locales/tr.json b/public/locales/tr.json index baea1476e..adb64d28f 100644 --- a/public/locales/tr.json +++ b/public/locales/tr.json @@ -555,6 +555,9 @@ "manual_run": "Manuel", "scheduled_run": "Zamanlanmış", "api": "API", + "sdk": "SDK", + "mcp": "MCP", + "cli": "CLI", "unknown_run_type": "Bilinmeyen" }, "run_status_chips": { diff --git a/public/locales/zh.json b/public/locales/zh.json index 14cd33944..9eea18c64 100644 --- a/public/locales/zh.json +++ b/public/locales/zh.json @@ -556,6 +556,9 @@ "manual_run": "手动运行", "scheduled_run": "计划运行", "api": "API", + "sdk": "SDK", + "mcp": "MCP", + "cli": "CLI", "unknown_run_type": "未知运行类型" }, "run_status_chips": { diff --git a/server/src/api/record.ts b/server/src/api/record.ts index 8fc927fb6..f39eb4bca 100644 --- a/server/src/api/record.ts +++ b/server/src/api/record.ts @@ -340,6 +340,8 @@ function formatRunResponse(run: any) { runByScheduleId: run.runByScheduleId, runByAPI: run.runByAPI, runBySDK: run.runBySDK, + runByMCP: run.runByMCP, + runByCLI: run.runByCLI, data: { textData: {}, listData: {}, @@ -477,7 +479,7 @@ router.get("/robots/:id/runs/:runId", requireAPIKey, async (req: Request, res: R } }); -async function createWorkflowAndStoreMetadata(id: string, userId: string, isSDK: boolean) { +async function createWorkflowAndStoreMetadata(id: string, userId: string, runSource: 'api' | 'sdk' | 'mcp' | 'cli') { try { const recording = await Robot.findOne({ where: { @@ -522,8 +524,10 @@ async function createWorkflowAndStoreMetadata(id: string, userId: string, isSDK: log: '', runId, runByUserId: userId, - runByAPI: !isSDK, - runBySDK: isSDK, + runByAPI: runSource === 'api', + runBySDK: runSource === 'sdk', + runByMCP: runSource === 'mcp', + runByCLI: runSource === 'cli', serializableOutput: {}, binaryOutput: {}, retryCount: 0 @@ -1194,11 +1198,11 @@ async function executeRun(id: string, userId: string, requestedFormats?: string[ } } -export async function handleRunRecording(id: string, userId: string, isSDK: boolean = false, requestedFormats?: string[]) { +export async function handleRunRecording(id: string, userId: string, runSource: 'api' | 'sdk' | 'mcp' | 'cli' = 'api', requestedFormats?: string[]) { let socket: Socket | null = null; try { - const result = await createWorkflowAndStoreMetadata(id, userId, isSDK); + const result = await createWorkflowAndStoreMetadata(id, userId, runSource); const { browserId, runId: newRunId } = result; if (!browserId || !newRunId || !userId) { @@ -1378,7 +1382,8 @@ router.post("/robots/:id/runs", requireAPIKey, async (req: AuthenticatedRequest, } const requestedFormats = req.body?.formats; - const runId = await handleRunRecording(req.params.id, req.user.id, false, requestedFormats); + const runSource = req.headers['x-run-source'] === 'mcp' ? 'mcp' : 'api'; + const runId = await handleRunRecording(req.params.id, req.user.id, runSource, requestedFormats); if (!runId) { throw new Error('Run ID is undefined'); diff --git a/server/src/api/sdk.ts b/server/src/api/sdk.ts index c9e6e2a74..5c521572c 100644 --- a/server/src/api/sdk.ts +++ b/server/src/api/sdk.ts @@ -489,7 +489,8 @@ router.post("/sdk/robots/:id/execute", requireAPIKey, async (req: AuthenticatedR logger.info(`[SDK] Starting execution for robot ${robotId}`); - const runId = await handleRunRecording(robotId, user.id.toString(), true); + const runSource = req.headers['x-run-source'] === 'cli' ? 'cli' : 'sdk'; + const runId = await handleRunRecording(robotId, user.id.toString(), runSource); if (!runId) { throw new Error('Failed to start robot execution'); } diff --git a/server/src/mcp-worker.ts b/server/src/mcp-worker.ts index 259ef2fda..34462ce2c 100644 --- a/server/src/mcp-worker.ts +++ b/server/src/mcp-worker.ts @@ -38,6 +38,7 @@ class MaxunMCPWorker { const headers = { 'Content-Type': 'application/json', 'x-api-key': this.apiKey, + 'x-run-source': 'mcp', ...options.headers }; diff --git a/server/src/models/Run.ts b/server/src/models/Run.ts index 5c45f2a8c..9437cc6df 100644 --- a/server/src/models/Run.ts +++ b/server/src/models/Run.ts @@ -24,6 +24,8 @@ interface RunAttributes { runByScheduleId?: string; runByAPI?: boolean; runBySDK?: boolean; + runByMCP?: boolean; + runByCLI?: boolean; serializableOutput: Record; binaryOutput: Record; retryCount?: number; @@ -47,6 +49,8 @@ class Run extends Model implements RunAttr public runByScheduleId!: string; public runByAPI!: boolean; public runBySDK!: boolean; + public runByMCP!: boolean; + public runByCLI!: boolean; public serializableOutput!: Record; public binaryOutput!: Record; public retryCount!: number; @@ -119,6 +123,14 @@ Run.init( type: DataTypes.BOOLEAN, allowNull: true, }, + runByMCP: { + type: DataTypes.BOOLEAN, + allowNull: true, + }, + runByCLI: { + type: DataTypes.BOOLEAN, + allowNull: true, + }, serializableOutput: { type: DataTypes.JSONB, allowNull: true, diff --git a/src/components/run/ColapsibleRow.tsx b/src/components/run/ColapsibleRow.tsx index 257476ec0..cbb6860c0 100644 --- a/src/components/run/ColapsibleRow.tsx +++ b/src/components/run/ColapsibleRow.tsx @@ -57,12 +57,16 @@ interface RunTypeChipProps { runByScheduledId?: string; runByAPI: boolean; runBySDK?: boolean; + runByMCP?: boolean; + runByCLI?: boolean; } -const RunTypeChip: React.FC = ({ runByUserId, runByScheduledId, runByAPI, runBySDK }) => { +const RunTypeChip: React.FC = ({ runByUserId, runByScheduledId, runByAPI, runBySDK, runByMCP, runByCLI }) => { const { t } = useTranslation(); if (runByScheduledId) return ; + if (runByCLI) return ; + if (runByMCP) return ; if (runBySDK) return ; if (runByAPI) return ; if (runByUserId) return ; @@ -86,14 +90,18 @@ export const CollapsibleRow = ({ row, handleDelete, isOpen, onToggleExpanded, cu const [openSettingsModal, setOpenSettingsModal] = useState(false); const [userEmail, setUserEmail] = useState(null); const runByLabel = row.runByScheduleId - ? `${row.runByScheduleId}` - : row.runByUserId - ? `${userEmail}` - : row.runBySDK - ? 'SDK' - : row.runByAPI - ? 'API' - : 'Unknown'; + ? `${row.runByScheduleId}` + : row.runByUserId + ? `${userEmail}` + : row.runByCLI + ? 'CLI' + : row.runByMCP + ? 'MCP' + : row.runBySDK + ? 'SDK' + : row.runByAPI + ? 'API' + : 'Unknown'; const logEndRef = useRef(null); @@ -259,6 +267,8 @@ export const CollapsibleRow = ({ row, handleDelete, isOpen, onToggleExpanded, cu runByScheduledId={row.runByScheduleId} runByAPI={row.runByAPI ?? false} runBySDK={row.runBySDK} + runByMCP={row.runByMCP} + runByCLI={row.runByCLI} /> diff --git a/src/components/run/RunsTable.tsx b/src/components/run/RunsTable.tsx index fd4521292..e1b9f430a 100644 --- a/src/components/run/RunsTable.tsx +++ b/src/components/run/RunsTable.tsx @@ -57,6 +57,8 @@ export interface Data { browserId: string; runByAPI?: boolean; runBySDK?: boolean; + runByMCP?: boolean; + runByCLI?: boolean; log: string; runId: string; robotId: string;