pi-ui-extend 0.1.13 → 0.1.17

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.
Files changed (111) hide show
  1. package/README.md +1 -1
  2. package/dist/app/app.d.ts +7 -0
  3. package/dist/app/app.js +102 -17
  4. package/dist/app/commands/command-controller.js +2 -0
  5. package/dist/app/commands/command-host.d.ts +5 -0
  6. package/dist/app/commands/command-model-actions.d.ts +2 -0
  7. package/dist/app/commands/command-model-actions.js +40 -4
  8. package/dist/app/commands/command-navigation-actions.d.ts +9 -0
  9. package/dist/app/commands/command-navigation-actions.js +62 -0
  10. package/dist/app/commands/command-registry.d.ts +2 -0
  11. package/dist/app/commands/command-registry.js +16 -0
  12. package/dist/app/constants.d.ts +0 -1
  13. package/dist/app/constants.js +0 -1
  14. package/dist/app/extensions/extension-ui-controller.d.ts +16 -5
  15. package/dist/app/extensions/extension-ui-controller.js +99 -61
  16. package/dist/app/icons.d.ts +1 -0
  17. package/dist/app/icons.js +2 -0
  18. package/dist/app/input/input-action-controller.d.ts +2 -0
  19. package/dist/app/input/input-action-controller.js +8 -1
  20. package/dist/app/logger.d.ts +25 -0
  21. package/dist/app/logger.js +90 -0
  22. package/dist/app/model/model-usage-status.js +30 -15
  23. package/dist/app/popup/menu-items-controller.d.ts +4 -0
  24. package/dist/app/popup/menu-items-controller.js +68 -6
  25. package/dist/app/popup/popup-action-controller.d.ts +2 -1
  26. package/dist/app/popup/popup-action-controller.js +7 -4
  27. package/dist/app/popup/popup-menu-controller.d.ts +36 -23
  28. package/dist/app/popup/popup-menu-controller.js +97 -326
  29. package/dist/app/rendering/conversation-entry-renderer.js +3 -3
  30. package/dist/app/rendering/conversation-viewport.d.ts +10 -2
  31. package/dist/app/rendering/conversation-viewport.js +157 -16
  32. package/dist/app/rendering/editor-panels.js +22 -9
  33. package/dist/app/rendering/popup-menu-renderer.d.ts +62 -0
  34. package/dist/app/rendering/popup-menu-renderer.js +405 -0
  35. package/dist/app/rendering/render-controller.js +30 -28
  36. package/dist/app/rendering/render-text.js +5 -2
  37. package/dist/app/rendering/status-line-renderer.d.ts +8 -1
  38. package/dist/app/rendering/status-line-renderer.js +217 -117
  39. package/dist/app/rendering/toast-controller.d.ts +12 -3
  40. package/dist/app/rendering/toast-controller.js +70 -12
  41. package/dist/app/runtime.d.ts +2 -1
  42. package/dist/app/runtime.js +20 -10
  43. package/dist/app/screen/mouse-controller.d.ts +2 -2
  44. package/dist/app/screen/mouse-controller.js +27 -48
  45. package/dist/app/screen/screen-styler.d.ts +1 -1
  46. package/dist/app/screen/screen-styler.js +9 -7
  47. package/dist/app/screen/scroll-controller.d.ts +12 -9
  48. package/dist/app/screen/scroll-controller.js +56 -45
  49. package/dist/app/screen/status-controller.js +2 -1
  50. package/dist/app/session/lazy-session-manager.d.ts +11 -0
  51. package/dist/app/session/lazy-session-manager.js +539 -0
  52. package/dist/app/session/pix-system-message.d.ts +16 -0
  53. package/dist/app/session/pix-system-message.js +64 -0
  54. package/dist/app/session/request-history.d.ts +4 -0
  55. package/dist/app/session/request-history.js +11 -0
  56. package/dist/app/session/session-event-controller.d.ts +11 -0
  57. package/dist/app/session/session-event-controller.js +58 -2
  58. package/dist/app/session/session-history.d.ts +18 -0
  59. package/dist/app/session/session-history.js +72 -3
  60. package/dist/app/session/session-lifecycle-controller.d.ts +6 -2
  61. package/dist/app/session/session-lifecycle-controller.js +7 -2
  62. package/dist/app/session/session-search.js +10 -0
  63. package/dist/app/session/tabs-controller.d.ts +17 -5
  64. package/dist/app/session/tabs-controller.js +308 -29
  65. package/dist/app/todo/todo-model.d.ts +4 -2
  66. package/dist/app/todo/todo-model.js +23 -13
  67. package/dist/app/types.d.ts +17 -6
  68. package/dist/app/workspace/workspace-actions-controller.d.ts +2 -0
  69. package/dist/app/workspace/workspace-actions-controller.js +12 -0
  70. package/dist/config.d.ts +6 -1
  71. package/dist/config.js +82 -25
  72. package/dist/default-pix-config.js +4 -0
  73. package/dist/fuzzy.d.ts +2 -0
  74. package/dist/fuzzy.js +27 -7
  75. package/dist/input-editor.d.ts +9 -0
  76. package/dist/input-editor.js +52 -0
  77. package/dist/schemas/pi-tools-suite-schema.d.ts +1 -0
  78. package/dist/schemas/pi-tools-suite-schema.js +1 -0
  79. package/dist/schemas/pix-schema.d.ts +3 -1
  80. package/dist/schemas/pix-schema.js +6 -4
  81. package/dist/terminal-width.d.ts +2 -0
  82. package/dist/terminal-width.js +64 -3
  83. package/dist/theme.js +6 -6
  84. package/dist/ui.d.ts +8 -0
  85. package/external/pi-tools-suite/README.md +3 -2
  86. package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +52 -8
  87. package/external/pi-tools-suite/src/antigravity-auth/commands.ts +3 -41
  88. package/external/pi-tools-suite/src/antigravity-auth/constants.ts +0 -2
  89. package/external/pi-tools-suite/src/antigravity-auth/index.ts +11 -18
  90. package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +129 -61
  91. package/external/pi-tools-suite/src/antigravity-auth/status.ts +82 -3
  92. package/external/pi-tools-suite/src/antigravity-auth/stream.ts +20 -7
  93. package/external/pi-tools-suite/src/antigravity-auth/types.ts +21 -0
  94. package/external/pi-tools-suite/src/config.ts +8 -0
  95. package/external/pi-tools-suite/src/dcp/index.ts +16 -1
  96. package/external/pi-tools-suite/src/dcp/state.ts +35 -0
  97. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +3 -0
  98. package/external/pi-tools-suite/src/todo/index.ts +123 -14
  99. package/external/pi-tools-suite/src/todo/state/persistence.ts +0 -1
  100. package/external/pi-tools-suite/src/todo/state/state-reducer.ts +26 -43
  101. package/external/pi-tools-suite/src/todo/todo.ts +12 -23
  102. package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +34 -16
  103. package/external/pi-tools-suite/src/todo/tool/types.ts +7 -28
  104. package/external/pi-tools-suite/src/todo/view/format.ts +2 -3
  105. package/external/pi-tools-suite/src/tool-descriptions.ts +6 -4
  106. package/external/pi-tools-suite/src/usage/index.ts +5 -2
  107. package/external/pi-tools-suite/src/usage/lib/google.ts +53 -40
  108. package/external/pi-tools-suite/src/usage/lib/types.ts +12 -2
  109. package/package.json +1 -1
  110. package/schemas/pi-tools-suite.json +4 -0
  111. package/schemas/pix.json +11 -2
