feat(macos): menu bar companion + widget, with CodexBar-style usage monitor integrated into Usage - #5196
Conversation
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configurationConfiguration used: Repository: lidge-jun/opencodex/.coderabbit.yaml Review profile: ASSERTIVE Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
⏳ DRAFT
What to do
Review readiness checklist
0/4 boxes ticked. Automatic draft conversion failed. Please convert this pull request to a draft manually until every box above is ticked. |
리뷰 · 우선순위 64 / 80이 PR은 맥 메뉴막대 앱을 브랜치에 실제로 있는 건 메뉴막대 앱(상태 아이콘, 팝오버, 프록시 끄기, 제공자 스위치), 그걸 zip으로 묶는 스크립트와 릴리스 잡, 가이드 다섯 언어, 그리고 제목이 말하는 위젯이랑 Usage 화면에 설정을 넣는 일은 이 diff에 없어요. CI도 빨개요. src/server/management/usage-timeline-routes.ts - 15초 캐시가 첫 요청의 취소 신호를 다음 요청이랑 같이 써요. 먼저 연 쪽이 연결을 끊으면 나중 요청도 같이 실패해요. 캐시에서 꺼낸 응답은 src/usage/timeline.ts - 캐시 키는 시각을 통 크기로 자르는데, 그래프 시작은 요청이 도착한 시각 그대로예요. 통 크기가 시간을 딱 나누지 않으면 같은 키인데 시작이 한 칸 다른 그래프가 나갈 수 있어요. 줄이 24개를 넘기면 나머지를 src/cli/dispatch.ts companion - 명령은 설정을 읽기만 해요. 메인테이너의 판단이 필요한 지점 릴리스 워크플로, 너의 추천 초안인 채로 두세요. 위젯이랑 대시보드가 들어오기 전에, 타임라인 캐시의 취소 신호와 주소 허용, 이 댓글은 grok-bot이 작성했습니다 |
|
추가 리뷰 · 우선순위 60 / 80지난 리뷰 뒤에 커밋 9개가 붙었어요. 그때 없던 Usage 화면 설정이 들어왔고, 위젯 확장( 타임라인에서 집었던 곳도 고쳐졌어요. 캐시는 먼저 연 연결의 취소 신호를 다음 요청에 넘기지 않아요. 캐시에서 꺼낸 응답은 이번 요청이랑 설정을 같이 넘겨요. 그래프 시작은 시간 칸에 맞춘 시각을 써요. 줄이 24개를 넘어서
app/Package.swift - 위젯이 시작되는 문을 app/Sources/OpenCodexWidget/Views.swift - 스냅샷에 있는 gui/src/pages/usage-companion-panel.tsx - 서버가 메인테이너의 판단이 필요한 지점 릴리스 잡, 너의 추천 위젯이 목록에 뜨는지 먼저 확인하세요. 안 뜨면 이 댓글은 grok-bot이 작성했습니다 |
|
Re #5744903890 — addressed in
|
추가 리뷰 · 우선순위 56 / 80지난 리뷰 뒤에 커밋이 두 개예요. 파일이 깨져 있으면 경고가 뜨고 스위치는 잠겨요. 스위치를 건드려도 저장하지 않아요. "기본값으로 교체"를 눌러야 서버가 기본값으로 파일을 다시 써요. 위젯 문장은 이제 메뉴막대 숫자를 따른다고 하지 않아요. 오늘 요청, 토큰, 비용, 그리고 여기서 고른 그래프를 보여 준다고 해요. 작은 위젯도 오늘 요청 수를 그려요.
app/Package.swift - 위젯이 켜지는 문은 그대로 app/Sources/MenuBarCore/PollingCoordinator.swift - 메뉴막대 앱은 메인테이너의 판단이 필요한 지점 릴리스 잡과 관리 API가 바뀐 상태라 너의 추천 Usage 화면의 깨진 파일 처리는 이대로 두세요. 위젯이 목록에 뜨는지 먼저 확인하세요. 안 뜨면 이 댓글은 grok-bot이 작성했습니다 |
추가 리뷰 · 우선순위 53 / 80지난 리뷰 팁 초안이고 라벨은 app/Sources/MenuBarUI/Views.swift - 오늘 칸 순서를 TOKENS → REQUESTS → COST로 바꿨는데, docs-site/src/content/docs/guides/macos-menu-bar.md - 영어만 “기본은 토큰”, “macOS 26 Liquid Glass” 문단이 들어갔어요. app/Package.swift / app/Sources/OpenCodexWidget/main.swift / app/Widget-Info.plist - 위젯 시작 경로는 그대로예요. app/Sources/MenuBarUI/PopoverPanel.swift - 메인테이너의 판단이 필요한 지점 토큰을 정수로만 줄이는 건 의도예요. 위젯 시작 문을 고치지 않은 채로 기능만 늘렸어요. Liquid Glass·토큰 기본값·설치 카드는 있어도, 위젯이 실제로 안 뜨면 제목의 “widget” 약속은 아직 못 지킨 거예요. 초안을 유지한 채 위젯 엔트리를 먼저 고칠지, 아니면 위젯을 다음 PR로 빼서 메뉴막대+Usage만 합칠지 정해야 해요. 베이스 너의 추천
이 댓글은 grok-bot이 작성했습니다 |
9a986a6 to
bd1fba1
Compare
|
Addressed in bd1fba1 (rebased onto current
|
추가 리뷰 · 우선순위 46 / 80지난 리뷰 팁 뒤에 커밋이 하나예요. 초안이고 라벨은 app/Sources/MenuBarUI/Views.swift - 지난번에 틀렸던 접근성 라벨은 맞춰졌어요. 토큰 칸은 “N tokens”, 요청 칸만 추정( docs-site/src/content/docs/{ja,ko,ru,zh-cn}/guides/macos-menu-bar.md - 영어와 같은 문단이 들어갔어요. 구조·해시 검사에 다시 걸릴 여지는 줄었어요. app/Sources/MenuBarUI/PopoverPanel.swift - app/Sources/OpenCodexWidget/main.swift / app/Package.swift - 시작 문은 그대로 app/Sources/MenuBarCore/PollingCoordinator.swift - 메뉴막대 쪽은 설정 파일이 깨져도 메인테이너의 판단이 필요한 지점 릴리스 잡·관리 API·앱 묶음이 바뀐 자리라 위젯 시작을 SPM + 메뉴막대에도 깨진 설정 경고를 넣을지는 선택이에요. 지금처럼 Usage만 보여 줘도, 앱이 파일을 고치지 않아서 위험은 작아요. 너의 추천
이 댓글은 grok-bot이 작성했습니다 |
bd1fba1 to
c55e211
Compare
Nine numbered docs covering the roadmap for a maintainer-owned macOS menu bar app in app/, consolidating the two competing community PRs (#387 Swift/SwiftUI, #421 Tauri/React). - 000_plan: constraints, dependency-ordered phase map, accept criteria - 001_pr_survey: head-to-head of both PRs; stack decision is Swift/AppKit runtime with HTTP management-API transport, plus the salvage list - 002_api_surface: live payload inventory, the seconds-vs-milliseconds quota timestamp trap, and the default-provider 400 trap - 003_design_read: Design Read and dial lock (V2/M1/D7), inheriting the existing gui/src/styles.css tokens - 010-050: diff-level decade docs, one per implementation phase
Adversarial Phase-0 review returned FAIL. Corrections, all verified against live source and the running proxy: - /api/stop calls stopServiceIfInstalled() before responding, so nothing restarts the proxy. The app now ships Stop proxy, never Restart, and never spawns a process. - /api/usage supports only 7d/30d/all; 24h silently degrades to 30d. The range is now a closed enum and the UI labels the range the response returned, not the one it requested. - defaultProvider lives on /api/config, not /api/settings. Added the model, the client method, and the test. - The bundle script now defines every path before use and copies Info.plist before plutil; it could not have run as previously written. - release.yml grants contents:write and id-token:write at workflow level, so the new jobs declare explicit least-privilege permissions. Added a separate attach-macos job so packaging can never block the npm publish, and pinned both new actions to full SHAs. - Re-surveyed PR #421 at head 049ef2a: the committed src-tauri/target tree was already removed by the contributor. The closing comment must credit that fix rather than repeat a stale defect. - /api/logs exists for per-request activity; documented as a deliberate v1 exclusion instead of an implicit gap. - Phase 1 no longer claims verification via a Phase 4 script. - Added StartupHealth service fields, security-review acceptance evidence to Phase 4, and removed developer-absolute paths from tracked docs.
…hip, plist) Round-2 adversarial review returned FAIL on 9 findings, most of them caused by round-1 edits that corrected prose without correcting the specs those documents actually lock. - 003 was never touched in round 1, so the design lock still mandated a 24h sparkline and a Restart button that 002/030 prove impossible. Wireframe now shows LAST 7 DAYS and Stop proxy. - release.yml's input is named dry-run, not dry_run. inputs.dry_run would resolve to null and the attach-macos guard would silently pass during a dry run — the exact failure that guard exists to prevent. - Bundle ownership was relocated, not resolved: Phase 2 claimed a launchable .app while the builder stayed in Phase 4. Phases 1-3 now verify through swift test/build/run; Phase 4 owns the bundle end to end. - app/Info.plist was missing CFBundleExecutable, CFBundlePackageType, and CFBundleIconFile, so the specified bundle would not have launched. - Re-read #421 at head 049ef2a: menubar/src/api.ts:12-13 returns the token into renderer memory, so the PR's isolation claim does not hold. Removed that credit from both the survey and the planned closing comment, and removed the remaining stale rejection sentences. - Scoped the absolute-path criterion to files this unit touches; unrelated historical devlogs already contain such paths. - loading is now explicitly exempt from the next-action rule, and per-section empty states are defined with their own copy. - The range-fallback test injects a stubbed response, since the closed UsageRange enum makes the curl path unreachable from production code.
Round-3 audit returned GO-WITH-FIXES (blockers=0). Cleared all three: - 010 still called the first bundle a Phase-2 deliverable, contradicting 000/020/040. Phase 4 owns the bundle end to end. - 001's salvage list still credited #421 with renderer-side token isolation, contradicting its own verified analysis, and claimed all four surfaces were adopted when per-request activity was deliberately excluded. - 040 and 050 still scoped the absolute-path rule to every tracked file, which pre-existing historical devlogs already violate. Both now match 000's unit-scoped wording.
Phase 1 of the macOS companion (010_phase1_core.md). Zero third-party dependencies; AppKit and Foundation only. - Discovery resolves the proxy from OPENCODEX_HOME/runtime-port.json with a 10100 fallback. The host is pinned to loopback and never read from the record, so a file write cannot redirect the app at another host. - ProxyModels mirror the live payloads. QuotaReport.normalized() absorbs two real traps: the window key differs per provider (weekly/monthly/custom), and weeklyResetAt arrives in seconds from openai but milliseconds from anthropic within the same array, so timestamps are disambiguated by magnitude. - UsageRange is a closed enum because the server silently degrades an unrecognized range to 30d; UsageReport.rangeLabel is derived from the response so the UI can never label 30 days of data as something else. - ProxyClient is an actor. ProxyError carries human sentences only, never a response body, since bodies can echo configuration. - Format renders an em dash for unknown and a real zero for zero; the live proxy reports 3.6e10 tokens, so everything is abbreviated. Testing is an executable target rather than a .testTarget: Command Line Tools resolves neither XCTest (module not found) nor the swift-testing runtime (Testing.framework fails to dlopen). Requiring full Xcode to run these tests would exclude most contributors. 31 cases pass via `swift run --package-path app MenuBarCoreTests`. Verified live against the running proxy: endpoint discovery, health (at-risk, service-managed), defaultProvider=openai from /api/config, 7-day usage (44.5K requests, 7.34B tokens, $6.15K), and four provider quotas with correctly resolved reset windows.
Adversarial review of c7fbf57 returned FAIL on 10 findings. All verified against the live proxy or Apple docs before folding. - Info.plist: add NSAllowsLocalNetworking. macOS 14 stopped allowing IP loads under ATS, so the packaged bundle could not reach 127.0.0.1 at all while swift run stayed green — the app's primary function, broken only in the artifact users would actually download. - ProxyClient: wire the lazy Keychain retry that the plan required and the code never implemented. A CredentialStore protocol is injected, the key loads once, and exactly one retry follows a 401 so a stale key cannot spin. - Keychain: set kSecUseDataProtectionKeychain on every query, without which kSecAttrAccessible is ignored on macOS; tighten to ThisDeviceOnly; update before add so a failed add cannot destroy a working key. - ProxyModels: live kimi reports fiveHourPercent alongside weeklyPercent, and cursor and google-antigravity each carry two customWindows. Added the five-hour fields and normalizedWindows() returning every window; normalized() keeps an explicit longest-horizon precedence. - isEmptyOrUnknown preserves three states so an omitted request count cannot render as "No requests". - Cancellation now propagates instead of reading as a stopped proxy, and unrelated transport failures get their own .transport case. - ProxyEndpoint is failable; baseURL is built once instead of force-unwrapped. - Format promotes at rollover: 999_999 renders 1.00M, not 1000K. - TransportSuite adds 14 cases over status mapping, the 401 retry path, cancellation, request shape, and body redaction. 51 pass, 0 fail. - Harness no longer counts a case as passed when it recorded a failure. Live re-verification across all six providers: Kimi 5h+week, Cursor's three windows, and correct primary-window selection for each.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
c55e211 to
90ae1d0
Compare
The branch opened this unit under devlog/_plan/ before dev published the same unit as closed at devlog/_fin/260725_macos_menubar_app/ in 8ae52e4. The rebase replayed the _plan addition on top of that publication, so the tree carried both copies: nine files, 2,605 lines, six byte-identical to their _fin counterparts. The three that differ are worse than redundant. 003_design_read.md, 010_phase1_core.md and 020_phase2_ui.md keep the decimal token text (12.4M, 36.5B) that this same pull request corrects to integers in the _fin copies, so the duplicate contradicted the corrected record two directories over. AGENTS.md defines _plan as units still open and _fin as closed work, and nothing in CI reads devlog/ - the file-size scanner excludes it - so no gate would have caught this. The _fin copies, including the 051_feature_summary.md this PR adds, remain the record.
…artifact pin Two defects in the release jobs this pull request adds, both found in review of the workflow surface. package-macos had no needs:, so a dispatch that validate-dispatch would reject still spun up a macOS runner and packaged an asset. Every other job in the file gates on that validation; this one now does too. The blast radius was bounded - contents: read, no secrets, and the script's own version guard - but running at all on a rejected dispatch is not the design. The upload step pinned actions/upload-artifact at v5.0.0 while ci.yml already pins v7.0.1, leaving the repository with two pins for one action and pairing a v5 upload against the v8 download in attach-macos. Both now use the SHA ci.yml already trusts, which is also the pairing actions/download-artifact v8 expects.
추가 리뷰 · 우선순위 40 / 80지난 리뷰 팁( .github/workflows/release.yml - .github/workflows/release.yml - devlog/_plan/260725_macos_menubar_app/ - 디렉터리가 통째로 사라졌어요. 기록은 메인테이너의 판단이 필요한 지점 초안을 Ready로 올릴 타이밍이에요. 위젯을 SPM + 메뉴막대에 깨진 설정( 너의 추천
이 댓글은 grok-bot이 작성했습니다 |
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> # Conflicts: # scripts/test-layout/layout.json # tests/fixtures/test-layout-expected.json
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Both sides register new test files in the layout maps; the resolution is the union of the two entry sets, not a choice between them.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9eb033e19b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| aggregation: settings.aggregation, | ||
| grouping: settings.chartGrouping, | ||
| }); | ||
| if (settings.models?.length) query.set("models", settings.models.join(",")); |
There was a problem hiding this comment.
Preserve an explicit empty model selection
When the user turns off the final model, toggleCompanionModels persists models: [], but this condition omits the query parameter and the server interprets an omitted model filter as “all models.” The chart therefore repopulates with every series even though the UI reports zero selected; the Swift client repeats the same omission in ProxyClient.timeline. Preserve an explicit no-model state across the client/API contract instead of dropping the empty array.
AGENTS.md reference: gui/AGENTS.md:L9-L10
Useful? React with 👍 / 👎.
| case .requests: rendered = Format.count(summary?.requests) | ||
| case .tokens: rendered = Format.tokens(summary?.totalTokens) | ||
| case .cost: rendered = Format.cost(summary?.estimatedCostUsd) | ||
| case .quota: rendered = Format.percent(quotas.compactMap(\.percent).min()) |
There was a problem hiding this comment.
Show the highest quota pressure instead of the lowest
When multiple providers report quotas, selecting .min() makes the headline show the least-used provider—for example, 10% while another provider is at 99% and about to block requests. QuotaReport.normalized() explicitly defines larger percentages as greater pressure, so this reduction and the equivalent one used for {quotaPercent} should use the maximum; the medium widget has the same inverted selection in OpenCodexWidget/Views.swift.
Useful? React with 👍 / 👎.
| if (popoverOpen || snapshot.settings.menuBarMetric == .quota), let quotas = try? await client.quotas() { | ||
| guard isCurrent(cycle) else { return false } | ||
| snapshot.quotas = quotas |
There was a problem hiding this comment.
Retain quota refreshes while the popover is closed
When menuBarMetric is quota and the popover is closed—the normal startup and steady-state case—the preceding condition fetches quotas, but this guard calls isCurrent, which additionally requires popoverOpen. The successful result is discarded before updating snapshot.quotas, leaving the quota headline unavailable until the panel is opened and stale after it closes; this guard should only verify that the refresh generation is current.
Useful? React with 👍 / 👎.
| private func openCompanionSettings() { | ||
| guard let url = URL(string: "\(endpoint.baseURL.absoluteString)/#/usage#usage-section-companion") else { return } | ||
| NSWorkspace.shared.open(url) |
There was a problem hiding this comment.
Use a route-compatible companion settings deep link
Selecting “Companion settings…” constructs a fragment containing a second #, so the dashboard router receives usage#usage-section-companion rather than the supported usage route. resolveAppHashChange treats that value as unknown and normalizes it to the dashboard, meaning this menu action does not open either Usage or the companion section; add a supported sub-route/scroll mechanism instead of appending an HTML fragment inside the hash route.
Useful? React with 👍 / 👎.
Consolidates the desktop stack that was carried as a GitHub native stack on top of the macOS menu bar companion. #5196 landed as 38a5ab9 by squash, which detached every child in the chain from its base, so the remaining work is applied here as one branch against the current dev instead of replayed through bases that no longer exist. Carries the standalone binary, the Tauri v2 cross-platform tray and webview shell, the GUI desktop shell integration, the WidgetKit appex bundle, and the signed desktop packaging for DMG, MSI, AppImage and deb. Co-authored-by: codex <codex@users.noreply.github.com> Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Summary
Supersedes #5160 (thanks @JayYun98). The CodexBar-style monitor from that PR is folded onto the maintainer companion in
app/, so there is one macOS companion: menu bar item + popover + a WidgetKit widget, all fed by the proxy's own usage accounting instead of a second loopback server.Proxy (
src/companion/,src/usage/timeline.ts, management routes)GET/PUT /api/companion/settings— persisted at<opencodex home>/companion.json(atomic,0600, unknown keys rejected, corrupt file → defaults +corrupt: true, never overwritten on GET). PartialPUTor{ reset: true }.GET /api/usage/timeline?hours=6|24|72|168&bucketMinutes=…&metric=total|input|output|cached&aggregation=sum|average|max&grouping=model|modelAccount&models=…— buckets built fromusageAttributions()(same per-attempt accounting as the Usage page, no double counting), ≤2000 buckets, top 23 series +otherfold (avg/max fold from request-level maps, not from already-aggregated rows), 15s cache keyed on a bucket-alignednow, in-flight promise shared without inheriting the first caller's abort signal,truncatedmetadata from the bounded ledger read.ocx companion show|set <k>=<v>…|reset.Dashboard — no second Usage tab. The existing Usage workspace gets a
Menu bar & widgetsection: live timeline preview (line / stacked), window / metric / grouping / aggregation, menu-bar metric, popover section switches, advanced template + model/provider filters, autosave with saved/error/retry, reset to defaults. All ten locales.App (
app/Sources/MenuBarCore,MenuBarUI) — menu-bar title driven bymenuBarMetric/menuBarTemplate({requests} {totalTokens} {inputTokens} {outputTokens} {costUsd} {quotaPercent}), TODAY / chart / MODELS / ACCOUNTS / providers sections toggled by settings, hidden providers honoured, native AppKit timeline chart with a fit-to-width legend, privacy-safeWidgetSnapshotwritten atomically +WidgetCenter.reloadAllTimelines().Widget (
app/Sources/OpenCodexWidget, packaged asOpenCodex.app/Contents/PlugIns/OpenCodexWidget.appex) — small / medium / large, states: running, degraded, unreachable, unauthorized, loading, missing, corrupt, stale (>10 min), deep link to#/usage, 5-minute timeline.~/Library/Containers/com.opencodex.menubar.widget/Data/Library/Application Support/OpenCodex/snapshot.json) rather than an App Group: with ad-hoc / non-team signing the App Group container is refused by TCC, the bundle container is not. Switching to an App Group is a two-line change once the release is team-signed.-e _NSExtensionMain(same as Apple's own widget appexes). Without it chronod spawns the process and it exits 0 before theWidgetBundleconnects, so the widget never appears in the gallery — this is what made the earlier packaging "register but not render".Follow-up round (high-volume UX)
2401634303 → 2B,333400000 → 333M— in the menu bar title, popover, widget and dashboard (Format.tokens(integer:),formatCompanionTokens). Popover TODAY and widget rows are ordered tokens · requests · cost.Switchper model and per provider (mixed state when partial), last-known total per row so rows don't reorder under the cursor,n of N on chart+Show all(→models: null). Pure helpersgroupCompanionModels/toggleCompanionModelsare unit-tested.User-Agent: OpenCodexMenuBar/<version>, the settings route recordscompanion.lastSeenAt, and the dashboard shows eitherMenu bar app connected · just now(steps collapsed under "Install on another Mac") or an open 3-step install card (release ZIP → Applications, right-click → Open for Gatekeeper,xattr -d com.apple.quarantine). Settings are re-fetched every 60s (skipped while an edit is pending) so the state stays live.NSGlassEffectViewunder#available(macOS 26, *),NSVisualEffectView(.popover)otherwise; widget honourswidgetRenderingMode == .accented(widgetAccentable()headline/chart, monochrome opacity steps instead of provider colours).scripts/build-macos-app.shbuilds both products, stages/versions/signs the appex before the host app;tests/gui/macos-build-script.test.tsasserts the packaging contract.Screenshots
Show allappearsNot captured: a quota bar — the seeded demo proxy has no provider that reports limits (
/api/provider-quotasreturns coverage only), so the widget shows its chart fallback; widget stale/missing states need a 5-minute WidgetKit refresh cycle and were verified via the reader's unit path instead.Merge-readiness round
devtip (1b57a57218); all 59 commits replayed with no conflicts. The earlier redtest 3/4was a base defect — the base predated the follow-up test fix in dev — and the rebase resolves it.devlog/_plan/260725_macos_menubar_app/(9 files, 2,605 lines).devpublished the same unit as closed under_fin/in8ae52e4291, and three of the duplicates still carried the12.4M/36.5Btext this PR corrects in_fin.package-macosnow gates onvalidate-dispatch, and itsupload-artifactpin moves to the v7.0.1 SHAci.ymlalready uses — one pin per action, and the pairingdownload-artifactv8 expects.Verification
cd app && swift build -c release && swift build -c release --product OpenCodexWidget && swift run MenuBarCoreTests(118 pass)&& swift run MenuBarUITests(10 pass)bun test gui/tests/usage-companion-utils.test.ts(7 pass),tests/server/companion-settings.test.ts(4 pass)bun test tests/gui/macos-build-script.test.ts(9 pass),tests/ci-workflows(144 pass), README parity (53 pass)bun run typecheck,bun run structure:check,bun run lint:i18n,bun run build:gui,git diff --checkOUT=.tmp/app-out bash scripts/build-macos-app.sh→pluginkit -m -v -i com.opencodex.menubar.widgetregisters, widget renders in the gallery and Notification Center (screenshots above), appex entitlements =com.apple.security.app-sandboxonly.NEW_OVERSIZED src/codex/history-provider.ts/GREW tests/server/server-combo-failover-e2e.test.tsare reported identically onorigin/dev; neither file is touched here.devtip and green across the full matrix (typecheck, gates, structure, privacy, four test shards, macOS/Linux/Windows, docker smoke, npm-global smokes, macOS app build).Checklist
structure/, devlog_fin/260725_macos_menubar_app/051).0600; companion routes sit behind the existing management auth).Co-authored-by: Jyun1998 yjunwoo14@gmail.com
Co-authored-by: Jay Yun 61632100+JayYun98@users.noreply.github.com
Review readiness checklist
This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:
All CI tests are green on my local testing.
I pushed my PR to the latest dev commit.
I resolved all correct Codex and CodeRabbit findings.
My PR is ready for review.
Link to Devin session: https://app.devin.ai/sessions/f2dce68b7ca349e3ba39dab55bc12300
Open in Devin Desktop: https://app.devin.ai/desktop/session/f2dce68b7ca349e3ba39dab55bc12300?variant=devin
Requested by: @lidge-jun