@herbertgao/pi-extensions 2026.9.18 → 2026.9.20
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 +20 -28
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +76 -59
- package/node_modules/@herbertgao/pi-cc-extensions/README.md +76 -59
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/preview.webp +0 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/config.ts +93 -34
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +346 -117
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/core.ts +6 -36
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary/index.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +24 -29
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +330 -360
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/index.ts +56 -108
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/session.ts +97 -15
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/reference/subagent.ts +80 -45
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/flush-docked-bash.ts +36 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer-layout.ts +229 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/footer.ts +479 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/startup-header.ts +92 -61
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/shell/working-message.ts +75 -40
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/index.ts +19 -3
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +1080 -229
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/default-mode.ts +232 -259
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +42 -41
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/markdown-enhance.ts +8 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/hover.ts +2 -7
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/interaction.ts +277 -113
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/layout.ts +15 -6
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/packets.ts +18 -72
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse/scroll.ts +61 -26
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/ansi-utils.ts +25 -57
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-component.ts +125 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-edit-render.ts +227 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-header.ts +136 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-highlight.ts +115 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-inline.ts +324 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-layout.ts +762 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-limits.ts +152 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-palette.ts +410 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-parse.ts +535 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-presentation.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-renderer.ts +21 -3219
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-text.ts +44 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/diff-write-render.ts +568 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/index.ts +47 -10
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/line-width-safety.ts +2 -2
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-execution.ts +1 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/grouping.ts +145 -190
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/mcp-title.ts +39 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{message-display.ts → tool/message-display.ts} +24 -90
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/names.ts +426 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/result.ts +90 -132
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/{show-more-hint.ts → tool/show-more-hint.ts} +10 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/transcript-refresh.ts +8 -26
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-color.ts +44 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/ansi-text.ts +76 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/component-tree.ts +34 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/format.ts +28 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/fullscreen-detect.ts +51 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/patch-keys.ts +124 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/sgr-mouse.ts +70 -0
- package/node_modules/@herbertgao/pi-cc-extensions/package.json +8 -8
- package/node_modules/pi-antigravity/README.md +29 -6
- package/node_modules/pi-antigravity/package.json +12 -7
- package/node_modules/pi-antigravity/src/auth/accounts.ts +327 -0
- package/node_modules/pi-antigravity/src/auth/index.ts +9 -0
- package/node_modules/pi-antigravity/src/client/client.ts +1 -1
- package/node_modules/pi-antigravity/src/image/image.ts +4 -4
- package/node_modules/pi-antigravity/src/index.ts +203 -13
- package/node_modules/pi-antigravity/src/search/index.ts +1 -0
- package/node_modules/pi-antigravity/src/search/search.ts +325 -0
- package/node_modules/pi-antigravity/src/stream/stream.ts +191 -14
- package/node_modules/pi-antigravity/src/types/enums.ts +45 -36
- package/node_modules/pi-antigravity/src/types/types.ts +5 -12
- package/node_modules/pi-antigravity/src/utils/http.ts +7 -2
- package/node_modules/pi-antigravity/src/utils/util.ts +31 -3
- package/node_modules/pi-typesafe/README.md +2 -0
- package/node_modules/pi-typesafe/package.json +2 -1
- package/node_modules/pi-web-access/CHANGELOG.md +72 -0
- package/node_modules/pi-web-access/README.md +34 -13
- package/node_modules/pi-web-access/chrome-cookies.ts +79 -25
- package/node_modules/pi-web-access/curator-page.ts +5 -1
- package/node_modules/pi-web-access/curator-server.ts +2 -0
- package/node_modules/pi-web-access/dist/index.js +1337 -1009
- package/node_modules/pi-web-access/exa.ts +20 -52
- package/node_modules/pi-web-access/extract.ts +41 -4
- package/node_modules/pi-web-access/gemini-search.ts +12 -4
- package/node_modules/pi-web-access/index.ts +29 -37
- package/node_modules/pi-web-access/openai-search.ts +35 -7
- package/node_modules/pi-web-access/package.json +6 -5
- package/node_modules/pi-web-access/page-query.ts +2 -2
- package/node_modules/pi-web-access/query-rewrite.ts +2 -3
- package/node_modules/pi-web-access/ssrf-protection.ts +27 -10
- package/node_modules/pi-web-access/storage.ts +48 -77
- package/node_modules/pi-web-access/summary-model-scope.ts +6 -77
- package/node_modules/pi-web-access/summary-review.ts +3 -4
- package/node_modules/pi-web-access/tavily.ts +67 -20
- package/node_modules/pi-web-access/tool-activation.ts +53 -33
- package/node_modules/pi-web-access/utils.ts +1 -1
- package/node_modules/pi-web-access/you.ts +164 -0
- package/node_modules/pi-web-access/zai.ts +161 -0
- package/package.json +7 -11
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.en.svg +0 -101
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/hero.svg +0 -101
- package/node_modules/@herbertgao/pi-cc-extensions/assets/readme/welcome.webp +0 -0
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/render-utils.ts +0 -5
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/types.ts +0 -30
- package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool/diff/write-display-utils.ts +0 -12
- package/node_modules/pi-footer/CHANGELOG.md +0 -145
- package/node_modules/pi-footer/LICENSE +0 -21
- package/node_modules/pi-footer/README.md +0 -354
- package/node_modules/pi-footer/package.json +0 -68
- package/node_modules/pi-footer/src/cache.ts +0 -106
- package/node_modules/pi-footer/src/colors.ts +0 -237
- package/node_modules/pi-footer/src/config.ts +0 -202
- package/node_modules/pi-footer/src/event-widgets.ts +0 -41
- package/node_modules/pi-footer/src/extension-statuses.ts +0 -102
- package/node_modules/pi-footer/src/git.ts +0 -135
- package/node_modules/pi-footer/src/index.ts +0 -268
- package/node_modules/pi-footer/src/metrics.ts +0 -141
- package/node_modules/pi-footer/src/presets.ts +0 -383
- package/node_modules/pi-footer/src/render.ts +0 -114
- package/node_modules/pi-footer/src/separators.ts +0 -52
- package/node_modules/pi-footer/src/types.ts +0 -122
- package/node_modules/pi-footer/src/ui/color-level-confirm.ts +0 -22
- package/node_modules/pi-footer/src/ui/color-options.ts +0 -195
- package/node_modules/pi-footer/src/ui/config-lifecycle.ts +0 -61
- package/node_modules/pi-footer/src/ui/edit-colors.ts +0 -13
- package/node_modules/pi-footer/src/ui/events.ts +0 -35
- package/node_modules/pi-footer/src/ui/extension-status-picker.ts +0 -60
- package/node_modules/pi-footer/src/ui/extension-statuses.ts +0 -73
- package/node_modules/pi-footer/src/ui/fields.ts +0 -252
- package/node_modules/pi-footer/src/ui/global-menu.ts +0 -126
- package/node_modules/pi-footer/src/ui/helpers.ts +0 -44
- package/node_modules/pi-footer/src/ui/layout.ts +0 -17
- package/node_modules/pi-footer/src/ui/line-list.ts +0 -19
- package/node_modules/pi-footer/src/ui/model.ts +0 -64
- package/node_modules/pi-footer/src/ui/navigation.ts +0 -29
- package/node_modules/pi-footer/src/ui/option-edit.ts +0 -93
- package/node_modules/pi-footer/src/ui/overlay-render.ts +0 -103
- package/node_modules/pi-footer/src/ui/screen-context.ts +0 -18
- package/node_modules/pi-footer/src/ui/screen-controller.ts +0 -27
- package/node_modules/pi-footer/src/ui/screen-render.ts +0 -32
- package/node_modules/pi-footer/src/ui/screen-state.ts +0 -20
- package/node_modules/pi-footer/src/ui/screen.ts +0 -243
- package/node_modules/pi-footer/src/ui/screens/add-widget.ts +0 -92
- package/node_modules/pi-footer/src/ui/screens/confirm-exit.ts +0 -52
- package/node_modules/pi-footer/src/ui/screens/controller.ts +0 -12
- package/node_modules/pi-footer/src/ui/screens/edit-colors.ts +0 -68
- package/node_modules/pi-footer/src/ui/screens/edit-widget.ts +0 -115
- package/node_modules/pi-footer/src/ui/screens/extension-status-row.ts +0 -61
- package/node_modules/pi-footer/src/ui/screens/global.ts +0 -75
- package/node_modules/pi-footer/src/ui/screens/line-list.ts +0 -108
- package/node_modules/pi-footer/src/ui/screens/main.ts +0 -99
- package/node_modules/pi-footer/src/ui/screens/terminal.ts +0 -121
- package/node_modules/pi-footer/src/ui/screens/widget-list.ts +0 -154
- package/node_modules/pi-footer/src/ui/terminal-menu.ts +0 -32
- package/node_modules/pi-footer/src/ui/theme.ts +0 -48
- package/node_modules/pi-footer/src/ui/title-bar.ts +0 -100
- package/node_modules/pi-footer/src/ui/widget-actions.ts +0 -76
- package/node_modules/pi-footer/src/ui.ts +0 -66
- package/node_modules/pi-footer/src/widgets/context.ts +0 -28
- package/node_modules/pi-footer/src/widgets/core/active-tools.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/context-window.ts +0 -24
- package/node_modules/pi-footer/src/widgets/core/cwd-basename.ts +0 -19
- package/node_modules/pi-footer/src/widgets/core/cwd.ts +0 -108
- package/node_modules/pi-footer/src/widgets/core/event.ts +0 -34
- package/node_modules/pi-footer/src/widgets/core/external-status.ts +0 -102
- package/node_modules/pi-footer/src/widgets/core/model-provider.ts +0 -20
- package/node_modules/pi-footer/src/widgets/core/model.ts +0 -35
- package/node_modules/pi-footer/src/widgets/core/provider.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/session-name.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/text-verbosity.ts +0 -17
- package/node_modules/pi-footer/src/widgets/core/thinking-level.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/ahead-behind.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/branch.ts +0 -81
- package/node_modules/pi-footer/src/widgets/git/clean.ts +0 -19
- package/node_modules/pi-footer/src/widgets/git/deletions.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/diff.ts +0 -38
- package/node_modules/pi-footer/src/widgets/git/insertions.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/remote.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/root.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/sha.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/staged.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/status.ts +0 -19
- package/node_modules/pi-footer/src/widgets/git/unstaged.ts +0 -17
- package/node_modules/pi-footer/src/widgets/git/untracked.ts +0 -17
- package/node_modules/pi-footer/src/widgets/instance.ts +0 -118
- package/node_modules/pi-footer/src/widgets/layout/custom-text.ts +0 -18
- package/node_modules/pi-footer/src/widgets/layout/flex-separator.ts +0 -32
- package/node_modules/pi-footer/src/widgets/layout/separator.ts +0 -57
- package/node_modules/pi-footer/src/widgets/layout/spacer.ts +0 -31
- package/node_modules/pi-footer/src/widgets/options.ts +0 -122
- package/node_modules/pi-footer/src/widgets/project/runtime.ts +0 -361
- package/node_modules/pi-footer/src/widgets/registry.ts +0 -223
- package/node_modules/pi-footer/src/widgets/session/assistant-messages.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/compactions.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/elapsed.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/last-activity.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/messages.ts +0 -19
- package/node_modules/pi-footer/src/widgets/session/session-id.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/session-start.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/tool-results.ts +0 -17
- package/node_modules/pi-footer/src/widgets/session/total-messages.ts +0 -19
- package/node_modules/pi-footer/src/widgets/session/total-time.ts +0 -18
- package/node_modules/pi-footer/src/widgets/session/user-messages.ts +0 -17
- package/node_modules/pi-footer/src/widgets/store.ts +0 -26
- package/node_modules/pi-footer/src/widgets/tokens/cache-hit-rate.ts +0 -55
- package/node_modules/pi-footer/src/widgets/tokens/cache-read.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/cache-write.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/context-bar.ts +0 -108
- package/node_modules/pi-footer/src/widgets/tokens/context-length.ts +0 -26
- package/node_modules/pi-footer/src/widgets/tokens/context-remaining.ts +0 -29
- package/node_modules/pi-footer/src/widgets/tokens/context.ts +0 -29
- package/node_modules/pi-footer/src/widgets/tokens/cost.ts +0 -51
- package/node_modules/pi-footer/src/widgets/tokens/input-speed.ts +0 -25
- package/node_modules/pi-footer/src/widgets/tokens/input-tokens.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/output-speed.ts +0 -25
- package/node_modules/pi-footer/src/widgets/tokens/output-tokens.ts +0 -18
- package/node_modules/pi-footer/src/widgets/tokens/tokens.ts +0 -20
- package/node_modules/pi-footer/src/widgets/tokens/total-speed.ts +0 -25
- package/node_modules/pi-footer/src/widgets/tokens/total-tokens.ts +0 -18
- package/node_modules/pi-footer/src/widgets/types.ts +0 -181
- package/node_modules/pi-footer/src/widgets/utils/colors.ts +0 -11
- package/node_modules/pi-footer/src/widgets/utils/context.ts +0 -112
- package/node_modules/pi-footer/src/widgets/utils/session.ts +0 -19
- package/node_modules/pi-footer/src/widgets/utils/token-format.ts +0 -78
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { chmodSync, existsSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import type { OAuthCredentials } from "@earendil-works/pi-ai";
|
|
6
|
+
import { defaultProjectId } from "../client/client.js";
|
|
7
|
+
import type { AntigravityApiKey, AntigravityOAuthCredentials } from "../types/types.js";
|
|
8
|
+
import { refreshAntigravityToken } from "./oauth.js";
|
|
9
|
+
|
|
10
|
+
const ACCOUNTS_FILE = "antigravity-accounts.json";
|
|
11
|
+
const AUTH_FILE = "auth.json";
|
|
12
|
+
const REFRESH_SKEW_MS = 5 * 60 * 1000;
|
|
13
|
+
|
|
14
|
+
export type StoredAccount = AntigravityOAuthCredentials & {
|
|
15
|
+
accountId: string;
|
|
16
|
+
addedAt: number;
|
|
17
|
+
lastUsedAt: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type AccountsFile = {
|
|
21
|
+
version: 1;
|
|
22
|
+
activeAccountId?: string;
|
|
23
|
+
accounts: Record<string, StoredAccount>;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type AccountSummary = {
|
|
27
|
+
accountId: string;
|
|
28
|
+
email?: string;
|
|
29
|
+
active: boolean;
|
|
30
|
+
lastUsedAt: number;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function agentDir(): string {
|
|
34
|
+
return process.env.PI_CODING_AGENT_DIR || join(homedir(), ".pi", "agent");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function accountsPath(): string {
|
|
38
|
+
return join(agentDir(), ACCOUNTS_FILE);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function authPath(): string {
|
|
42
|
+
return join(agentDir(), AUTH_FILE);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function readJsonObject(path: string): Record<string, unknown> | undefined {
|
|
46
|
+
if (!existsSync(path)) return undefined;
|
|
47
|
+
try {
|
|
48
|
+
const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
|
|
49
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return undefined;
|
|
50
|
+
return parsed as Record<string, unknown>;
|
|
51
|
+
} catch {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function writePrivateJson(path: string, value: unknown): void {
|
|
57
|
+
const tempPath = `${path}.${process.pid}.tmp`;
|
|
58
|
+
writeFileSync(tempPath, `${JSON.stringify(value, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
59
|
+
chmodSync(tempPath, 0o600);
|
|
60
|
+
renameSync(tempPath, path);
|
|
61
|
+
chmodSync(path, 0o600);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function emptyStore(): AccountsFile {
|
|
65
|
+
return { version: 1, accounts: {} };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function isStoredAccount(value: unknown): value is StoredAccount {
|
|
69
|
+
if (!value || typeof value !== "object") return false;
|
|
70
|
+
const account = value as Partial<StoredAccount>;
|
|
71
|
+
return typeof account.refresh === "string" && account.refresh.length > 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function normalizeStore(value: Record<string, unknown> | undefined): AccountsFile {
|
|
75
|
+
if (!value || value.version !== 1 || !value.accounts || typeof value.accounts !== "object") {
|
|
76
|
+
return emptyStore();
|
|
77
|
+
}
|
|
78
|
+
const accounts: Record<string, StoredAccount> = {};
|
|
79
|
+
for (const [key, raw] of Object.entries(value.accounts as Record<string, unknown>)) {
|
|
80
|
+
if (!isStoredAccount(raw)) continue;
|
|
81
|
+
const accountId =
|
|
82
|
+
typeof raw.accountId === "string" && raw.accountId.trim() ? raw.accountId : key;
|
|
83
|
+
accounts[accountId] = { ...raw, accountId };
|
|
84
|
+
}
|
|
85
|
+
const activeAccountId =
|
|
86
|
+
typeof value.activeAccountId === "string"
|
|
87
|
+
? value.activeAccountId
|
|
88
|
+
: typeof value.activeEmail === "string"
|
|
89
|
+
? value.activeEmail
|
|
90
|
+
: undefined;
|
|
91
|
+
return {
|
|
92
|
+
version: 1,
|
|
93
|
+
activeAccountId: activeAccountId && accounts[activeAccountId] ? activeAccountId : undefined,
|
|
94
|
+
accounts,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function loadAccountStore(): AccountsFile {
|
|
99
|
+
return normalizeStore(readJsonObject(accountsPath()));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function saveAccountStore(store: AccountsFile): void {
|
|
103
|
+
writePrivateJson(accountsPath(), store);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function accountIdFor(credentials: OAuthCredentials): string {
|
|
107
|
+
const email = (credentials as AntigravityOAuthCredentials).email?.trim().toLowerCase();
|
|
108
|
+
if (email) return email;
|
|
109
|
+
return `account-${createHash("sha256").update(credentials.refresh).digest("hex").slice(0, 16)}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function accountFromCredentials(
|
|
113
|
+
credentials: AntigravityOAuthCredentials,
|
|
114
|
+
existing?: StoredAccount,
|
|
115
|
+
): StoredAccount {
|
|
116
|
+
const now = Date.now();
|
|
117
|
+
return {
|
|
118
|
+
...credentials,
|
|
119
|
+
accountId: existing?.accountId || accountIdFor(credentials),
|
|
120
|
+
addedAt: existing?.addedAt || now,
|
|
121
|
+
lastUsedAt: now,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function sortedAccounts(store: AccountsFile): StoredAccount[] {
|
|
126
|
+
return Object.values(store.accounts).sort((a, b) => {
|
|
127
|
+
if (a.addedAt !== b.addedAt) return a.addedAt - b.addedAt;
|
|
128
|
+
return a.accountId.localeCompare(b.accountId);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function findAccount(
|
|
133
|
+
store: AccountsFile,
|
|
134
|
+
credentials: OAuthCredentials,
|
|
135
|
+
): StoredAccount | undefined {
|
|
136
|
+
const email = (credentials as AntigravityOAuthCredentials).email?.trim().toLowerCase();
|
|
137
|
+
return Object.values(store.accounts).find(
|
|
138
|
+
(account) =>
|
|
139
|
+
account.refresh === credentials.refresh ||
|
|
140
|
+
(email !== undefined && account.email?.trim().toLowerCase() === email),
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function findAccountId(store: AccountsFile, selector: string): string | undefined {
|
|
145
|
+
const normalized = selector.trim().toLowerCase();
|
|
146
|
+
if (!normalized) return undefined;
|
|
147
|
+
const entries = sortedAccounts(store);
|
|
148
|
+
if (/^\d+$/.test(normalized)) {
|
|
149
|
+
const index = Number.parseInt(normalized, 10);
|
|
150
|
+
if (index >= 1 && index <= entries.length) return entries[index - 1]?.accountId;
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
return entries.find(
|
|
154
|
+
(account) =>
|
|
155
|
+
account.accountId.toLowerCase() === normalized ||
|
|
156
|
+
account.email?.trim().toLowerCase() === normalized,
|
|
157
|
+
)?.accountId;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function readAuthFile(): Record<string, unknown> {
|
|
161
|
+
const path = authPath();
|
|
162
|
+
if (!existsSync(path)) return {};
|
|
163
|
+
const parsed = readJsonObject(path);
|
|
164
|
+
if (!parsed) {
|
|
165
|
+
throw new Error("Refusing to overwrite unreadable Pi auth.json");
|
|
166
|
+
}
|
|
167
|
+
return parsed;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function writeActiveCredential(account: StoredAccount | undefined): void {
|
|
171
|
+
const auth = readAuthFile();
|
|
172
|
+
if (account) {
|
|
173
|
+
auth.antigravity = {
|
|
174
|
+
type: "oauth",
|
|
175
|
+
email: account.email,
|
|
176
|
+
access: account.access,
|
|
177
|
+
refresh: account.refresh,
|
|
178
|
+
expires: account.expires,
|
|
179
|
+
projectId: account.projectId,
|
|
180
|
+
};
|
|
181
|
+
} else {
|
|
182
|
+
delete auth.antigravity;
|
|
183
|
+
}
|
|
184
|
+
writePrivateJson(authPath(), auth);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function currentAuthCredentials(): AntigravityOAuthCredentials | undefined {
|
|
188
|
+
const current = readJsonObject(authPath())?.antigravity;
|
|
189
|
+
if (!current || typeof current !== "object") return undefined;
|
|
190
|
+
const record = current as Record<string, unknown>;
|
|
191
|
+
if (typeof record.refresh !== "string" || !record.refresh) return undefined;
|
|
192
|
+
return {
|
|
193
|
+
access: typeof record.access === "string" ? record.access : "",
|
|
194
|
+
refresh: record.refresh,
|
|
195
|
+
expires: typeof record.expires === "number" ? record.expires : 0,
|
|
196
|
+
projectId: typeof record.projectId === "string" ? record.projectId : undefined,
|
|
197
|
+
email: typeof record.email === "string" ? record.email : undefined,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function syncCurrentAuth(): void {
|
|
202
|
+
const credentials = currentAuthCredentials();
|
|
203
|
+
if (!credentials) return;
|
|
204
|
+
const store = loadAccountStore();
|
|
205
|
+
const existing = findAccount(store, credentials);
|
|
206
|
+
const account = accountFromCredentials(credentials, existing);
|
|
207
|
+
store.accounts[account.accountId] = account;
|
|
208
|
+
store.activeAccountId = account.accountId;
|
|
209
|
+
saveAccountStore(store);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function rememberAccount(credentials: AntigravityOAuthCredentials): StoredAccount {
|
|
213
|
+
syncCurrentAuth();
|
|
214
|
+
const store = loadAccountStore();
|
|
215
|
+
const existing = findAccount(store, credentials);
|
|
216
|
+
const account = accountFromCredentials(credentials, existing);
|
|
217
|
+
store.accounts[account.accountId] = account;
|
|
218
|
+
store.activeAccountId = account.accountId;
|
|
219
|
+
saveAccountStore(store);
|
|
220
|
+
return account;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function updateRememberedAccount(
|
|
224
|
+
previous: OAuthCredentials,
|
|
225
|
+
credentials: AntigravityOAuthCredentials,
|
|
226
|
+
): void {
|
|
227
|
+
const store = loadAccountStore();
|
|
228
|
+
const existing = findAccount(store, previous);
|
|
229
|
+
if (!existing) return;
|
|
230
|
+
const account = accountFromCredentials(credentials, existing);
|
|
231
|
+
delete store.accounts[existing.accountId];
|
|
232
|
+
store.accounts[account.accountId] = account;
|
|
233
|
+
if (store.activeAccountId === existing.accountId) {
|
|
234
|
+
store.activeAccountId = account.accountId;
|
|
235
|
+
}
|
|
236
|
+
saveAccountStore(store);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function listAccounts(): AccountSummary[] {
|
|
240
|
+
syncCurrentAuth();
|
|
241
|
+
const store = loadAccountStore();
|
|
242
|
+
return sortedAccounts(store).map((account) => ({
|
|
243
|
+
accountId: account.accountId,
|
|
244
|
+
email: account.email,
|
|
245
|
+
active: account.accountId === store.activeAccountId,
|
|
246
|
+
lastUsedAt: account.lastUsedAt,
|
|
247
|
+
}));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async function ensureFresh(account: StoredAccount): Promise<StoredAccount> {
|
|
251
|
+
if (account.access && account.expires > Date.now() + REFRESH_SKEW_MS) return account;
|
|
252
|
+
return accountFromCredentials(await refreshAntigravityToken(account), account);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function apiKeyFor(account: StoredAccount): AntigravityApiKey {
|
|
256
|
+
const email = account.email?.trim();
|
|
257
|
+
return {
|
|
258
|
+
token: account.access,
|
|
259
|
+
projectId: account.projectId || defaultProjectId(email || "antigravity-default"),
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export async function activateAccount(selector: string): Promise<StoredAccount> {
|
|
264
|
+
syncCurrentAuth();
|
|
265
|
+
const store = loadAccountStore();
|
|
266
|
+
const accountId = findAccountId(store, selector);
|
|
267
|
+
if (!accountId) throw new Error(`Antigravity account not found: ${selector}`);
|
|
268
|
+
const existing = store.accounts[accountId];
|
|
269
|
+
if (!existing) throw new Error(`Antigravity account not found: ${selector}`);
|
|
270
|
+
const account = await ensureFresh(existing);
|
|
271
|
+
account.lastUsedAt = Date.now();
|
|
272
|
+
store.accounts[account.accountId] = account;
|
|
273
|
+
store.activeAccountId = account.accountId;
|
|
274
|
+
saveAccountStore(store);
|
|
275
|
+
writeActiveCredential(account);
|
|
276
|
+
return account;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export async function removeAccount(selector: string): Promise<StoredAccount | undefined> {
|
|
280
|
+
syncCurrentAuth();
|
|
281
|
+
const store = loadAccountStore();
|
|
282
|
+
const accountId = findAccountId(store, selector);
|
|
283
|
+
if (!accountId) throw new Error(`Antigravity account not found: ${selector}`);
|
|
284
|
+
delete store.accounts[accountId];
|
|
285
|
+
let next: StoredAccount | undefined;
|
|
286
|
+
if (store.activeAccountId === accountId) {
|
|
287
|
+
next = sortedAccounts(store).sort((a, b) => b.lastUsedAt - a.lastUsedAt)[0];
|
|
288
|
+
if (next) {
|
|
289
|
+
next = await ensureFresh(next);
|
|
290
|
+
next.lastUsedAt = Date.now();
|
|
291
|
+
store.accounts[next.accountId] = next;
|
|
292
|
+
}
|
|
293
|
+
store.activeAccountId = next?.accountId;
|
|
294
|
+
writeActiveCredential(next);
|
|
295
|
+
}
|
|
296
|
+
saveAccountStore(store);
|
|
297
|
+
return next;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Switch to the next stored account that has not already been tried in this request.
|
|
302
|
+
* Used when Antigravity returns a hard quota wall (HTTP 429).
|
|
303
|
+
*/
|
|
304
|
+
export async function failoverToNextAccount(
|
|
305
|
+
triedAccessTokens: ReadonlySet<string>,
|
|
306
|
+
): Promise<AntigravityApiKey | undefined> {
|
|
307
|
+
syncCurrentAuth();
|
|
308
|
+
const store = loadAccountStore();
|
|
309
|
+
const candidates = sortedAccounts(store).filter(
|
|
310
|
+
(account) => !triedAccessTokens.has(account.access),
|
|
311
|
+
);
|
|
312
|
+
for (const candidate of candidates) {
|
|
313
|
+
try {
|
|
314
|
+
const account = await ensureFresh(candidate);
|
|
315
|
+
if (!account.access || triedAccessTokens.has(account.access)) continue;
|
|
316
|
+
account.lastUsedAt = Date.now();
|
|
317
|
+
store.accounts[account.accountId] = account;
|
|
318
|
+
store.activeAccountId = account.accountId;
|
|
319
|
+
saveAccountStore(store);
|
|
320
|
+
writeActiveCredential(account);
|
|
321
|
+
return apiKeyFor(account);
|
|
322
|
+
} catch {
|
|
323
|
+
// Skip accounts whose refresh token is no longer valid.
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return undefined;
|
|
327
|
+
}
|
|
@@ -12,3 +12,12 @@ export {
|
|
|
12
12
|
OAUTH_CALLBACK_TIMEOUT_MS,
|
|
13
13
|
} from "./oauth.js";
|
|
14
14
|
export type { OAuthCredentials, OAuthLoginCallbacks } from "./oauth.js";
|
|
15
|
+
export {
|
|
16
|
+
activateAccount,
|
|
17
|
+
failoverToNextAccount,
|
|
18
|
+
listAccounts,
|
|
19
|
+
rememberAccount,
|
|
20
|
+
removeAccount,
|
|
21
|
+
updateRememberedAccount,
|
|
22
|
+
} from "./accounts.js";
|
|
23
|
+
export type { AccountSummary, StoredAccount } from "./accounts.js";
|
|
@@ -61,7 +61,7 @@ export function endpointCandidates(): string[] {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
const DEFAULT_USER_AGENT =
|
|
64
|
-
"antigravity/cli/1.
|
|
64
|
+
"antigravity/cli/1.2.4 (aidev_client; os_type=linux; arch=amd64; cl=982146307; auth_method=consumer)";
|
|
65
65
|
|
|
66
66
|
/** Default User-Agent matching pure Antigravity CLI wire fingerprint. */
|
|
67
67
|
export function defaultUserAgent(): string {
|
|
@@ -42,15 +42,15 @@ export type ImageGenerateRequest = {
|
|
|
42
42
|
project: string;
|
|
43
43
|
model: string;
|
|
44
44
|
request: {
|
|
45
|
-
contents: Array<{ role:
|
|
46
|
-
systemInstruction: { role:
|
|
45
|
+
contents: Array<{ role: "user"; parts: Array<{ text: string }> }>;
|
|
46
|
+
systemInstruction: { role: "user"; parts: Array<{ text: string }> };
|
|
47
47
|
generationConfig: {
|
|
48
48
|
imageConfig: { aspectRatio: string };
|
|
49
49
|
candidateCount: number;
|
|
50
50
|
};
|
|
51
51
|
};
|
|
52
|
-
requestType:
|
|
53
|
-
userAgent:
|
|
52
|
+
requestType: "agent";
|
|
53
|
+
userAgent: "antigravity";
|
|
54
54
|
requestId: string;
|
|
55
55
|
};
|
|
56
56
|
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { StringEnum, Type } from "@earendil-works/pi-ai";
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
import
|
|
3
|
+
// Namespace import: Oh My Pi rewrites this specifier onto bundled pi-ai, which
|
|
4
|
+
// does not export registerApiProvider. A static named import fails plugin load.
|
|
5
|
+
import * as piAiCompat from "@earendil-works/pi-ai/compat";
|
|
6
|
+
import {
|
|
7
|
+
activateAccount,
|
|
8
|
+
getApiKey,
|
|
9
|
+
listAccounts,
|
|
10
|
+
loginAntigravity,
|
|
11
|
+
rememberAccount,
|
|
12
|
+
refreshAntigravityToken,
|
|
13
|
+
removeAccount,
|
|
14
|
+
updateRememberedAccount,
|
|
15
|
+
} from "./auth/index.js";
|
|
16
|
+
import { DEFAULT_ENDPOINT } from "./client/index.js";
|
|
6
17
|
import { getLastDiagnostics, runWithDiagnostics } from "./diagnostics/index.js";
|
|
7
18
|
import {
|
|
8
19
|
DEFAULT_IMAGE_MODEL,
|
|
@@ -10,6 +21,7 @@ import {
|
|
|
10
21
|
IMAGE_ASPECT_RATIOS,
|
|
11
22
|
parseImageCommandArgs,
|
|
12
23
|
} from "./image/index.js";
|
|
24
|
+
import { executeAntigravitySearch, parseSearchCommandArgs } from "./search/index.js";
|
|
13
25
|
import {
|
|
14
26
|
applyAntigravityCatalog,
|
|
15
27
|
discoverAntigravityModels,
|
|
@@ -26,7 +38,7 @@ import {
|
|
|
26
38
|
formatUsageSummary,
|
|
27
39
|
resolveApiKeyFromContext,
|
|
28
40
|
} from "./usage/index.js";
|
|
29
|
-
import {
|
|
41
|
+
import { redactSecrets, maskEmail } from "./utils/index.js";
|
|
30
42
|
|
|
31
43
|
/**
|
|
32
44
|
* Pi's interactive `notify` writes into the chat transcript. `console.log` in that
|
|
@@ -45,6 +57,22 @@ function emitCommandOutput(
|
|
|
45
57
|
else console.log(text);
|
|
46
58
|
}
|
|
47
59
|
|
|
60
|
+
async function loginAndRemember(
|
|
61
|
+
callbacks: Parameters<typeof loginAntigravity>[0],
|
|
62
|
+
): ReturnType<typeof loginAntigravity> {
|
|
63
|
+
const credentials = await loginAntigravity(callbacks);
|
|
64
|
+
rememberAccount(credentials);
|
|
65
|
+
return credentials;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function refreshAndRemember(
|
|
69
|
+
credentials: Parameters<typeof refreshAntigravityToken>[0],
|
|
70
|
+
): ReturnType<typeof refreshAntigravityToken> {
|
|
71
|
+
const refreshed = await refreshAntigravityToken(credentials);
|
|
72
|
+
updateRememberedAccount(credentials, refreshed);
|
|
73
|
+
return refreshed;
|
|
74
|
+
}
|
|
75
|
+
|
|
48
76
|
async function withUsage(
|
|
49
77
|
ctx: ExtensionCommandContext,
|
|
50
78
|
fn: (usage: Awaited<ReturnType<typeof fetchAccountUsage>>) => string,
|
|
@@ -68,17 +96,29 @@ async function withUsage(
|
|
|
68
96
|
}
|
|
69
97
|
}
|
|
70
98
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
99
|
+
type CompatApiProviderRegistrar = (provider: {
|
|
100
|
+
api: typeof ANTIGRAVITY_API;
|
|
101
|
+
stream: typeof streamAntigravity;
|
|
102
|
+
streamSimple: typeof streamAntigravity;
|
|
103
|
+
}) => void;
|
|
76
104
|
|
|
77
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Pi dispatches custom APIs through the compat registry. Oh My Pi does not
|
|
107
|
+
* export `registerApiProvider` and registers the stream inside `registerProvider`.
|
|
108
|
+
*/
|
|
109
|
+
function registerCompatApiProvider(): void {
|
|
110
|
+
const register = (piAiCompat as { registerApiProvider?: CompatApiProviderRegistrar })
|
|
111
|
+
.registerApiProvider;
|
|
112
|
+
if (typeof register !== "function") return;
|
|
113
|
+
register({
|
|
78
114
|
api: ANTIGRAVITY_API,
|
|
79
115
|
stream: streamAntigravity,
|
|
80
116
|
streamSimple: streamAntigravity,
|
|
81
117
|
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export default function (pi: ExtensionAPI): void {
|
|
121
|
+
registerCompatApiProvider();
|
|
82
122
|
|
|
83
123
|
const initialCatalog = getCurrentAntigravityCatalog();
|
|
84
124
|
|
|
@@ -90,8 +130,8 @@ export default function (pi: ExtensionAPI): void {
|
|
|
90
130
|
refreshModels: refreshAntigravityModels,
|
|
91
131
|
oauth: {
|
|
92
132
|
name: PROVIDER_NAME,
|
|
93
|
-
login:
|
|
94
|
-
refreshToken:
|
|
133
|
+
login: loginAndRemember,
|
|
134
|
+
refreshToken: refreshAndRemember,
|
|
95
135
|
getApiKey,
|
|
96
136
|
},
|
|
97
137
|
streamSimple: streamAntigravity,
|
|
@@ -112,6 +152,51 @@ export default function (pi: ExtensionAPI): void {
|
|
|
112
152
|
},
|
|
113
153
|
});
|
|
114
154
|
|
|
155
|
+
pi.registerCommand("antigravity.accounts", {
|
|
156
|
+
description: "List, switch, or remove linked Antigravity Google accounts",
|
|
157
|
+
handler: async (args, ctx) => {
|
|
158
|
+
const command = args.trim();
|
|
159
|
+
try {
|
|
160
|
+
if (command.startsWith("switch ")) {
|
|
161
|
+
const account = await activateAccount(command.slice("switch ".length));
|
|
162
|
+
emitCommandOutput(
|
|
163
|
+
ctx,
|
|
164
|
+
`Active Antigravity account: ${account.email || account.accountId}`,
|
|
165
|
+
);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (command.startsWith("remove ")) {
|
|
169
|
+
const remaining = await removeAccount(command.slice("remove ".length));
|
|
170
|
+
const next = remaining
|
|
171
|
+
? ` Active account is now ${remaining.email || remaining.accountId}.`
|
|
172
|
+
: "";
|
|
173
|
+
emitCommandOutput(ctx, `Antigravity account removed.${next}`);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const accounts = listAccounts();
|
|
177
|
+
if (accounts.length === 0) {
|
|
178
|
+
emitCommandOutput(
|
|
179
|
+
ctx,
|
|
180
|
+
"No linked Antigravity accounts. Run /login antigravity to add one.",
|
|
181
|
+
"warning",
|
|
182
|
+
);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const lines = accounts.map(
|
|
186
|
+
(account, index) =>
|
|
187
|
+
`${account.active ? "* " : " "}${index + 1}. ${account.email || account.accountId}`,
|
|
188
|
+
);
|
|
189
|
+
emitCommandOutput(
|
|
190
|
+
ctx,
|
|
191
|
+
`${lines.join("\n")}\nUse /antigravity.accounts switch <index|email> or /antigravity.accounts remove <index|email>.`,
|
|
192
|
+
);
|
|
193
|
+
} catch (error) {
|
|
194
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
195
|
+
emitCommandOutput(ctx, msg, "error");
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
|
|
115
200
|
pi.registerCommand("antigravity.refresh", {
|
|
116
201
|
description: "Force refresh Antigravity dynamic model catalog",
|
|
117
202
|
handler: async (_args, ctx) => {
|
|
@@ -163,6 +248,12 @@ export default function (pi: ExtensionAPI): void {
|
|
|
163
248
|
description: "Show sanitized Antigravity provider diagnostics",
|
|
164
249
|
handler: async (_args, ctx) => {
|
|
165
250
|
const d = getLastDiagnostics();
|
|
251
|
+
const accounts = listAccounts();
|
|
252
|
+
const active = accounts.find((account) => account.active);
|
|
253
|
+
const activeLabel = active
|
|
254
|
+
? maskEmail(active.email) ||
|
|
255
|
+
(active.accountId.includes("@") ? maskEmail(active.accountId) : active.accountId)
|
|
256
|
+
: "none";
|
|
166
257
|
const lines = [
|
|
167
258
|
`provider=${PROVIDER_ID}`,
|
|
168
259
|
`lastResolvedRuntimeModel=${d.resolvedRuntimeModel || "none"}`,
|
|
@@ -171,12 +262,14 @@ export default function (pi: ExtensionAPI): void {
|
|
|
171
262
|
`lastEndpoint=${d.endpoint || "none"}`,
|
|
172
263
|
`lastStatus=${d.status ?? "none"}`,
|
|
173
264
|
`lastProjectId=${d.projectId || "none"}`,
|
|
265
|
+
`linkedAccounts=${accounts.length || "none"}`,
|
|
266
|
+
`activeAccount=${activeLabel || "none"}`,
|
|
174
267
|
...(d.latencyMs !== undefined ? [`lastLatencyMs=${d.latencyMs}`] : []),
|
|
175
268
|
`toolSchemaWarnings=${d.toolSchemaWarnings || "none"}`,
|
|
176
269
|
`lastError=${d.error ? redactSecrets(d.error) : "none"}`,
|
|
177
270
|
"transport=native-streamSimple",
|
|
178
271
|
"runtimeCli=not-used",
|
|
179
|
-
"commands=/antigravity.usage /antigravity.models /antigravity.refresh /antigravity.doctor /antigravity.image",
|
|
272
|
+
"commands=/antigravity.usage /antigravity.models /antigravity.accounts /antigravity.refresh /antigravity.doctor /antigravity.image /antigravity.search",
|
|
180
273
|
];
|
|
181
274
|
emitCommandOutput(ctx, `Antigravity doctor\n${lines.join("\n")}`);
|
|
182
275
|
},
|
|
@@ -222,6 +315,44 @@ export default function (pi: ExtensionAPI): void {
|
|
|
222
315
|
},
|
|
223
316
|
});
|
|
224
317
|
|
|
318
|
+
pi.registerCommand("antigravity.search", {
|
|
319
|
+
description:
|
|
320
|
+
"Search the web via Antigravity Grounding (usage: /antigravity.search [--thinking] [--url <url>] <query>)",
|
|
321
|
+
handler: async (args, ctx) => {
|
|
322
|
+
const parsed = parseSearchCommandArgs(args || "");
|
|
323
|
+
if (!parsed.query) {
|
|
324
|
+
emitCommandOutput(
|
|
325
|
+
ctx,
|
|
326
|
+
"Usage: /antigravity.search [--thinking] [--url <url>] <query>",
|
|
327
|
+
"warning",
|
|
328
|
+
);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
try {
|
|
332
|
+
const apiKey = await resolveApiKeyFromContext(ctx);
|
|
333
|
+
if (!apiKey) {
|
|
334
|
+
emitCommandOutput(
|
|
335
|
+
ctx,
|
|
336
|
+
"No Antigravity credentials. Run /login antigravity first.",
|
|
337
|
+
"warning",
|
|
338
|
+
);
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
if (ctx.hasUI) ctx.ui.notify("Searching Google via Antigravity Grounding…", "info");
|
|
342
|
+
const result = await executeAntigravitySearch({
|
|
343
|
+
apiKey,
|
|
344
|
+
query: parsed.query,
|
|
345
|
+
urls: parsed.urls,
|
|
346
|
+
thinking: parsed.thinking,
|
|
347
|
+
});
|
|
348
|
+
emitCommandOutput(ctx, result);
|
|
349
|
+
} catch (error) {
|
|
350
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
351
|
+
emitCommandOutput(ctx, `Antigravity search failed: ${redactSecrets(msg)}`, "warning");
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
|
|
225
356
|
pi.registerTool({
|
|
226
357
|
name: "generate_image",
|
|
227
358
|
label: "Generate image",
|
|
@@ -277,4 +408,63 @@ export default function (pi: ExtensionAPI): void {
|
|
|
277
408
|
};
|
|
278
409
|
},
|
|
279
410
|
});
|
|
411
|
+
|
|
412
|
+
pi.registerTool({
|
|
413
|
+
name: "google_search",
|
|
414
|
+
label: "Google Search",
|
|
415
|
+
description:
|
|
416
|
+
"Search the web using Google Search via Antigravity Grounding powered by Gemini 3 Flash. Returns real-time web results with multi-angle deep search and source citations. Use this whenever current, real-time information or web research is needed.",
|
|
417
|
+
promptSnippet: "Real-time Google Search grounding via Antigravity (Gemini 3 Flash)",
|
|
418
|
+
promptGuidelines: [
|
|
419
|
+
"Use google_search when you need real-time, up-to-date web information, latest news, documentation, or fact checking.",
|
|
420
|
+
"Pass specific URLs into the urls array if you want the search engine to fetch and analyze specific pages.",
|
|
421
|
+
"Set instruction to provide special research directives from the lead agent (e.g. focus on issues, compare benchmarks, restrict time range).",
|
|
422
|
+
],
|
|
423
|
+
parameters: Type.Object({
|
|
424
|
+
query: Type.String({ description: "Search query or question." }),
|
|
425
|
+
instruction: Type.Optional(
|
|
426
|
+
Type.String({
|
|
427
|
+
description:
|
|
428
|
+
"Specific directive or focus from the lead agent (e.g., '重点关注英文社区近七天的讨论', 'focus on GitHub issues and benchmarks', 'ignore marketing announcements').",
|
|
429
|
+
}),
|
|
430
|
+
),
|
|
431
|
+
urls: Type.Optional(
|
|
432
|
+
Type.Array(Type.String(), {
|
|
433
|
+
description: "Optional specific URLs to fetch and analyze alongside the search.",
|
|
434
|
+
}),
|
|
435
|
+
),
|
|
436
|
+
thinking: Type.Optional(
|
|
437
|
+
Type.Boolean({
|
|
438
|
+
description: "Enable deeper thinking for complex analysis (default: false).",
|
|
439
|
+
}),
|
|
440
|
+
),
|
|
441
|
+
}),
|
|
442
|
+
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
443
|
+
const apiKey = await ctx.modelRegistry.getApiKeyForProvider("antigravity");
|
|
444
|
+
if (!apiKey) {
|
|
445
|
+
throw new Error("No Antigravity credentials. Run /login antigravity first.");
|
|
446
|
+
}
|
|
447
|
+
onUpdate?.({
|
|
448
|
+
content: [{ type: "text", text: `Searching Google for: "${params.query}"…` }],
|
|
449
|
+
details: {},
|
|
450
|
+
});
|
|
451
|
+
try {
|
|
452
|
+
const result = await executeAntigravitySearch({
|
|
453
|
+
apiKey,
|
|
454
|
+
query: params.query,
|
|
455
|
+
instruction: params.instruction,
|
|
456
|
+
urls: params.urls,
|
|
457
|
+
thinking: params.thinking,
|
|
458
|
+
signal,
|
|
459
|
+
});
|
|
460
|
+
return {
|
|
461
|
+
content: [{ type: "text", text: result }],
|
|
462
|
+
details: {},
|
|
463
|
+
};
|
|
464
|
+
} catch (error) {
|
|
465
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
466
|
+
throw new Error(redactSecrets(msg), { cause: error });
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
});
|
|
280
470
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./search.js";
|