Commit Graph

15 Commits

Author SHA1 Message Date
tigerenwork 55d8b50f1d fix: 增强配置容错与启动稳定性,优化错误状态交互 2026-07-05 20:25:06 +08:00
tigerenwork 8286c81afb WIP:feat: capture and log raw response body in Feishu decoding error for better debugging 2026-07-02 10:02:02 +08:00
tigerenwork 64a35752c5 fix: use rawValue for customer field to handle all Feishu field types 2026-06-29 21:03:27 +08:00
tigerenwork 0ce557ac8e feat: add customer field to Bug model with configurable mapping
- Bug model: added optional 'customer' property
- FieldMappings: added 'customerField' (default: 客户名称)
- BugMapper: reads customer value from configured column
- BugRow: displays customer in blue when present
- Settings: Customer field text field in Field Mappings section

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 20:50:46 +08:00
tigerenwork 723c50fc9d feat: configurable status mappings, scrollable settings, case-insensitive assignee
- Added FieldMappings.statusMappings: [FeishuValue: BugStatus] dict
  mapping Feishu select-option text to standard Bugger statuses.
  Pre-populated with common Chinese/English values:
  开发进行中→inProgress, 验收完毕→resolved, Bug Triage→open, etc.
- BugMapper.mapStatus() looks up Feishu value in the mapping,
  falling back to .unknown (shown as active bug).
- Settings: status mapping section with one TextField per status
  showing comma-separated Feishu values. Editable and saved to config.
- Settings: wrapped Form in ScrollView, removed fixed height,
  use minHeight/maxHeight — no more truncated buttons.
- Assignee matching now uses case-insensitive contains instead of
  exact match, handling display name variations from Feishu API.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 00:51:03 +08:00
tigerenwork 0f8565f65d fix: OAuth token endpoint wraps in {code,message,data} — not flat
The /authen/v1/oidc/access_token response uses the standard Feishu envelope
with 'message' (not 'msg') field. Updated FeishuAPIResponse to handle both
'msg' and 'message' via custom init(from:). exchangeCode and
refreshAccessToken now decode via FeishuAPIResponse<OAuthTokenData>.

Tenant token endpoint (/auth/v3/tenant_access_token/internal) returns flat
response — TenantAccessTokenData decoded directly.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 00:33:53 +08:00
tigerenwork ae918a379c fix: remove redundant CodingKeys conflicting with .convertFromSnakeCase
JSONDecoder.keyDecodingStrategy = .convertFromSnakeCase converts JSON keys
like 'tenant_access_token' to Swift names like 'tenantAccessToken'. Having
explicit CodingKeys that map to the ORIGINAL snake_case keys (e.g.,
case tenantAccessToken = "tenant_access_token") causes a collision:
the decoder first converts the JSON key, then looks for a CodingKey
matching the original name — but the converted name doesn't match.

Removed redundant CodingKeys from OAuthTokenData, TenantAccessTokenData,
UserInfoData, and RecordItem. .convertFromSnakeCase handles all these
mappings automatically.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 00:30:51 +08:00
tigerenwork f1ef89ef55 fix: tenant_access_token response is flat, not wrapped in data key
The /auth/v3/tenant_access_token/internal endpoint, like the OAuth token
endpoints, returns code, msg, tenant_access_token, and expire at the top
level of the JSON response — NOT nested under a 'data' key.

FeishuAPIResponse<TenantAccessTokenData> decoded successfully but with
data=nil (extra fields silently ignored by JSONDecoder). The guard then
threw FeishuError.apiError(code: 0, message: 'ok') — appearing to succeed
but always failing. This blocked the subsequent OAuth token exchange call.

- Added code/msg fields to TenantAccessTokenData
- tenantAccessToken() now decodes TenantAccessTokenData directly and
  checks data.code != 0 instead of going through FeishuAPIResponse

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 00:28:39 +08:00
tigerenwork ba56b2385e debug: switch to print() for critical path, manual JSON body
print() goes to stdout which is always visible in Xcode console regardless
of log level filtering. Also replaced JSONEncoder with manual JSON string
construction to rule out any Encodable issues with [String: String].

