Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
11fd150
Design local subscription limit probe
cbusillo May 5, 2026
c36ea9e
Add OpenAI limit probe prototype
cbusillo May 5, 2026
5dc9c1c
Make limit probe keyboard accessible
cbusillo May 5, 2026
f5f52b8
Capture sanitized network probe shapes
cbusillo May 5, 2026
e921749
Redact identifiers from network probe fields
cbusillo May 5, 2026
ed09def
Include network candidates in probe reports
cbusillo May 5, 2026
56b2f4b
Clarify Every Code rate limit source
cbusillo May 5, 2026
ff4beda
Focus Codex connector on Every Code cache
cbusillo May 5, 2026
f19f303
Prioritize live Codex rate limit API
cbusillo May 6, 2026
696dab2
Add direct Codex rate limit probe
cbusillo May 6, 2026
577773b
Model OpenAI usage as percent pressure
cbusillo May 6, 2026
d1580bd
Add Gemini and Claude provider probes
cbusillo May 6, 2026
d25f22f
Document Gemini probe credential inputs
cbusillo May 6, 2026
fa349eb
Extract provider connector runtime
cbusillo May 6, 2026
ecc6ab5
Add local snapshot store
cbusillo May 6, 2026
b15860e
Wire preview app to snapshot cache
cbusillo May 6, 2026
b95d524
Use cached OpenAI limits for fast mode forecast
cbusillo May 6, 2026
cc40192
Add local account configuration store
cbusillo May 6, 2026
0e81b79
Add WidgetKit snapshot glance
cbusillo May 6, 2026
3e216f5
Refine widget rate-limit decision UX
cbusillo May 6, 2026
b6722a9
Load local Gemini client metadata
cbusillo May 6, 2026
6743338
Clarify Claude allowance status
cbusillo May 6, 2026
12243da
Add Claude subscription statusline cache
cbusillo May 6, 2026
0e0b32d
Mark stale Claude subscription snapshots
cbusillo May 6, 2026
5a4d139
Add Every Code Claude usage estimate
cbusillo May 6, 2026
90ac709
Add Claude web usage parser
cbusillo May 6, 2026
854c2eb
Add Claude web usage probe
cbusillo May 6, 2026
c04f7e2
Merge provider snapshots by account
cbusillo May 6, 2026
48cb431
Add in-app Claude web capture
cbusillo May 6, 2026
5dda537
Add signed app packaging script
cbusillo May 7, 2026
1c39c95
Ignore packaged app artifacts
cbusillo May 7, 2026
40e8ec0
Add native app widget packaging
cbusillo May 7, 2026
9a668f7
Merge pull request #14 from cbusillo/feature/openai-limit-probe
cbusillo May 7, 2026
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
1 change: 1 addition & 0 deletions .github/github-repo-workflow.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"productGoals": "docs/product-goals.md",
"architecture": "docs/architecture.md",
"designDirection": "docs/design-direction.md",
"localLimitProbe": "docs/local-limit-probe.md",
"providerUsageAccess": "docs/provider-usage-access.md",
"repoSettings": "docs/repo-settings.md"
},
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ DerivedData/
*.xcworkspace/xcuserdata/
.swiftpm/
.idea/
dist/
12 changes: 12 additions & 0 deletions Config/ContextPanel.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<false/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.shinycomputers.contextpanel</string>
</array>
</dict>
</plist>
27 changes: 27 additions & 0 deletions Config/ContextPanelWidget-Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widgetkit-extension</string>
</dict>
</dict>
</plist>
12 changes: 12 additions & 0 deletions Config/ContextPanelWidget.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.shinycomputers.contextpanel</string>
</array>
</dict>
</plist>
651 changes: 651 additions & 0 deletions ContextPanel.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

