pi-ui-extend 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +307 -0
- package/bin/pix.mjs +219 -0
- package/dist/app/app.d.ts +96 -0
- package/dist/app/app.js +871 -0
- package/dist/app/blink-controller.d.ts +23 -0
- package/dist/app/blink-controller.js +82 -0
- package/dist/app/cli.d.ts +8 -0
- package/dist/app/cli.js +83 -0
- package/dist/app/clipboard.d.ts +1 -0
- package/dist/app/clipboard.js +24 -0
- package/dist/app/command-controller.d.ts +18 -0
- package/dist/app/command-controller.js +58 -0
- package/dist/app/command-host.d.ts +44 -0
- package/dist/app/command-host.js +1 -0
- package/dist/app/command-model-actions.d.ts +12 -0
- package/dist/app/command-model-actions.js +176 -0
- package/dist/app/command-navigation-actions.d.ts +19 -0
- package/dist/app/command-navigation-actions.js +267 -0
- package/dist/app/command-registry.d.ts +32 -0
- package/dist/app/command-registry.js +225 -0
- package/dist/app/command-runtime.d.ts +5 -0
- package/dist/app/command-runtime.js +32 -0
- package/dist/app/command-session-actions.d.ts +20 -0
- package/dist/app/command-session-actions.js +295 -0
- package/dist/app/constants.d.ts +52 -0
- package/dist/app/constants.js +103 -0
- package/dist/app/conversation-entry-renderer.d.ts +21 -0
- package/dist/app/conversation-entry-renderer.js +81 -0
- package/dist/app/conversation-shell-renderer.d.ts +5 -0
- package/dist/app/conversation-shell-renderer.js +43 -0
- package/dist/app/conversation-tool-renderer.d.ts +16 -0
- package/dist/app/conversation-tool-renderer.js +216 -0
- package/dist/app/conversation-viewport.d.ts +55 -0
- package/dist/app/conversation-viewport.js +252 -0
- package/dist/app/dcp-stats.d.ts +2 -0
- package/dist/app/dcp-stats.js +116 -0
- package/dist/app/editor-layout-renderer.d.ts +31 -0
- package/dist/app/editor-layout-renderer.js +211 -0
- package/dist/app/editor-panels.d.ts +4 -0
- package/dist/app/editor-panels.js +130 -0
- package/dist/app/extension-actions-controller.d.ts +22 -0
- package/dist/app/extension-actions-controller.js +60 -0
- package/dist/app/extension-event-bus.d.ts +3 -0
- package/dist/app/extension-event-bus.js +23 -0
- package/dist/app/extension-ui-controller.d.ts +57 -0
- package/dist/app/extension-ui-controller.js +532 -0
- package/dist/app/file-link-opener.d.ts +2 -0
- package/dist/app/file-link-opener.js +66 -0
- package/dist/app/file-links.d.ts +10 -0
- package/dist/app/file-links.js +117 -0
- package/dist/app/guards.d.ts +3 -0
- package/dist/app/guards.js +9 -0
- package/dist/app/icons.d.ts +37 -0
- package/dist/app/icons.js +97 -0
- package/dist/app/id.d.ts +1 -0
- package/dist/app/id.js +4 -0
- package/dist/app/image-click-targets.d.ts +5 -0
- package/dist/app/image-click-targets.js +32 -0
- package/dist/app/image-opener.d.ts +2 -0
- package/dist/app/image-opener.js +64 -0
- package/dist/app/input-action-controller.d.ts +47 -0
- package/dist/app/input-action-controller.js +209 -0
- package/dist/app/input-controller.d.ts +60 -0
- package/dist/app/input-controller.js +425 -0
- package/dist/app/input-paste-handler.d.ts +27 -0
- package/dist/app/input-paste-handler.js +146 -0
- package/dist/app/menu-items-controller.d.ts +32 -0
- package/dist/app/menu-items-controller.js +182 -0
- package/dist/app/message-content.d.ts +8 -0
- package/dist/app/message-content.js +115 -0
- package/dist/app/model-ref.d.ts +13 -0
- package/dist/app/model-ref.js +50 -0
- package/dist/app/model-usage-controller.d.ts +35 -0
- package/dist/app/model-usage-controller.js +99 -0
- package/dist/app/model-usage-status.d.ts +125 -0
- package/dist/app/model-usage-status.js +749 -0
- package/dist/app/mouse-controller.d.ts +182 -0
- package/dist/app/mouse-controller.js +968 -0
- package/dist/app/native-modifiers.d.ts +3 -0
- package/dist/app/native-modifiers.js +60 -0
- package/dist/app/nerd-font-controller.d.ts +11 -0
- package/dist/app/nerd-font-controller.js +90 -0
- package/dist/app/popup-action-controller.d.ts +44 -0
- package/dist/app/popup-action-controller.js +278 -0
- package/dist/app/popup-menu-controller.d.ts +183 -0
- package/dist/app/popup-menu-controller.js +1070 -0
- package/dist/app/prompt-enhancer-controller.d.ts +40 -0
- package/dist/app/prompt-enhancer-controller.js +215 -0
- package/dist/app/queued-message-controller.d.ts +62 -0
- package/dist/app/queued-message-controller.js +377 -0
- package/dist/app/render-controller.d.ts +41 -0
- package/dist/app/render-controller.js +378 -0
- package/dist/app/render-text.d.ts +19 -0
- package/dist/app/render-text.js +67 -0
- package/dist/app/request-history.d.ts +23 -0
- package/dist/app/request-history.js +131 -0
- package/dist/app/runtime.d.ts +39 -0
- package/dist/app/runtime.js +195 -0
- package/dist/app/screen-selection.d.ts +6 -0
- package/dist/app/screen-selection.js +9 -0
- package/dist/app/screen-styler.d.ts +34 -0
- package/dist/app/screen-styler.js +168 -0
- package/dist/app/scroll-controller.d.ts +51 -0
- package/dist/app/scroll-controller.js +207 -0
- package/dist/app/session-event-controller.d.ts +69 -0
- package/dist/app/session-event-controller.js +338 -0
- package/dist/app/session-history.d.ts +23 -0
- package/dist/app/session-history.js +164 -0
- package/dist/app/session-lifecycle-controller.d.ts +55 -0
- package/dist/app/session-lifecycle-controller.js +116 -0
- package/dist/app/session-search.d.ts +24 -0
- package/dist/app/session-search.js +215 -0
- package/dist/app/shell-command.d.ts +27 -0
- package/dist/app/shell-command.js +176 -0
- package/dist/app/shell-controller.d.ts +28 -0
- package/dist/app/shell-controller.js +124 -0
- package/dist/app/slash-commands.d.ts +6 -0
- package/dist/app/slash-commands.js +75 -0
- package/dist/app/startup-checks.d.ts +8 -0
- package/dist/app/startup-checks.js +59 -0
- package/dist/app/startup-info.d.ts +3 -0
- package/dist/app/startup-info.js +176 -0
- package/dist/app/status-controller.d.ts +35 -0
- package/dist/app/status-controller.js +105 -0
- package/dist/app/status-line-renderer.d.ts +68 -0
- package/dist/app/status-line-renderer.js +453 -0
- package/dist/app/subagents-files.d.ts +10 -0
- package/dist/app/subagents-files.js +193 -0
- package/dist/app/subagents-model.d.ts +23 -0
- package/dist/app/subagents-model.js +224 -0
- package/dist/app/subagents-widget-controller.d.ts +43 -0
- package/dist/app/subagents-widget-controller.js +311 -0
- package/dist/app/tab-line-renderer.d.ts +26 -0
- package/dist/app/tab-line-renderer.js +222 -0
- package/dist/app/tabs-controller.d.ts +100 -0
- package/dist/app/tabs-controller.js +885 -0
- package/dist/app/terminal-controller.d.ts +40 -0
- package/dist/app/terminal-controller.js +135 -0
- package/dist/app/terminal-edit-shortcuts.d.ts +23 -0
- package/dist/app/terminal-edit-shortcuts.js +138 -0
- package/dist/app/terminal-output-buffer.d.ts +20 -0
- package/dist/app/terminal-output-buffer.js +52 -0
- package/dist/app/toast-controller.d.ts +13 -0
- package/dist/app/toast-controller.js +40 -0
- package/dist/app/toast-renderer.d.ts +9 -0
- package/dist/app/toast-renderer.js +79 -0
- package/dist/app/todo-model.d.ts +22 -0
- package/dist/app/todo-model.js +179 -0
- package/dist/app/todo-widget-controller.d.ts +21 -0
- package/dist/app/todo-widget-controller.js +59 -0
- package/dist/app/tool-block-renderer.d.ts +26 -0
- package/dist/app/tool-block-renderer.js +439 -0
- package/dist/app/types.d.ts +550 -0
- package/dist/app/types.js +1 -0
- package/dist/app/update.d.ts +36 -0
- package/dist/app/update.js +315 -0
- package/dist/app/voice-controller.d.ts +52 -0
- package/dist/app/voice-controller.js +600 -0
- package/dist/app/workspace-actions-controller.d.ts +40 -0
- package/dist/app/workspace-actions-controller.js +215 -0
- package/dist/app/workspace-undo.d.ts +44 -0
- package/dist/app/workspace-undo.js +215 -0
- package/dist/config.d.ts +62 -0
- package/dist/config.js +527 -0
- package/dist/context-progress-bar.d.ts +17 -0
- package/dist/context-progress-bar.js +48 -0
- package/dist/default-pix-config.d.ts +1 -0
- package/dist/default-pix-config.js +375 -0
- package/dist/fuzzy.d.ts +23 -0
- package/dist/fuzzy.js +165 -0
- package/dist/input-editor-files.d.ts +15 -0
- package/dist/input-editor-files.js +62 -0
- package/dist/input-editor.d.ts +186 -0
- package/dist/input-editor.js +835 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +12 -0
- package/dist/markdown-format.d.ts +22 -0
- package/dist/markdown-format.js +542 -0
- package/dist/sdk.d.ts +3 -0
- package/dist/sdk.js +1 -0
- package/dist/syntax-highlight.d.ts +22 -0
- package/dist/syntax-highlight.js +528 -0
- package/dist/terminal-width.d.ts +6 -0
- package/dist/terminal-width.js +245 -0
- package/dist/theme.d.ts +56 -0
- package/dist/theme.js +118 -0
- package/dist/tool-renderers/apply-patch.d.ts +2 -0
- package/dist/tool-renderers/apply-patch.js +36 -0
- package/dist/tool-renderers/ast.d.ts +2 -0
- package/dist/tool-renderers/ast.js +5 -0
- package/dist/tool-renderers/compress.d.ts +2 -0
- package/dist/tool-renderers/compress.js +126 -0
- package/dist/tool-renderers/index.d.ts +3 -0
- package/dist/tool-renderers/index.js +44 -0
- package/dist/tool-renderers/question.d.ts +2 -0
- package/dist/tool-renderers/question.js +88 -0
- package/dist/tool-renderers/read.d.ts +2 -0
- package/dist/tool-renderers/read.js +36 -0
- package/dist/tool-renderers/repo.d.ts +2 -0
- package/dist/tool-renderers/repo.js +13 -0
- package/dist/tool-renderers/shell.d.ts +3 -0
- package/dist/tool-renderers/shell.js +27 -0
- package/dist/tool-renderers/skill.d.ts +2 -0
- package/dist/tool-renderers/skill.js +132 -0
- package/dist/tool-renderers/subagents.d.ts +2 -0
- package/dist/tool-renderers/subagents.js +51 -0
- package/dist/tool-renderers/todo.d.ts +2 -0
- package/dist/tool-renderers/todo.js +9 -0
- package/dist/tool-renderers/types.d.ts +42 -0
- package/dist/tool-renderers/types.js +1 -0
- package/dist/tool-renderers/utils.d.ts +31 -0
- package/dist/tool-renderers/utils.js +230 -0
- package/dist/tool-renderers/web.d.ts +3 -0
- package/dist/tool-renderers/web.js +9 -0
- package/dist/tool-renderers/write.d.ts +2 -0
- package/dist/tool-renderers/write.js +42 -0
- package/dist/ui.d.ts +56 -0
- package/dist/ui.js +94 -0
- package/docs/release.md +81 -0
- package/extensions/question/contract.ts +100 -0
- package/extensions/question/index.ts +34 -0
- package/extensions/question/render.ts +28 -0
- package/extensions/question/result.ts +86 -0
- package/extensions/question/tool-description.ts +11 -0
- package/extensions/question/tui.ts +629 -0
- package/extensions/question/types.ts +123 -0
- package/extensions/session-title/config.ts +169 -0
- package/extensions/session-title/index.ts +459 -0
- package/extensions/terminal-bell/index.ts +315 -0
- package/external/pi-tools-suite/README.md +242 -0
- package/external/pi-tools-suite/index.ts +1 -0
- package/external/pi-tools-suite/licenses/ollama-pi-web-search.MIT +21 -0
- package/external/pi-tools-suite/licenses/opencode-mystatus-MIT.txt +21 -0
- package/external/pi-tools-suite/package.json +53 -0
- package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +194 -0
- package/external/pi-tools-suite/src/antigravity-auth/commands.ts +80 -0
- package/external/pi-tools-suite/src/antigravity-auth/constants.ts +26 -0
- package/external/pi-tools-suite/src/antigravity-auth/headers.ts +20 -0
- package/external/pi-tools-suite/src/antigravity-auth/index.ts +104 -0
- package/external/pi-tools-suite/src/antigravity-auth/models.ts +86 -0
- package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +305 -0
- package/external/pi-tools-suite/src/antigravity-auth/payload.ts +423 -0
- package/external/pi-tools-suite/src/antigravity-auth/status.ts +78 -0
- package/external/pi-tools-suite/src/antigravity-auth/stream.ts +302 -0
- package/external/pi-tools-suite/src/antigravity-auth/types.ts +113 -0
- package/external/pi-tools-suite/src/ast-grep/index.ts +6 -0
- package/external/pi-tools-suite/src/ast-grep/render.ts +70 -0
- package/external/pi-tools-suite/src/ast-grep/schema.ts +109 -0
- package/external/pi-tools-suite/src/ast-grep/tool.ts +345 -0
- package/external/pi-tools-suite/src/ast-grep/types.ts +55 -0
- package/external/pi-tools-suite/src/ast-grep/utils.ts +65 -0
- package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +222 -0
- package/external/pi-tools-suite/src/async-subagents/commands.ts +518 -0
- package/external/pi-tools-suite/src/async-subagents/constants.ts +11 -0
- package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +74 -0
- package/external/pi-tools-suite/src/async-subagents/core/attachment-bridge.ts +133 -0
- package/external/pi-tools-suite/src/async-subagents/core/cleanup.ts +66 -0
- package/external/pi-tools-suite/src/async-subagents/core/concurrency.ts +90 -0
- package/external/pi-tools-suite/src/async-subagents/core/config.ts +819 -0
- package/external/pi-tools-suite/src/async-subagents/core/log-limits.ts +166 -0
- package/external/pi-tools-suite/src/async-subagents/core/model-fallback.ts +133 -0
- package/external/pi-tools-suite/src/async-subagents/core/paths.ts +47 -0
- package/external/pi-tools-suite/src/async-subagents/core/pi-invocation.ts +35 -0
- package/external/pi-tools-suite/src/async-subagents/core/presets.ts +67 -0
- package/external/pi-tools-suite/src/async-subagents/core/process.ts +15 -0
- package/external/pi-tools-suite/src/async-subagents/core/prompt.ts +66 -0
- package/external/pi-tools-suite/src/async-subagents/core/registry.ts +224 -0
- package/external/pi-tools-suite/src/async-subagents/core/retry.ts +191 -0
- package/external/pi-tools-suite/src/async-subagents/core/routing.ts +259 -0
- package/external/pi-tools-suite/src/async-subagents/core/sessions.ts +138 -0
- package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +688 -0
- package/external/pi-tools-suite/src/async-subagents/core/state.ts +281 -0
- package/external/pi-tools-suite/src/async-subagents/core/stop.ts +131 -0
- package/external/pi-tools-suite/src/async-subagents/core/structured-result.ts +237 -0
- package/external/pi-tools-suite/src/async-subagents/core/tool-guard.ts +34 -0
- package/external/pi-tools-suite/src/async-subagents/core/types.ts +150 -0
- package/external/pi-tools-suite/src/async-subagents/core/ultrawork-auto.ts +184 -0
- package/external/pi-tools-suite/src/async-subagents/core/utils.ts +11 -0
- package/external/pi-tools-suite/src/async-subagents/format.ts +41 -0
- package/external/pi-tools-suite/src/async-subagents/index.ts +422 -0
- package/external/pi-tools-suite/src/async-subagents/lib.ts +88 -0
- package/external/pi-tools-suite/src/async-subagents/live.ts +10 -0
- package/external/pi-tools-suite/src/async-subagents/polling.ts +83 -0
- package/external/pi-tools-suite/src/async-subagents/render.ts +230 -0
- package/external/pi-tools-suite/src/async-subagents/subagent-overlay.ts +77 -0
- package/external/pi-tools-suite/src/async-subagents/tasks.ts +120 -0
- package/external/pi-tools-suite/src/async-subagents/tools/cleanup.ts +99 -0
- package/external/pi-tools-suite/src/async-subagents/tools/result.ts +179 -0
- package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +372 -0
- package/external/pi-tools-suite/src/async-subagents/tools/status.ts +60 -0
- package/external/pi-tools-suite/src/async-subagents/tools/stop.ts +79 -0
- package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +152 -0
- package/external/pi-tools-suite/src/async-subagents/tools/wait.ts +67 -0
- package/external/pi-tools-suite/src/async-subagents/types.ts +45 -0
- package/external/pi-tools-suite/src/async-subagents/ui.ts +5 -0
- package/external/pi-tools-suite/src/compress/commands.ts +440 -0
- package/external/pi-tools-suite/src/compress/compress-tool.ts +368 -0
- package/external/pi-tools-suite/src/compress/compression-blocks.ts +524 -0
- package/external/pi-tools-suite/src/compress/config.ts +310 -0
- package/external/pi-tools-suite/src/compress/dcp-tui-filter.ts +498 -0
- package/external/pi-tools-suite/src/compress/index.ts +397 -0
- package/external/pi-tools-suite/src/compress/prompts.ts +269 -0
- package/external/pi-tools-suite/src/compress/pruner-candidates.ts +176 -0
- package/external/pi-tools-suite/src/compress/pruner-compression-blocks.ts +260 -0
- package/external/pi-tools-suite/src/compress/pruner-message-ids.ts +147 -0
- package/external/pi-tools-suite/src/compress/pruner-metadata.ts +268 -0
- package/external/pi-tools-suite/src/compress/pruner-nudge.ts +315 -0
- package/external/pi-tools-suite/src/compress/pruner-tools.ts +263 -0
- package/external/pi-tools-suite/src/compress/pruner-types.ts +25 -0
- package/external/pi-tools-suite/src/compress/pruner.ts +92 -0
- package/external/pi-tools-suite/src/compress/state.ts +486 -0
- package/external/pi-tools-suite/src/compress/ui.ts +308 -0
- package/external/pi-tools-suite/src/config.ts +176 -0
- package/external/pi-tools-suite/src/context-usage.ts +31 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +355 -0
- package/external/pi-tools-suite/src/index.ts +46 -0
- package/external/pi-tools-suite/src/lib/lsp.ts +62 -0
- package/external/pi-tools-suite/src/lib/project.ts +42 -0
- package/external/pi-tools-suite/src/lib/tool-args.ts +137 -0
- package/external/pi-tools-suite/src/lsp/_shared/config.ts +156 -0
- package/external/pi-tools-suite/src/lsp/_shared/glob.ts +60 -0
- package/external/pi-tools-suite/src/lsp/_shared/output.ts +102 -0
- package/external/pi-tools-suite/src/lsp/_shared/paths.ts +138 -0
- package/external/pi-tools-suite/src/lsp/_shared/runner.ts +64 -0
- package/external/pi-tools-suite/src/lsp/_shared/template.ts +23 -0
- package/external/pi-tools-suite/src/lsp/_shared/trust.ts +116 -0
- package/external/pi-tools-suite/src/lsp/_shared/types.ts +98 -0
- package/external/pi-tools-suite/src/lsp/async.ts +29 -0
- package/external/pi-tools-suite/src/lsp/child-process.ts +81 -0
- package/external/pi-tools-suite/src/lsp/client.ts +340 -0
- package/external/pi-tools-suite/src/lsp/constants.ts +9 -0
- package/external/pi-tools-suite/src/lsp/diagnostics-store.ts +64 -0
- package/external/pi-tools-suite/src/lsp/documents.ts +24 -0
- package/external/pi-tools-suite/src/lsp/index.ts +31 -0
- package/external/pi-tools-suite/src/lsp/lsp-utils.ts +37 -0
- package/external/pi-tools-suite/src/lsp/manager.ts +190 -0
- package/external/pi-tools-suite/src/lsp/mutation-events.ts +78 -0
- package/external/pi-tools-suite/src/lsp/renderer.ts +1 -0
- package/external/pi-tools-suite/src/lsp/tool-result.ts +6 -0
- package/external/pi-tools-suite/src/lsp/tsserver.ts +107 -0
- package/external/pi-tools-suite/src/lsp/types.ts +15 -0
- package/external/pi-tools-suite/src/model-tools/apply-patch.ts +590 -0
- package/external/pi-tools-suite/src/model-tools/index.ts +430 -0
- package/external/pi-tools-suite/src/model-tools/path-utils.ts +6 -0
- package/external/pi-tools-suite/src/model-tools/tool-args.ts +11 -0
- package/external/pi-tools-suite/src/prompt-commands/index.ts +349 -0
- package/external/pi-tools-suite/src/repo-discovery/index.ts +384 -0
- package/external/pi-tools-suite/src/repo-discovery/project.ts +7 -0
- package/external/pi-tools-suite/src/startup-section.ts +13 -0
- package/external/pi-tools-suite/src/terminal-bell/index.ts +309 -0
- package/external/pi-tools-suite/src/todo/index.ts +201 -0
- package/external/pi-tools-suite/src/todo/state/auto-clear.ts +13 -0
- package/external/pi-tools-suite/src/todo/state/invariants.ts +21 -0
- package/external/pi-tools-suite/src/todo/state/persistence.ts +94 -0
- package/external/pi-tools-suite/src/todo/state/replay.ts +38 -0
- package/external/pi-tools-suite/src/todo/state/selectors.ts +49 -0
- package/external/pi-tools-suite/src/todo/state/state-reducer.ts +362 -0
- package/external/pi-tools-suite/src/todo/state/state.ts +18 -0
- package/external/pi-tools-suite/src/todo/state/store.ts +52 -0
- package/external/pi-tools-suite/src/todo/state/task-graph.ts +57 -0
- package/external/pi-tools-suite/src/todo/todo.ts +487 -0
- package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +143 -0
- package/external/pi-tools-suite/src/todo/tool/types.ts +188 -0
- package/external/pi-tools-suite/src/todo/view/format.ts +18 -0
- package/external/pi-tools-suite/src/todo/view/labels.ts +13 -0
- package/external/pi-tools-suite/src/tool-descriptions.ts +369 -0
- package/external/pi-tools-suite/src/usage/index.ts +152 -0
- package/external/pi-tools-suite/src/usage/lib/copilot.ts +535 -0
- package/external/pi-tools-suite/src/usage/lib/google.ts +478 -0
- package/external/pi-tools-suite/src/usage/lib/openai.ts +268 -0
- package/external/pi-tools-suite/src/usage/lib/types.ts +114 -0
- package/external/pi-tools-suite/src/usage/lib/utils.ts +134 -0
- package/external/pi-tools-suite/src/usage/lib/zhipu.ts +228 -0
- package/external/pi-tools-suite/src/web-search/index.ts +397 -0
- package/package.json +89 -0
- package/skills/context7/SKILL.md +69 -0
- package/skills/context7/scripts/context7.sh +73 -0
- package/skills/handoff/SKILL.md +15 -0
- package/skills/pdf/LICENSE.txt +30 -0
- package/skills/pdf/SKILL.md +314 -0
- package/skills/pdf/forms.md +294 -0
- package/skills/pdf/reference.md +612 -0
- package/skills/pdf/scripts/check_bounding_boxes.py +65 -0
- package/skills/pdf/scripts/check_fillable_fields.py +11 -0
- package/skills/pdf/scripts/convert_pdf_to_images.py +33 -0
- package/skills/pdf/scripts/create_validation_image.py +37 -0
- package/skills/pdf/scripts/extract_form_field_info.py +122 -0
- package/skills/pdf/scripts/extract_form_structure.py +115 -0
- package/skills/pdf/scripts/fill_fillable_fields.py +98 -0
- package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +107 -0
- package/skills/playwright-cli/SKILL.md +388 -0
- package/skills/playwright-cli/references/element-attributes.md +23 -0
- package/skills/playwright-cli/references/playwright-tests.md +39 -0
- package/skills/playwright-cli/references/request-mocking.md +87 -0
- package/skills/playwright-cli/references/running-code.md +241 -0
- package/skills/playwright-cli/references/session-management.md +225 -0
- package/skills/playwright-cli/references/spec-driven-testing.md +305 -0
- package/skills/playwright-cli/references/storage-state.md +275 -0
- package/skills/playwright-cli/references/test-generation.md +134 -0
- package/skills/playwright-cli/references/tracing.md +139 -0
- package/skills/playwright-cli/references/video-recording.md +143 -0
- package/skills/simplify/SKILL.md +51 -0
- package/skills/skill-creator/LICENSE.txt +202 -0
- package/skills/skill-creator/SKILL.md +485 -0
- package/skills/skill-creator/agents/analyzer.md +274 -0
- package/skills/skill-creator/agents/comparator.md +202 -0
- package/skills/skill-creator/agents/grader.md +223 -0
- package/skills/skill-creator/assets/eval_review.html +146 -0
- package/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/skills/skill-creator/references/schemas.md +430 -0
- package/skills/skill-creator/scripts/__init__.py +0 -0
- package/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/skills/skill-creator/scripts/generate_report.py +326 -0
- package/skills/skill-creator/scripts/improve_description.py +247 -0
- package/skills/skill-creator/scripts/package_skill.py +136 -0
- package/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/skills/skill-creator/scripts/run_eval.py +310 -0
- package/skills/skill-creator/scripts/run_loop.py +328 -0
- package/skills/skill-creator/scripts/utils.py +47 -0
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
7
|
+
import { formatCompactProgressBar } from "../context-progress-bar.js";
|
|
8
|
+
const OPENAI_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
9
|
+
const ZAI_QUOTA_URL = "https://api.z.ai/api/monitor/usage/quota/limit";
|
|
10
|
+
const ZHIPU_QUOTA_URL = "https://bigmodel.cn/api/monitor/usage/quota/limit";
|
|
11
|
+
const GOOGLE_QUOTA_API_URL = "https://cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels";
|
|
12
|
+
const GOOGLE_TOKEN_REFRESH_URL = "https://oauth2.googleapis.com/token";
|
|
13
|
+
const REQUEST_TIMEOUT_MS = 10_000;
|
|
14
|
+
const DAY_SECONDS = 86_400;
|
|
15
|
+
const HOUR_SECONDS = 3_600;
|
|
16
|
+
const DEFAULT_ANTIGRAVITY_PROJECT_ID = "rising-fact-p41fc";
|
|
17
|
+
const GOOGLE_CLIENT_ID = process.env.PIX_ANTIGRAVITY_GOOGLE_CLIENT_ID ?? process.env.ANTIGRAVITY_GOOGLE_CLIENT_ID ?? "";
|
|
18
|
+
const GOOGLE_CLIENT_SECRET = process.env.PIX_ANTIGRAVITY_GOOGLE_CLIENT_SECRET ?? process.env.ANTIGRAVITY_GOOGLE_CLIENT_SECRET ?? "";
|
|
19
|
+
const OPENAI_QUOTA_PROVIDERS = new Set(["openai", "openai-codex"]);
|
|
20
|
+
const ZHIPU_QUOTA_PROVIDERS = new Set(["zai", "zhipuai-coding-plan"]);
|
|
21
|
+
const ANTIGRAVITY_QUOTA_PROVIDERS = new Set(["antigravity", "google-antigravity"]);
|
|
22
|
+
export function modelUsageDescriptor(model) {
|
|
23
|
+
if (!model)
|
|
24
|
+
return undefined;
|
|
25
|
+
const provider = model.provider.toLowerCase();
|
|
26
|
+
if (OPENAI_QUOTA_PROVIDERS.has(provider)) {
|
|
27
|
+
return { kind: "openai", modelKey: `${model.provider}/${model.id}` };
|
|
28
|
+
}
|
|
29
|
+
if (ZHIPU_QUOTA_PROVIDERS.has(provider)) {
|
|
30
|
+
return { kind: "zhipu", modelKey: `${model.provider}/${model.id}` };
|
|
31
|
+
}
|
|
32
|
+
if (ANTIGRAVITY_QUOTA_PROVIDERS.has(provider)) {
|
|
33
|
+
const quotaModelKey = resolveAntigravityQuotaModelKey(model);
|
|
34
|
+
const account = readActiveAntigravityQuotaAccount();
|
|
35
|
+
if (!quotaModelKey || !account)
|
|
36
|
+
return undefined;
|
|
37
|
+
return {
|
|
38
|
+
kind: "google-antigravity",
|
|
39
|
+
modelKey: `${model.provider}/${model.id}@${account.cacheKey}`,
|
|
40
|
+
quotaModelKey,
|
|
41
|
+
account,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
export async function queryModelUsageStatus(descriptor) {
|
|
47
|
+
switch (descriptor.kind) {
|
|
48
|
+
case "openai":
|
|
49
|
+
return await queryOpenAIModelUsage(descriptor.modelKey);
|
|
50
|
+
case "zhipu":
|
|
51
|
+
return await queryZhipuModelUsage(descriptor.modelKey);
|
|
52
|
+
case "google-antigravity":
|
|
53
|
+
return await queryGoogleAntigravityModelUsage(descriptor);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export async function queryAccountUsageReport(now = Date.now()) {
|
|
57
|
+
const [openai, zai, googleAccounts] = await Promise.all([
|
|
58
|
+
queryOpenAIAccountUsage(now),
|
|
59
|
+
queryZaiAccountUsage(now),
|
|
60
|
+
queryGoogleAntigravityAccountUsage(now),
|
|
61
|
+
]);
|
|
62
|
+
return {
|
|
63
|
+
...(openai ? { openai } : {}),
|
|
64
|
+
...(zai ? { zai } : {}),
|
|
65
|
+
googleAccounts,
|
|
66
|
+
generatedAt: now,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export function formatAccountUsageReport(report, now = report.generatedAt) {
|
|
70
|
+
const lines = [];
|
|
71
|
+
if (report.openai) {
|
|
72
|
+
lines.push("OpenAI Account Quota", "", `Account: ${report.openai.account}${report.openai.planType ? ` (${report.openai.planType})` : ""}`, "");
|
|
73
|
+
if (report.openai.error)
|
|
74
|
+
lines.push(`Unavailable: ${report.openai.error}`);
|
|
75
|
+
else {
|
|
76
|
+
for (const window of report.openai.windows)
|
|
77
|
+
lines.push(...formatProviderWindow(window, now, 30));
|
|
78
|
+
if (report.openai.limitReached)
|
|
79
|
+
lines.push("", "⚠️ Rate limit reached!");
|
|
80
|
+
for (const limit of report.openai.additionalLimits ?? []) {
|
|
81
|
+
lines.push(`Additional limit: ${limit.name}`);
|
|
82
|
+
if (limit.meteredFeature)
|
|
83
|
+
lines.push(`Metered feature: ${limit.meteredFeature}`);
|
|
84
|
+
lines.push("");
|
|
85
|
+
for (const window of limit.windows)
|
|
86
|
+
lines.push(...formatProviderWindow(window, now, 30));
|
|
87
|
+
if (limit.limitReached)
|
|
88
|
+
lines.push("", "⚠️ Rate limit reached!");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
lines.push("");
|
|
92
|
+
}
|
|
93
|
+
if (report.zai) {
|
|
94
|
+
lines.push("Z.ai Account Quota", "", `Account: ${report.zai.account} (Z.ai)`, "");
|
|
95
|
+
if (report.zai.error)
|
|
96
|
+
lines.push(`Unavailable: ${report.zai.error}`);
|
|
97
|
+
else {
|
|
98
|
+
for (const window of report.zai.windows)
|
|
99
|
+
lines.push(...formatProviderWindow(window, now, 30));
|
|
100
|
+
if (report.zai.mcp) {
|
|
101
|
+
lines.push("MCP monthly quota");
|
|
102
|
+
lines.push(`${formatQuotaBar(report.zai.mcp.remainingPercent, 30)} ${report.zai.mcp.remainingPercent}% remaining`);
|
|
103
|
+
if (report.zai.mcp.used !== undefined && report.zai.mcp.limit !== undefined) {
|
|
104
|
+
lines.push(`Used: ${report.zai.mcp.used.toLocaleString()} / ${report.zai.mcp.limit.toLocaleString()}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
lines.push("");
|
|
109
|
+
}
|
|
110
|
+
if (report.googleAccounts.length > 0) {
|
|
111
|
+
lines.push("Google Cloud Account Quota", "");
|
|
112
|
+
for (const account of report.googleAccounts) {
|
|
113
|
+
lines.push(account.account, "");
|
|
114
|
+
if (account.error)
|
|
115
|
+
lines.push(`Unavailable: ${account.error}`);
|
|
116
|
+
else {
|
|
117
|
+
for (const window of account.windows) {
|
|
118
|
+
lines.push(`${window.label.padEnd(14)} ${formatDurationShort(window.resetAt, now).padEnd(7)} ${formatQuotaBar(window.remainingPercent, 20)} ${window.remainingPercent}%`);
|
|
119
|
+
}
|
|
120
|
+
if (account.limitReached)
|
|
121
|
+
lines.push("", "⚠️ Rate limit reached!");
|
|
122
|
+
}
|
|
123
|
+
lines.push("");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return lines.join("\n").trimEnd();
|
|
127
|
+
}
|
|
128
|
+
export function formatModelUsageStatusLabel(status, now = Date.now()) {
|
|
129
|
+
if (!status)
|
|
130
|
+
return "";
|
|
131
|
+
const parts = [];
|
|
132
|
+
if (status.hourly)
|
|
133
|
+
parts.push(formatUsageWindow("H", status.hourly, now));
|
|
134
|
+
if (status.weekly)
|
|
135
|
+
parts.push(formatUsageWindow("W", status.weekly, now));
|
|
136
|
+
const limitsLabel = parts.join(" • ");
|
|
137
|
+
return status.accountEmail && limitsLabel ? `${status.accountEmail} ${limitsLabel}` : limitsLabel;
|
|
138
|
+
}
|
|
139
|
+
export function modelUsageRemainingPercent(status) {
|
|
140
|
+
if (!status)
|
|
141
|
+
return undefined;
|
|
142
|
+
const values = [];
|
|
143
|
+
if (status.weekly)
|
|
144
|
+
values.push(status.weekly.remainingPercent);
|
|
145
|
+
if (status.hourly)
|
|
146
|
+
values.push(status.hourly.remainingPercent);
|
|
147
|
+
return values.length > 0 ? Math.min(...values) : undefined;
|
|
148
|
+
}
|
|
149
|
+
export function openAIUsageStatusFromResponse(data, modelKey, now = Date.now()) {
|
|
150
|
+
const rateLimit = selectOpenAIRateLimitForModel(data, modelKey);
|
|
151
|
+
if (!rateLimit)
|
|
152
|
+
return undefined;
|
|
153
|
+
const windows = [rateLimit.primary_window, rateLimit.secondary_window].filter(isRateLimitWindow);
|
|
154
|
+
const weekly = selectWeeklyWindow(windows);
|
|
155
|
+
const hourly = selectHourlyWindow(windows);
|
|
156
|
+
if (!weekly && !hourly)
|
|
157
|
+
return undefined;
|
|
158
|
+
return {
|
|
159
|
+
modelKey,
|
|
160
|
+
provider: "openai",
|
|
161
|
+
updatedAt: now,
|
|
162
|
+
...(weekly ? { weekly: modelUsageWindow(weekly, now) } : {}),
|
|
163
|
+
...(hourly ? { hourly: modelUsageWindow(hourly, now) } : {}),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
async function queryOpenAIModelUsage(modelKey) {
|
|
167
|
+
const authData = await readOpenAIAuth();
|
|
168
|
+
if (!authData || authData.type !== "oauth" || !authData.access)
|
|
169
|
+
return undefined;
|
|
170
|
+
if (isExpired(authData))
|
|
171
|
+
return undefined;
|
|
172
|
+
const usage = await fetchOpenAIUsage(authData.access);
|
|
173
|
+
return openAIUsageStatusFromResponse(usage, modelKey);
|
|
174
|
+
}
|
|
175
|
+
async function queryOpenAIAccountUsage(now) {
|
|
176
|
+
const authData = await readOpenAIAuth();
|
|
177
|
+
if (!authData || authData.type !== "oauth" || !authData.access)
|
|
178
|
+
return undefined;
|
|
179
|
+
if (isExpired(authData))
|
|
180
|
+
return {
|
|
181
|
+
account: accountLabelFromOpenAIAuth(authData),
|
|
182
|
+
windows: [],
|
|
183
|
+
limitReached: false,
|
|
184
|
+
error: "OAuth token is expired",
|
|
185
|
+
};
|
|
186
|
+
try {
|
|
187
|
+
const usage = await fetchOpenAIUsage(authData.access);
|
|
188
|
+
const windows = [usage.rate_limit?.primary_window, usage.rate_limit?.secondary_window]
|
|
189
|
+
.filter(isRateLimitWindow)
|
|
190
|
+
.map((window) => accountWindowFromRateLimit(window, now));
|
|
191
|
+
const additionalLimits = (usage.additional_rate_limits ?? [])
|
|
192
|
+
.filter((limit) => !!limit.rate_limit)
|
|
193
|
+
.map((limit) => ({
|
|
194
|
+
name: limit.limit_name,
|
|
195
|
+
...(limit.metered_feature ? { meteredFeature: limit.metered_feature } : {}),
|
|
196
|
+
windows: [limit.rate_limit.primary_window, limit.rate_limit.secondary_window]
|
|
197
|
+
.filter(isRateLimitWindow)
|
|
198
|
+
.map((window) => accountWindowFromRateLimit(window, now)),
|
|
199
|
+
limitReached: limit.rate_limit.limit_reached === true,
|
|
200
|
+
}));
|
|
201
|
+
return {
|
|
202
|
+
account: accountLabelFromOpenAIAuth(authData),
|
|
203
|
+
...(usage.plan_type ? { planType: usage.plan_type } : {}),
|
|
204
|
+
windows,
|
|
205
|
+
limitReached: usage.rate_limit?.limit_reached === true,
|
|
206
|
+
...(additionalLimits.length > 0 ? { additionalLimits } : {}),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
return {
|
|
211
|
+
account: accountLabelFromOpenAIAuth(authData),
|
|
212
|
+
windows: [],
|
|
213
|
+
limitReached: false,
|
|
214
|
+
error: error instanceof Error ? error.message : String(error),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async function readOpenAIAuth() {
|
|
219
|
+
const authData = await readOpenCodeAuth();
|
|
220
|
+
const piAuth = await readPiAuth();
|
|
221
|
+
const piOpenAI = piAuth["openai-codex"];
|
|
222
|
+
if ((!authData.openai || isExpired(authData.openai)) && piOpenAI?.type === "oauth" && piOpenAI.access) {
|
|
223
|
+
const credential = {
|
|
224
|
+
type: "oauth",
|
|
225
|
+
access: piOpenAI.access,
|
|
226
|
+
};
|
|
227
|
+
if (piOpenAI.refresh)
|
|
228
|
+
credential.refresh = piOpenAI.refresh;
|
|
229
|
+
if (typeof piOpenAI.expires === "number")
|
|
230
|
+
credential.expires = piOpenAI.expires;
|
|
231
|
+
if (piOpenAI.email)
|
|
232
|
+
credential.email = piOpenAI.email;
|
|
233
|
+
authData.openai = credential;
|
|
234
|
+
}
|
|
235
|
+
return authData.openai;
|
|
236
|
+
}
|
|
237
|
+
function accountLabelFromOpenAIAuth(authData) {
|
|
238
|
+
return authData.email || (authData.access ? openAIAccountEmailFromJwt(authData.access) : undefined) || "OpenAI";
|
|
239
|
+
}
|
|
240
|
+
async function readOpenCodeAuth() {
|
|
241
|
+
try {
|
|
242
|
+
const content = await readFile(join(homedir(), ".local/share/opencode/auth.json"), "utf8");
|
|
243
|
+
return JSON.parse(content);
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
return {};
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
async function readPiAuth() {
|
|
250
|
+
try {
|
|
251
|
+
const content = await readFile(join(getAgentDir(), "auth.json"), "utf8");
|
|
252
|
+
return JSON.parse(content);
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
return {};
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
function isExpired(credential) {
|
|
259
|
+
return typeof credential?.expires === "number" && credential.expires < Date.now();
|
|
260
|
+
}
|
|
261
|
+
export function zhipuUsageStatusFromResponse(data, modelKey, now = Date.now()) {
|
|
262
|
+
if (!data.success || data.code !== 200)
|
|
263
|
+
return undefined;
|
|
264
|
+
const tokensLimit = data.data.limits.find((l) => l.type === "TOKENS_LIMIT");
|
|
265
|
+
if (!tokensLimit)
|
|
266
|
+
return undefined;
|
|
267
|
+
const remainingPercent = clampPercent(100 - Math.round(tokensLimit.percentage));
|
|
268
|
+
const resetAt = typeof tokensLimit.nextResetTime === "number" && tokensLimit.nextResetTime > now
|
|
269
|
+
? tokensLimit.nextResetTime
|
|
270
|
+
: now + 5 * HOUR_SECONDS * 1000;
|
|
271
|
+
return {
|
|
272
|
+
modelKey,
|
|
273
|
+
provider: "zhipu",
|
|
274
|
+
updatedAt: now,
|
|
275
|
+
hourly: {
|
|
276
|
+
remainingPercent,
|
|
277
|
+
resetAt,
|
|
278
|
+
windowSeconds: 5 * HOUR_SECONDS,
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
async function queryZhipuModelUsage(modelKey) {
|
|
283
|
+
const apiKey = await readZhipuApiKey();
|
|
284
|
+
if (!apiKey)
|
|
285
|
+
return undefined;
|
|
286
|
+
const response = await fetchZhipuQuota(apiKey, modelKey.startsWith("zhipuai") ? ZHIPU_QUOTA_URL : ZAI_QUOTA_URL);
|
|
287
|
+
return zhipuUsageStatusFromResponse(response, modelKey);
|
|
288
|
+
}
|
|
289
|
+
async function queryZaiAccountUsage(now) {
|
|
290
|
+
const apiKey = await readZhipuApiKey();
|
|
291
|
+
if (!apiKey)
|
|
292
|
+
return undefined;
|
|
293
|
+
try {
|
|
294
|
+
const response = await fetchZhipuQuota(apiKey, ZAI_QUOTA_URL);
|
|
295
|
+
if (!response.success || response.code !== 200) {
|
|
296
|
+
return { account: maskCredential(apiKey), windows: [], error: response.msg || "quota request failed" };
|
|
297
|
+
}
|
|
298
|
+
const tokenLimits = response.data.limits
|
|
299
|
+
.filter((limit) => limit.type === "TOKENS_LIMIT")
|
|
300
|
+
.map((limit) => ({
|
|
301
|
+
label: "5-hour token limit",
|
|
302
|
+
remainingPercent: clampPercent(100 - Math.round(limit.percentage)),
|
|
303
|
+
resetAt: typeof limit.nextResetTime === "number" && limit.nextResetTime > now ? limit.nextResetTime : now + 5 * HOUR_SECONDS * 1000,
|
|
304
|
+
windowSeconds: 5 * HOUR_SECONDS,
|
|
305
|
+
}));
|
|
306
|
+
const mcpLimit = response.data.limits.find((limit) => /MCP/iu.test(limit.type));
|
|
307
|
+
return {
|
|
308
|
+
account: maskCredential(apiKey),
|
|
309
|
+
windows: tokenLimits,
|
|
310
|
+
...(mcpLimit ? {
|
|
311
|
+
mcp: {
|
|
312
|
+
label: "MCP monthly quota",
|
|
313
|
+
remainingPercent: clampPercent(100 - Math.round(mcpLimit.percentage)),
|
|
314
|
+
resetAt: typeof mcpLimit.nextResetTime === "number" && mcpLimit.nextResetTime > now ? mcpLimit.nextResetTime : now,
|
|
315
|
+
windowSeconds: 30 * DAY_SECONDS,
|
|
316
|
+
used: mcpLimit.usage,
|
|
317
|
+
limit: mcpLimit.currentValue,
|
|
318
|
+
},
|
|
319
|
+
} : {}),
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
return {
|
|
324
|
+
account: maskCredential(apiKey),
|
|
325
|
+
windows: [],
|
|
326
|
+
error: error instanceof Error ? error.message : String(error),
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
async function readZhipuApiKey() {
|
|
331
|
+
const piAuth = await readPiAuth();
|
|
332
|
+
const zaiCredential = piAuth.zai;
|
|
333
|
+
if (zaiCredential?.key && (zaiCredential.type === "api" || zaiCredential.type === "api_key")) {
|
|
334
|
+
return zaiCredential.key;
|
|
335
|
+
}
|
|
336
|
+
const opencodeAuth = await readOpenCodeAuth();
|
|
337
|
+
const zaiPlan = opencodeAuth["zai-coding-plan"];
|
|
338
|
+
if (zaiPlan?.key)
|
|
339
|
+
return zaiPlan.key;
|
|
340
|
+
const zhipuPlan = opencodeAuth["zhipuai-coding-plan"];
|
|
341
|
+
if (zhipuPlan?.key)
|
|
342
|
+
return zhipuPlan.key;
|
|
343
|
+
return undefined;
|
|
344
|
+
}
|
|
345
|
+
async function fetchZhipuQuota(apiKey, url) {
|
|
346
|
+
const response = await fetchWithTimeout(url, {
|
|
347
|
+
headers: {
|
|
348
|
+
Authorization: apiKey,
|
|
349
|
+
"Content-Type": "application/json",
|
|
350
|
+
"User-Agent": "pi-ui-extend/0.1.0",
|
|
351
|
+
},
|
|
352
|
+
});
|
|
353
|
+
if (!response.ok) {
|
|
354
|
+
const errorText = await response.text();
|
|
355
|
+
throw new Error(`Zhipu quota request failed (${response.status}): ${errorText}`);
|
|
356
|
+
}
|
|
357
|
+
return response.json();
|
|
358
|
+
}
|
|
359
|
+
// ---------------------------------------------------------------------------
|
|
360
|
+
// Google Antigravity quota
|
|
361
|
+
// ---------------------------------------------------------------------------
|
|
362
|
+
export function resolveAntigravityQuotaModelKey(model) {
|
|
363
|
+
const source = `${model.id} ${model.name ?? ""}`.toLowerCase();
|
|
364
|
+
const normalized = source.replace(/[_\s]+/gu, "-");
|
|
365
|
+
if (normalized.includes("claude") && normalized.includes("opus") && normalized.includes("thinking")) {
|
|
366
|
+
return "claude-opus-4-6-thinking";
|
|
367
|
+
}
|
|
368
|
+
if (normalized.includes("claude") && normalized.includes("sonnet"))
|
|
369
|
+
return "claude-sonnet-4-6";
|
|
370
|
+
if (normalized.includes("gemini-2.5-flash") || /\bg2[.-]?5-flash\b/u.test(normalized))
|
|
371
|
+
return "gemini-2.5-flash";
|
|
372
|
+
if (normalized.includes("gemini-3-flash") || /\bg3-flash\b/u.test(normalized))
|
|
373
|
+
return "gemini-3-flash";
|
|
374
|
+
if (normalized.includes("gemini-3") && normalized.includes("flash"))
|
|
375
|
+
return "gemini-3-flash";
|
|
376
|
+
if (normalized.includes("gemini-3") && normalized.includes("pro"))
|
|
377
|
+
return "gemini-3.1-pro-low";
|
|
378
|
+
if (/\bg3(?:-pro)?\b/u.test(normalized))
|
|
379
|
+
return "gemini-3.1-pro-low";
|
|
380
|
+
return undefined;
|
|
381
|
+
}
|
|
382
|
+
export function googleAntigravityUsageStatusFromResponse(data, descriptor, now = Date.now()) {
|
|
383
|
+
const quotaInfo = data.models[descriptor.quotaModelKey]?.quotaInfo;
|
|
384
|
+
if (!quotaInfo || !Number.isFinite(quotaInfo.remainingFraction))
|
|
385
|
+
return undefined;
|
|
386
|
+
const resetAt = parseResetTime(quotaInfo.resetTime, now);
|
|
387
|
+
const window = {
|
|
388
|
+
remainingPercent: clampPercent(Math.round((quotaInfo.remainingFraction ?? 0) * 100)),
|
|
389
|
+
resetAt,
|
|
390
|
+
windowSeconds: Math.max(0, Math.round((resetAt - now) / 1000)),
|
|
391
|
+
};
|
|
392
|
+
const weekly = window.windowSeconds >= DAY_SECONDS ? window : undefined;
|
|
393
|
+
const hourly = weekly ? undefined : window;
|
|
394
|
+
return {
|
|
395
|
+
modelKey: descriptor.modelKey,
|
|
396
|
+
provider: "google-antigravity",
|
|
397
|
+
updatedAt: now,
|
|
398
|
+
...(descriptor.account.email ? { accountEmail: descriptor.account.email } : {}),
|
|
399
|
+
...(weekly ? { weekly } : {}),
|
|
400
|
+
...(hourly ? { hourly } : {}),
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
async function queryGoogleAntigravityModelUsage(descriptor) {
|
|
404
|
+
const { access_token } = await refreshGoogleAccessToken(descriptor.account.refreshToken);
|
|
405
|
+
const response = await fetchGoogleAntigravityQuota(access_token, descriptor.account.projectId);
|
|
406
|
+
return googleAntigravityUsageStatusFromResponse(response, descriptor);
|
|
407
|
+
}
|
|
408
|
+
const GOOGLE_ACCOUNT_QUOTA_WINDOWS = [
|
|
409
|
+
{ label: "Claude Opus", quotaModelKey: "claude-opus-4-6-thinking" },
|
|
410
|
+
{ label: "Claude Sonnet", quotaModelKey: "claude-sonnet-4-6" },
|
|
411
|
+
{ label: "G2.5 Flash", quotaModelKey: "gemini-2.5-flash" },
|
|
412
|
+
{ label: "G3 Flash", quotaModelKey: "gemini-3-flash" },
|
|
413
|
+
{ label: "G3 Pro", quotaModelKey: "gemini-3.1-pro-low" },
|
|
414
|
+
];
|
|
415
|
+
async function queryGoogleAntigravityAccountUsage(now) {
|
|
416
|
+
const accounts = readAllAntigravityQuotaAccounts();
|
|
417
|
+
const results = await Promise.all(accounts.map(async (account) => {
|
|
418
|
+
const accountLabel = account.email ?? maskCredential(account.refreshToken);
|
|
419
|
+
try {
|
|
420
|
+
const { access_token } = await refreshGoogleAccessToken(account.refreshToken);
|
|
421
|
+
const response = await fetchGoogleAntigravityQuota(access_token, account.projectId);
|
|
422
|
+
const windows = GOOGLE_ACCOUNT_QUOTA_WINDOWS.map((window) => googleAccountWindowFromResponse(response, window.label, window.quotaModelKey, now))
|
|
423
|
+
.filter((window) => window !== undefined);
|
|
424
|
+
return {
|
|
425
|
+
account: accountLabel,
|
|
426
|
+
windows,
|
|
427
|
+
limitReached: windows.some((window) => window.remainingPercent <= 0),
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
catch (error) {
|
|
431
|
+
return {
|
|
432
|
+
account: accountLabel,
|
|
433
|
+
windows: [],
|
|
434
|
+
limitReached: false,
|
|
435
|
+
error: error instanceof Error ? error.message : String(error),
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
}));
|
|
439
|
+
return results;
|
|
440
|
+
}
|
|
441
|
+
function readActiveAntigravityQuotaAccount() {
|
|
442
|
+
const accounts = readAllAntigravityQuotaAccounts();
|
|
443
|
+
const credential = readPiAuthSync().antigravity;
|
|
444
|
+
return accounts[clampAccountIndex(credential?.activeIndex, accounts.length)];
|
|
445
|
+
}
|
|
446
|
+
function readAllAntigravityQuotaAccounts() {
|
|
447
|
+
const credential = readPiAuthSync().antigravity;
|
|
448
|
+
if (!credential)
|
|
449
|
+
return [];
|
|
450
|
+
const accounts = storedAntigravityAccounts(credential);
|
|
451
|
+
if (accounts.length > 0) {
|
|
452
|
+
return accounts.map((account, accountIndex) => antigravityQuotaAccount(account, {
|
|
453
|
+
...(credential.email ? { fallbackEmail: credential.email } : {}),
|
|
454
|
+
accountIndex,
|
|
455
|
+
accountCount: accounts.length,
|
|
456
|
+
})).filter((account) => account !== undefined);
|
|
457
|
+
}
|
|
458
|
+
const fallbackAccount = antigravityAccountFromCredential(credential);
|
|
459
|
+
const account = fallbackAccount ? antigravityQuotaAccount(fallbackAccount, {
|
|
460
|
+
...(credential.email ? { fallbackEmail: credential.email } : {}),
|
|
461
|
+
}) : undefined;
|
|
462
|
+
return account ? [account] : [];
|
|
463
|
+
}
|
|
464
|
+
function readPiAuthSync() {
|
|
465
|
+
try {
|
|
466
|
+
return JSON.parse(readFileSync(join(getAgentDir(), "auth.json"), "utf8"));
|
|
467
|
+
}
|
|
468
|
+
catch {
|
|
469
|
+
return {};
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
function storedAntigravityAccounts(credential) {
|
|
473
|
+
return Array.isArray(credential.accounts)
|
|
474
|
+
? credential.accounts.filter((account) => account.enabled !== false && !!account.refreshToken)
|
|
475
|
+
: [];
|
|
476
|
+
}
|
|
477
|
+
function antigravityAccountFromCredential(credential) {
|
|
478
|
+
if (credential.type !== "oauth" || !credential.refresh)
|
|
479
|
+
return undefined;
|
|
480
|
+
const refresh = splitAntigravityRefresh(credential.refresh);
|
|
481
|
+
if (!refresh.refreshToken)
|
|
482
|
+
return undefined;
|
|
483
|
+
return {
|
|
484
|
+
refreshToken: refresh.refreshToken,
|
|
485
|
+
projectId: refresh.projectId || refresh.managedProjectId || DEFAULT_ANTIGRAVITY_PROJECT_ID,
|
|
486
|
+
enabled: true,
|
|
487
|
+
...(credential.email ? { email: credential.email } : {}),
|
|
488
|
+
...(refresh.managedProjectId ? { managedProjectId: refresh.managedProjectId } : {}),
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
function antigravityQuotaAccount(account, options = {}) {
|
|
492
|
+
const refreshToken = account.refreshToken;
|
|
493
|
+
if (!refreshToken)
|
|
494
|
+
return undefined;
|
|
495
|
+
const email = account.email || options.fallbackEmail;
|
|
496
|
+
const projectId = account.projectId || account.managedProjectId || DEFAULT_ANTIGRAVITY_PROJECT_ID;
|
|
497
|
+
return {
|
|
498
|
+
refreshToken,
|
|
499
|
+
projectId,
|
|
500
|
+
cacheKey: email ? email.toLowerCase() : shortHash(refreshToken),
|
|
501
|
+
...(email ? { email } : {}),
|
|
502
|
+
...(typeof options.accountIndex === "number" ? { accountIndex: options.accountIndex } : {}),
|
|
503
|
+
...(typeof options.accountCount === "number" ? { accountCount: options.accountCount } : {}),
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
function splitAntigravityRefresh(refresh) {
|
|
507
|
+
const [refreshToken = "", projectId = "", managedProjectId = ""] = refresh.split("|");
|
|
508
|
+
return {
|
|
509
|
+
refreshToken: refreshToken || refresh,
|
|
510
|
+
...(projectId ? { projectId } : {}),
|
|
511
|
+
...(managedProjectId ? { managedProjectId } : {}),
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
function clampAccountIndex(index, accountCount) {
|
|
515
|
+
if (!Number.isInteger(index) || accountCount <= 0)
|
|
516
|
+
return 0;
|
|
517
|
+
return Math.max(0, Math.min(index, accountCount - 1));
|
|
518
|
+
}
|
|
519
|
+
function shortHash(value) {
|
|
520
|
+
return createHash("sha256").update(value).digest("hex").slice(0, 12);
|
|
521
|
+
}
|
|
522
|
+
async function refreshGoogleAccessToken(refreshToken) {
|
|
523
|
+
if (!GOOGLE_CLIENT_ID || !GOOGLE_CLIENT_SECRET) {
|
|
524
|
+
throw new Error("Antigravity Google OAuth credentials are not configured; set PIX_ANTIGRAVITY_GOOGLE_CLIENT_ID and PIX_ANTIGRAVITY_GOOGLE_CLIENT_SECRET.");
|
|
525
|
+
}
|
|
526
|
+
const response = await fetchWithTimeout(GOOGLE_TOKEN_REFRESH_URL, {
|
|
527
|
+
method: "POST",
|
|
528
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
529
|
+
body: new URLSearchParams({
|
|
530
|
+
client_id: GOOGLE_CLIENT_ID,
|
|
531
|
+
client_secret: GOOGLE_CLIENT_SECRET,
|
|
532
|
+
refresh_token: refreshToken,
|
|
533
|
+
grant_type: "refresh_token",
|
|
534
|
+
}),
|
|
535
|
+
});
|
|
536
|
+
if (!response.ok) {
|
|
537
|
+
const errorText = await response.text();
|
|
538
|
+
throw new Error(`Google token refresh failed (${response.status}): ${errorText}`);
|
|
539
|
+
}
|
|
540
|
+
return response.json();
|
|
541
|
+
}
|
|
542
|
+
async function fetchGoogleAntigravityQuota(accessToken, projectId) {
|
|
543
|
+
const response = await fetchWithTimeout(GOOGLE_QUOTA_API_URL, {
|
|
544
|
+
method: "POST",
|
|
545
|
+
headers: {
|
|
546
|
+
"Content-Type": "application/json",
|
|
547
|
+
Authorization: `Bearer ${accessToken}`,
|
|
548
|
+
"User-Agent": "antigravity/1.18.3 darwin/arm64",
|
|
549
|
+
},
|
|
550
|
+
body: JSON.stringify({ project: projectId }),
|
|
551
|
+
});
|
|
552
|
+
if (!response.ok) {
|
|
553
|
+
const errorText = await response.text();
|
|
554
|
+
throw new Error(`Google quota request failed (${response.status}): ${errorText}`);
|
|
555
|
+
}
|
|
556
|
+
return response.json();
|
|
557
|
+
}
|
|
558
|
+
function parseResetTime(value, now) {
|
|
559
|
+
if (!value)
|
|
560
|
+
return now;
|
|
561
|
+
const resetAt = Date.parse(value);
|
|
562
|
+
return Number.isFinite(resetAt) && resetAt > now ? resetAt : now;
|
|
563
|
+
}
|
|
564
|
+
async function fetchOpenAIUsage(accessToken) {
|
|
565
|
+
const headers = {
|
|
566
|
+
Authorization: `Bearer ${accessToken}`,
|
|
567
|
+
"User-Agent": "pi-ui-extend/0.1.0",
|
|
568
|
+
};
|
|
569
|
+
const accountId = getAccountIdFromJwt(accessToken);
|
|
570
|
+
if (accountId)
|
|
571
|
+
headers["ChatGPT-Account-Id"] = accountId;
|
|
572
|
+
const response = await fetchWithTimeout(OPENAI_USAGE_URL, { headers });
|
|
573
|
+
if (!response.ok) {
|
|
574
|
+
const errorText = await response.text();
|
|
575
|
+
throw new Error(`OpenAI usage request failed (${response.status}): ${errorText}`);
|
|
576
|
+
}
|
|
577
|
+
return response.json();
|
|
578
|
+
}
|
|
579
|
+
async function fetchWithTimeout(url, options) {
|
|
580
|
+
const controller = new AbortController();
|
|
581
|
+
const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
582
|
+
try {
|
|
583
|
+
return await fetch(url, {
|
|
584
|
+
...options,
|
|
585
|
+
signal: controller.signal,
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
catch (error) {
|
|
589
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
590
|
+
throw new Error(`Request timeout (${Math.round(REQUEST_TIMEOUT_MS / 1000)}s)`);
|
|
591
|
+
}
|
|
592
|
+
throw error;
|
|
593
|
+
}
|
|
594
|
+
finally {
|
|
595
|
+
clearTimeout(timeoutId);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
function getAccountIdFromJwt(token) {
|
|
599
|
+
const payload = parseJwt(token);
|
|
600
|
+
return payload?.["https://api.openai.com/auth"]?.chatgpt_account_id;
|
|
601
|
+
}
|
|
602
|
+
function openAIAccountEmailFromJwt(token) {
|
|
603
|
+
const payload = parseJwt(token);
|
|
604
|
+
return payload?.email || payload?.["https://api.openai.com/auth"]?.email;
|
|
605
|
+
}
|
|
606
|
+
function parseJwt(token) {
|
|
607
|
+
try {
|
|
608
|
+
const parts = token.split(".");
|
|
609
|
+
const payloadPart = parts[1];
|
|
610
|
+
if (parts.length !== 3 || !payloadPart)
|
|
611
|
+
return undefined;
|
|
612
|
+
const payloadJson = base64UrlDecode(payloadPart);
|
|
613
|
+
return JSON.parse(payloadJson);
|
|
614
|
+
}
|
|
615
|
+
catch {
|
|
616
|
+
return undefined;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
function base64UrlDecode(input) {
|
|
620
|
+
const base64 = input.replace(/-/gu, "+").replace(/_/gu, "/");
|
|
621
|
+
const padLength = (4 - (base64.length % 4)) % 4;
|
|
622
|
+
return Buffer.from(`${base64}${"=".repeat(padLength)}`, "base64").toString("utf8");
|
|
623
|
+
}
|
|
624
|
+
function isRateLimitWindow(value) {
|
|
625
|
+
return value !== null && value !== undefined
|
|
626
|
+
&& Number.isFinite(value.used_percent)
|
|
627
|
+
&& Number.isFinite(value.limit_window_seconds)
|
|
628
|
+
&& Number.isFinite(value.reset_after_seconds);
|
|
629
|
+
}
|
|
630
|
+
function selectOpenAIRateLimitForModel(data, modelKey) {
|
|
631
|
+
const additionalLimit = (data.additional_rate_limits ?? []).find((limit) => {
|
|
632
|
+
if (!limit.rate_limit)
|
|
633
|
+
return false;
|
|
634
|
+
return openAIModelMatchesAdditionalLimit(modelKey, limit);
|
|
635
|
+
});
|
|
636
|
+
return additionalLimit?.rate_limit ?? data.rate_limit;
|
|
637
|
+
}
|
|
638
|
+
function openAIModelMatchesAdditionalLimit(modelKey, limit) {
|
|
639
|
+
const normalizedModel = normalizeOpenAILimitName(modelKey.split("/").at(-1) ?? modelKey);
|
|
640
|
+
const normalizedLimitName = normalizeOpenAILimitName(limit.limit_name);
|
|
641
|
+
const normalizedMeteredFeature = limit.metered_feature ? normalizeOpenAILimitName(limit.metered_feature) : "";
|
|
642
|
+
return (!!normalizedLimitName && (normalizedModel.includes(normalizedLimitName) || normalizedLimitName.includes(normalizedModel)))
|
|
643
|
+
|| (!!normalizedMeteredFeature && (normalizedModel.includes(normalizedMeteredFeature) || normalizedMeteredFeature.includes(normalizedModel)));
|
|
644
|
+
}
|
|
645
|
+
function normalizeOpenAILimitName(value) {
|
|
646
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/gu, "");
|
|
647
|
+
}
|
|
648
|
+
function selectWeeklyWindow(windows) {
|
|
649
|
+
return windows
|
|
650
|
+
.filter((window) => window.limit_window_seconds >= 6 * DAY_SECONDS)
|
|
651
|
+
.sort((a, b) => b.limit_window_seconds - a.limit_window_seconds)[0];
|
|
652
|
+
}
|
|
653
|
+
function selectHourlyWindow(windows) {
|
|
654
|
+
return windows
|
|
655
|
+
.filter((window) => window.limit_window_seconds <= 6 * HOUR_SECONDS)
|
|
656
|
+
.sort((a, b) => a.limit_window_seconds - b.limit_window_seconds)[0];
|
|
657
|
+
}
|
|
658
|
+
function modelUsageWindow(window, now) {
|
|
659
|
+
return {
|
|
660
|
+
remainingPercent: clampPercent(Math.round(100 - window.used_percent)),
|
|
661
|
+
resetAt: now + Math.max(0, Math.round(window.reset_after_seconds)) * 1000,
|
|
662
|
+
windowSeconds: Math.max(0, Math.round(window.limit_window_seconds)),
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
function accountWindowFromRateLimit(window, now) {
|
|
666
|
+
const windowSeconds = Math.max(0, Math.round(window.limit_window_seconds));
|
|
667
|
+
return {
|
|
668
|
+
label: accountWindowLabel(windowSeconds),
|
|
669
|
+
remainingPercent: clampPercent(Math.round(100 - window.used_percent)),
|
|
670
|
+
resetAt: now + Math.max(0, Math.round(window.reset_after_seconds)) * 1000,
|
|
671
|
+
windowSeconds,
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
function googleAccountWindowFromResponse(data, label, quotaModelKey, now) {
|
|
675
|
+
const quotaInfo = data.models[quotaModelKey]?.quotaInfo;
|
|
676
|
+
if (!quotaInfo || !Number.isFinite(quotaInfo.remainingFraction))
|
|
677
|
+
return undefined;
|
|
678
|
+
const resetAt = parseResetTime(quotaInfo.resetTime, now);
|
|
679
|
+
return {
|
|
680
|
+
label,
|
|
681
|
+
remainingPercent: clampPercent(Math.round((quotaInfo.remainingFraction ?? 0) * 100)),
|
|
682
|
+
resetAt,
|
|
683
|
+
windowSeconds: Math.max(0, Math.round((resetAt - now) / 1000)),
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
function clampPercent(percent) {
|
|
687
|
+
return Math.max(0, Math.min(100, percent));
|
|
688
|
+
}
|
|
689
|
+
function accountWindowLabel(windowSeconds) {
|
|
690
|
+
if (windowSeconds >= 6 * DAY_SECONDS)
|
|
691
|
+
return `${Math.round(windowSeconds / DAY_SECONDS)}-day limit`;
|
|
692
|
+
if (windowSeconds >= HOUR_SECONDS)
|
|
693
|
+
return `${Math.round(windowSeconds / HOUR_SECONDS)}-hour limit`;
|
|
694
|
+
return `${Math.max(1, Math.round(windowSeconds / 60))}-minute limit`;
|
|
695
|
+
}
|
|
696
|
+
function formatProviderWindow(window, now, width) {
|
|
697
|
+
return [
|
|
698
|
+
window.label,
|
|
699
|
+
`${formatQuotaBar(window.remainingPercent, width)} ${window.remainingPercent}% remaining`,
|
|
700
|
+
`Resets in: ${formatDurationLong(window.resetAt, now)}`,
|
|
701
|
+
"",
|
|
702
|
+
];
|
|
703
|
+
}
|
|
704
|
+
function formatQuotaBar(percent, width) {
|
|
705
|
+
const filled = Math.max(0, Math.min(width, Math.round((percent / 100) * width)));
|
|
706
|
+
return `${"█".repeat(filled)}${"░".repeat(width - filled)}`;
|
|
707
|
+
}
|
|
708
|
+
function formatDurationLong(resetAt, now) {
|
|
709
|
+
const totalMinutes = Math.max(0, Math.ceil((resetAt - now) / 60_000));
|
|
710
|
+
const days = Math.floor(totalMinutes / 1440);
|
|
711
|
+
const hours = Math.floor((totalMinutes % 1440) / 60);
|
|
712
|
+
const minutes = totalMinutes % 60;
|
|
713
|
+
if (days > 0)
|
|
714
|
+
return `${days}d ${hours}h ${minutes}m`;
|
|
715
|
+
if (hours > 0)
|
|
716
|
+
return `${hours}h ${minutes}m`;
|
|
717
|
+
return `${minutes}m`;
|
|
718
|
+
}
|
|
719
|
+
function formatDurationShort(resetAt, now) {
|
|
720
|
+
const totalMinutes = Math.max(0, Math.ceil((resetAt - now) / 60_000));
|
|
721
|
+
const days = Math.floor(totalMinutes / 1440);
|
|
722
|
+
const hours = Math.floor((totalMinutes % 1440) / 60);
|
|
723
|
+
const minutes = totalMinutes % 60;
|
|
724
|
+
if (days > 0)
|
|
725
|
+
return `${days}d ${hours}h`;
|
|
726
|
+
if (hours > 0)
|
|
727
|
+
return `${hours}h ${minutes}m`;
|
|
728
|
+
return `${minutes}m`;
|
|
729
|
+
}
|
|
730
|
+
function maskCredential(value) {
|
|
731
|
+
const visible = value.trim();
|
|
732
|
+
if (visible.length <= 8)
|
|
733
|
+
return visible ? "****" : "unknown";
|
|
734
|
+
return `${visible.slice(0, 4)}****${visible.slice(-4)}`;
|
|
735
|
+
}
|
|
736
|
+
function formatUsageWindow(_prefix, window, now) {
|
|
737
|
+
return `${window.remainingPercent}% ${formatCompactProgressBar(window.remainingPercent)} ${formatResetCountdown(window.resetAt, now)}`;
|
|
738
|
+
}
|
|
739
|
+
function formatResetCountdown(resetAt, now) {
|
|
740
|
+
const totalMinutes = Math.max(0, Math.ceil((resetAt - now) / 60_000));
|
|
741
|
+
const days = Math.floor(totalMinutes / 1440);
|
|
742
|
+
const hours = Math.floor((totalMinutes % 1440) / 60);
|
|
743
|
+
const minutes = totalMinutes % 60;
|
|
744
|
+
if (days > 0)
|
|
745
|
+
return `${days}d${hours}h`;
|
|
746
|
+
if (hours > 0)
|
|
747
|
+
return `${hours}h${minutes}m`;
|
|
748
|
+
return `${minutes}m`;
|
|
749
|
+
}
|