Canvas: Implement Agent Sketch node, update dodo-app.jsonschema
- Add Gemini API key to the project
- Update dodo schema to support Gemini API key
- Update dodo schema to support Agent Sketch node
Change-Id: I6a96186f86ad169152ca0021b38130e485ebbf14
diff --git a/apps/canvas/front/src/lib/categories.ts b/apps/canvas/front/src/lib/categories.ts
index 8753455..6c655e1 100644
--- a/apps/canvas/front/src/lib/categories.ts
+++ b/apps/canvas/front/src/lib/categories.ts
@@ -1,7 +1,8 @@
-import { NodeType, InitData } from "@/lib/state";
+import { NodeType, InitData } from "config";
export interface CategoryItem<T extends NodeType> {
title: string;
+ // TODO(gio): make InitData generic
init: InitData;
type: T;
}
@@ -102,4 +103,82 @@
},
],
},
+ {
+ title: "AI", // New Category for AI tools
+ items: [
+ {
+ title: "Agent Sketch",
+ init: {
+ ...defaultInit,
+ label: "Agent Sketch", // Default label for the node
+ type: "sketch:latest",
+ ports: [
+ {
+ id: "agent",
+ name: "agent",
+ value: 2001,
+ },
+ {
+ id: "p8080",
+ name: "p8080",
+ value: 8080,
+ },
+ {
+ id: "p8081",
+ name: "p8081",
+ value: 8081,
+ },
+ {
+ id: "p8082",
+ name: "p8082",
+ value: 8082,
+ },
+ {
+ id: "p8083",
+ name: "p8083",
+ value: 8083,
+ },
+ {
+ id: "p8084",
+ name: "p8084",
+ value: 8084,
+ },
+ ],
+ envVars: [
+ {
+ id: "agent",
+ name: "DODO_PORT_AGENT",
+ source: null,
+ },
+ {
+ id: "p8080",
+ name: "DODO_PORT_P8080",
+ source: null,
+ },
+ {
+ id: "p8081",
+ name: "DODO_PORT_P8081",
+ source: null,
+ },
+ {
+ id: "p8082",
+ name: "DODO_PORT_P8082",
+ source: null,
+ },
+ {
+ id: "p8083",
+ name: "DODO_PORT_P8083",
+ source: null,
+ },
+ {
+ id: "p8084",
+ name: "DODO_PORT_P8084",
+ source: null,
+ },
+ ],
+ },
+ type: "app",
+ },
+ ],
+ },
];
diff --git a/apps/canvas/front/src/lib/config.ts b/apps/canvas/front/src/lib/config.ts
index 39db5b4..f6dee32 100644
--- a/apps/canvas/front/src/lib/config.ts
+++ b/apps/canvas/front/src/lib/config.ts
@@ -34,7 +34,11 @@
return 4;
case "gateway-https":
return 5;
- case undefined:
+ case "gateway-tcp":
+ return 7;
+ case "network":
+ return 8;
+ case undefined: // For NANode
return 100;
}
}
@@ -153,6 +157,7 @@
}),
);
const noSource = apps
+ .filter((n) => n.data.type !== "sketch:latest")
.filter((n) => n.data == null || n.data.repository == null || n.data.repository.repoNodeId === "")
.map(
(n): Message => ({
@@ -194,44 +199,46 @@
onLooseHighlight: (store) => store.updateNode(n.id, { selected: false }),
}),
);
- const noIngress = apps.flatMap((n): Message[] => {
- if (n.data == null) {
- return [];
- }
- return (n.data.ports || [])
- .filter(
- (p) =>
- !nodes
- .filter((i) => i.type === "gateway-https")
- .some((i) => {
- if (
- i.data &&
- i.data.https &&
- i.data.https.serviceId === n.id &&
- i.data.https.portId === p.id
- ) {
- return true;
- }
- return false;
- }),
- )
- .map(
- (p): Message => ({
- id: `${n.id}-${p.id}-no-ingress`,
- type: "WARNING",
- nodeId: n.id,
- message: `Connect to gateway: ${p.name} - ${p.value}`,
- onHighlight: (store) => {
- store.updateNode(n.id, { selected: true });
- store.setHighlightCategory("gateways", true);
- },
- onLooseHighlight: (store) => {
- store.updateNode(n.id, { selected: false });
- store.setHighlightCategory("gateways", false);
- },
- }),
- );
- });
+ const noIngress = apps
+ .filter((n) => n.data.type !== "sketch:latest")
+ .flatMap((n): Message[] => {
+ if (n.data == null) {
+ return [];
+ }
+ return (n.data.ports || [])
+ .filter(
+ (p) =>
+ !nodes
+ .filter((i) => i.type === "gateway-https")
+ .some((i) => {
+ if (
+ i.data &&
+ i.data.https &&
+ i.data.https.serviceId === n.id &&
+ i.data.https.portId === p.id
+ ) {
+ return true;
+ }
+ return false;
+ }),
+ )
+ .map(
+ (p): Message => ({
+ id: `${n.id}-${p.id}-no-ingress`,
+ type: "WARNING",
+ nodeId: n.id,
+ message: `Connect to gateway: ${p.name} - ${p.value}`,
+ onHighlight: (store) => {
+ store.updateNode(n.id, { selected: true });
+ store.setHighlightCategory("gateways", true);
+ },
+ onLooseHighlight: (store) => {
+ store.updateNode(n.id, { selected: false });
+ store.setHighlightCategory("gateways", false);
+ },
+ }),
+ );
+ });
const multipleIngress = apps
.filter((n) => n.data != null && n.data.ports != null)
.flatMap((n) =>
diff --git a/apps/canvas/front/src/lib/state.ts b/apps/canvas/front/src/lib/state.ts
index 8ada938..3733e56 100644
--- a/apps/canvas/front/src/lib/state.ts
+++ b/apps/canvas/front/src/lib/state.ts
@@ -46,7 +46,7 @@
case "volume":
return n.data.label || "Volume";
case undefined:
- throw new Error("MUST NOT REACH!");
+ throw new Error(`nodeLabel: Node type is undefined. Node ID: ${n.id}, Data: ${JSON.stringify(n.data)}`);
}
} catch (e) {
console.error("opaa", e);
@@ -106,7 +106,9 @@
}
return true;
case undefined:
- throw new Error("MUST NOT REACH!");
+ throw new Error(
+ `nodeIsConnectable: Node type is undefined. Node ID: ${n.id}, Handle: ${handle}, Data: ${JSON.stringify(n.data)}`,
+ );
}
}
@@ -153,12 +155,12 @@
};
const defaultEnv: Env = {
- managerAddr: undefined,
deployKeyPublic: undefined,
instanceId: undefined,
networks: [],
integrations: {
github: false,
+ gemini: false,
},
services: [],
user: {
@@ -225,7 +227,6 @@
const projectIdSelector = (state: AppState) => state.projectId;
const categoriesSelector = (state: AppState) => state.categories;
const messagesSelector = (state: AppState) => state.messages;
-const githubServiceSelector = (state: AppState) => state.githubService;
const envSelector = (state: AppState) => state.env;
const zoomSelector = (state: AppState) => state.zoom;
const githubRepositoriesSelector = (state: AppState) => state.githubRepositories;
@@ -268,8 +269,12 @@
return useStateStore(envSelector);
}
-export function useGithubService(): GitHubService | null {
- return useStateStore(githubServiceSelector);
+export function useGithubService(): boolean {
+ return useStateStore(envSelector).integrations.github;
+}
+
+export function useGeminiService(): boolean {
+ return useStateStore(envSelector).integrations.gemini;
}
export function useGithubRepositories(): GitHubRepository[] {
@@ -457,7 +462,9 @@
if (c.sourceHandle === "env_var" && c.targetHandle === "env_var") {
const sourceEnvVars = nodeEnvVarNames(sn);
if (sourceEnvVars.length === 0) {
- throw new Error("MUST NOT REACH!");
+ throw new Error(
+ `onConnect (env_var): Source node ${sn.id} (type: ${sn.type}) has no env vars to connect from.`,
+ );
}
const id = uuidv4();
if (sourceEnvVars.length === 1) {
diff --git a/apps/canvas/front/src/lib/types.ts b/apps/canvas/front/src/lib/types.ts
index 983b13e..09d1ba0 100644
--- a/apps/canvas/front/src/lib/types.ts
+++ b/apps/canvas/front/src/lib/types.ts
@@ -1,4 +1,4 @@
-import { AppNode } from "./state";
+import { AppNode } from "config";
export interface NodeDetailsProps<T extends AppNode = AppNode> {
node: T;