tau-all-agent 0.1.0
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/LICENSE +21 -0
- package/README.md +45 -0
- package/extensions/answer.ts +601 -0
- package/extensions/branch-term.ts +405 -0
- package/extensions/btw.ts +444 -0
- package/extensions/ghostty.ts +301 -0
- package/extensions/git-diff-stats.ts +277 -0
- package/extensions/git-pr-status.ts +286 -0
- package/extensions/insights.ts +2367 -0
- package/extensions/interlude.ts +144 -0
- package/extensions/loop.ts +529 -0
- package/extensions/memory.ts +1889 -0
- package/extensions/notify.ts +161 -0
- package/extensions/openai-fast.ts +227 -0
- package/extensions/openai-verbosity.ts +223 -0
- package/extensions/review.ts +4347 -0
- package/extensions/sandbox/index.ts +2578 -0
- package/extensions/telegram/README.md +81 -0
- package/extensions/telegram/daemon.mjs +1942 -0
- package/extensions/telegram/index.ts +1073 -0
- package/extensions/telegram/message-text.d.mts +1 -0
- package/extensions/telegram/message-text.mjs +16 -0
- package/extensions/usage/anthropic.ts +198 -0
- package/extensions/usage/github-copilot.ts +204 -0
- package/extensions/usage/google-gemini-cli.ts +232 -0
- package/extensions/usage/index.ts +2310 -0
- package/extensions/usage/minimax.ts +208 -0
- package/extensions/usage/openai-codex.ts +180 -0
- package/extensions/usage/openrouter.ts +168 -0
- package/extensions/usage/providers.ts +26 -0
- package/extensions/usage/shared.ts +156 -0
- package/extensions/usage/types.ts +53 -0
- package/extensions/usage/zai.ts +186 -0
- package/extensions/websearch/README.md +68 -0
- package/extensions/websearch/browser/chromium.ts +330 -0
- package/extensions/websearch/browser/discovery.ts +75 -0
- package/extensions/websearch/browser/firefox.ts +150 -0
- package/extensions/websearch/browser/sqlite.ts +38 -0
- package/extensions/websearch/config.ts +76 -0
- package/extensions/websearch/index.ts +312 -0
- package/extensions/websearch/normalize.ts +155 -0
- package/extensions/websearch/providers/anthropic.pi.ts +139 -0
- package/extensions/websearch/providers/gemini.browser.ts +185 -0
- package/extensions/websearch/providers/gemini.pi.ts +108 -0
- package/extensions/websearch/providers/openai-codex.browser.ts +77 -0
- package/extensions/websearch/providers/openai-codex.pi.ts +49 -0
- package/extensions/websearch/providers/openai-codex.shared.ts +123 -0
- package/extensions/websearch/providers/pi-model.shared.ts +86 -0
- package/extensions/websearch/providers/search-prompt.shared.ts +17 -0
- package/extensions/websearch/providers/shared.ts +76 -0
- package/extensions/websearch/types.ts +52 -0
- package/extensions/worktree.ts +2223 -0
- package/package.json +76 -0
- package/skills/browser-tools/SKILL.md +253 -0
- package/skills/browser-tools/scripts/browser-content.js +100 -0
- package/skills/browser-tools/scripts/browser-cookies.js +33 -0
- package/skills/browser-tools/scripts/browser-dismiss-cookies.js +455 -0
- package/skills/browser-tools/scripts/browser-eval.js +75 -0
- package/skills/browser-tools/scripts/browser-logs-tail.js +89 -0
- package/skills/browser-tools/scripts/browser-nav.js +28 -0
- package/skills/browser-tools/scripts/browser-net-summary.js +115 -0
- package/skills/browser-tools/scripts/browser-pick.js +133 -0
- package/skills/browser-tools/scripts/browser-screenshot.js +18 -0
- package/skills/browser-tools/scripts/browser-start.js +281 -0
- package/skills/browser-tools/scripts/browser-watch.js +321 -0
- package/skills/browser-tools/scripts/utils.js +60 -0
- package/skills/git-clean-history/SKILL.md +45 -0
- package/skills/git-commit/SKILL.md +55 -0
- package/skills/homeassistant-ops/SKILL.md +184 -0
- package/skills/homeassistant-ops/references/api.md +64 -0
- package/skills/homeassistant-ops/references/id_conventions.md +71 -0
- package/skills/homeassistant-ops/references/playbook.md +150 -0
- package/skills/homeassistant-ops/scripts/ha_ops.js +2942 -0
- package/skills/openscad/SKILL.md +149 -0
- package/skills/openscad/examples/parametric_box.scad +92 -0
- package/skills/openscad/examples/phone_stand.scad +95 -0
- package/skills/openscad/scripts/common.sh +49 -0
- package/skills/openscad/scripts/export-stl.sh +56 -0
- package/skills/openscad/scripts/extract-params.sh +147 -0
- package/skills/openscad/scripts/multi-preview.sh +68 -0
- package/skills/openscad/scripts/preview.sh +74 -0
- package/skills/openscad/scripts/render-with-params.sh +91 -0
- package/skills/openscad/scripts/validate.sh +46 -0
- package/skills/oracle/SKILL.md +64 -0
- package/skills/oracle/scripts/oracle +249 -0
- package/skills/oracle/scripts/oracle-bundle +227 -0
- package/skills/sentry/SKILL.md +198 -0
- package/skills/sentry/lib/auth.js +99 -0
- package/skills/sentry/scripts/fetch-event.js +325 -0
- package/skills/sentry/scripts/fetch-issue.js +368 -0
- package/skills/sentry/scripts/list-issues.js +245 -0
- package/skills/sentry/scripts/search-events.js +291 -0
- package/skills/sentry/scripts/search-logs.js +234 -0
- package/skills/update-changelog/SKILL.md +135 -0
- package/skills/web-design/SKILL.md +117 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import type { LiveUsageMeter, LiveUsageStat } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export class LiveUsageUnavailableError extends Error {
|
|
4
|
+
constructor(message: string) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "LiveUsageUnavailableError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class HttpError extends Error {
|
|
11
|
+
constructor(
|
|
12
|
+
readonly status: number,
|
|
13
|
+
message: string,
|
|
14
|
+
) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = "HttpError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function isLiveUsageUnavailableError(error: unknown): error is LiveUsageUnavailableError {
|
|
21
|
+
return error instanceof LiveUsageUnavailableError;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function readObject(value: unknown): Record<string, unknown> | undefined {
|
|
25
|
+
return value && typeof value === "object" ? (value as Record<string, unknown>) : undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function readNumber(value: unknown): number | undefined {
|
|
29
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : undefined;
|
|
30
|
+
if (typeof value === "string" && value.trim()) {
|
|
31
|
+
const parsed = Number(value);
|
|
32
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function readString(value: unknown): string | undefined {
|
|
38
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function readArray<T>(value: unknown): T[] {
|
|
42
|
+
return Array.isArray(value) ? (value as T[]) : [];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function clampPercent(value: number): number {
|
|
46
|
+
return Math.max(0, Math.min(100, value));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function formatPlan(plan?: string): string | undefined {
|
|
50
|
+
if (!plan) return undefined;
|
|
51
|
+
return plan
|
|
52
|
+
.split(/[_\s-]+/)
|
|
53
|
+
.filter(Boolean)
|
|
54
|
+
.map((part) => part[0]?.toUpperCase() + part.slice(1))
|
|
55
|
+
.join(" ");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function formatCount(value: number): string {
|
|
59
|
+
if (!Number.isFinite(value)) return "0";
|
|
60
|
+
if (Math.abs(value) >= 1_000_000_000) return `${(value / 1_000_000_000).toFixed(1)}B`;
|
|
61
|
+
if (Math.abs(value) >= 1_000_000) return `${(value / 1_000_000).toFixed(1)}M`;
|
|
62
|
+
if (Math.abs(value) >= 10_000) return `${(value / 1_000).toFixed(1)}K`;
|
|
63
|
+
return Math.round(value).toLocaleString("en-US");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function formatCurrency(value: number, currency = "USD"): string {
|
|
67
|
+
if (!Number.isFinite(value)) value = 0;
|
|
68
|
+
|
|
69
|
+
const minimumFractionDigits = Math.abs(value) >= 1 ? 2 : 4;
|
|
70
|
+
const maximumFractionDigits = Math.abs(value) >= 1_000 ? 2 : minimumFractionDigits;
|
|
71
|
+
|
|
72
|
+
return value.toLocaleString("en-US", {
|
|
73
|
+
style: "currency",
|
|
74
|
+
currency,
|
|
75
|
+
minimumFractionDigits,
|
|
76
|
+
maximumFractionDigits,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function parseISODate(value: string | undefined): Date | undefined {
|
|
81
|
+
if (!value) return undefined;
|
|
82
|
+
|
|
83
|
+
const parsed = new Date(value);
|
|
84
|
+
return Number.isFinite(parsed.getTime()) ? parsed : undefined;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function parseEpochDate(value: number | undefined): Date | undefined {
|
|
88
|
+
if (value === undefined) return undefined;
|
|
89
|
+
|
|
90
|
+
const epoch = value > 1_000_000_000_000 ? value : value * 1000;
|
|
91
|
+
const parsed = new Date(epoch);
|
|
92
|
+
return Number.isFinite(parsed.getTime()) ? parsed : undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function formatRelativeResetDescription(resetsAt?: Date): string | undefined {
|
|
96
|
+
if (!resetsAt) return undefined;
|
|
97
|
+
|
|
98
|
+
const deltaMs = resetsAt.getTime() - Date.now();
|
|
99
|
+
if (deltaMs <= 0) return "reset imminent";
|
|
100
|
+
|
|
101
|
+
const minutes = Math.round(deltaMs / 60_000);
|
|
102
|
+
if (minutes < 60) return `resets in ~${minutes}m`;
|
|
103
|
+
|
|
104
|
+
const hours = Math.round(deltaMs / 3_600_000);
|
|
105
|
+
if (hours < 48) return `resets in ~${hours}h`;
|
|
106
|
+
|
|
107
|
+
const days = Math.round(deltaMs / 86_400_000);
|
|
108
|
+
return `resets in ~${days}d`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function formatWindowLabel(windowMinutes?: number): string | undefined {
|
|
112
|
+
if (!windowMinutes || windowMinutes <= 0) return undefined;
|
|
113
|
+
if (windowMinutes % (24 * 60) === 0) return `${windowMinutes / (24 * 60)}d`;
|
|
114
|
+
if (windowMinutes % 60 === 0) return `${windowMinutes / 60}h`;
|
|
115
|
+
return `${windowMinutes}m`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function formatUsedPercent(usedPercent: number): string {
|
|
119
|
+
return `${Math.round(clampPercent(usedPercent))}% used`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function makeMeterItem(options: {
|
|
123
|
+
key: string;
|
|
124
|
+
label: string;
|
|
125
|
+
usedPercent: number;
|
|
126
|
+
windowLabel?: string;
|
|
127
|
+
resetsAt?: Date;
|
|
128
|
+
resetDescription?: string;
|
|
129
|
+
detail?: string;
|
|
130
|
+
}): LiveUsageMeter {
|
|
131
|
+
const usedPercent = clampPercent(options.usedPercent);
|
|
132
|
+
return {
|
|
133
|
+
kind: "meter",
|
|
134
|
+
key: options.key,
|
|
135
|
+
label: options.label,
|
|
136
|
+
usedPercent,
|
|
137
|
+
windowLabel: options.windowLabel,
|
|
138
|
+
resetDescription: options.resetDescription ?? formatRelativeResetDescription(options.resetsAt),
|
|
139
|
+
detail: options.detail,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function makeStatItem(options: {
|
|
144
|
+
key: string;
|
|
145
|
+
label: string;
|
|
146
|
+
value: string;
|
|
147
|
+
detail?: string;
|
|
148
|
+
}): LiveUsageStat {
|
|
149
|
+
return {
|
|
150
|
+
kind: "stat",
|
|
151
|
+
key: options.key,
|
|
152
|
+
label: options.label,
|
|
153
|
+
value: options.value,
|
|
154
|
+
detail: options.detail,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
export interface RGB {
|
|
4
|
+
r: number;
|
|
5
|
+
g: number;
|
|
6
|
+
b: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type SupportedProviderId =
|
|
10
|
+
| "openai-codex"
|
|
11
|
+
| "anthropic"
|
|
12
|
+
| "github-copilot"
|
|
13
|
+
| "google-gemini-cli"
|
|
14
|
+
| "openrouter"
|
|
15
|
+
| "zai"
|
|
16
|
+
| "minimax";
|
|
17
|
+
|
|
18
|
+
export interface LiveUsageMeter {
|
|
19
|
+
kind: "meter";
|
|
20
|
+
key: string;
|
|
21
|
+
label: string;
|
|
22
|
+
usedPercent: number;
|
|
23
|
+
windowLabel?: string;
|
|
24
|
+
resetDescription?: string;
|
|
25
|
+
detail?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface LiveUsageStat {
|
|
29
|
+
kind: "stat";
|
|
30
|
+
key: string;
|
|
31
|
+
label: string;
|
|
32
|
+
value: string;
|
|
33
|
+
detail?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type LiveUsageItem = LiveUsageMeter | LiveUsageStat;
|
|
37
|
+
|
|
38
|
+
export interface LiveUsageSnapshot {
|
|
39
|
+
plan?: string;
|
|
40
|
+
fetchedAt: Date;
|
|
41
|
+
items: LiveUsageItem[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type LiveUsageAvailability = { available: true } | { available: false; reason: string };
|
|
45
|
+
|
|
46
|
+
export interface UsageProviderDefinition {
|
|
47
|
+
id: SupportedProviderId;
|
|
48
|
+
label: string;
|
|
49
|
+
shortLabel: string;
|
|
50
|
+
color: RGB;
|
|
51
|
+
getLiveUsageAvailability?(ctx: ExtensionContext): Promise<LiveUsageAvailability>;
|
|
52
|
+
fetchLiveUsage(ctx: ExtensionContext, signal?: AbortSignal): Promise<LiveUsageSnapshot>;
|
|
53
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
import {
|
|
3
|
+
clampPercent,
|
|
4
|
+
formatCount,
|
|
5
|
+
formatPlan,
|
|
6
|
+
formatWindowLabel,
|
|
7
|
+
LiveUsageUnavailableError,
|
|
8
|
+
makeMeterItem,
|
|
9
|
+
parseEpochDate,
|
|
10
|
+
readArray,
|
|
11
|
+
readNumber,
|
|
12
|
+
readObject,
|
|
13
|
+
readString,
|
|
14
|
+
} from "./shared.js";
|
|
15
|
+
import type { LiveUsageItem, LiveUsageSnapshot, UsageProviderDefinition } from "./types.js";
|
|
16
|
+
|
|
17
|
+
const PROVIDER_ID = "zai";
|
|
18
|
+
const QUOTA_URL = "https://api.z.ai/api/monitor/usage/quota/limit";
|
|
19
|
+
|
|
20
|
+
type LimitType = "TIME_LIMIT" | "TOKENS_LIMIT";
|
|
21
|
+
|
|
22
|
+
interface LimitEntry {
|
|
23
|
+
type: LimitType;
|
|
24
|
+
windowMinutes?: number;
|
|
25
|
+
usage?: number;
|
|
26
|
+
currentValue?: number;
|
|
27
|
+
remaining?: number;
|
|
28
|
+
usedPercent: number;
|
|
29
|
+
nextResetTime?: Date;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function windowMinutes(unit: number | undefined, count: number | undefined): number | undefined {
|
|
33
|
+
if (!unit || !count || count <= 0) return undefined;
|
|
34
|
+
switch (unit) {
|
|
35
|
+
case 5:
|
|
36
|
+
return count;
|
|
37
|
+
case 3:
|
|
38
|
+
return count * 60;
|
|
39
|
+
case 1:
|
|
40
|
+
return count * 24 * 60;
|
|
41
|
+
case 6:
|
|
42
|
+
return count * 7 * 24 * 60;
|
|
43
|
+
default:
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function parseLimitEntry(value: unknown): LimitEntry | undefined {
|
|
49
|
+
const input = readObject(value);
|
|
50
|
+
const rawType = readString(input?.type);
|
|
51
|
+
if (rawType !== "TIME_LIMIT" && rawType !== "TOKENS_LIMIT") return undefined;
|
|
52
|
+
|
|
53
|
+
const usage = readNumber(input?.usage);
|
|
54
|
+
const currentValue = readNumber(input?.currentValue);
|
|
55
|
+
const remaining = readNumber(input?.remaining);
|
|
56
|
+
const explicitPercent = readNumber(input?.percentage);
|
|
57
|
+
const resolvedWindowMinutes = windowMinutes(readNumber(input?.unit), readNumber(input?.number));
|
|
58
|
+
const nextResetTime = parseEpochDate(readNumber(input?.nextResetTime));
|
|
59
|
+
|
|
60
|
+
let usedPercent = explicitPercent;
|
|
61
|
+
if ((usedPercent === undefined || usedPercent <= 0) && usage !== undefined && usage > 0) {
|
|
62
|
+
if (remaining !== undefined) {
|
|
63
|
+
usedPercent = ((usage - remaining) / usage) * 100;
|
|
64
|
+
} else if (currentValue !== undefined) {
|
|
65
|
+
usedPercent = (currentValue / usage) * 100;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (usedPercent === undefined) return undefined;
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
type: rawType,
|
|
73
|
+
windowMinutes: resolvedWindowMinutes,
|
|
74
|
+
usage,
|
|
75
|
+
currentValue,
|
|
76
|
+
remaining,
|
|
77
|
+
usedPercent: clampPercent(usedPercent),
|
|
78
|
+
nextResetTime:
|
|
79
|
+
nextResetTime && Number.isFinite(nextResetTime.getTime()) ? nextResetTime : undefined,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function makeLimitMeter(key: string, label: string, entry: LimitEntry): LiveUsageItem {
|
|
84
|
+
let detail: string | undefined;
|
|
85
|
+
if (entry.usage !== undefined && entry.usage > 0 && entry.remaining !== undefined) {
|
|
86
|
+
detail = `${formatCount(entry.remaining)} / ${formatCount(entry.usage)} remaining`;
|
|
87
|
+
} else if (entry.usage !== undefined && entry.usage > 0 && entry.currentValue !== undefined) {
|
|
88
|
+
detail = `${formatCount(entry.currentValue)} / ${formatCount(entry.usage)} used`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!detail && entry.type === "TIME_LIMIT" && entry.windowMinutes) {
|
|
92
|
+
const windowLabel = formatWindowLabel(entry.windowMinutes);
|
|
93
|
+
if (windowLabel) detail = `${windowLabel} window`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return makeMeterItem({
|
|
97
|
+
key,
|
|
98
|
+
label,
|
|
99
|
+
usedPercent: entry.usedPercent,
|
|
100
|
+
windowLabel: entry.type === "TOKENS_LIMIT" ? formatWindowLabel(entry.windowMinutes) : undefined,
|
|
101
|
+
resetsAt: entry.nextResetTime,
|
|
102
|
+
detail,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function fetchUsage(ctx: ExtensionContext, signal?: AbortSignal): Promise<LiveUsageSnapshot> {
|
|
107
|
+
const apiKey = await ctx.modelRegistry.getApiKeyForProvider(PROVIDER_ID);
|
|
108
|
+
if (!apiKey) throw new LiveUsageUnavailableError("no auth configured");
|
|
109
|
+
|
|
110
|
+
const response = await fetch(QUOTA_URL, {
|
|
111
|
+
method: "GET",
|
|
112
|
+
headers: {
|
|
113
|
+
authorization: `Bearer ${apiKey}`,
|
|
114
|
+
accept: "application/json",
|
|
115
|
+
},
|
|
116
|
+
signal,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
if (response.status === 401 || response.status === 403) {
|
|
120
|
+
throw new Error("z.ai API key is invalid or expired.");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!response.ok) {
|
|
124
|
+
const bodyText = await response.text().catch(() => "");
|
|
125
|
+
throw new Error(
|
|
126
|
+
bodyText.trim()
|
|
127
|
+
? `z.ai usage request failed (${response.status}): ${bodyText.trim()}`
|
|
128
|
+
: `z.ai usage request failed (${response.status}).`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const payload = (await response.json()) as Record<string, unknown>;
|
|
133
|
+
if (payload.success !== true || readNumber(payload.code) !== 200) {
|
|
134
|
+
throw new Error(readString(payload.msg) ?? "z.ai usage request failed.");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const data = readObject(payload.data);
|
|
138
|
+
const rawLimits = readArray<unknown>(data?.limits);
|
|
139
|
+
const limits = rawLimits
|
|
140
|
+
.map(parseLimitEntry)
|
|
141
|
+
.filter((entry): entry is LimitEntry => Boolean(entry));
|
|
142
|
+
|
|
143
|
+
const tokenLimits = limits
|
|
144
|
+
.filter((entry) => entry.type === "TOKENS_LIMIT")
|
|
145
|
+
.sort(
|
|
146
|
+
(left, right) =>
|
|
147
|
+
(left.windowMinutes ?? Number.MAX_SAFE_INTEGER) -
|
|
148
|
+
(right.windowMinutes ?? Number.MAX_SAFE_INTEGER),
|
|
149
|
+
);
|
|
150
|
+
const timeLimit = limits.find((entry) => entry.type === "TIME_LIMIT");
|
|
151
|
+
|
|
152
|
+
const items: LiveUsageItem[] = [];
|
|
153
|
+
const sessionTokenLimit = tokenLimits[0];
|
|
154
|
+
const longTokenLimit = tokenLimits.length > 1 ? tokenLimits[tokenLimits.length - 1] : undefined;
|
|
155
|
+
|
|
156
|
+
if (sessionTokenLimit) items.push(makeLimitMeter("session-tokens", "Tokens", sessionTokenLimit));
|
|
157
|
+
if (longTokenLimit && longTokenLimit !== sessionTokenLimit) {
|
|
158
|
+
items.push(makeLimitMeter("tokens", "Tokens", longTokenLimit));
|
|
159
|
+
}
|
|
160
|
+
if (timeLimit) items.push(makeLimitMeter("time", "Time", timeLimit));
|
|
161
|
+
|
|
162
|
+
if (items.length === 0) {
|
|
163
|
+
throw new Error("z.ai did not return any live quota data.");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
plan: formatPlan(
|
|
168
|
+
readString(data?.planName) ??
|
|
169
|
+
readString(data?.plan) ??
|
|
170
|
+
readString(data?.plan_type) ??
|
|
171
|
+
readString(data?.packageName),
|
|
172
|
+
),
|
|
173
|
+
fetchedAt: new Date(),
|
|
174
|
+
items,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const zaiProvider: UsageProviderDefinition = {
|
|
179
|
+
id: PROVIDER_ID,
|
|
180
|
+
label: "z.ai",
|
|
181
|
+
shortLabel: "zai",
|
|
182
|
+
color: { r: 232, g: 90, b: 106 },
|
|
183
|
+
fetchLiveUsage: fetchUsage,
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export default zaiProvider;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# websearch
|
|
2
|
+
|
|
3
|
+
Web search via Gemini, OpenAI, or Claude, leveraging Pi or browser sessions.
|
|
4
|
+
|
|
5
|
+
Current routes:
|
|
6
|
+
|
|
7
|
+
- `pi:openai-codex`
|
|
8
|
+
- `pi:anthropic`
|
|
9
|
+
- `pi:gemini`
|
|
10
|
+
- `firefox:gemini`
|
|
11
|
+
- `firefox:openai-codex`
|
|
12
|
+
- `chromium:gemini`
|
|
13
|
+
- `chromium:openai-codex`
|
|
14
|
+
|
|
15
|
+
## Config
|
|
16
|
+
|
|
17
|
+
Optional config file:
|
|
18
|
+
|
|
19
|
+
- `~/.pi/websearch.json`
|
|
20
|
+
|
|
21
|
+
Example:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"routes": [
|
|
26
|
+
"pi:openai-codex",
|
|
27
|
+
"pi:anthropic",
|
|
28
|
+
"pi:gemini",
|
|
29
|
+
"firefox:gemini",
|
|
30
|
+
"firefox:openai-codex",
|
|
31
|
+
"chromium:gemini",
|
|
32
|
+
"chromium:openai-codex"
|
|
33
|
+
],
|
|
34
|
+
"profiles": {
|
|
35
|
+
"firefox": "default-release",
|
|
36
|
+
"chromium": "Default"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Defaults:
|
|
42
|
+
|
|
43
|
+
- default routes match the example above
|
|
44
|
+
- if `routes` is set, only those routes are tried, in that order
|
|
45
|
+
- Pi routes try the current Pi model first within the matching provider family, then fall back to another available model from that family
|
|
46
|
+
|
|
47
|
+
`profiles` only applies to browser-backed routes. It pins which local Firefox or Chromium profile to read cookies from.
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
In Pi, the extension exposes:
|
|
52
|
+
|
|
53
|
+
- tool: `websearch`
|
|
54
|
+
|
|
55
|
+
Examples:
|
|
56
|
+
|
|
57
|
+
- `websearch({ query: "latest Todoist release notes" })`
|
|
58
|
+
- `websearch({ query: "how does Pi compaction work" })`
|
|
59
|
+
|
|
60
|
+
## Notes
|
|
61
|
+
|
|
62
|
+
- Pi-backed API routes are preferred, with browser fallbacks after that.
|
|
63
|
+
- Browser routes use existing local browser sessions.
|
|
64
|
+
- Browser profiles are auto-discovered unless pinned in config.
|
|
65
|
+
- Supported Chromium-family browsers are Chromium, Chrome, Brave, and Edge.
|
|
66
|
+
- Browser discovery currently supports macOS and Linux, not Windows.
|
|
67
|
+
- On macOS, Chromium-based browsers may prompt for Keychain access so cookies can be decrypted.
|
|
68
|
+
- If one route fails, the extension falls through to the next one.
|