@@ -24,7 +24,7 @@ export const MSG_NO_TODOS = "No todos yet. Ask the agent to add some!";
24
24
  // ---------------------------------------------------------------------------
25
25
 
26
26
  export type TaskStatus = "pending" | "in_progress" | "deferred" | "completed" | "deleted";
27
- export type TaskPriority = "low" | "medium" | "high" | "urgent";
27
+ export type TodoThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
28
28
 
29
29
  export type TaskAction = "create" | "update" | "batch_create" | "batch_update" | "list" | "get" | "delete" | "clear" | "export" | "import";
30
30
 
@@ -34,10 +34,9 @@ export interface Task {
34
34
  description?: string;
35
35
  activeForm?: string;
36
36
  status: TaskStatus;
37
- priority?: TaskPriority;
37
+ thinking?: TodoThinkingLevel;
38
38
  parentId?: number;
39
39
  blockedBy?: number[];
40
- tags?: string[];
41
40
  owner?: string;
42
41
  metadata?: Record<string, unknown>;
43
42
  }
@@ -67,20 +66,16 @@ export interface TaskMutationParams {
67
66
  description?: string;
68
67
  activeForm?: string;
69
68
  status?: TaskStatus;
70
- priority?: TaskPriority;
69
+ thinking?: TodoThinkingLevel;
71
70
  parentId?: number | null;
72
71
  clearParent?: boolean;
73
72
  blockedBy?: number[];
74
73
  addBlockedBy?: number[];
75
74
  removeBlockedBy?: number[];
76
- tags?: string[];
77
- addTags?: string[];
78
- removeTags?: string[];
79
75
  owner?: string;
80
76
  metadata?: Record<string, unknown>;
81
77
  id?: number;
82
78
  includeDeleted?: boolean;
83
- tag?: string;
84
79
  blockedOnly?: boolean;
85
80
  items?: TaskMutationParams[];
86
81
  format?: "json" | "markdown";
@@ -108,9 +103,9 @@ export const TodoParamsSchema = Type.Object({
108
103
  description: "Target status (update) or list filter (list)",
109
104
  }),
110
105
  ),