105 changes: 105 additions & 0 deletions ContextPanel.xcodeproj/xcshareddata/xcschemes/ContextPanel.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FF80548BA14604BB9B1E9115"
BuildableName = "ContextPanel.app"
BlueprintName = "ContextPanel"
ReferencedContainer = "container:ContextPanel.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "92EF616FEBA46FCB02E887BE"
BuildableName = "ContextPanelWidgetExtension.appex"
BlueprintName = "ContextPanelWidgetExtension"
ReferencedContainer = "container:ContextPanel.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FF80548BA14604BB9B1E9115"
BuildableName = "ContextPanel.app"
BlueprintName = "ContextPanel"
ReferencedContainer = "container:ContextPanel.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FF80548BA14604BB9B1E9115"
BuildableName = "ContextPanel.app"
BlueprintName = "ContextPanel"
ReferencedContainer = "container:ContextPanel.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FF80548BA14604BB9B1E9115"
BuildableName = "ContextPanel.app"
BlueprintName = "ContextPanel"
ReferencedContainer = "container:ContextPanel.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
56 changes: 56 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,34 @@ let package = Package(
.executable(
name: "ContextPanelPreview",
targets: ["ContextPanelPreview"]
),
.executable(
name: "OpenAILimitProbe",
targets: ["OpenAILimitProbe"]
),
.executable(
name: "CodexRateLimitProbe",
targets: ["CodexRateLimitProbe"]
),
.executable(
name: "GeminiQuotaProbe",
targets: ["GeminiQuotaProbe"]
),
.executable(
name: "ClaudeLimitProbe",
targets: ["ClaudeLimitProbe"]
),
.executable(
name: "ClaudeWebUsageProbe",
targets: ["ClaudeWebUsageProbe"]
),
.executable(
name: "SnapshotStoreProbe",
targets: ["SnapshotStoreProbe"]
),
.executable(
name: "ContextPanelWidget",
targets: ["ContextPanelWidget"]
)
],
targets: [
Expand All @@ -23,6 +51,34 @@ let package = Package(
name: "ContextPanelPreview",
dependencies: ["ContextPanelCore"]
),
.executableTarget(
name: "OpenAILimitProbe",
dependencies: ["ContextPanelCore"]
),
.executableTarget(
name: "CodexRateLimitProbe",
dependencies: ["ContextPanelCore"]
),
.executableTarget(
name: "GeminiQuotaProbe",
dependencies: ["ContextPanelCore"]
),
.executableTarget(
name: "ClaudeLimitProbe",
dependencies: ["ContextPanelCore"]
),
.executableTarget(
name: "ClaudeWebUsageProbe",
dependencies: ["ContextPanelCore"]
),
.executableTarget(
name: "SnapshotStoreProbe",
dependencies: ["ContextPanelCore"]
),
.executableTarget(
name: "ContextPanelWidget",
dependencies: ["ContextPanelCore"]
),
.testTarget(
name: "ContextPanelCoreTests",
dependencies: ["ContextPanelCore"]
Expand Down
78 changes: 78 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,82 @@ Useful entry points:

- [Product Goals](docs/product-goals.md)
- [Architecture](docs/architecture.md)
- [macOS Release Path](docs/release.md)
- [Repository Settings](docs/repo-settings.md)

## Local App Bundle

To build the native macOS app with the embedded WidgetKit extension:

```sh
xcodegen generate --spec project.yml
xcodebuild \
-project ContextPanel.xcodeproj \
-scheme ContextPanel \
-configuration Debug \
-destination 'platform=macOS' \
-allowProvisioningUpdates \
build
```

To build a quick launchable macOS app bundle from the SwiftPM app shell:

```sh
scripts/package-macos-app.sh --output dist --identity auto
open "dist/Context Panel.app"
```

When a Developer ID Application identity is available in Keychain, the script
uses it through `codesign`; otherwise it falls back to ad-hoc signing. This is
the interim friend-installable path for the app shell only; use the Xcode build
when testing the widget extension.

## Local Provider Probes

The package includes development probes for validating provider limit signals
without printing secrets or raw provider responses:

```sh
swift run CodexRateLimitProbe --auth ~/.codex/auth.json
GEMINI_OAUTH_CLIENT_ID=... GEMINI_OAUTH_CLIENT_SECRET=... \
swift run GeminiQuotaProbe --auth ~/.gemini/oauth_creds.json
swift run ClaudeLimitProbe
swift run SnapshotStoreProbe --codex-auth ~/.codex/auth.json --include-claude
```

The Codex and Gemini probes can return live percent-window quota buckets for
their respective CLI-backed accounts. The Claude probe intentionally reports
local auth/subscription metadata and local stats-cache freshness until a Claude
Code status-line cache has been populated.

To capture Claude subscription usage percentages, configure Claude Code's
status line to call the helper in this repo. Claude Code sends the helper a JSON
payload after session responses; the helper stores only five-hour and weekly
used percentages plus reset timestamps under Context Panel's Application
Support directory.

```json
{
"statusLine": {
"type": "command",
"command": "/absolute/path/to/context-panel/scripts/claude-statusline-cache.sh"
}
}
```

The helper does not store auth tokens, prompts, transcript contents, emails,
organization IDs, or raw Claude session JSON. Claude Code's non-interactive
`claude -p` path does not appear to run the status-line hook, so Context Panel
marks old Claude subscription readings stale instead of treating them as live.
For Every Code-driven Claude usage, Context Panel also reads `ccusage` aggregate
block output when available and shows a clearly marked estimated 5-hour token
window; that estimate is useful for "am I likely to run out soon?" but is not
Anthropic's official subscription percentage.

For Gemini, use the OAuth client values from the locally installed Gemini CLI;
they are intentionally not checked into this repository.

The probes call the same `ContextPanelCore` connectors the app will use, so
passing probe output is also a smoke test for the production connector runtime.
`SnapshotStoreProbe` additionally writes and reloads the local JSON cache shape
that the app and widget will consume.
Loading