Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions changelog/unreleased/workbuddy-checkin-timezone-and-errors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
### English

- WorkBuddy CN automatic check-in now reads the configured time in Asia/Shanghai instead of the container's local timezone. The supported Docker Compose install sets no `TZ`, so the time was previously treated as UTC and fired eight hours off the intended local time.
- A dropped connection or timeout during the WorkBuddy daily check-in is now reported as a transport error instead of `checkin parse: unexpected end of JSON input`.

### 中文

- WorkBuddy CN 的自动签到改为按 Asia/Shanghai 解释配置的时间,不再跟随容器本地时区。官方 Docker Compose 部署未设置 `TZ`,此前该时间被当作 UTC 处理,实际触发时刻比预期偏差八小时。
- WorkBuddy 每日签到遇到连接中断或超时时,现在会如实报告传输层错误,不再显示 `checkin parse: unexpected end of JSON input`。
2 changes: 1 addition & 1 deletion internal/providers/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ var WorkBuddy = ProviderDescriptor{
ID: "cn", Label: "CN", ChatBase: "https://copilot.tencent.com",
BillingBase: "https://www.codebuddy.cn", AuthBase: "https://copilot.tencent.com",
DefaultDomain: "codebuddy.cn",
Checkin: &CheckinPolicy{Timezone: "Local"},
Checkin: &CheckinPolicy{Timezone: "Asia/Shanghai"},
},
{
ID: "global", Label: "Global", ChatBase: "https://www.workbuddy.ai",
Expand Down
3 changes: 3 additions & 0 deletions internal/providers/workbuddy/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,9 @@ func (c *Client) DailyCheckin(ctx context.Context, accountID string) (string, er
break
}
}
if err != nil {
return "", err
}
text := strings.TrimSpace(string(body))
classified := Classify(status, text)
if classified.Kind == accounts.KindAuth {
Expand Down
32 changes: 32 additions & 0 deletions internal/providers/workbuddy/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1567,6 +1567,38 @@ func TestDailyCheckinAlreadyCheckedInHTTP400(t *testing.T) {
}
}

func TestDailyCheckinSurfacesTransportError(t *testing.T) {
originalDelays := dailyCheckinRetryDelays
dailyCheckinRetryDelays = []time.Duration{0, 0}
t.Cleanup(func() { dailyCheckinRetryDelays = originalDelays })

client, store := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
hijacker, ok := w.(http.Hijacker)
if !ok {
t.Errorf("response writer is not a hijacker")
return
}
conn, _, err := hijacker.Hijack()
if err != nil {
t.Errorf("hijack: %v", err)
return
}
_ = conn.Close()
}))
payload, _ := json.Marshal(Credential{
AccessToken: "at", RefreshToken: "rt", ExpiresAt: 4102444800, Domain: DomainCN, UID: "u1",
})
_ = store.SaveCredentialPayload(context.Background(), "acc1", CredentialFormat, payload)

_, err := client.DailyCheckin(context.Background(), "acc1")
if err == nil {
t.Fatal("a dropped connection must fail the check-in")
}
if strings.Contains(err.Error(), "checkin parse") {
t.Fatalf("transport error was reported as a parse error: %v", err)
}
}

func TestDailyCheckinRetriesTransientFailures(t *testing.T) {
var calls atomic.Int32
client, store := newTestClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
Expand Down
Loading