Skip to content

Commit d299bf4

Browse files
AnnatarHeclaude
andcommitted
feat(daemon): send session-project path mapping to server
Parse session_id and workspace from Claude Code statusline input, send the session→project mapping to the daemon via unix socket, which forwards it to the server via POST /api/v1/cc/session-project. This enables the server to fix incorrect pwd values during OTEL ingestion. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 523d614 commit d299bf4

5 files changed

Lines changed: 104 additions & 4 deletions

File tree

commands/cc_statusline.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,24 @@ func commandCCStatusline(c *cli.Context) error {
6666
var result ccStatuslineResult
6767
config, err := configService.ReadConfigFile(ctx)
6868
if err == nil {
69+
// Send session-project mapping via daemon socket (fire-and-forget, ~1ms)
70+
if data.SessionID != "" {
71+
projectPath := ""
72+
if data.Workspace != nil {
73+
projectPath = data.Workspace.CurrentDir
74+
if projectPath == "" {
75+
projectPath = data.Workspace.ProjectDir
76+
}
77+
}
78+
if projectPath != "" {
79+
socketPath := config.SocketPath
80+
if socketPath == "" {
81+
socketPath = model.DefaultSocketPath
82+
}
83+
daemon.SendSessionProject(socketPath, data.SessionID, projectPath)
84+
}
85+
}
86+
6987
result = getDaemonInfoWithFallback(ctx, config, data.Cwd)
7088
}
7189

daemon/client.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,25 @@ func SendLocalDataToSocket(
5151
return nil
5252
}
5353

54+
// SendSessionProject sends a session-to-project mapping to the daemon (fire-and-forget)
55+
func SendSessionProject(socketPath string, sessionID, projectPath string) {
56+
conn, err := net.DialTimeout("unix", socketPath, 10*time.Millisecond)
57+
if err != nil {
58+
return
59+
}
60+
defer conn.Close()
61+
62+
msg := SocketMessage{
63+
Type: SocketMessageTypeSessionProject,
64+
Payload: SessionProjectRequest{
65+
SessionID: sessionID,
66+
ProjectPath: projectPath,
67+
},
68+
}
69+
70+
json.NewEncoder(conn).Encode(msg)
71+
}
72+
5473
// RequestCCInfo requests CC info (cost data and git info) from the daemon
5574
func RequestCCInfo(socketPath string, timeRange CCInfoTimeRange, workingDir string, timeout time.Duration) (*CCInfoResponse, error) {
5675
conn, err := net.DialTimeout("unix", socketPath, timeout)

daemon/socket.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package daemon
22

33
import (
4+
"context"
45
"encoding/json"
56
"fmt"
67
"log/slog"
@@ -17,12 +18,18 @@ import (
1718
type SocketMessageType string
1819

1920
const (
20-
SocketMessageTypeSync SocketMessageType = "sync"
21-
SocketMessageTypeHeartbeat SocketMessageType = "heartbeat"
22-
SocketMessageTypeStatus SocketMessageType = "status"
23-
SocketMessageTypeCCInfo SocketMessageType = "cc_info"
21+
SocketMessageTypeSync SocketMessageType = "sync"
22+
SocketMessageTypeHeartbeat SocketMessageType = "heartbeat"
23+
SocketMessageTypeStatus SocketMessageType = "status"
24+
SocketMessageTypeCCInfo SocketMessageType = "cc_info"
25+
SocketMessageTypeSessionProject SocketMessageType = "session_project"
2426
)
2527

28+
type SessionProjectRequest struct {
29+
SessionID string `json:"sessionId"`
30+
ProjectPath string `json:"projectPath"`
31+
}
32+
2633
type CCInfoTimeRange string
2734

2835
const (
@@ -179,6 +186,15 @@ func (p *SocketHandler) handleConnection(conn net.Conn) {
179186
encoder.Encode(map[string]string{"status": "ok"})
180187
case SocketMessageTypeCCInfo:
181188
p.handleCCInfo(conn, msg)
189+
case SocketMessageTypeSessionProject:
190+
if payload, ok := msg.Payload.(map[string]interface{}); ok {
191+
sessionID, _ := payload["sessionId"].(string)
192+
projectPath, _ := payload["projectPath"].(string)
193+
if sessionID != "" && projectPath != "" {
194+
go model.SendSessionProjectUpdate(context.Background(), *p.config, sessionID, projectPath)
195+
slog.Debug("session_project update dispatched", slog.String("sessionId", sessionID))
196+
}
197+
}
182198
default:
183199
slog.Error("Unknown message type:", slog.String("messageType", string(msg.Type)))
184200
}

model/api_session_project.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package model
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"time"
7+
)
8+
9+
type sessionProjectRequest struct {
10+
SessionID string `json:"session_id"`
11+
ProjectPath string `json:"project_path"`
12+
}
13+
14+
type sessionProjectResponse struct{}
15+
16+
// SendSessionProjectUpdate sends a session-to-project path mapping to the server
17+
func SendSessionProjectUpdate(ctx context.Context, config ShellTimeConfig, sessionID, projectPath string) error {
18+
ctx, span := modelTracer.Start(ctx, "session_project.send")
19+
defer span.End()
20+
21+
var resp sessionProjectResponse
22+
err := SendHTTPRequestJSON(HTTPRequestOptions[*sessionProjectRequest, sessionProjectResponse]{
23+
Context: ctx,
24+
Endpoint: Endpoint{
25+
APIEndpoint: config.APIEndpoint,
26+
Token: config.Token,
27+
},
28+
Method: http.MethodPost,
29+
Path: "/api/v1/cc/session-project",
30+
Payload: &sessionProjectRequest{
31+
SessionID: sessionID,
32+
ProjectPath: projectPath,
33+
},
34+
Response: &resp,
35+
Timeout: 5 * time.Second,
36+
})
37+
38+
return err
39+
}

model/cc_statusline_types.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ type CCStatuslineInput struct {
88
ContextWindow CCStatuslineContextWindow `json:"context_window"`
99
Cwd string `json:"cwd"`
1010
Version string `json:"version"`
11+
SessionID string `json:"session_id"`
12+
Workspace *CCStatuslineWorkspace `json:"workspace"`
13+
}
14+
15+
// CCStatuslineWorkspace represents workspace information from Claude Code
16+
type CCStatuslineWorkspace struct {
17+
CurrentDir string `json:"current_dir"`
18+
ProjectDir string `json:"project_dir"`
1119
}
1220

1321
// CCStatuslineModel represents model information

0 commit comments

Comments
 (0)