@herbertgao/pi-extensions 2026.9.2 → 2026.9.4
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 +4 -2
- package/THIRD_PARTY_NOTICES.md +49 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +3 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +16 -11
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +20 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +12 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +98 -0
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
- package/node_modules/@herbertgao/sol-pi/LICENSE +19 -0
- package/node_modules/@herbertgao/sol-pi/README.md +159 -0
- package/node_modules/@herbertgao/sol-pi/SECURITY.md +26 -0
- package/node_modules/@herbertgao/sol-pi/THIRD_PARTY_NOTICES.md +19 -0
- package/node_modules/@herbertgao/sol-pi/agents-install.md +150 -0
- package/node_modules/@herbertgao/sol-pi/assets/sol-pi-hero.png +0 -0
- package/node_modules/@herbertgao/sol-pi/docs/compatibility.md +69 -0
- package/node_modules/@herbertgao/sol-pi/docs/configuration.md +75 -0
- package/node_modules/@herbertgao/sol-pi/package.json +76 -0
- package/node_modules/@herbertgao/sol-pi/scripts/check-pi-compat.mjs +32 -0
- package/node_modules/@herbertgao/sol-pi/scripts/check-sol-pi-config.mjs +120 -0
- package/node_modules/@herbertgao/sol-pi/sol-pi.example.json +10 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/config.ts +135 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/file-queue.ts +74 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/index.ts +185 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/action-fusion/then-run.ts +128 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/archive.ts +53 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/candidate.ts +101 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/config.ts +71 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/index.ts +220 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/journal.ts +25 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/provider.ts +164 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/evidence-preserving-reducer/receipt.ts +177 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/index.ts +227 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/ledger.ts +20 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/observation-pack/observation.ts +252 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/economics.ts +237 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/extension.ts +455 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/index.ts +49 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/plan.ts +79 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/state.ts +208 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/extensions/online-context-compact/tools.ts +100 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/index.ts +42 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/runtime-paths.ts +17 -0
- package/node_modules/@herbertgao/sol-pi/src/sol-pi/tui.ts +71 -0
- package/node_modules/@narumitw/pi-btw/dist/index.ts +12 -2
- package/node_modules/@narumitw/pi-btw/dist/index.ts.map +2 -2
- package/node_modules/@narumitw/pi-btw/package.json +1 -1
- package/node_modules/@narumitw/pi-btw/src/btw.ts +13 -2
- package/node_modules/pi-antigravity/LICENSE +21 -0
- package/node_modules/pi-antigravity/README.md +194 -0
- package/node_modules/pi-antigravity/package.json +67 -0
- package/node_modules/pi-antigravity/src/auth/index.ts +14 -0
- package/node_modules/pi-antigravity/src/auth/oauth.ts +442 -0
- package/node_modules/pi-antigravity/src/client/client.ts +561 -0
- package/node_modules/pi-antigravity/src/client/index.ts +1 -0
- package/node_modules/pi-antigravity/src/diagnostics/diagnostics.ts +96 -0
- package/node_modules/pi-antigravity/src/diagnostics/index.ts +1 -0
- package/node_modules/pi-antigravity/src/image/image.ts +336 -0
- package/node_modules/pi-antigravity/src/image/index.ts +1 -0
- package/node_modules/pi-antigravity/src/index.ts +280 -0
- package/node_modules/pi-antigravity/src/models/discovery.ts +154 -0
- package/node_modules/pi-antigravity/src/models/grouping.ts +424 -0
- package/node_modules/pi-antigravity/src/models/index.ts +3 -0
- package/node_modules/pi-antigravity/src/models/models.ts +500 -0
- package/node_modules/pi-antigravity/src/stream/index.ts +1 -0
- package/node_modules/pi-antigravity/src/stream/stream.ts +1460 -0
- package/node_modules/pi-antigravity/src/types/enums.ts +42 -0
- package/node_modules/pi-antigravity/src/types/index.ts +2 -0
- package/node_modules/pi-antigravity/src/types/types.ts +292 -0
- package/node_modules/pi-antigravity/src/usage/index.ts +1 -0
- package/node_modules/pi-antigravity/src/usage/usage.ts +371 -0
- package/node_modules/pi-antigravity/src/utils/http.ts +91 -0
- package/node_modules/pi-antigravity/src/utils/index.ts +3 -0
- package/node_modules/pi-antigravity/src/utils/security.ts +73 -0
- package/node_modules/pi-antigravity/src/utils/util.ts +132 -0
- package/node_modules/pi-antigravity/tsconfig.json +21 -0
- package/node_modules/pi-lens/CHANGELOG.md +176 -0
- package/node_modules/pi-lens/README.md +13 -8
- package/node_modules/pi-lens/config/dependency-cruiser-eager-allowlist.json +3 -1
- package/node_modules/pi-lens/dist/clients/analysed-root.js +1 -0
- package/node_modules/pi-lens/dist/clients/ast-grep-tool-logger.js +1 -1
- package/node_modules/pi-lens/dist/clients/biome-client.js +13 -2
- package/node_modules/pi-lens/dist/clients/bounded-telemetry.js +2 -0
- package/node_modules/pi-lens/dist/clients/cache-observability.js +122 -1
- package/node_modules/pi-lens/dist/clients/complexity-client.js +5 -0
- package/node_modules/pi-lens/dist/clients/config-diagnostic-codes.js +2 -0
- package/node_modules/pi-lens/dist/clients/config-resolve.js +6 -3
- package/node_modules/pi-lens/dist/clients/config-schema.js +17 -0
- package/node_modules/pi-lens/dist/clients/config-warn.js +2 -2
- package/node_modules/pi-lens/dist/clients/dead-code-client.js +8 -1
- package/node_modules/pi-lens/dist/clients/dependency-checker.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/dispatcher.js +38 -15
- package/node_modules/pi-lens/dist/clients/dispatch/facts/function-facts.js +1 -1
- package/node_modules/pi-lens/dist/clients/dispatch/facts/import-facts.js +5 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/actionlint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/biome-check.js +2 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cpp-check.js +4 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/credo.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/cue-vet.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dart-analyze.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/detekt.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/dotnet-build.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/elixir-check.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/eslint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/fish-indent.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/gleam-check.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/go-vet.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/golangci-lint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/hadolint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-lint.js +3 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/helm-render.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/htmlhint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/javac.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ktlint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/lsp.js +19 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/markdownlint.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/mypy.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/oxlint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/php-lint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/phpstan.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/prisma-validate.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/psscriptanalyzer.js +12 -12
- package/node_modules/pi-lens/dist/clients/dispatch/runners/pyright.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rubocop.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/ruff.js +2 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/rust-clippy.js +12 -11
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shellcheck.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/shfmt.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spellcheck.js +2 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/spotbugs.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/sqlfluff.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/stylelint.js +3 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/swiftlint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/taplo.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/terragrunt.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/tflint.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/trivy-config.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/availability-policy.js +5 -0
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/candidate-probe.js +2 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/utils/runner-helpers.js +8 -2
- package/node_modules/pi-lens/dist/clients/dispatch/runners/vale.js +2 -1
- package/node_modules/pi-lens/dist/clients/dispatch/runners/yamllint.js +4 -3
- package/node_modules/pi-lens/dist/clients/dispatch/runners/zig-check.js +2 -1
- package/node_modules/pi-lens/dist/clients/effective-config.js +12 -2
- package/node_modules/pi-lens/dist/clients/extension-log.js +2 -0
- package/node_modules/pi-lens/dist/clients/file-role.js +18 -1
- package/node_modules/pi-lens/dist/clients/finding-delivery-gate.js +4 -0
- package/node_modules/pi-lens/dist/clients/formatters.js +54 -188
- package/node_modules/pi-lens/dist/clients/generation-guard.js +4 -0
- package/node_modules/pi-lens/dist/clients/gitleaks-client.js +42 -6
- package/node_modules/pi-lens/dist/clients/govulncheck-client.js +48 -7
- package/node_modules/pi-lens/dist/clients/installer/index.js +196 -52
- package/node_modules/pi-lens/dist/clients/jscpd-client.js +9 -1
- package/node_modules/pi-lens/dist/clients/knip-client.js +9 -2
- package/node_modules/pi-lens/dist/clients/language-profile.js +12 -2
- package/node_modules/pi-lens/dist/clients/latency-logger.js +2 -0
- package/node_modules/pi-lens/dist/clients/lens-config.js +4 -0
- package/node_modules/pi-lens/dist/clients/lsp/config.js +36 -7
- package/node_modules/pi-lens/dist/clients/lsp/diagnostic-binding.js +24 -1
- package/node_modules/pi-lens/dist/clients/lsp/document-drift.js +78 -0
- package/node_modules/pi-lens/dist/clients/lsp/index.js +281 -45
- package/node_modules/pi-lens/dist/clients/lsp/server.js +62 -15
- package/node_modules/pi-lens/dist/clients/lsp/workspace-diagnostics-cache.js +37 -4
- package/node_modules/pi-lens/dist/clients/mcp/analyze.js +3 -0
- package/node_modules/pi-lens/dist/clients/mcp/session.js +2 -0
- package/node_modules/pi-lens/dist/clients/opengrep-client.js +2 -0
- package/node_modules/pi-lens/dist/clients/package-manager.js +2 -1
- package/node_modules/pi-lens/dist/clients/php-cs-fixer-config.js +8 -3
- package/node_modules/pi-lens/dist/clients/pipeline.js +3 -2
- package/node_modules/pi-lens/dist/clients/project-diagnostics/fresh-fetch.js +38 -10
- package/node_modules/pi-lens/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +10 -9
- package/node_modules/pi-lens/dist/clients/project-lens-config.js +3 -0
- package/node_modules/pi-lens/dist/clients/read-guard-logger.js +2 -0
- package/node_modules/pi-lens/dist/clients/review-graph-logger.js +2 -0
- package/node_modules/pi-lens/dist/clients/ruff-client.js +18 -9
- package/node_modules/pi-lens/dist/clients/runtime-agent-end.js +9 -0
- package/node_modules/pi-lens/dist/clients/runtime-coordinator.js +5 -0
- package/node_modules/pi-lens/dist/clients/runtime-session.js +13 -4
- package/node_modules/pi-lens/dist/clients/runtime-tool-result.js +4 -1
- package/node_modules/pi-lens/dist/clients/runtime-turn.js +48 -1
- package/node_modules/pi-lens/dist/clients/security-scan-client.js +2 -2
- package/node_modules/pi-lens/dist/clients/session-event-guard.js +69 -1
- package/node_modules/pi-lens/dist/clients/sg-runner.js +2 -1
- package/node_modules/pi-lens/dist/clients/situational-tool-telemetry.js +108 -0
- package/node_modules/pi-lens/dist/clients/test-runner-client.js +291 -76
- package/node_modules/pi-lens/dist/clients/tool-config.js +199 -0
- package/node_modules/pi-lens/dist/clients/tool-cwd.js +299 -0
- package/node_modules/pi-lens/dist/clients/tool-probe.js +53 -0
- package/node_modules/pi-lens/dist/clients/tool-set-policy.js +42 -0
- package/node_modules/pi-lens/dist/clients/trivy-client.js +9 -1
- package/node_modules/pi-lens/dist/clients/turn-context.js +52 -0
- package/node_modules/pi-lens/dist/clients/widget-state.js +4 -3
- package/node_modules/pi-lens/dist/index.js +21969 -20005
- package/node_modules/pi-lens/dist/mcp/analyze-cli.js +3 -2
- package/node_modules/pi-lens/dist/mcp/server.js +135 -159
- package/node_modules/pi-lens/dist/tools/activate-tools.js +10 -7
- package/node_modules/pi-lens/dist/tools/ast-grep-outline.js +2 -13
- package/node_modules/pi-lens/dist/tools/ast-grep-replace.js +8 -13
- package/node_modules/pi-lens/dist/tools/ast-grep-search.js +83 -39
- package/node_modules/pi-lens/dist/tools/effective-config.js +4 -3
- package/node_modules/pi-lens/dist/tools/lens-diagnostic-mark.js +1 -10
- package/node_modules/pi-lens/dist/tools/lens-diagnostics.js +353 -85
- package/node_modules/pi-lens/dist/tools/lsp-diagnostics.js +21 -110
- package/node_modules/pi-lens/dist/tools/lsp-navigation.js +20 -44
- package/node_modules/pi-lens/dist/tools/module-report.js +5 -9
- package/node_modules/pi-lens/dist/tools/project-report.js +9 -17
- package/node_modules/pi-lens/dist/tools/render-compact.js +280 -1
- package/node_modules/pi-lens/dist/tools/symbol-search.js +2 -2
- package/node_modules/pi-lens/docs/agent-guide.md +3 -4
- package/node_modules/pi-lens/docs/agent-tools.md +46 -22
- package/node_modules/pi-lens/docs/configuration.md +9 -0
- package/node_modules/pi-lens/docs/environment-variables.md +0 -5
- package/node_modules/pi-lens/docs/features.md +5 -5
- package/node_modules/pi-lens/docs/globalconfig.md +11 -1
- package/node_modules/pi-lens/docs/mcp.md +13 -1
- package/node_modules/pi-lens/docs/pi-lens-fixer.md +71 -0
- package/node_modules/pi-lens/docs/pi-lens-investigator.md +15 -0
- package/node_modules/pi-lens/docs/pi-lens-monitor.md +88 -0
- package/node_modules/pi-lens/docs/pi-lens-reviewer.md +35 -0
- package/node_modules/pi-lens/docs/pi-lens-subagent.md +12 -4
- package/node_modules/pi-lens/docs/public-api-stability.md +1 -0
- package/node_modules/pi-lens/docs/real-harness.md +46 -0
- package/node_modules/pi-lens/docs/release-qa-baseline.md +5 -1
- package/node_modules/pi-lens/docs/servercapabilities.md +1 -6
- package/node_modules/pi-lens/docs/settings.md +37 -2
- package/node_modules/pi-lens/docs/tools_improvement2.md +4 -4
- package/node_modules/pi-lens/docs/tree-sitter_rules_catalog.md +1 -1
- package/node_modules/pi-lens/docs/usage.md +13 -3
- package/node_modules/pi-lens/package.json +10 -1
- package/node_modules/pi-lens/rules/tree-sitter-queries/typescript/sql-injection.yml +7 -3
- package/node_modules/pi-lens/skills/pi-lens-ast-grep/SKILL.md +8 -6
- package/node_modules/pi-lens/skills/pi-lens-lsp-navigation/SKILL.md +19 -9
- package/node_modules/pi-web-access/CHANGELOG.md +29 -0
- package/node_modules/pi-web-access/README.md +33 -22
- package/node_modules/pi-web-access/abortable.ts +17 -0
- package/node_modules/pi-web-access/crawl4ai.ts +204 -0
- package/node_modules/pi-web-access/credential-source.ts +1 -0
- package/node_modules/pi-web-access/curator-page.ts +20 -2
- package/node_modules/pi-web-access/curator-run.ts +44 -0
- package/node_modules/pi-web-access/curator-server.ts +3 -1
- package/node_modules/pi-web-access/duckduckgo.ts +1 -1
- package/node_modules/pi-web-access/extract.ts +57 -12
- package/node_modules/pi-web-access/fetch-params.ts +1 -1
- package/node_modules/pi-web-access/gemini-search.ts +9 -5
- package/node_modules/pi-web-access/index.ts +25 -18
- package/node_modules/pi-web-access/openai-search.ts +35 -13
- package/node_modules/pi-web-access/package.json +2 -2
- package/node_modules/pi-web-access/page-query.ts +5 -2
- package/node_modules/pi-web-access/query-rewrite.ts +5 -2
- package/node_modules/pi-web-access/serpapi.ts +220 -0
- package/node_modules/pi-web-access/ssrf-protection.ts +5 -1
- package/node_modules/pi-web-access/storage.ts +10 -0
- package/node_modules/pi-web-access/summary-review.ts +34 -21
- package/node_modules/pi-web-access/utils.ts +6 -4
- package/package.json +11 -5
- package/node_modules/pi-lens/dist/tools/ast-dump.js +0 -103
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
3
|
+
import {
|
|
4
|
+
antigravityHeaders,
|
|
5
|
+
endpointCandidates,
|
|
6
|
+
jsonOrTextError,
|
|
7
|
+
parseApiKey,
|
|
8
|
+
} from "../client/client.js";
|
|
9
|
+
import { AntigravityRequestType, AntigravityUserAgent, GeminiRole } from "../types/enums.js";
|
|
10
|
+
import { antigravityFetch } from "../utils/http.js";
|
|
11
|
+
import { safeError } from "../utils/security.js";
|
|
12
|
+
import { antigravityRequestEnvelope, sanitizeText } from "../utils/util.js";
|
|
13
|
+
|
|
14
|
+
export const DEFAULT_IMAGE_MODEL = "gemini-3-pro-image";
|
|
15
|
+
export const IMAGE_ASPECT_RATIOS = [
|
|
16
|
+
"1:1",
|
|
17
|
+
"2:3",
|
|
18
|
+
"3:2",
|
|
19
|
+
"3:4",
|
|
20
|
+
"4:3",
|
|
21
|
+
"4:5",
|
|
22
|
+
"5:4",
|
|
23
|
+
"9:16",
|
|
24
|
+
"16:9",
|
|
25
|
+
"21:9",
|
|
26
|
+
] as const;
|
|
27
|
+
export type ImageAspectRatio = (typeof IMAGE_ASPECT_RATIOS)[number];
|
|
28
|
+
|
|
29
|
+
const IMAGE_MODEL_FALLBACKS = [
|
|
30
|
+
DEFAULT_IMAGE_MODEL,
|
|
31
|
+
"gemini-3.1-flash-image",
|
|
32
|
+
"gemini-3-pro-image-preview",
|
|
33
|
+
];
|
|
34
|
+
const IMAGE_SYSTEM_INSTRUCTION =
|
|
35
|
+
"You are an AI image generator. Generate images based on user descriptions. Focus on creating high-quality, visually appealing images that match the user's request.";
|
|
36
|
+
const DEFAULT_IMAGE_DIR = join(".pi", "generated-images");
|
|
37
|
+
const MAX_PROMPT_CHARS = 8000;
|
|
38
|
+
|
|
39
|
+
export type GeneratedImage = { data: string; mimeType: string };
|
|
40
|
+
|
|
41
|
+
export type ImageGenerateRequest = {
|
|
42
|
+
project: string;
|
|
43
|
+
model: string;
|
|
44
|
+
request: {
|
|
45
|
+
contents: Array<{ role: GeminiRole.User; parts: Array<{ text: string }> }>;
|
|
46
|
+
systemInstruction: { role: GeminiRole.User; parts: Array<{ text: string }> };
|
|
47
|
+
generationConfig: {
|
|
48
|
+
imageConfig: { aspectRatio: string };
|
|
49
|
+
candidateCount: number;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
requestType: AntigravityRequestType.Agent;
|
|
53
|
+
userAgent: AntigravityUserAgent.Antigravity;
|
|
54
|
+
requestId: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type ImageCommandArgs = {
|
|
58
|
+
prompt: string;
|
|
59
|
+
aspectRatio?: string;
|
|
60
|
+
model?: string;
|
|
61
|
+
path?: string;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export type GenerateImageOptions = ImageCommandArgs & {
|
|
65
|
+
apiKey: string;
|
|
66
|
+
cwd: string;
|
|
67
|
+
signal?: AbortSignal;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export type GenerateImageResult = {
|
|
71
|
+
images: GeneratedImage[];
|
|
72
|
+
savedPaths: string[];
|
|
73
|
+
text: string[];
|
|
74
|
+
model: string;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
type ImageStreamChunk = {
|
|
78
|
+
error?: { message?: string };
|
|
79
|
+
response?: {
|
|
80
|
+
candidates?: Array<{
|
|
81
|
+
content?: {
|
|
82
|
+
parts?: Array<{ text?: string; inlineData?: { mimeType?: string; data?: string } }>;
|
|
83
|
+
};
|
|
84
|
+
}>;
|
|
85
|
+
};
|
|
86
|
+
candidates?: Array<{
|
|
87
|
+
content?: {
|
|
88
|
+
parts?: Array<{ text?: string; inlineData?: { mimeType?: string; data?: string } }>;
|
|
89
|
+
};
|
|
90
|
+
}>;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
function imageExtension(mimeType: string): string {
|
|
94
|
+
const lower = mimeType.toLowerCase();
|
|
95
|
+
if (lower.includes("jpeg") || lower.includes("jpg")) return "jpg";
|
|
96
|
+
if (lower.includes("webp")) return "webp";
|
|
97
|
+
if (lower.includes("gif")) return "gif";
|
|
98
|
+
return "png";
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function assertSafeImageModel(modelId: string): string {
|
|
102
|
+
const id = modelId.trim();
|
|
103
|
+
if (id.length === 0 || id.length > 80) {
|
|
104
|
+
throw new Error("Unsupported image model id.");
|
|
105
|
+
}
|
|
106
|
+
if (!/^(gemini-[a-z0-9.+-]*image[a-z0-9.+-]*|imagen-[a-z0-9.+-]+)$/i.test(id)) {
|
|
107
|
+
throw new Error(`Unsupported image model: ${id}`);
|
|
108
|
+
}
|
|
109
|
+
return id;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function assertSafeAspectRatio(ratio: string): ImageAspectRatio {
|
|
113
|
+
const value = ratio.trim();
|
|
114
|
+
for (const allowed of IMAGE_ASPECT_RATIOS) {
|
|
115
|
+
if (allowed === value) return allowed;
|
|
116
|
+
}
|
|
117
|
+
throw new Error(
|
|
118
|
+
`Unsupported aspect ratio: ${value}. Use one of ${IMAGE_ASPECT_RATIOS.join(", ")}.`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function parseImageCommandArgs(args: string): ImageCommandArgs {
|
|
123
|
+
const tokens = args.trim().split(/\s+/).filter(Boolean);
|
|
124
|
+
const out: ImageCommandArgs = { prompt: "" };
|
|
125
|
+
const rest: string[] = [];
|
|
126
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
127
|
+
const token = tokens[i];
|
|
128
|
+
if (token === undefined) continue;
|
|
129
|
+
const next = tokens[i + 1];
|
|
130
|
+
if ((token === "--ratio" || token === "--aspect-ratio") && next) {
|
|
131
|
+
out.aspectRatio = next;
|
|
132
|
+
i += 1;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (token === "--model" && next) {
|
|
136
|
+
out.model = next;
|
|
137
|
+
i += 1;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (token === "--path" && next) {
|
|
141
|
+
out.path = next;
|
|
142
|
+
i += 1;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
rest.push(token);
|
|
146
|
+
}
|
|
147
|
+
out.prompt = rest.join(" ");
|
|
148
|
+
return out;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function resolveImageSavePath(
|
|
152
|
+
cwd: string,
|
|
153
|
+
requested?: string,
|
|
154
|
+
mimeType = "image/png",
|
|
155
|
+
index?: number,
|
|
156
|
+
): string {
|
|
157
|
+
const ext = imageExtension(mimeType);
|
|
158
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
159
|
+
const suffix = index === undefined ? "" : `-${index + 1}`;
|
|
160
|
+
const defaultName = `image-${stamp}${suffix}.${ext}`;
|
|
161
|
+
const root = resolve(cwd);
|
|
162
|
+
const target = requested?.trim()
|
|
163
|
+
? resolve(root, requested.trim())
|
|
164
|
+
: resolve(root, DEFAULT_IMAGE_DIR, defaultName);
|
|
165
|
+
const rel = relative(root, target);
|
|
166
|
+
if (!rel || rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
|
|
167
|
+
throw new Error("Image save path must be inside the working directory.");
|
|
168
|
+
}
|
|
169
|
+
if (!extname(target)) return join(target, defaultName);
|
|
170
|
+
if (index === undefined) return target;
|
|
171
|
+
const currentExt = extname(target);
|
|
172
|
+
return `${target.slice(0, -currentExt.length)}${suffix}${currentExt}`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function buildImageGenerateRequest(
|
|
176
|
+
prompt: string,
|
|
177
|
+
model: string,
|
|
178
|
+
projectId: string,
|
|
179
|
+
aspectRatio: string,
|
|
180
|
+
): ImageGenerateRequest {
|
|
181
|
+
const envelope = antigravityRequestEnvelope(model, false);
|
|
182
|
+
return {
|
|
183
|
+
project: projectId,
|
|
184
|
+
model,
|
|
185
|
+
request: {
|
|
186
|
+
contents: [{ role: GeminiRole.User, parts: [{ text: sanitizeText(prompt) }] }],
|
|
187
|
+
systemInstruction: {
|
|
188
|
+
role: GeminiRole.User,
|
|
189
|
+
parts: [{ text: IMAGE_SYSTEM_INSTRUCTION }],
|
|
190
|
+
},
|
|
191
|
+
generationConfig: {
|
|
192
|
+
imageConfig: { aspectRatio },
|
|
193
|
+
candidateCount: 1,
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
requestType: AntigravityRequestType.Agent,
|
|
197
|
+
userAgent: AntigravityUserAgent.Antigravity,
|
|
198
|
+
requestId: envelope.requestId,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function collectImagesFromParts(
|
|
203
|
+
parts: Array<{ text?: string; inlineData?: { mimeType?: string; data?: string } }> | undefined,
|
|
204
|
+
images: GeneratedImage[],
|
|
205
|
+
text: string[],
|
|
206
|
+
): void {
|
|
207
|
+
for (const part of parts || []) {
|
|
208
|
+
if (part.text) text.push(part.text);
|
|
209
|
+
if (part.inlineData?.data) {
|
|
210
|
+
images.push({
|
|
211
|
+
data: part.inlineData.data,
|
|
212
|
+
mimeType: part.inlineData.mimeType || "image/png",
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export async function collectImagesFromSse(
|
|
219
|
+
response: Response,
|
|
220
|
+
signal?: AbortSignal,
|
|
221
|
+
): Promise<{ images: GeneratedImage[]; text: string[] }> {
|
|
222
|
+
if (!response.body) throw new Error("No response body");
|
|
223
|
+
const reader = response.body.getReader();
|
|
224
|
+
const decoder = new TextDecoder();
|
|
225
|
+
let buffer = "";
|
|
226
|
+
const images: GeneratedImage[] = [];
|
|
227
|
+
const text: string[] = [];
|
|
228
|
+
try {
|
|
229
|
+
while (true) {
|
|
230
|
+
if (signal?.aborted) throw new Error("Request was aborted");
|
|
231
|
+
const result = await reader.read();
|
|
232
|
+
if (result.done) break;
|
|
233
|
+
if (!(result.value instanceof Uint8Array)) continue;
|
|
234
|
+
buffer += decoder.decode(result.value, { stream: true });
|
|
235
|
+
const lines = buffer.split("\n");
|
|
236
|
+
buffer = lines.pop() || "";
|
|
237
|
+
for (const line of lines) {
|
|
238
|
+
if (!line.startsWith("data:")) continue;
|
|
239
|
+
const json = line.slice(5).trim();
|
|
240
|
+
if (!json || json === "[DONE]") continue;
|
|
241
|
+
let chunk: ImageStreamChunk;
|
|
242
|
+
try {
|
|
243
|
+
chunk = JSON.parse(json) as ImageStreamChunk;
|
|
244
|
+
} catch {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (chunk.error?.message) throw new Error(chunk.error.message);
|
|
248
|
+
const responseData = chunk.response || chunk;
|
|
249
|
+
for (const candidate of responseData.candidates || []) {
|
|
250
|
+
collectImagesFromParts(candidate.content?.parts, images, text);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
} finally {
|
|
255
|
+
reader.releaseLock();
|
|
256
|
+
}
|
|
257
|
+
return { images, text };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function writeImage(filePath: string, image: GeneratedImage): Promise<string> {
|
|
261
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
262
|
+
await writeFile(filePath, Buffer.from(image.data, "base64"));
|
|
263
|
+
return filePath;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export async function generateAntigravityImage(
|
|
267
|
+
options: GenerateImageOptions,
|
|
268
|
+
): Promise<GenerateImageResult> {
|
|
269
|
+
const prompt = options.prompt.trim();
|
|
270
|
+
if (!prompt) throw new Error("Image prompt is required.");
|
|
271
|
+
if (prompt.length > MAX_PROMPT_CHARS) {
|
|
272
|
+
throw new Error(`Image prompt is too long (max ${MAX_PROMPT_CHARS} characters).`);
|
|
273
|
+
}
|
|
274
|
+
const aspectRatio = assertSafeAspectRatio(options.aspectRatio || "1:1");
|
|
275
|
+
const preferred = assertSafeImageModel(options.model || DEFAULT_IMAGE_MODEL);
|
|
276
|
+
const models = [preferred, ...IMAGE_MODEL_FALLBACKS.filter((id) => id !== preferred)];
|
|
277
|
+
const creds = parseApiKey(options.apiKey);
|
|
278
|
+
const headers = antigravityHeaders(creds.token);
|
|
279
|
+
|
|
280
|
+
let lastError = "no endpoint available";
|
|
281
|
+
for (const model of models) {
|
|
282
|
+
const body = JSON.stringify(
|
|
283
|
+
buildImageGenerateRequest(prompt, model, creds.projectId, aspectRatio),
|
|
284
|
+
);
|
|
285
|
+
for (const endpoint of endpointCandidates()) {
|
|
286
|
+
if (options.signal?.aborted) throw new Error("Request was aborted");
|
|
287
|
+
try {
|
|
288
|
+
const response = await antigravityFetch(
|
|
289
|
+
`${endpoint}/v1internal:streamGenerateContent?alt=sse`,
|
|
290
|
+
{
|
|
291
|
+
method: "POST",
|
|
292
|
+
headers,
|
|
293
|
+
body,
|
|
294
|
+
signal: options.signal,
|
|
295
|
+
},
|
|
296
|
+
);
|
|
297
|
+
if (!response.ok) {
|
|
298
|
+
lastError = jsonOrTextError(await response.text()).slice(0, 400);
|
|
299
|
+
if (response.status === 404 || [403, 429, 500, 502, 503, 504].includes(response.status)) {
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
throw new Error(
|
|
303
|
+
`Antigravity image request failed (${response.status}): ${safeError(lastError)}`,
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
const parsed = await collectImagesFromSse(response, options.signal);
|
|
307
|
+
if (!parsed.images.length) {
|
|
308
|
+
lastError = parsed.text.join(" ").trim() || "No image data returned.";
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
const savedPaths: string[] = [];
|
|
312
|
+
const many = parsed.images.length > 1;
|
|
313
|
+
for (const [index, image] of parsed.images.entries()) {
|
|
314
|
+
savedPaths.push(
|
|
315
|
+
await writeImage(
|
|
316
|
+
resolveImageSavePath(
|
|
317
|
+
options.cwd,
|
|
318
|
+
options.path,
|
|
319
|
+
image.mimeType,
|
|
320
|
+
many ? index : undefined,
|
|
321
|
+
),
|
|
322
|
+
image,
|
|
323
|
+
),
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
return { images: parsed.images, savedPaths, text: parsed.text, model };
|
|
327
|
+
} catch (error) {
|
|
328
|
+
lastError = safeError(error);
|
|
329
|
+
if (options.signal?.aborted) {
|
|
330
|
+
throw new Error("Request was aborted", { cause: error });
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
throw new Error(`Antigravity image generation failed: ${safeError(lastError)}`);
|
|
336
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./image.js";
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { StringEnum, Type } from "@earendil-works/pi-ai";
|
|
3
|
+
import { registerApiProvider } from "@earendil-works/pi-ai/compat";
|
|
4
|
+
import { getApiKey, loginAntigravity, refreshAntigravityToken } from "./auth/index.js";
|
|
5
|
+
import { DEFAULT_ENDPOINT, endpointCandidates } from "./client/index.js";
|
|
6
|
+
import { getLastDiagnostics, runWithDiagnostics } from "./diagnostics/index.js";
|
|
7
|
+
import {
|
|
8
|
+
DEFAULT_IMAGE_MODEL,
|
|
9
|
+
generateAntigravityImage,
|
|
10
|
+
IMAGE_ASPECT_RATIOS,
|
|
11
|
+
parseImageCommandArgs,
|
|
12
|
+
} from "./image/index.js";
|
|
13
|
+
import {
|
|
14
|
+
applyAntigravityCatalog,
|
|
15
|
+
discoverAntigravityModels,
|
|
16
|
+
getCurrentAntigravityCatalog,
|
|
17
|
+
PROVIDER_ID,
|
|
18
|
+
PROVIDER_NAME,
|
|
19
|
+
refreshAntigravityModels,
|
|
20
|
+
resolvedCatalog,
|
|
21
|
+
} from "./models/index.js";
|
|
22
|
+
import { ANTIGRAVITY_API, streamAntigravity } from "./stream/index.js";
|
|
23
|
+
import {
|
|
24
|
+
fetchAccountUsage,
|
|
25
|
+
formatModelsList,
|
|
26
|
+
formatUsageSummary,
|
|
27
|
+
resolveApiKeyFromContext,
|
|
28
|
+
} from "./usage/index.js";
|
|
29
|
+
import { prewarmConnection, redactSecrets } from "./utils/index.js";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Pi's interactive `notify` writes into the chat transcript. `console.log` in that
|
|
33
|
+
* mode prints to the raw terminal and paints over the TUI. Use one channel only.
|
|
34
|
+
*/
|
|
35
|
+
function emitCommandOutput(
|
|
36
|
+
ctx: ExtensionCommandContext,
|
|
37
|
+
text: string,
|
|
38
|
+
type: "info" | "warning" | "error" = "info",
|
|
39
|
+
): void {
|
|
40
|
+
if (ctx.hasUI) {
|
|
41
|
+
ctx.ui.notify(text, type);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (type === "warning" || type === "error") console.error(text);
|
|
45
|
+
else console.log(text);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function withUsage(
|
|
49
|
+
ctx: ExtensionCommandContext,
|
|
50
|
+
fn: (usage: Awaited<ReturnType<typeof fetchAccountUsage>>) => string,
|
|
51
|
+
): Promise<void> {
|
|
52
|
+
try {
|
|
53
|
+
const apiKey = await resolveApiKeyFromContext(ctx);
|
|
54
|
+
if (!apiKey) {
|
|
55
|
+
emitCommandOutput(
|
|
56
|
+
ctx,
|
|
57
|
+
"No Antigravity credentials. Run /login antigravity first.",
|
|
58
|
+
"warning",
|
|
59
|
+
);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (ctx.hasUI) ctx.ui.notify("Fetching Antigravity usage…", "info");
|
|
63
|
+
const usage = await runWithDiagnostics(() => fetchAccountUsage(apiKey));
|
|
64
|
+
emitCommandOutput(ctx, fn(usage));
|
|
65
|
+
} catch (error) {
|
|
66
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
67
|
+
emitCommandOutput(ctx, `Antigravity usage failed: ${msg}`, "warning");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default function (pi: ExtensionAPI): void {
|
|
72
|
+
// Open the TLS connection up front so the first message of a session does not pay
|
|
73
|
+
// the handshake. Opt out with ANTIGRAVITY_NO_PREWARM=1.
|
|
74
|
+
const primaryEndpoint = endpointCandidates()[0];
|
|
75
|
+
if (primaryEndpoint) prewarmConnection(primaryEndpoint);
|
|
76
|
+
|
|
77
|
+
registerApiProvider({
|
|
78
|
+
api: ANTIGRAVITY_API,
|
|
79
|
+
stream: streamAntigravity,
|
|
80
|
+
streamSimple: streamAntigravity,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const initialCatalog = getCurrentAntigravityCatalog();
|
|
84
|
+
|
|
85
|
+
pi.registerProvider(PROVIDER_ID, {
|
|
86
|
+
name: PROVIDER_NAME,
|
|
87
|
+
baseUrl: DEFAULT_ENDPOINT,
|
|
88
|
+
api: ANTIGRAVITY_API,
|
|
89
|
+
models: initialCatalog.models,
|
|
90
|
+
refreshModels: refreshAntigravityModels,
|
|
91
|
+
oauth: {
|
|
92
|
+
name: PROVIDER_NAME,
|
|
93
|
+
login: loginAntigravity,
|
|
94
|
+
refreshToken: refreshAntigravityToken,
|
|
95
|
+
getApiKey,
|
|
96
|
+
},
|
|
97
|
+
streamSimple: streamAntigravity,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
pi.registerCommand("antigravity.usage", {
|
|
101
|
+
description: "Show Antigravity shared quota pools (Gemini / Claude+GPT, 5h + weekly)",
|
|
102
|
+
handler: async (_args, ctx) => {
|
|
103
|
+
await withUsage(ctx, formatUsageSummary);
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
pi.registerCommand("antigravity.models", {
|
|
108
|
+
description: "List Antigravity runtime models + remaining pool fraction",
|
|
109
|
+
handler: async (args, ctx) => {
|
|
110
|
+
const all = /\ball\b/i.test(args || "");
|
|
111
|
+
await withUsage(ctx, (usage) => formatModelsList(usage, { all }));
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
pi.registerCommand("antigravity.refresh", {
|
|
116
|
+
description: "Force refresh Antigravity dynamic model catalog",
|
|
117
|
+
handler: async (_args, ctx) => {
|
|
118
|
+
const apiKey = await resolveApiKeyFromContext(ctx);
|
|
119
|
+
if (!apiKey) {
|
|
120
|
+
emitCommandOutput(
|
|
121
|
+
ctx,
|
|
122
|
+
"No Antigravity credentials. Run /login antigravity first.",
|
|
123
|
+
"warning",
|
|
124
|
+
);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (ctx.hasUI) ctx.ui.notify("Refreshing Antigravity models…", "info");
|
|
128
|
+
try {
|
|
129
|
+
if (typeof ctx.modelRegistry?.refresh === "function") {
|
|
130
|
+
const result = await ctx.modelRegistry.refresh({
|
|
131
|
+
force: true,
|
|
132
|
+
providers: [PROVIDER_ID],
|
|
133
|
+
});
|
|
134
|
+
if (result?.errors?.has(PROVIDER_ID)) {
|
|
135
|
+
throw result.errors.get(PROVIDER_ID)!;
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
const discovered = await discoverAntigravityModels(apiKey);
|
|
139
|
+
const next = resolvedCatalog(discovered, getCurrentAntigravityCatalog());
|
|
140
|
+
if (discovered.models.length > 0) {
|
|
141
|
+
applyAntigravityCatalog(next);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const catalog = getCurrentAntigravityCatalog();
|
|
145
|
+
const count = catalog.models.length;
|
|
146
|
+
const sample = catalog.models
|
|
147
|
+
.slice(0, 4)
|
|
148
|
+
.map((m) => m.name || m.id)
|
|
149
|
+
.join(", ");
|
|
150
|
+
emitCommandOutput(
|
|
151
|
+
ctx,
|
|
152
|
+
`Antigravity models refreshed (${count} available: ${sample}${count > 4 ? ", …" : ""})`,
|
|
153
|
+
"info",
|
|
154
|
+
);
|
|
155
|
+
} catch (error) {
|
|
156
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
157
|
+
emitCommandOutput(ctx, `Antigravity model refresh failed: ${redactSecrets(msg)}`, "error");
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
pi.registerCommand("antigravity.doctor", {
|
|
163
|
+
description: "Show sanitized Antigravity provider diagnostics",
|
|
164
|
+
handler: async (_args, ctx) => {
|
|
165
|
+
const d = getLastDiagnostics();
|
|
166
|
+
const lines = [
|
|
167
|
+
`provider=${PROVIDER_ID}`,
|
|
168
|
+
`lastResolvedRuntimeModel=${d.resolvedRuntimeModel || "none"}`,
|
|
169
|
+
`availableModels=${d.availableModels || "none"}`,
|
|
170
|
+
`matchedModel=${d.matchedModelDebug || "none"}`,
|
|
171
|
+
`lastEndpoint=${d.endpoint || "none"}`,
|
|
172
|
+
`lastStatus=${d.status ?? "none"}`,
|
|
173
|
+
`lastProjectId=${d.projectId || "none"}`,
|
|
174
|
+
...(d.latencyMs !== undefined ? [`lastLatencyMs=${d.latencyMs}`] : []),
|
|
175
|
+
`toolSchemaWarnings=${d.toolSchemaWarnings || "none"}`,
|
|
176
|
+
`lastError=${d.error ? redactSecrets(d.error) : "none"}`,
|
|
177
|
+
"transport=native-streamSimple",
|
|
178
|
+
"runtimeCli=not-used",
|
|
179
|
+
"commands=/antigravity.usage /antigravity.models /antigravity.refresh /antigravity.doctor /antigravity.image",
|
|
180
|
+
];
|
|
181
|
+
emitCommandOutput(ctx, `Antigravity doctor\n${lines.join("\n")}`);
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
pi.registerCommand("antigravity.image", {
|
|
186
|
+
description:
|
|
187
|
+
"Generate an image via Antigravity (usage: /antigravity.image [--ratio 16:9] <prompt>)",
|
|
188
|
+
handler: async (args, ctx) => {
|
|
189
|
+
const parsed = parseImageCommandArgs(args || "");
|
|
190
|
+
if (!parsed.prompt) {
|
|
191
|
+
emitCommandOutput(
|
|
192
|
+
ctx,
|
|
193
|
+
"Usage: /antigravity.image [--ratio 16:9] [--model gemini-3-pro-image] [--path file.png] <prompt>",
|
|
194
|
+
"warning",
|
|
195
|
+
);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
try {
|
|
199
|
+
const apiKey = await resolveApiKeyFromContext(ctx);
|
|
200
|
+
if (!apiKey) {
|
|
201
|
+
emitCommandOutput(
|
|
202
|
+
ctx,
|
|
203
|
+
"No Antigravity credentials. Run /login antigravity first.",
|
|
204
|
+
"warning",
|
|
205
|
+
);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (ctx.hasUI) ctx.ui.notify("Generating Antigravity image…", "info");
|
|
209
|
+
const result = await generateAntigravityImage({
|
|
210
|
+
apiKey,
|
|
211
|
+
cwd: ctx.cwd,
|
|
212
|
+
prompt: parsed.prompt,
|
|
213
|
+
aspectRatio: parsed.aspectRatio,
|
|
214
|
+
model: parsed.model,
|
|
215
|
+
path: parsed.path,
|
|
216
|
+
});
|
|
217
|
+
emitCommandOutput(ctx, `Saved image to ${result.savedPaths.join(", ")}`);
|
|
218
|
+
} catch (error) {
|
|
219
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
220
|
+
emitCommandOutput(ctx, `Antigravity image failed: ${redactSecrets(msg)}`, "warning");
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
pi.registerTool({
|
|
226
|
+
name: "generate_image",
|
|
227
|
+
label: "Generate image",
|
|
228
|
+
description:
|
|
229
|
+
"Generate an image via Antigravity using the signed-in Google account. Saves under .pi/generated-images/ unless path is set.",
|
|
230
|
+
promptSnippet: "Generate images via Antigravity OAuth (Gemini image models)",
|
|
231
|
+
promptGuidelines: [
|
|
232
|
+
"Use generate_image when the user asks to create, draw, or generate an image.",
|
|
233
|
+
],
|
|
234
|
+
parameters: Type.Object({
|
|
235
|
+
prompt: Type.String({ description: "Image description." }),
|
|
236
|
+
aspectRatio: Type.Optional(StringEnum(IMAGE_ASPECT_RATIOS)),
|
|
237
|
+
model: Type.Optional(
|
|
238
|
+
Type.String({
|
|
239
|
+
description: `Image model id. Default: ${DEFAULT_IMAGE_MODEL}.`,
|
|
240
|
+
}),
|
|
241
|
+
),
|
|
242
|
+
path: Type.Optional(
|
|
243
|
+
Type.String({
|
|
244
|
+
description: "Project-relative file or directory to save the image.",
|
|
245
|
+
}),
|
|
246
|
+
),
|
|
247
|
+
}),
|
|
248
|
+
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
249
|
+
const apiKey = await ctx.modelRegistry.getApiKeyForProvider("antigravity");
|
|
250
|
+
if (!apiKey) {
|
|
251
|
+
throw new Error("No Antigravity credentials. Run /login antigravity first.");
|
|
252
|
+
}
|
|
253
|
+
onUpdate?.({ content: [{ type: "text", text: "Generating image…" }], details: {} });
|
|
254
|
+
const result = await generateAntigravityImage({
|
|
255
|
+
apiKey,
|
|
256
|
+
cwd: ctx.cwd,
|
|
257
|
+
prompt: params.prompt,
|
|
258
|
+
aspectRatio: params.aspectRatio,
|
|
259
|
+
model: params.model,
|
|
260
|
+
path: params.path,
|
|
261
|
+
signal,
|
|
262
|
+
});
|
|
263
|
+
const notes = result.text.join(" ").trim();
|
|
264
|
+
return {
|
|
265
|
+
content: [
|
|
266
|
+
{
|
|
267
|
+
type: "text" as const,
|
|
268
|
+
text: `Saved image to ${result.savedPaths.join(", ")}${notes ? `. ${notes}` : ""}`,
|
|
269
|
+
},
|
|
270
|
+
...result.images.map((image) => ({
|
|
271
|
+
type: "image" as const,
|
|
272
|
+
data: image.data,
|
|
273
|
+
mimeType: image.mimeType,
|
|
274
|
+
})),
|
|
275
|
+
],
|
|
276
|
+
details: { model: result.model, savedPaths: result.savedPaths },
|
|
277
|
+
};
|
|
278
|
+
},
|
|
279
|
+
});
|
|
280
|
+
}
|