All critical send() calls now use print() instead of os.Logger.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 00:26:15 +08:00
tigerenwork f61f8332b8 fix: add step-by-step logs in exchangeCode to pinpoint crash location
The log stops after tenant_token fetch but before the OAuth token exchange
HTTP request. Added per-line logging to find exactly where it dies:
URL construction, body encoding, or the send() call.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 00:23:39 +08:00
tigerenwork 12e22da9b3 fix: change HTTP request/response logging from .debug to .info level
Debug-level os.Logger messages are filtered out in Console.app by default,
hiding the actual OAuth token exchange request/response. Changed all
FeishuAuthService.send() log calls to .info level so they appear without
changing Console filter settings.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 00:23:07 +08:00
tigerenwork e275e49795 feat: add comprehensive debug logging to OAuth flow
Added os.Logger-based logging (subsystem: com.xorbitlab.bugger) covering:
- FeishuAuthService.send(): raw HTTP request URL, method, body, auth header,
  response status code, and raw response body (up to 1000 chars)
- exchangeCode(): before/after token exchange with token previews
- TokenManager.handleCallback(): each step with success/failure
- LocalOAuthServer: server start, connection received, code extracted

View logs in: Xcode debug console, or Console.app → filter 'bugger'

Also fixed type inference ambiguity in generic send<T>() calls.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 00:18:25 +08:00
tigerenwork cf9de54792 fix: decode OAuth token response directly, not via FeishuAPIResponse wrapper
The /authen/v1/oidc/access_token and refresh_access_token endpoints return
token fields (access_token, refresh_token, expires_in) at the top level
of the JSON response, not nested under a 'data' key like standard Feishu
API responses. This caused 'Feishu API error (0): ok' — code 0 with no
wrapped data.

Changed exchangeCode and refreshAccessToken to decode OAuthTokenData
directly from the response. Removed unused decodeAPIResponse helper.

Also removed redirect_uri from token exchange body (not required by
Feishu's token endpoint).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 00:13:48 +08:00
tigerenwork 9f8fb52726 feat: switch OAuth redirect to localhost loopback (RFC 8252)
Feishu rejected the 'bugger://' custom URL scheme redirect with 参数不合法.
Switched to the standard desktop OAuth flow: a one-shot local HTTP server
on 127.0.0.1:18923 that receives the authorization code from the browser.

Changes:
- Added LocalOAuthServer (Network.framework, one-shot HTTP listener)
- FeishuAuthService now uses dynamic http://127.0.0.1:PORT/callback redirect
- Removed CFBundleURLTypes from Info.plist (no custom scheme needed)
- Removed NSAppleEventManager handler from AppDelegate
- TokenManager.handleCallback changed from URL to code string
- OAuthSetupView starts server before opening browser
- Added offline_access scope for refresh token support
- generate_xcode_project.py: removed dev cert requirements,
  auto-quotes paths with special chars

Builds successfully with xcodebuild.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-28 23:48:49 +08:00
tigerenwork d9d8ce3e46 feat: full macOS menu bar app implementation
SwiftUI macOS menu bar app for tracking Feishu Bitable bugs:
- Menu bar icon with active bug count badge
- Popover with bug list sorted by priority, click to open in Feishu
- Floating always-on-top widget (NSPanel, optional)
- OAuth 2.0 flow with Feishu Open API (user_access_token + refresh)
- PollerService with configurable interval (default 5 min)
- Diff engine detecting new bugs, status/priority/assignee changes
- Native macOS notifications (UserNotifications framework)
- Keychain-backed token storage
- Settings window with field mapping, polling interval, widget toggle
- Support for custom Bitable column name mappings
- Zero third-party dependencies

17 source files across Models/Services/Views/ViewModels/Utils layers.
Xcode project generated via scripts/generate_xcode_project.py.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-28 20:56:44 +08:00