apteva 0.4.18 → 0.4.20

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.
Files changed (84) hide show
  1. package/dist/ActivityPage.h769ek3a.js +3 -0
  2. package/dist/ApiDocsPage.kf6bbwkk.js +4 -0
  3. package/dist/{App.nps62kvt.js → App.039re6cf.js} +3 -3
  4. package/dist/App.2jmkqm8c.js +4 -0
  5. package/dist/{App.np463xvy.js → App.2yy66bnp.js} +3 -3
  6. package/dist/App.3515wsb4.js +4 -0
  7. package/dist/App.7v1w3ys9.js +4 -0
  8. package/dist/{App.nft7h9jt.js → App.c90t3dxg.js} +3 -3
  9. package/dist/App.edwahsvz.js +4 -0
  10. package/dist/App.jfx3der4.js +4 -0
  11. package/dist/App.n4jb3c22.js +13 -0
  12. package/dist/{App.mq6jqare.js → App.p02f4ret.js} +1 -1
  13. package/dist/App.q3bpx15d.js +20 -0
  14. package/dist/App.r0a2nmqs.js +267 -0
  15. package/dist/App.s2yrcz15.js +4 -0
  16. package/dist/App.s5j82a5j.js +4 -0
  17. package/dist/App.tg1b94tx.js +4 -0
  18. package/dist/ConnectionsPage.a67fjgbf.js +3 -0
  19. package/dist/McpPage.d4p3xvtk.js +3 -0
  20. package/dist/SettingsPage.46sqpe39.js +3 -0
  21. package/dist/SkillsPage.j9hkqm99.js +3 -0
  22. package/dist/TasksPage.6pvkb7s7.js +3 -0
  23. package/dist/TelemetryPage.5zq9msb5.js +3 -0
  24. package/dist/TestsPage.24432yqt.js +3 -0
  25. package/dist/apteva-kit.css +1 -1
  26. package/dist/index.html +1 -1
  27. package/dist/styles.css +1 -1
  28. package/package.json +9 -4
  29. package/src/channels/index.ts +40 -0
  30. package/src/channels/telegram.ts +306 -0
  31. package/src/db.ts +180 -0
  32. package/src/integrations/agentdojo.ts +1 -1
  33. package/src/mcp-handler.ts +31 -24
  34. package/src/mcp-platform.ts +353 -2
  35. package/src/providers.ts +22 -9
  36. package/src/routes/api/agents.ts +15 -2
  37. package/src/routes/api/channels.ts +182 -0
  38. package/src/routes/api/integrations.ts +13 -5
  39. package/src/routes/api/mcp.ts +27 -9
  40. package/src/routes/api/system.ts +12 -1
  41. package/src/routes/api/telemetry.ts +30 -0
  42. package/src/routes/api/triggers.ts +22 -2
  43. package/src/routes/api.ts +3 -1
  44. package/src/routes/auth.ts +11 -2
  45. package/src/server.ts +39 -4
  46. package/src/triggers/agentdojo.ts +23 -18
  47. package/src/tui/AgentList.tsx +145 -0
  48. package/src/tui/App.tsx +102 -0
  49. package/src/tui/Login.tsx +104 -0
  50. package/src/tui/api.ts +72 -0
  51. package/src/tui/index.tsx +7 -0
  52. package/src/web/App.tsx +2 -2
  53. package/src/web/components/agents/AgentPanel.tsx +4 -37
  54. package/src/web/components/common/Icons.tsx +8 -0
  55. package/src/web/components/connections/OverviewTab.tsx +22 -68
  56. package/src/web/components/connections/TriggersTab.tsx +549 -70
  57. package/src/web/components/dashboard/Dashboard.tsx +5 -4
  58. package/src/web/components/layout/Header.tsx +196 -4
  59. package/src/web/components/settings/SettingsPage.tsx +269 -1
  60. package/src/web/context/AuthContext.tsx +18 -11
  61. package/src/web/context/TelemetryContext.tsx +14 -1
  62. package/src/web/context/index.ts +1 -1
  63. package/src/web/hooks/useAgents.ts +7 -3
  64. package/src/web/hooks/useOnboarding.ts +9 -30
  65. package/dist/ActivityPage.yv28a2vj.js +0 -3
  66. package/dist/ApiDocsPage.4ccwjjbk.js +0 -4
  67. package/dist/App.155wke5v.js +0 -4
  68. package/dist/App.2e19nvn4.js +0 -13
  69. package/dist/App.2ye1b5n0.js +0 -4
  70. package/dist/App.4da4ycbe.js +0 -4
  71. package/dist/App.b6wtzd1j.js +0 -4
  72. package/dist/App.fjrh28tf.js +0 -4
  73. package/dist/App.htc36cy8.js +0 -4
  74. package/dist/App.me6reaa6.js +0 -4
  75. package/dist/App.n5q6p960.js +0 -4
  76. package/dist/App.q8ws33cc.js +0 -181
  77. package/dist/App.tb0y0jmt.js +0 -40
  78. package/dist/ConnectionsPage.52evzrp7.js +0 -3
  79. package/dist/McpPage.bjqrp0n2.js +0 -3
  80. package/dist/SettingsPage.es76hnj2.js +0 -3
  81. package/dist/SkillsPage.06h8yf0h.js +0 -3
  82. package/dist/TasksPage.99df66mk.js +0 -3
  83. package/dist/TelemetryPage.bmdnxhq7.js +0 -3
  84. package/dist/TestsPage.denxrg8c.js +0 -3