111
- priority: Type.Optional(
112
- StringEnum(["low", "medium", "high", "urgent"] as const, {
113
- description: "Task priority (create/update) or list/export filter (list/export)",
106
+ thinking: Type.Optional(
107
+ StringEnum(["off", "minimal", "low", "medium", "high", "xhigh"] as const, {
108
+ description: "Per-task thinking level used when todoThinking is enabled and this task is in_progress",
114
109
  }),
115
110
  ),
116
111
  parentId: Type.Optional(
@@ -138,21 +133,6 @@ export const TodoParamsSchema = Type.Object({
138
133
  description: "Task ids to remove from blockedBy (update only, additive merge)",
139
134
  }),
140
135
  ),
141
- tags: Type.Optional(
142
- Type.Array(Type.String(), {
143
- description: "Tags to set on create/update, replacing the previous tag list",
144
- }),
145
- ),
146
- addTags: Type.Optional(
147
- Type.Array(Type.String(), {
148
- description: "Tags to add on update without replacing existing tags",
149
- }),
150
- ),
151
- removeTags: Type.Optional(
152
- Type.Array(Type.String(), {
153
- description: "Tags to remove on update",
154
- }),
155
- ),
156
136
  owner: Type.Optional(Type.String({ description: "Agent/owner assigned to this task" })),
157
137
  metadata: Type.Optional(
158
138
  Type.Record(Type.String(), Type.Unknown(), {
@@ -169,7 +149,6 @@ export const TodoParamsSchema = Type.Object({
169
149
  description: "If true, list action returns deleted (tombstoned) tasks as well. Default: false.",
170
150
  }),
171
151
  ),
172
- tag: Type.Optional(Type.String({ description: "Tag filter for list/export" })),
173
152
  blockedOnly: Type.Optional(Type.Boolean({ description: "If true, list only tasks blocked by another task" })),
174
153
  items: Type.Optional(
175
154
  Type.Array(Type.Record(Type.String(), Type.Unknown()), {
@@ -182,7 +161,7 @@ export const TodoParamsSchema = Type.Object({
182
161
  }),
183
162
  ),
184
163
  content: Type.Optional(Type.String({ description: "Import content for action=import" })),
185
- replace: Type.Optional(Type.Boolean({ description: "For import, replace existing tasks instead of appending. Default: false." })),
164
+ replace: Type.Optional(Type.Boolean({ description: "For import/create/batch_create, replace existing tasks instead of appending. Use batch_create with replace:true when starting a new plan that supersedes old unfinished todos. Default: false." })),
186
165
  });
187
166
 
188
167
  export type TodoParams = Static<typeof TodoParamsSchema>;
@@ -10,9 +10,8 @@ export { formatStatusLabel };
10
10
  */
11
11
  export function formatCommandTaskLine(t: Task, glyph: string): string {
12
12
  const form = t.status === "in_progress" && t.activeForm ? ` (${t.activeForm})` : "";
13
- const priority = t.priority ? ` (${t.priority})` : "";
13
+ const thinking = t.thinking ? ` {thinking:${t.thinking}}` : "";
14
14
  const parent = t.parentId !== undefined ? ` ↳ #${t.parentId}` : "";
15
15
  const block = t.blockedBy?.length ? ` ⛓ ${t.blockedBy.map((id) => `#${id}`).join(",")}` : "";
16
- const tags = t.tags?.length ? ` ${t.tags.map((tag) => `#${tag}`).join(" ")}` : "";
17
- return ` ${glyph} #${t.id} ${t.subject}${priority}${form}${parent}${block}${tags}`;
16
+ return ` ${glyph} #${t.id} ${t.subject}${thinking}${form}${parent}${block}`;
18
17
  }
@@ -249,10 +249,11 @@ export const REPO_DISCOVERY_TOOL_NAMES = REPO_DISCOVERY_TOOLS.map((tool) => tool
249
249
  export const TODO_TOOL_DESCRIPTION: ToolDescription = {
250
250
  name: "todo",
251
251
  label: "Todo",
252
- description: "Track and keep in sync non-trivial multi-step work as todos. Actions: create, update, batch_create, batch_update, list, get, delete, clear, export, import. Supports priorities, tags, parent/subtask hierarchy, blockers, deferred out-of-scope items, and dependencies; skip trivial or chat-only requests. Resynchronize the plan when requirements are added, canceled, or become obsolete, whether from user input or discovered facts. Keep exactly one task in_progress and complete it only after verification.",
253
- promptSnippet: "Track/sync non-trivial multi-step work; resync when requirements or discoveries change the plan; keep one task in_progress",
252
+ description: "Track and keep in sync non-trivial multi-step work as todos. Actions: create, update, batch_create, batch_update, list, get, delete, clear, export, import. Supports parent/subtask hierarchy, blockers, deferred out-of-scope items, dependencies, and replace:true on create/batch_create/import for intentionally replacing an obsolete plan; skip trivial or chat-only requests. Resynchronize the plan when requirements are added, canceled, or become obsolete, whether from user input or discovered facts. For multi-step plans, include a final user-facing report todo in the initial create/batch_create plan when possible. Keep exactly one task in_progress and complete it only after verification.",
253
+ promptSnippet: "Track/sync non-trivial multi-step work; include final report item; resync when requirements change; keep one task in_progress",
254
254
  promptGuidelines: [
255
255
  "Use `todo` for complex work with 3+ steps, explicit user task lists, or new non-trivial requirements. Skip single trivial tasks and purely conversational requests.",
256
+ "For any multi-step implementation/debugging plan, include a final todo item in the initial create/batch_create plan for the user-facing final report. Give that final-report todo explicit description/acceptance criteria covering changed files/behavior, verification commands/results, remaining manual actions, and never substitute a compression/housekeeping note for the final report.",
256
257
  "When the user adds, removes, cancels, reprioritizes, or changes the goal, scope, requirements, constraints, or chosen approach, use `todo` before continuing to synchronize the plan: update still-relevant tasks, defer or delete obsolete tasks, add new required tasks, and adjust dependencies/order.",
257
258
  "When your own investigation or verification discovers new facts that make the current todo plan stale, incomplete, impossible, unsafe, or no longer the best approach, use `todo` to revise the plan immediately instead of following outdated tasks.",
258
259
  "Update todos as part of the workflow, not as end-of-task cleanup: whenever you start, finish, block, split, abandon, or materially change a step, call `todo` immediately before continuing.",
@@ -260,9 +261,10 @@ export const TODO_TOOL_DESCRIPTION: ToolDescription = {
260
261
  "If implementation is partial, tests fail, or a blocker remains, keep the task in_progress and add/update a blocker task instead of completing it.",
261
262
  "Never use `clear`, `delete`, or batch deletion to hide unfinished, stale, or forgotten todos. Defer obsolete items or update them with the reason; only delete when the user explicitly asks or the item was created by mistake.",
262
263
  "Before giving a final response for work that used todos, ensure every visible todo is completed, deferred, or intentionally still in_progress with a blocker/explanation.",
263
- "Keep subjects short and imperative; put details in description only when needed. Use priority, tags, and parentId for large plans; use blockedBy on create and addBlockedBy/removeBlockedBy on update for dependencies.",
264
+ "Keep subjects short and imperative; put details in description only when needed. Use parentId for large plans; use blockedBy on create and addBlockedBy/removeBlockedBy on update for dependencies.",
264
265
  "Use batch_create/batch_update for large explicit plans, but still keep exactly one visible task in_progress unless the user asks otherwise.",
265
- "list hides deleted tombstones unless includeDeleted:true; pass status, priority, tag, or blockedOnly only when you need a filtered list.",
266
+ "When starting a new plan that supersedes existing unfinished todos, use batch_create with replace:true instead of appending; only omit replace when intentionally extending the current plan.",
267
+ "list hides deleted tombstones unless includeDeleted:true; pass status or blockedOnly only when you need a filtered list.",
266
268
  "Use export/import for handoff or plan migration; import with replace:true only when the user explicitly wants to overwrite the current todo state.",
267
269
  "When every visible todo is completed, todo state clears automatically; do not call clear afterward just to remove completed tasks.",
268
270
  "Optional project persistence is controlled by `/todos persist on|off|status` or the discoverable `/todos-persist on|off|status` alias; when resuming a persisted plan, ask the user which items are in scope and run `/todos scope <id...>` or `/todos-scope <id...>` so out-of-scope active tasks become deferred.",
@@ -1,4 +1,4 @@
1
- import { getAgentDir, type ExtensionAPI, type ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
1
+ import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
2
2
  import { readFile } from "node:fs/promises";
3
3
  import { homedir } from "node:os";
4
4
  import { join } from "node:path";
@@ -37,7 +37,10 @@ async function readOpenCodeAuth(): Promise<{ authData: AuthData; error?: string
37
37
 
38
38
  async function readPiAuth(): Promise<PiAuthData> {
39
39
  try {
40
- const content = await readFile(join(getAgentDir(), "auth.json"), "utf-8");
40
+ const authPath = process.env.NODE_ENV === "test" && process.env.PI_TOOLS_SUITE_TEST_AUTH_PATH
41
+ ? process.env.PI_TOOLS_SUITE_TEST_AUTH_PATH
42
+ : join(homedir(), ".pi", "agent", "auth.json");
43
+ const content = await readFile(authPath, "utf-8");
41
44
  return JSON.parse(content) as PiAuthData;
42
45
  } catch {
43
46
  return {};
@@ -1,13 +1,12 @@
1
1
  /**
2
2
  * Google Cloud 额度查询模块
3
3
  *
4
- * [输入]: ~/.config/opencode/antigravity-accounts.json 中的账号信息
4
+ * [输入]: ~/.pi/agent/auth.json 中的 Antigravity 账号信息
5
5
  * [输出]: 格式化的额度使用情况(按重置时间自动分组)
6
6
  * [定位]: 被 usage.ts 调用,处理 Google Cloud 账号
7
7
  * [同步]: usage.ts, types.ts, utils.ts
8
8
  */
9
9
 
10
- import { getAgentDir } from "@earendil-works/pi-coding-agent";
11
10
  import { readFile } from "node:fs/promises";
12
11
  import { homedir } from "node:os";
13
12
  import { join } from "node:path";
@@ -15,7 +14,6 @@ import { join } from "node:path";
15
14
  import {
16
15
  type QueryResult,
17
16
  type AntigravityAccount,
18
- type AntigravityAccountsFile,
19
17
  HIGH_USAGE_THRESHOLD,
20
18
  } from "./types";
21
19
  import { createProgressBar, fetchWithTimeout, safeMax } from "./utils";
@@ -54,9 +52,16 @@ type PiAntigravityCredential = {
54
52
  type?: string;
55
53
  refresh?: string;
56
54
  email?: string;
55
+ clientId?: string;
56
+ clientSecret?: string;
57
+ googleClientId?: string;
58
+ googleClientSecret?: string;
59
+ oauthClient?: { clientId?: string; clientSecret?: string };
57
60
  accounts?: AntigravityAccount[];
58
61
  };
59
62
 
63
+ type GoogleOAuthClientCredentials = { clientId: string; clientSecret?: string };
64
+
60
65
  // ============================================================================
61
66
  // 常量
62
67
  // ============================================================================
@@ -86,18 +91,10 @@ const MODEL_DISPLAY_NAMES: Record<string, string> = {
86
91
  "claude-opus-4-6-thinking": "Claude Opus",
87
92
  };
88
93
 
89
- // 获取 Antigravity 账号文件路径
90
- function getAntigravityAccountsPath(): string {
91
- const home = homedir();
92
- const configDir =
93
- process.platform === "win32"
94
- ? process.env.APPDATA || join(home, "AppData", "Roaming")
95
- : join(home, ".config");
96
- return join(configDir, "opencode", "antigravity-accounts.json");
97
- }
98
-
99
94
  function getPiAuthPath(): string {
100
- return join(getAgentDir(), "auth.json");
95
+ return process.env.NODE_ENV === "test" && process.env.PI_TOOLS_SUITE_TEST_AUTH_PATH
96
+ ? process.env.PI_TOOLS_SUITE_TEST_AUTH_PATH
97
+ : join(homedir(), ".pi", "agent", "auth.json");
101
98
  }
102
99
 
103
100
  function splitPiRefresh(refresh: string): AntigravityAccount | null {
@@ -113,15 +110,37 @@ function splitPiRefresh(refresh: string): AntigravityAccount | null {
113
110
  };
114
111
  }
115
112
 
116
- async function readAntigravityAccounts(): Promise<AntigravityAccount[]> {
117
- try {
118
- const content = await readFile(getAntigravityAccountsPath(), "utf-8");
119
- const file = JSON.parse(content) as AntigravityAccountsFile;
120
- return file.accounts ?? [];
121
- } catch (error) {
122
- if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
113
+ function getAccountRefreshToken(account: AntigravityAccount): string | undefined {
114
+ if (account.refreshToken) return account.refreshToken;
115
+ if (!account.refresh) return undefined;
116
+ return splitPiRefresh(account.refresh)?.refreshToken;
117
+ }
118
+
119
+ function stringProperty(source: unknown, keys: string[]): string | undefined {
120
+ if (!source || typeof source !== "object") return undefined;
121
+ const record = source as Record<string, unknown>;
122
+ for (const key of keys) {
123
+ const value = record[key];
124
+ if (typeof value === "string" && value) return value;
123
125
  }
126
+ return undefined;
127
+ }
124
128
 
129
+ function getGoogleOAuthClientCredentials(...sources: unknown[]): GoogleOAuthClientCredentials | undefined {
130
+ for (const source of sources) {
131
+ const nested = source && typeof source === "object"
132
+ ? (source as Record<string, unknown>).oauthClient
133
+ : undefined;
134
+ const nestedClientId = stringProperty(nested, ["clientId", "client_id", "id"]);
135
+ const nestedClientSecret = stringProperty(nested, ["clientSecret", "client_secret", "secret"]);
136
+ const clientId = nestedClientId ?? stringProperty(source, ["clientId", "client_id", "googleClientId", "google_client_id", "oauthClientId", "oauth_client_id"]);
137
+ const clientSecret = nestedClientSecret ?? stringProperty(source, ["clientSecret", "client_secret", "googleClientSecret", "google_client_secret", "oauthClientSecret", "oauth_client_secret"]);
138
+ if (clientId) return { clientId, ...(clientSecret ? { clientSecret } : {}) };
139
+ }
140
+ return undefined;
141
+ }
142
+
143
+ async function readAntigravityAccounts(): Promise<AntigravityAccount[]> {
125
144
  try {
126
145
  const content = await readFile(getPiAuthPath(), "utf-8");
127
146
  const credential = (JSON.parse(content) as Record<string, PiAntigravityCredential>)[
@@ -129,8 +148,11 @@ async function readAntigravityAccounts(): Promise<AntigravityAccount[]> {
129
148
  ];
130
149
 
131
150
  if (!credential) return [];
151
+ const credentialClient = getGoogleOAuthClientCredentials(credential);
132
152
  const accounts = Array.isArray(credential.accounts)
133
- ? credential.accounts.filter((account) => account.refreshToken)
153
+ ? credential.accounts
154
+ .filter((account) => getAccountRefreshToken(account))
155
+ .map((account) => ({ ...credentialClient, ...account }))
134
156
  : [];
135
157
  const primaryAccount =
136
158
  credential.type === "oauth" && credential.refresh
@@ -138,6 +160,7 @@ async function readAntigravityAccounts(): Promise<AntigravityAccount[]> {
138
160
  : null;
139
161
  if (primaryAccount) {
140
162
  primaryAccount.email = credential.email;
163
+ Object.assign(primaryAccount, credentialClient);
141
164
  accounts.unshift(primaryAccount);
142
165
  }
143
166
 
@@ -156,15 +179,6 @@ async function readAntigravityAccounts(): Promise<AntigravityAccount[]> {
156
179
 
157
180
  const GOOGLE_TOKEN_REFRESH_URL = "https://oauth2.googleapis.com/token";
158
181
 
159
- const GOOGLE_CLIENT_ID =
160
- process.env.PIX_ANTIGRAVITY_GOOGLE_CLIENT_ID ??
161
- process.env.ANTIGRAVITY_GOOGLE_CLIENT_ID ??
162
- "";
163
- const GOOGLE_CLIENT_SECRET =
164
- process.env.PIX_ANTIGRAVITY_GOOGLE_CLIENT_SECRET ??
165
- process.env.ANTIGRAVITY_GOOGLE_CLIENT_SECRET ??
166
- "";
167
-
168
182
  // ============================================================================
169
183
  // 工具函数
170
184
  // ============================================================================
@@ -255,20 +269,19 @@ function extractModelQuotas(data: GoogleQuotaResponse): ModelQuota[] {
255
269
  * 刷新 Google access token
256
270
  */
257
271
  async function refreshAccessToken(
258
- refreshToken: string,
272
+ account: AntigravityAccount,
259
273
  ): Promise<{ access_token: string; expires_in: number }> {
260
- if (!GOOGLE_CLIENT_ID || !GOOGLE_CLIENT_SECRET) {
261
- throw new Error(
262
- "Antigravity Google OAuth credentials are not configured; set PIX_ANTIGRAVITY_GOOGLE_CLIENT_ID and PIX_ANTIGRAVITY_GOOGLE_CLIENT_SECRET.",
263
- );
264
- }
274
+ const refreshToken = getAccountRefreshToken(account);
275
+ if (!refreshToken) throw new Error("Missing refresh token, cannot query quota.");
276
+ const clientCredentials = getGoogleOAuthClientCredentials(account);
277
+ if (!clientCredentials) throw new Error(`Antigravity Google OAuth client credentials are missing in Pi auth: ${getPiAuthPath()}.`);
265
278
 
266
279
  const params = new URLSearchParams({
267
- client_id: GOOGLE_CLIENT_ID,
268
- client_secret: GOOGLE_CLIENT_SECRET,
280
+ client_id: clientCredentials.clientId,
269
281
  refresh_token: refreshToken,
270
282
  grant_type: "refresh_token",
271
283
  });
284
+ if (clientCredentials.clientSecret) params.set("client_secret", clientCredentials.clientSecret);
272
285
 
273
286
  const response = await fetch(GOOGLE_TOKEN_REFRESH_URL, {
274
287
  method: "POST",
@@ -326,7 +339,7 @@ async function fetchAccountQuota(
326
339
  }> {
327
340
  try {
328
341
  // 刷新 access token
329
- const { access_token } = await refreshAccessToken(account.refreshToken);
342
+ const { access_token } = await refreshAccessToken(account);
330
343
 
331
344
  // 使用 projectId 或 managedProjectId
332
345
  const projectId = account.projectId || account.managedProjectId;
@@ -73,13 +73,23 @@ export interface CopilotQuotaConfig {
73
73
  }
74
74
 
75
75
  /**
76
- * Antigravity 账号(来自 ~/.config/opencode/antigravity-accounts.json)
76
+ * Antigravity 账号(来自 ~/.pi/agent/auth.json)
77
77
  */
78
78
  export interface AntigravityAccount {
79
79
  email?: string;
80
- refreshToken: string;
80
+ access?: string;
81
+ refresh?: string;
82
+ expires?: number;
83
+ refreshToken?: string;
81
84
  projectId?: string;
82
85
  managedProjectId?: string;
86
+ clientId?: string;
87
+ clientSecret?: string;
88
+ googleClientId?: string;
89
+ googleClientSecret?: string;
90
+ oauthClient?: { clientId?: string; clientSecret?: string };
91
+ fingerprint?: { apiClient?: string; [key: string]: unknown };
92
+ fingerprintHistory?: Array<{ fingerprint?: { apiClient?: string; [key: string]: unknown }; [key: string]: unknown }>;
83
93
  addedAt: number;
84
94
  lastUsed: number;
85
95
  rateLimitResetTimes?: Record<string, number>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-ui-extend",
3
- "version": "0.1.13",
3
+ "version": "0.1.17",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,6 +16,10 @@
16
16
  },
17
17
  "description": "List of disabled module names (e.g. ['lsp', 'prompt-commands'])."
18
18
  },
19
+ "todoThinking": {
20
+ "type": "boolean",
21
+ "description": "Enable per-todo thinking levels and automatic thinking switch/restore when tasks become in-progress/completed."
22
+ },
19
23
  "terminalBell": {
20
24
  "type": "object",
21
25
  "properties": {
package/schemas/pix.json CHANGED
@@ -5,6 +5,15 @@
5
5
  "type": "string",
6
6
  "description": "JSON Schema URL used by editors for validation and autocomplete."
7
7
  },
8
+ "ignoreContextFiles": {
9
+ "type": "boolean",
10
+ "description": "Disable AGENTS.md / CLAUDE.md discovery for sessions started in this project, equivalent to pi --no-context-files."
11
+ },
12
+ "maxProjectSessions": {
13
+ "type": "number",
14
+ "description": "Maximum number of pi session JSONL files to retain per project. Set to 0 to disable automatic session deletion.",
15
+ "minimum": 0
16
+ },
8
17
  "defaultModel": {
9
18
  "type": "object",
10
19
  "properties": {
@@ -39,7 +48,7 @@
39
48
  "const": "xhigh"
40
49
  }
41
50
  ],
42
- "description": "Model thinking budget level."
51
+ "description": "Default model thinking budget level."
43
52
  }
44
53
  },
45
54
  "description": "Default model selection for new sessions."
@@ -293,6 +302,6 @@
293
302
  "$id": "https://unpkg.com/pi-ui-extend/schemas/pix.json",
294
303
  "$schema": "https://json-schema.org/draft-07/schema#",
295
304
  "title": "Pix Configuration",
296
- "description": "Configuration for the pix terminal renderer (~/.config/pi/pix.jsonc).",
305
+ "description": "Configuration for the pix terminal renderer (~/.config/pi/pix.jsonc, with project overrides in <cwd>/.pi/pix.jsonc).",
297
306
  "additionalProperties": true
298
307
  }