Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions webapp/src/client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {PluginConfig} from '@/components/system_console/plugin_config_types';
import type {ToolAnswer} from '@/components/tool_types';
import type {Composition, ConversationResponse} from '@/types/conversation';
import {UserAgent, CreateAgentRequest, UpdateAgentRequest, ServiceInfo} from '@/types/agents';
import {Automation, AutomationUpdate} from '@/types/automations';

import manifest from './manifest';

Expand Down Expand Up @@ -1131,3 +1132,89 @@ export async function renderCustomPrompt(id: string, channelId?: string, botUser
url,
});
}

// --- Automation CRUD (mocked until channel-automation is wired) ---

let mockAutomations: Automation[] = [];

function cloneAutomation(automation: Automation): Automation {
return JSON.parse(JSON.stringify(automation));
}

export async function getAutomations(): Promise<Automation[]> {
return mockAutomations.map(cloneAutomation);
}

export async function getAutomation(id: string): Promise<Automation> {
const automation = mockAutomations.find((a) => a.id === id);
if (!automation) {
throw new ClientError(Client4.url, {
message: 'Automation not found',
status_code: 404,
url: `/automations/${id}`,
});
}
return cloneAutomation(automation);
}

export async function createAutomation(data: AutomationUpdate): Promise<Automation> {
const now = Date.now();
let createdBy = '';
try {
const me = await Client4.getMe();
createdBy = me.id;
} catch {
createdBy = '';
}
const created: Automation = {
id: `mock-${now}`,
name: data.name,
enabled: data.enabled ?? true,
trigger: data.trigger,
actions: data.actions,
created_at: now,
updated_at: now,
created_by: createdBy,
};
mockAutomations = [...mockAutomations, created];
return cloneAutomation(created);
}
Comment on lines +1160 to +1181

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Generated automation id can collide. mock-${now} uses only Date.now(), so two automations created within the same millisecond share an id. Downstream this breaks React key uniqueness in the list and causes updateAutomation/deleteAutomation (which match by id) to hit the wrong record.

Proposed fix
-    const created: Automation = {
-        id: `mock-${now}`,
+    const created: Automation = {
+        id: `mock-${now}-${Math.random().toString(36).slice(2, 8)}`,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export async function createAutomation(data: AutomationUpdate): Promise<Automation> {
const now = Date.now();
let createdBy = '';
try {
const me = await Client4.getMe();
createdBy = me.id;
} catch {
createdBy = '';
}
const created: Automation = {
id: `mock-${now}`,
name: data.name,
enabled: data.enabled ?? true,
trigger: data.trigger,
actions: data.actions,
created_at: now,
updated_at: now,
created_by: createdBy,
};
mockAutomations = [...mockAutomations, created];
return cloneAutomation(created);
}
export async function createAutomation(data: AutomationUpdate): Promise<Automation> {
const now = Date.now();
let createdBy = '';
try {
const me = await Client4.getMe();
createdBy = me.id;
} catch {
createdBy = '';
}
const created: Automation = {
id: `mock-${now}-${Math.random().toString(36).slice(2, 8)}`,
name: data.name,
enabled: data.enabled ?? true,
trigger: data.trigger,
actions: data.actions,
created_at: now,
updated_at: now,
created_by: createdBy,
};
mockAutomations = [...mockAutomations, created];
return cloneAutomation(created);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webapp/src/client.tsx` around lines 1160 - 1181, The createAutomation
function generates IDs using only Date.now(), allowing same-millisecond
collisions. Update the ID generation in createAutomation to append a unique
per-creation value while preserving the mock- identifier format, so
mockAutomations entries remain distinct for React keys and
updateAutomation/deleteAutomation lookups.


export async function updateAutomation(id: string, data: AutomationUpdate): Promise<Automation> {
const index = mockAutomations.findIndex((a) => a.id === id);
if (index < 0) {
throw new ClientError(Client4.url, {
message: 'Automation not found',
status_code: 404,
url: `/automations/${id}`,
});
}

const existing = mockAutomations[index];
const updated: Automation = {
...existing,
name: data.name,
trigger: data.trigger,
actions: data.actions,
updated_at: Date.now(),
enabled: data.enabled ?? existing.enabled,
};
mockAutomations = [
...mockAutomations.slice(0, index),
updated,
...mockAutomations.slice(index + 1),
];
return cloneAutomation(updated);
}

export async function deleteAutomation(id: string): Promise<void> {
const exists = mockAutomations.some((a) => a.id === id);
if (!exists) {
throw new ClientError(Client4.url, {
message: 'Automation not found',
status_code: 404,
url: `/automations/${id}`,
});
}
mockAutomations = mockAutomations.filter((a) => a.id !== id);
}
Loading
Loading