package/src/db.ts CHANGED
@@ -217,6 +217,33 @@ export interface SubscriptionRow {
217
217
  updated_at: string;
218
218
  }
219
219
 
220
+ // Channel: external messaging platform bound to an agent
221
+ export interface Channel {
222
+ id: string;
223
+ type: "telegram"; // future: "slack", "discord"
224
+ name: string;
225
+ agent_id: string;
226
+ config: string; // encrypted JSON
227
+ status: "stopped" | "running" | "error";
228
+ error: string | null;
229
+ project_id: string | null;
230
+ created_at: string;
231
+ updated_at: string;
232
+ }
233
+
234
+ export interface ChannelRow {
235
+ id: string;
236
+ type: string;
237
+ name: string;
238
+ agent_id: string;
239
+ config: string;
240
+ status: string;
241
+ error: string | null;
242
+ project_id: string | null;
243
+ created_at: string;
244
+ updated_at: string;
245
+ }
246
+
220
247
  export interface McpServerRow {
221
248
  id: string;
222
249
  name: string;
@@ -737,6 +764,33 @@ function runMigrations() {
737
764
  CREATE INDEX IF NOT EXISTS idx_subscriptions_trigger_instance ON subscriptions(trigger_instance_id);
738
765
  `,
739
766
  },
767
+ {
768
+ name: "032_create_channels",
769
+ sql: `
770
+ CREATE TABLE IF NOT EXISTS channels (
771
+ id TEXT PRIMARY KEY,
772
+ type TEXT NOT NULL,
773
+ name TEXT NOT NULL,
774
+ agent_id TEXT NOT NULL,
775
+ config TEXT NOT NULL,
776
+ status TEXT DEFAULT 'stopped',
777
+ error TEXT,
778
+ project_id TEXT,
779
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
780
+ updated_at TEXT DEFAULT CURRENT_TIMESTAMP,
781
+ FOREIGN KEY (agent_id) REFERENCES agents(id) ON DELETE CASCADE
782
+ );
783
+ CREATE INDEX IF NOT EXISTS idx_channels_agent ON channels(agent_id);
784
+ CREATE INDEX IF NOT EXISTS idx_channels_status ON channels(status);
785
+ `,
786
+ },
787
+ {
788
+ name: "033_add_telemetry_seen",
789
+ sql: `
790
+ ALTER TABLE telemetry_events ADD COLUMN seen INTEGER DEFAULT 0;
791
+ CREATE INDEX IF NOT EXISTS idx_telemetry_seen ON telemetry_events(seen);
792
+ `,
793
+ },
740
794
  {
741
795
  name: "029_fix_provider_keys_unique_constraint",
742
796
  sql: `
@@ -1768,6 +1822,7 @@ export interface TelemetryEvent {
1768
1822
  duration_ms: number | null;
1769
1823
  error: string | null;
1770
1824
  received_at: string;
1825
+ seen?: boolean;
1771
1826
  }
1772
1827
 
1773
1828
  interface TelemetryEventRow {
@@ -1785,6 +1840,7 @@ interface TelemetryEventRow {
1785
1840
  duration_ms: number | null;
1786
1841
  error: string | null;
1787
1842
  received_at: string;
1843
+ seen?: number;
1788
1844
  }
1789
1845
 
1790
1846
  // Telemetry operations
@@ -2066,6 +2122,45 @@ export const TelemetryDB = {
2066
2122
  const row = db.query("SELECT COUNT(*) as count FROM telemetry_events").get() as { count: number };
2067
2123
  return row.count;
2068
2124
  },
2125
+
2126
+ // --- Notification helpers (piggyback on telemetry with `seen` flag) ---
2127
+
2128
+ // Notification-worthy filter: errors + agent crashes
2129
+ getNotifications(limit = 50): TelemetryEvent[] {
2130
+ const rows = db.query(`
2131
+ SELECT * FROM telemetry_events
2132
+ WHERE (level = 'error' OR (category = 'system' AND type = 'agent_stopped') OR category = 'ERROR')
2133
+ ORDER BY timestamp DESC
2134
+ LIMIT ?
2135
+ `).all(limit) as TelemetryEventRow[];
2136
+ return rows.map(rowToTelemetryEvent);
2137
+ },
2138
+
2139
+ getUnseenCount(): number {
2140
+ const row = db.query(`
2141
+ SELECT COUNT(*) as count FROM telemetry_events
2142
+ WHERE seen = 0
2143
+ AND (level = 'error' OR (category = 'system' AND type = 'agent_stopped') OR category = 'ERROR')
2144
+ `).get() as { count: number };
2145
+ return row.count;
2146
+ },
2147
+
2148
+ markSeen(ids: string[]): number {
2149
+ if (ids.length === 0) return 0;
2150
+ const placeholders = ids.map(() => "?").join(",");
2151
+ const result = db.run(
2152
+ `UPDATE telemetry_events SET seen = 1 WHERE id IN (${placeholders})`,
2153
+ ids
2154
+ );
2155
+ return result.changes;
2156
+ },
2157
+
2158
+ markAllSeen(): number {
2159
+ const result = db.run(
2160
+ `UPDATE telemetry_events SET seen = 1 WHERE seen = 0 AND (level = 'error' OR (category = 'system' AND type = 'agent_stopped') OR category = 'ERROR')`
2161
+ );
2162
+ return result.changes;
2163
+ },
2069
2164
  };
2070
2165
 
2071
2166
  function rowToTelemetryEvent(row: TelemetryEventRow): TelemetryEvent {
@@ -2084,6 +2179,7 @@ function rowToTelemetryEvent(row: TelemetryEventRow): TelemetryEvent {
2084
2179
  duration_ms: row.duration_ms,
2085
2180
  error: row.error,
2086
2181
  received_at: row.received_at,
2182
+ seen: row.seen === 1,
2087
2183
  };
2088
2184
  }
2089
2185
 
@@ -2726,6 +2822,90 @@ export const SubscriptionDB = {
2726
2822
  },
2727
2823
  };
2728
2824
 
2825
+ // --- Channel DB ---
2826
+
2827
+ function rowToChannel(row: ChannelRow): Channel {
2828
+ return {
2829
+ id: row.id,
2830
+ type: row.type as Channel["type"],
2831
+ name: row.name,
2832
+ agent_id: row.agent_id,
2833
+ config: row.config,
2834
+ status: row.status as Channel["status"],
2835
+ error: row.error,
2836
+ project_id: row.project_id,
2837
+ created_at: row.created_at,
2838
+ updated_at: row.updated_at,
2839
+ };
2840
+ }
2841
+
2842
+ export const ChannelDB = {
2843
+ create(channel: { type: string; name: string; agent_id: string; config: string; project_id?: string | null }): Channel {
2844
+ const id = generateId();
2845
+ const now = new Date().toISOString();
2846
+ db.run(
2847
+ `INSERT INTO channels (id, type, name, agent_id, config, status, project_id, created_at, updated_at)
2848
+ VALUES (?, ?, ?, ?, ?, 'stopped', ?, ?, ?)`,
2849
+ [id, channel.type, channel.name, channel.agent_id, channel.config, channel.project_id || null, now, now]
2850
+ );
2851
+ return this.findById(id)!;
2852
+ },
2853
+
2854
+ findById(id: string): Channel | null {
2855
+ const row = db.query("SELECT * FROM channels WHERE id = ?").get(id) as ChannelRow | null;
2856
+ return row ? rowToChannel(row) : null;
2857
+ },
2858
+
2859
+ findAll(): Channel[] {
2860
+ const rows = db.query("SELECT * FROM channels ORDER BY created_at DESC").all() as ChannelRow[];
2861
+ return rows.map(rowToChannel);
2862
+ },
2863
+
2864
+ findByAgentId(agentId: string): Channel[] {
2865
+ const rows = db.query("SELECT * FROM channels WHERE agent_id = ?").all(agentId) as ChannelRow[];
2866
+ return rows.map(rowToChannel);
2867
+ },
2868
+
2869
+ findRunning(): Channel[] {
2870
+ const rows = db.query("SELECT * FROM channels WHERE status = 'running'").all() as ChannelRow[];
2871
+ return rows.map(rowToChannel);
2872
+ },
2873
+
2874
+ update(id: string, updates: { name?: string; agent_id?: string; config?: string; project_id?: string | null }): Channel | null {
2875
+ const channel = this.findById(id);
2876
+ if (!channel) return null;
2877
+
2878
+ const fields: string[] = [];
2879
+ const values: (string | null)[] = [];
2880
+
2881
+ if (updates.name !== undefined) { fields.push("name = ?"); values.push(updates.name); }
2882
+ if (updates.agent_id !== undefined) { fields.push("agent_id = ?"); values.push(updates.agent_id); }
2883
+ if (updates.config !== undefined) { fields.push("config = ?"); values.push(updates.config); }
2884
+ if (updates.project_id !== undefined) { fields.push("project_id = ?"); values.push(updates.project_id || null); }
2885
+
2886
+ if (fields.length === 0) return channel;
2887
+
2888
+ fields.push("updated_at = ?");
2889
+ values.push(new Date().toISOString());
2890
+ values.push(id);
2891
+
2892
+ db.run(`UPDATE channels SET ${fields.join(", ")} WHERE id = ?`, values);
2893
+ return this.findById(id);
2894
+ },
2895
+
2896
+ setStatus(id: string, status: Channel["status"], error?: string | null): void {
2897
+ db.run(
2898
+ "UPDATE channels SET status = ?, error = ?, updated_at = ? WHERE id = ?",
2899
+ [status, error || null, new Date().toISOString(), id]
2900
+ );
2901
+ },
2902
+
2903
+ delete(id: string): boolean {
2904
+ const result = db.run("DELETE FROM channels WHERE id = ?", [id]);
2905
+ return result.changes > 0;
2906
+ },
2907
+ };
2908
+
2729
2909
  // Generate unique ID
2730
2910
  export function generateId(): string {
2731
2911
  return Math.random().toString(36).substring(2, 15);
@@ -127,7 +127,7 @@ export const AgentDojoProvider: IntegrationProvider = {
127
127
 
128
128
  return credentials.map((cred: any) => ({
129
129
  id: String(cred.id),
130
- appId: String(cred.provider_id || cred.toolkit_id || cred.provider_name),
130
+ appId: cred.provider_name || cred.toolkit_name || String(cred.provider_id || cred.toolkit_id),
131
131
  appName: cred.provider_name || cred.name || cred.toolkit_name || String(cred.provider_id),
132
132
  status: (cred.status === "active" || cred.is_valid !== false) ? "active" as const : "failed" as const,
133
133
  createdAt: cred.created_at || new Date().toISOString(),
@@ -73,34 +73,28 @@ function evaluateExpression(
73
73
  args: Record<string, any>,
74
74
  helpers: ReturnType<typeof templateHelpers>,
75
75
  ): any {
76
- // Handle args.* references
76
+ // Handle args.* references (e.g. args.name, args.query)
77
77
  if (expr.startsWith("args.")) {
78
78
  const key = expr.slice(5);
79
79
  return args[key] ?? null;
80
80
  }
81
81
 
82
- // Handle helper functions and values
83
- try {
84
- const fn = new Function(
85
- "args",
86
- "uuid",
87
- "now",
88
- "timestamp",
89
- "random_int",
90
- "random_float",
91
- `return ${expr}`,
92
- );
93
- return fn(
94
- args,
95
- helpers.uuid,
96
- helpers.now,
97
- helpers.timestamp,
98
- helpers.random_int,
99
- helpers.random_float,
100
- );
101
- } catch {
102
- return expr;
103
- }
82
+ // Handle known helper values
83
+ if (expr === "now") return helpers.now;
84
+ if (expr === "timestamp") return helpers.timestamp;
85
+
86
+ // Handle known helper function calls
87
+ const uuidMatch = expr.match(/^uuid\(\)$/);
88
+ if (uuidMatch) return helpers.uuid();
89
+
90
+ const randIntMatch = expr.match(/^random_int\(\s*(\d+)\s*,\s*(\d+)\s*\)$/);
91
+ if (randIntMatch) return helpers.random_int(Number(randIntMatch[1]), Number(randIntMatch[2]));
92
+
93
+ const randFloatMatch = expr.match(/^random_float\(\s*([\d.]+)\s*,\s*([\d.]+)\s*\)$/);
94
+ if (randFloatMatch) return helpers.random_float(Number(randFloatMatch[1]), Number(randFloatMatch[2]));
95
+
96
+ // Return expression as-is if not recognized — never execute arbitrary code
97
+ return expr;
104
98
  }
105
99
 
106
100
  // Execute a mock handler — returns the rendered mock_response
@@ -197,7 +191,11 @@ async function executeHttp(
197
191
  }
198
192
  }
199
193
 
200
- // Execute a JavaScript handler — runs code string with args + credentials
194
+ // Execute a JavaScript handler — runs user-defined code in a restricted scope.
195
+ // SECURITY NOTE: This intentionally allows authenticated admins to define custom tool logic.
196
+ // The code runs in a restricted Function scope with only args, credentials, and helpers exposed.
197
+ // process, require, import, Bun, fetch etc. are NOT passed in — but note that new Function()
198
+ // still has access to globalThis. For full sandboxing, consider using a Worker or subprocess.
201
199
  function executeJavascript(
202
200
  tool: McpServerTool,
203
201
  args: Record<string, any>,
@@ -210,6 +208,15 @@ function executeJavascript(
210
208
  };
211
209
  }
212
210
 
211
+ // Basic static checks — block obvious dangerous patterns
212
+ const dangerous = /\b(process|require|import|Bun|Deno|eval|Function|child_process|exec|spawn)\b/;
213
+ if (dangerous.test(tool.code)) {
214
+ return {
215
+ content: [{ type: "text", text: "Error: Tool code contains disallowed keywords (process, require, import, eval, exec, spawn)" }],
216
+ isError: true,
217
+ };
218
+ }
219
+
213
220
  try {
214
221
  const helpers = templateHelpers();
215
222
  const fn = new Function(