Skip to content

Commit 87e0862

Browse files
committed
Add deploy script
1 parent c053920 commit 87e0862

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1058
-303
lines changed

.vscode/settings.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,8 @@
5757
"[css][html][markdown][yaml]": {
5858
"editor.defaultFormatter": "esbenp.prettier-vscode"
5959
},
60-
"typos.config": ".github/workflows/typos.toml"
60+
"typos.config": ".github/workflows/typos.toml",
61+
"[typescriptreact]": {
62+
"editor.defaultFormatter": "vscode.typescript-language-features"
63+
}
6164
}

agent/agentclaude/agentclaude.go

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import (
1515
"github.com/spf13/afero"
1616
)
1717

18-
func New(ctx context.Context, apiKey, systemPrompt, taskPrompt string) error {
18+
func New(ctx context.Context, apiKey, systemPrompt, taskPrompt string, onPause func()) error {
1919
claudePath, err := exec.LookPath("claude")
2020
if err != nil {
2121
return fmt.Errorf("claude not found: %w", err)
@@ -25,7 +25,13 @@ func New(ctx context.Context, apiKey, systemPrompt, taskPrompt string) error {
2525
2626
The user is running this task entirely autonomously.
2727
28-
You must use the coder-agent MCP server to periodically report your progress.
28+
Use the coder-agent MCP server to report your progress. You must report when you:
29+
- Start a new task.
30+
- Complete a task (e.g. push a commit, open a PR, etc.)
31+
- Make progress on a task.
32+
33+
You should report your progress frequently. Try to report as much as possible.
34+
2935
If you do not, the user will not be able to see your progress.
3036
`, systemPrompt, "")
3137
if err != nil {
@@ -42,34 +48,31 @@ If you do not, the user will not be able to see your progress.
4248
ProjectDirectory: wd,
4349
APIKey: apiKey,
4450
AllowedTools: []string{},
45-
MCPServers: map[string]ClaudeConfigMCP{
46-
// "coder-agent": {
47-
// Command: "coder",
48-
// Args: []string{"agent", "mcp"},
49-
// },
51+
MCPServers: map[string]ClaudeConfigMCP{
52+
"coder-agent": {
53+
Command: "coder",
54+
Args: []string{"agent", "mcp"},
55+
Env: map[string]string{
56+
"CODER_AGENT_TOKEN": os.Getenv("CODER_AGENT_TOKEN"),
57+
},
58+
},
5059
},
5160
})
5261
if err != nil {
5362
return fmt.Errorf("failed to configure claude: %w", err)
5463
}
5564

56-
cmd := exec.CommandContext(ctx, claudePath, taskPrompt)
57-
58-
handlePause := func() {
59-
// We need to notify the user that we've paused!
60-
fmt.Println("We would normally notify the user...")
61-
}
62-
65+
cmd := exec.CommandContext(ctx, claudePath, "--dangerously-skip-permissions", taskPrompt)
6366
// Create a simple wrapper that starts monitoring only after first write
6467
stdoutWriter := &delayedPauseWriter{
6568
writer: os.Stdout,
6669
pauseWindow: 2 * time.Second,
67-
onPause: handlePause,
70+
onPause: onPause,
6871
}
6972
stderrWriter := &delayedPauseWriter{
7073
writer: os.Stderr,
7174
pauseWindow: 2 * time.Second,
72-
onPause: handlePause,
75+
onPause: onPause,
7376
}
7477

7578
cmd.Stdout = stdoutWriter
@@ -197,6 +200,11 @@ func injectClaudeMD(fs afero.Fs, coderPrompt, systemPrompt string, configPath st
197200
newContent += cleanContent
198201
}
199202

203+
err = fs.MkdirAll(filepath.Dir(configPath), 0755)
204+
if err != nil {
205+
return fmt.Errorf("failed to create claude config directory: %w", err)
206+
}
207+
200208
// Write the updated content back to the file
201209
err = afero.WriteFile(fs, configPath, []byte(newContent), 0644)
202210
if err != nil {
@@ -226,9 +234,9 @@ type ClaudeConfig struct {
226234
}
227235

228236
type ClaudeConfigMCP struct {
229-
Command string
230-
Args []string
231-
Env map[string]string
237+
Command string `json:"command"`
238+
Args []string `json:"args"`
239+
Env map[string]string `json:"env"`
232240
}
233241

234242
func configureClaude(fs afero.Fs, cfg ClaudeConfig) error {
@@ -240,7 +248,6 @@ func configureClaude(fs afero.Fs, cfg ClaudeConfig) error {
240248
if err != nil {
241249
if os.IsNotExist(err) {
242250
config = make(map[string]any)
243-
err = nil
244251
} else {
245252
return fmt.Errorf("failed to stat claude config: %w", err)
246253
}
@@ -265,6 +272,7 @@ func configureClaude(fs afero.Fs, cfg ClaudeConfig) error {
265272
config["hasCompletedOnboarding"] = true
266273
// Stops Claude from asking for permissions.
267274
config["bypassPermissionsModeAccepted"] = true
275+
config["autoUpdaterStatus"] = "disabled"
268276

269277
projects, ok := config["projects"].(map[string]any)
270278
if !ok {
@@ -291,6 +299,8 @@ func configureClaude(fs afero.Fs, cfg ClaudeConfig) error {
291299
allowedTools = append(allowedTools, tool)
292300
}
293301
project["allowedTools"] = allowedTools
302+
project["hasTrustDialogAccepted"] = true
303+
project["hasCompletedProjectOnboarding"] = true
294304

295305
mcpServers, ok := project["mcpServers"].(map[string]any)
296306
if !ok {

agent/agentmcp/agentmcp.go

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,83 @@ package agentmcp
22

33
import (
44
"context"
5+
"errors"
56

67
"github.com/coder/coder/v2/buildinfo"
8+
"github.com/coder/coder/v2/codersdk/agentsdk"
79
"github.com/mark3labs/mcp-go/mcp"
810
"github.com/mark3labs/mcp-go/server"
911
)
1012

11-
func New(ctx context.Context) error {
13+
func New(ctx context.Context, sdk *agentsdk.Client) error {
1214
srv := server.NewMCPServer(
1315
"Coder Agent",
1416
buildinfo.Version(),
17+
server.WithInstructions(`Report your progress when starting, working on, or completing a task.
18+
19+
You MUST report tasks when starting something new, or when you've completed a task.
20+
21+
You MUST report intermediate progress on a task if you've been working on it for a while.
22+
23+
Examples of sending a task:
24+
- Working on a new part of the codebase.
25+
- Starting on an issue (you should include the issue URL as "link").
26+
- Opening a pull request (you should include the PR URL as "link").
27+
- Completing a task (you should set "done" to true).
28+
- Starting a new task (you should set "done" to false).
29+
`),
1530
)
1631

17-
tool := mcp.NewTool("summarize_task",
18-
mcp.WithDescription("Summarize your progress on a task."))
32+
tool := mcp.NewTool("report_task",
33+
mcp.WithDescription(`Report progress on a task.`),
34+
mcp.WithString("summary", mcp.Description(`A summary of your progress on a task.
35+
36+
Good Summaries:
37+
- "Taking a look at the login page..."
38+
- "Found a bug! Fixing it now..."
39+
- "Investigating the GitHub Issue..."`), mcp.Required()),
40+
mcp.WithString("link", mcp.Description(`A relevant link to your work. e.g. GitHub issue link, pull request link, etc.`), mcp.Required()),
41+
mcp.WithBoolean("done", mcp.Description(`Whether the task the user requested is complete.`), mcp.Required()),
42+
mcp.WithString("emoji", mcp.Description(`A relevant emoji to your work.`), mcp.Required()),
43+
)
1944

2045
srv.AddTool(tool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
46+
args := request.Params.Arguments
47+
48+
summary, ok := args["summary"].(string)
49+
if !ok {
50+
return nil, errors.New("summary is required")
51+
}
52+
53+
link, ok := args["link"].(string)
54+
if !ok {
55+
return nil, errors.New("link is required")
56+
}
57+
58+
emoji, ok := args["emoji"].(string)
59+
if !ok {
60+
return nil, errors.New("emoji is required")
61+
}
62+
63+
done, ok := args["done"].(bool)
64+
if !ok {
65+
return nil, errors.New("done is required")
66+
}
67+
68+
err := sdk.PostTask(ctx, agentsdk.PostTaskRequest{
69+
Reporter: "claude",
70+
Summary: summary,
71+
URL: link,
72+
Completion: done,
73+
Icon: emoji,
74+
})
75+
if err != nil {
76+
return nil, err
77+
}
78+
2179
return &mcp.CallToolResult{
2280
Content: []mcp.Content{
23-
mcp.NewTextContent("Task summarized."),
81+
mcp.NewTextContent("Thanks for reporting!"),
2482
},
2583
}, nil
2684
})

cli/agent.go

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,23 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
377377
Use: "mcp",
378378
Short: "Start the MCP server",
379379
Handler: func(inv *serpent.Invocation) error {
380-
return agentmcp.New(inv.Context())
380+
client := agentsdk.New(r.agentURL)
381+
token, _ := inv.ParsedFlags().GetString(varAgentToken)
382+
if token == "" {
383+
tokenFile, _ := inv.ParsedFlags().GetString(varAgentTokenFile)
384+
if tokenFile != "" {
385+
tokenBytes, err := os.ReadFile(tokenFile)
386+
if err != nil {
387+
return xerrors.Errorf("read token file %q: %w", tokenFile, err)
388+
}
389+
token = strings.TrimSpace(string(tokenBytes))
390+
}
391+
}
392+
if token == "" {
393+
return xerrors.Errorf("CODER_AGENT_TOKEN or CODER_AGENT_TOKEN_FILE must be set for token auth")
394+
}
395+
client.SetSessionToken(token)
396+
return agentmcp.New(inv.Context(), client)
381397
},
382398
},
383399
{
@@ -401,7 +417,35 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
401417
},
402418
},
403419
Handler: func(inv *serpent.Invocation) error {
404-
return agentclaude.New(inv.Context(), claudeAPIKey, claudeSystemPrompt, claudeTaskPrompt)
420+
client := agentsdk.New(r.agentURL)
421+
token, _ := inv.ParsedFlags().GetString(varAgentToken)
422+
if token == "" {
423+
tokenFile, _ := inv.ParsedFlags().GetString(varAgentTokenFile)
424+
if tokenFile != "" {
425+
tokenBytes, err := os.ReadFile(tokenFile)
426+
if err != nil {
427+
return xerrors.Errorf("read token file %q: %w", tokenFile, err)
428+
}
429+
token = strings.TrimSpace(string(tokenBytes))
430+
}
431+
}
432+
if token == "" {
433+
return xerrors.Errorf("CODER_AGENT_TOKEN or CODER_AGENT_TOKEN_FILE must be set for token auth")
434+
}
435+
client.SetSessionToken(token)
436+
437+
client.PostTask(inv.Context(), agentsdk.PostTaskRequest{
438+
Reporter: "claude",
439+
Summary: "Starting on your task...",
440+
Icon: "🤖",
441+
Completion: false,
442+
})
443+
444+
return agentclaude.New(inv.Context(), claudeAPIKey, claudeSystemPrompt, claudeTaskPrompt, func() {
445+
_ = client.PatchTasks(inv.Context(), agentsdk.PatchTasksRequest{
446+
WaitingForUserInput: true,
447+
})
448+
})
405449
},
406450
},
407451
},

cli/server.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"sync/atomic"
3232
"time"
3333

34+
"github.com/SherClockHolmes/webpush-go"
3435
"github.com/charmbracelet/lipgloss"
3536
"github.com/coreos/go-oidc/v3/oidc"
3637
"github.com/coreos/go-systemd/daemon"
@@ -775,6 +776,39 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
775776
return xerrors.Errorf("set deployment id: %w", err)
776777
}
777778

779+
var vapidPublicKey string
780+
var vapidPrivateKey string
781+
err = options.Database.InTx(func(tx database.Store) error {
782+
vapidPublicKey, err = tx.GetVAPIDPublicKey(ctx)
783+
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
784+
return xerrors.Errorf("get vapid public key: %w", err)
785+
}
786+
vapidPrivateKey, err = tx.GetVAPIDPrivateKey(ctx)
787+
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
788+
return xerrors.Errorf("get vapid private key: %w", err)
789+
}
790+
if vapidPublicKey == "" || vapidPrivateKey == "" {
791+
vapidPrivateKey, vapidPublicKey, err = webpush.GenerateVAPIDKeys()
792+
if err != nil {
793+
return xerrors.Errorf("generate vapid keys: %w", err)
794+
}
795+
err = tx.InsertVAPIDPublicKey(ctx, vapidPublicKey)
796+
if err != nil {
797+
return xerrors.Errorf("insert vapid public key: %w", err)
798+
}
799+
err = tx.InsertVAPIDPrivateKey(ctx, vapidPrivateKey)
800+
if err != nil {
801+
return xerrors.Errorf("insert vapid private key: %w", err)
802+
}
803+
}
804+
return nil
805+
}, nil)
806+
if err != nil {
807+
return xerrors.Errorf("insert vapid keys: %w", err)
808+
}
809+
options.NotificationsVAPIDPrivateKey = vapidPrivateKey
810+
options.NotificationsVAPIDPublicKey = vapidPublicKey
811+
778812
githubOAuth2ConfigParams, err := getGithubOAuth2ConfigParams(ctx, options.Database, vals)
779813
if err != nil {
780814
return xerrors.Errorf("get github oauth2 config params: %w", err)

coderd/apidoc/docs.go

Lines changed: 9 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy