dodo: implement dodo_get_project_config

Change-Id: I8167f83d776d43d97c5b2a744524f88e517c2107
diff --git a/dodo_tools/dodo.go b/dodo_tools/dodo.go
new file mode 100644
index 0000000..a5228de
--- /dev/null
+++ b/dodo_tools/dodo.go
@@ -0,0 +1,84 @@
+package dodo_tools
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/http"
+
+	"sketch.dev/llm"
+)
+
+type GetProjectConfigTool struct {
+}
+
+const (
+	getProjectSchemaInputSchema = `
+{
+	"type": "object",
+	"properties": {
+		"apiBaseAddress": {
+			"type": "string",
+			"description": "The base address of the dodo API"
+		},
+		"projectId": {
+			"type": "string",
+			"description": "The ID of the dodo project to get infrastructure configuration."
+		}
+	},
+	"required": ["apiBaseAddress", "projectId"]
+}
+`
+)
+
+func NewGetProjectConfigTool() *llm.Tool {
+	tool := &GetProjectConfigTool{}
+	return &llm.Tool{
+		Name:        "dodo_get_project_config",
+		Description: "A tool for getting current state of the infrastructure configuration of a dodo project",
+		InputSchema: llm.MustSchema(getProjectSchemaInputSchema),
+		Run:         tool.Run,
+		EndsTurn:    true,
+	}
+}
+
+type GetProjectConfigInput struct {
+	ApiBaseAddress string `json:"apiBaseAddress"`
+	ProjectId      string `json:"projectId"`
+}
+
+type GetProjectConfigOutput struct {
+	Config string `json:"config"`
+}
+
+func (d *GetProjectConfigTool) Run(ctx context.Context, m json.RawMessage) ([]llm.Content, error) {
+	var input GetProjectConfigInput
+	if err := json.Unmarshal(m, &input); err != nil {
+		return nil, err
+	}
+	resp, err := http.Get(fmt.Sprintf("%s/api/project/%s/config", input.ApiBaseAddress, input.ProjectId))
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	body, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	if resp.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("failed to get project config: %s", string(body))
+	}
+	var config map[string]interface{}
+	if err := json.Unmarshal(body, &config); err != nil {
+		return nil, fmt.Errorf("got invalid project config: %s %s", err, string(body))
+	}
+	output := GetProjectConfigOutput{
+		Config: string(body),
+	}
+	jsonOutput, err := json.Marshal(output)
+	if err != nil {
+		return nil, err
+	}
+	return llm.TextContent(string(jsonOutput)), nil
+}