pi-meta-oauth 0.7.3 → 0.7.5
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 +13 -6
- package/extensions/meta.ts +5 -1
- package/package.json +5 -5
- package/src/meta/models.ts +90 -7
- package/src/meta/muse-policy.ts +26 -4
- package/src/meta/types.ts +9 -1
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ Prefer a static key instead? Set `META_API_KEY` (or `MODEL_API_KEY`) and skip
|
|
|
48
48
|
|
|
49
49
|
## Models
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
Bundled models use a 1,048,576-token context window, up to 256K output tokens, image input, and reasoning levels `minimal`, `low`, `medium`, `high`, and `xhigh`. `muse-spark-1.3`, `muse-wonder-1.0`, and `muse-wonder-full-1.0` additionally support `max`; their `-contributor` variants do too when [opted in](#contributor-max-opt-in). Meta's catalog endpoint has returned bare model IDs without names, prices, or modalities since at least 2026-10-23, so these bundled definitions supply that metadata for known IDs; an unknown ID still appears with default values.
|
|
52
52
|
|
|
53
53
|
| id | pricing (input/output/cached) $/M |
|
|
54
54
|
| --- | --- |
|
|
@@ -57,6 +57,12 @@ Fallback models use a 1,048,576-token context window, up to 256K output tokens,
|
|
|
57
57
|
| `muse-spark-1.2` | 1.25 / 4.25 / 0.15 |
|
|
58
58
|
| `muse-spark-1.2-contributor` | 0.10 / 0.20 / 0.002 |
|
|
59
59
|
| `muse-spark-1.1` | 1.25 / 4.25 / 0.15 |
|
|
60
|
+
| `muse-wonder-1.0` | 4.00 / 20.00 / 0.20 |
|
|
61
|
+
| `muse-wonder-1.0-contributor` | no published per-token rate (Muse Code subscription); Pi shows $0 |
|
|
62
|
+
| `muse-wonder-full-1.0` | no published per-token rate (Muse Code subscription); Pi shows $0 |
|
|
63
|
+
| `muse-wonder-full-1.0-contributor` | no published per-token rate (Muse Code subscription); Pi shows $0 |
|
|
64
|
+
|
|
65
|
+
The catalog also lists Meta's non-chat models, which are billed per generated image, per hour of audio, or per video frame rather than per token: the `muse-image-*`, `muse-voice-*`, and `sam-*` families. The extension hides them from the chat model list, including entries an earlier release cached in `models-store.json`.
|
|
60
66
|
|
|
61
67
|
> **Contributor-model privacy:** discounted contributor models allow Meta to use prompts and completions for product improvement, including training future Meta models. Use a standard model such as `muse-spark-1.3` if you do not want the contributor terms. See [Meta's model documentation](https://dev.meta.ai/docs/models).
|
|
62
68
|
|
|
@@ -75,7 +81,7 @@ Then open a **new** terminal and restart Pi. Shells that were already open don't
|
|
|
75
81
|
|
|
76
82
|
The header is the same on every OS. The captured string names `linux-x86_64`, but the platform segment doesn't appear to be checked: it was accepted from Windows (2026-09-26), and oh-my-pi sends the same fixed string on every platform.
|
|
77
83
|
|
|
78
|
-
With the flag set, the extension exposes `max` on `muse-spark-1.3-contributor` and sends the captured Muse `User-Agent` on
|
|
84
|
+
With the flag set, the extension exposes `max` on the gated contributor models (`muse-spark-1.3-contributor`, `muse-wonder-1.0-contributor`, and `muse-wonder-full-1.0-contributor`) and sends the captured Muse `User-Agent` on those models' requests to `https://api.meta.ai/v1` only. Other models, proxies and custom `baseUrl`s, and any `User-Agent` you set yourself are left untouched. Without the flag, Contributor `max` is hidden and Pi's own `User-Agent` is sent.
|
|
79
85
|
|
|
80
86
|
> **Warning:** this makes Pi identify as Meta's first-party Muse client. It relies on undocumented server behavior, is not supported by Meta, may stop working without notice, and may conflict with Meta's terms. Enable it only if you accept that risk for your account.
|
|
81
87
|
|
|
@@ -135,10 +141,11 @@ also bypasses isolation and can run these probes when credentials are available.
|
|
|
135
141
|
The cache probe makes two billable Responses requests plus one retry on a cache
|
|
136
142
|
miss. The fingerprint probe makes two tiny Contributor-`max` requests; it sends
|
|
137
143
|
the header directly, independently of `META_MUSE_USER_AGENT`. The catalog probe
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
no usable
|
|
144
|
+
makes one catalog request and fails when a live model ID has neither a bundled
|
|
145
|
+
definition nor a non-chat prefix. All three probes resolve credentials from
|
|
146
|
+
`PI_META_LIVE_API_KEY`, `META_API_KEY`, `MODEL_API_KEY`, or an unexpired minted
|
|
147
|
+
key in `~/.pi/agent/auth.json`, in that order, and skip when no usable
|
|
148
|
+
credential exists.
|
|
142
149
|
|
|
143
150
|
```bash
|
|
144
151
|
bun run test:live tests/meta-cache.test.ts
|
package/extensions/meta.ts
CHANGED
|
@@ -15,7 +15,11 @@ export {
|
|
|
15
15
|
MUSE_USER_AGENT_ENV_VAR,
|
|
16
16
|
} from "../src/meta/constants.ts";
|
|
17
17
|
export { refreshMetaModels } from "../src/meta/model-store.ts";
|
|
18
|
-
export {
|
|
18
|
+
export {
|
|
19
|
+
isNonChatModelId,
|
|
20
|
+
metaFallbackCost,
|
|
21
|
+
toProviderModels,
|
|
22
|
+
} from "../src/meta/models.ts";
|
|
19
23
|
export { isMuseUserAgentEnabled } from "../src/meta/muse-policy.ts";
|
|
20
24
|
export {
|
|
21
25
|
loginMeta,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-meta-oauth",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.5",
|
|
4
4
|
"description": "Meta Model API OAuth provider for pi",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -32,13 +32,13 @@
|
|
|
32
32
|
"image": "https://raw.githubusercontent.com/BlockedPath/pi-meta-oauth/main/preview.png"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"@earendil-works/pi-ai": ">=0.83.0 <0.
|
|
36
|
-
"@earendil-works/pi-coding-agent": ">=0.83.0 <0.
|
|
35
|
+
"@earendil-works/pi-ai": ">=0.83.0 <2.0.0",
|
|
36
|
+
"@earendil-works/pi-coding-agent": ">=0.83.0 <2.0.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@biomejs/biome": "2.5.15",
|
|
40
|
-
"@earendil-works/pi-ai": "
|
|
41
|
-
"@earendil-works/pi-coding-agent": "
|
|
40
|
+
"@earendil-works/pi-ai": "1.4.0",
|
|
41
|
+
"@earendil-works/pi-coding-agent": "1.4.0",
|
|
42
42
|
"bun-types": "latest",
|
|
43
43
|
"typescript": "^5.9.0"
|
|
44
44
|
},
|
package/src/meta/models.ts
CHANGED
|
@@ -21,6 +21,24 @@ const CONTRIBUTOR_COST = {
|
|
|
21
21
|
cacheRead: 0.002,
|
|
22
22
|
cacheWrite: 0,
|
|
23
23
|
};
|
|
24
|
+
/** Meta Model API list price for muse-wonder-1.0 (dev.meta.ai pricing page, 2026-10-23). */
|
|
25
|
+
const WONDER_COST = {
|
|
26
|
+
input: 4,
|
|
27
|
+
output: 20,
|
|
28
|
+
cacheRead: 0.2,
|
|
29
|
+
cacheWrite: 0,
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Muse Wonder Full and the Wonder Contributor variants are reachable only
|
|
33
|
+
* through a Muse Code subscription; Meta publishes no per-token rate for
|
|
34
|
+
* them, so Pi shows $0. This is a representation of "unpriced", not a fact.
|
|
35
|
+
*/
|
|
36
|
+
const SUBSCRIPTION_ONLY_COST = {
|
|
37
|
+
input: 0,
|
|
38
|
+
output: 0,
|
|
39
|
+
cacheRead: 0,
|
|
40
|
+
cacheWrite: 0,
|
|
41
|
+
};
|
|
24
42
|
const UNKNOWN_MODEL_COST = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
25
43
|
const THINKING_LEVELS = [
|
|
26
44
|
"minimal",
|
|
@@ -32,7 +50,13 @@ const THINKING_LEVELS = [
|
|
|
32
50
|
] as const;
|
|
33
51
|
type ThinkingLevelMap = NonNullable<MetaProviderModel["thinkingLevelMap"]>;
|
|
34
52
|
|
|
35
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* Data-only definitions; callers always receive fresh model objects. The live
|
|
55
|
+
* catalog has carried no `metadata["muse-code"]` block since at least
|
|
56
|
+
* 2026-10-23, so names, prices, and image input for known IDs come from here.
|
|
57
|
+
* Spark first keeps the long-standing default model; after the first refresh
|
|
58
|
+
* the catalog's own order wins anyway.
|
|
59
|
+
*/
|
|
36
60
|
const FALLBACK_DEFINITIONS = [
|
|
37
61
|
{
|
|
38
62
|
id: "muse-spark-1.3",
|
|
@@ -64,8 +88,54 @@ const FALLBACK_DEFINITIONS = [
|
|
|
64
88
|
cost: STANDARD_COST,
|
|
65
89
|
max: false,
|
|
66
90
|
},
|
|
91
|
+
{
|
|
92
|
+
id: "muse-wonder-1.0",
|
|
93
|
+
name: "Muse Wonder 1.0",
|
|
94
|
+
cost: WONDER_COST,
|
|
95
|
+
max: true,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
id: "muse-wonder-1.0-contributor",
|
|
99
|
+
name: "Muse Wonder 1.0 Contributor",
|
|
100
|
+
cost: SUBSCRIPTION_ONLY_COST,
|
|
101
|
+
max: false,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: "muse-wonder-full-1.0",
|
|
105
|
+
name: "Muse Wonder Full 1.0",
|
|
106
|
+
cost: SUBSCRIPTION_ONLY_COST,
|
|
107
|
+
max: true,
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: "muse-wonder-full-1.0-contributor",
|
|
111
|
+
name: "Muse Wonder Full 1.0 Contributor",
|
|
112
|
+
cost: SUBSCRIPTION_ONLY_COST,
|
|
113
|
+
max: false,
|
|
114
|
+
},
|
|
67
115
|
] as const;
|
|
68
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Catalog families Meta bills per generated image, per hour of audio, or per
|
|
119
|
+
* video frame instead of per token (dev.meta.ai pricing page, 2026-10-23):
|
|
120
|
+
* Muse Image, Muse Voice Transcribe, and Segment Anything. They share the
|
|
121
|
+
* catalog with the chat models but are not chat models, so neither the live
|
|
122
|
+
* catalog nor a cached entry may list them as one. Prefixes, not exact IDs,
|
|
123
|
+
* so a new version of a family stays hidden without a code change.
|
|
124
|
+
*/
|
|
125
|
+
export const NON_CHAT_MODEL_ID_PREFIXES = [
|
|
126
|
+
"muse-image-",
|
|
127
|
+
"muse-voice-",
|
|
128
|
+
"sam-",
|
|
129
|
+
] as const;
|
|
130
|
+
|
|
131
|
+
export function isNonChatModelId(id: string): boolean {
|
|
132
|
+
return NON_CHAT_MODEL_ID_PREFIXES.some((prefix) => id.startsWith(prefix));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function isBundledModelId(id: string): boolean {
|
|
136
|
+
return FALLBACK_DEFINITIONS.some((model) => model.id === id);
|
|
137
|
+
}
|
|
138
|
+
|
|
69
139
|
function nonemptyString(value: unknown): string | undefined {
|
|
70
140
|
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
71
141
|
}
|
|
@@ -166,7 +236,7 @@ export function toProviderModels(catalog: unknown): MetaProviderModel[] {
|
|
|
166
236
|
for (const value of data) {
|
|
167
237
|
const entry = asRecord(value);
|
|
168
238
|
const id = nonemptyString(entry?.id);
|
|
169
|
-
if (!id || seen.has(id)) continue;
|
|
239
|
+
if (!id || seen.has(id) || isNonChatModelId(id)) continue;
|
|
170
240
|
const metadata = asRecord(asRecord(entry?.metadata)?.["muse-code"]);
|
|
171
241
|
if (metadata?.is_hidden === true) continue;
|
|
172
242
|
const fallback = defaultModel(id);
|
|
@@ -308,7 +378,14 @@ function storedCost(
|
|
|
308
378
|
return cost;
|
|
309
379
|
}
|
|
310
380
|
|
|
311
|
-
/**
|
|
381
|
+
/**
|
|
382
|
+
* Cache contents cross the same untrusted-data boundary as the remote catalog.
|
|
383
|
+
* For bundled IDs the name, price, and input come from FALLBACK_DEFINITIONS:
|
|
384
|
+
* the bare catalog cannot have advertised them, so a cached copy is at best an
|
|
385
|
+
* older release's bundled value and at worst its unknown-model default (0.7.4
|
|
386
|
+
* persisted Wonder as a $0 text-only model). Cached limits, effort names,
|
|
387
|
+
* headers, and compat flags are kept; unknown IDs restore every cached field.
|
|
388
|
+
*/
|
|
312
389
|
export function restoreProviderModels(value: unknown): MetaProviderModel[] {
|
|
313
390
|
if (!Array.isArray(value)) return [];
|
|
314
391
|
const models: MetaProviderModel[] = [];
|
|
@@ -321,12 +398,16 @@ export function restoreProviderModels(value: unknown): MetaProviderModel[] {
|
|
|
321
398
|
)
|
|
322
399
|
continue;
|
|
323
400
|
const id = nonemptyString(entry.id);
|
|
324
|
-
|
|
401
|
+
// A catalog persisted by an earlier release may hold non-chat IDs.
|
|
402
|
+
if (!id || seen.has(id) || isNonChatModelId(id)) continue;
|
|
325
403
|
const fallback = defaultModel(id);
|
|
404
|
+
const bundled = isBundledModelId(id);
|
|
326
405
|
models.push(
|
|
327
406
|
gateMuseMaxEffort({
|
|
328
407
|
...fallback,
|
|
329
|
-
name:
|
|
408
|
+
name: bundled
|
|
409
|
+
? fallback.name
|
|
410
|
+
: (nonemptyString(entry.name) ?? fallback.name),
|
|
330
411
|
api: "openai-responses",
|
|
331
412
|
// The extension only persists the direct endpoint; a cached URL must never redirect the key.
|
|
332
413
|
baseUrl: META_API_BASE_URL,
|
|
@@ -338,8 +419,10 @@ export function restoreProviderModels(value: unknown): MetaProviderModel[] {
|
|
|
338
419
|
entry.thinkingLevelMap,
|
|
339
420
|
fallback.thinkingLevelMap ?? {},
|
|
340
421
|
),
|
|
341
|
-
input:
|
|
342
|
-
|
|
422
|
+
input: bundled
|
|
423
|
+
? fallback.input
|
|
424
|
+
: modelInput(entry.input, fallback.input),
|
|
425
|
+
cost: bundled ? fallback.cost : storedCost(entry.cost, fallback.cost),
|
|
343
426
|
contextWindow: positiveInteger(
|
|
344
427
|
entry.contextWindow,
|
|
345
428
|
fallback.contextWindow,
|
package/src/meta/muse-policy.ts
CHANGED
|
@@ -4,6 +4,20 @@ import type { MetaEnv, MetaProviderModel } from "./types.ts";
|
|
|
4
4
|
/** Models whose max effort requires the captured Muse client fingerprint. */
|
|
5
5
|
export const MUSE_USER_AGENT_MODEL_IDS: ReadonlySet<string> = new Set([
|
|
6
6
|
"muse-spark-1.3-contributor",
|
|
7
|
+
"muse-wonder-1.0-contributor",
|
|
8
|
+
"muse-wonder-full-1.0-contributor",
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Models known to accept literal max effort with any User-Agent. Each also
|
|
13
|
+
* has a bundled definition in models.ts that declares max, but they are
|
|
14
|
+
* listed here so the gate — which runs on every model list — is the single
|
|
15
|
+
* authority and a stale cached `max: null` cannot mask that support.
|
|
16
|
+
*/
|
|
17
|
+
export const MAX_EFFORT_MODEL_IDS: ReadonlySet<string> = new Set([
|
|
18
|
+
"muse-spark-1.3",
|
|
19
|
+
"muse-wonder-1.0",
|
|
20
|
+
"muse-wonder-full-1.0",
|
|
7
21
|
]);
|
|
8
22
|
|
|
9
23
|
/** Recognize the documented, explicit Muse fingerprint opt-in values. */
|
|
@@ -15,18 +29,26 @@ export function isMuseUserAgentEnabled(env: MetaEnv = process.env): boolean {
|
|
|
15
29
|
}
|
|
16
30
|
|
|
17
31
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
32
|
+
* Apply known literal-max support: always on MAX_EFFORT_MODEL_IDS, and on
|
|
33
|
+
* fingerprint-dependent models only when opted in. A future server-advertised
|
|
34
|
+
* mapping to a different effort remains authoritative. Runs on every model
|
|
35
|
+
* list (fallbacks, catalog, cached restore) so a stale cached `max: null`
|
|
36
|
+
* cannot mask known support.
|
|
20
37
|
*/
|
|
21
38
|
export function gateMuseMaxEffort(
|
|
22
39
|
model: MetaProviderModel,
|
|
23
40
|
env: MetaEnv = process.env,
|
|
24
41
|
): MetaProviderModel {
|
|
25
42
|
const map = model.thinkingLevelMap;
|
|
26
|
-
if (!map
|
|
43
|
+
if (!map) return model;
|
|
27
44
|
if (map.max != null && map.max !== "max") return model;
|
|
28
45
|
|
|
29
|
-
|
|
46
|
+
let max: string | null;
|
|
47
|
+
if (MAX_EFFORT_MODEL_IDS.has(model.id)) max = "max";
|
|
48
|
+
else if (MUSE_USER_AGENT_MODEL_IDS.has(model.id))
|
|
49
|
+
max = isMuseUserAgentEnabled(env) ? "max" : null;
|
|
50
|
+
else return model;
|
|
51
|
+
|
|
30
52
|
if (map.max === max) return model;
|
|
31
53
|
return { ...model, thinkingLevelMap: { ...map, max } };
|
|
32
54
|
}
|
package/src/meta/types.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import type { ProviderConfig } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Pi 0.99+ registers chat, image, and classifier model configs as a union; Omit
|
|
5
|
+
* over that union keeps only their shared keys. Narrow to the chat member, which
|
|
6
|
+
* is the whole element type on older Pi versions.
|
|
7
|
+
*/
|
|
8
|
+
type ProviderModel = Extract<
|
|
9
|
+
NonNullable<ProviderConfig["models"]>[number],
|
|
10
|
+
{ reasoning: boolean }
|
|
11
|
+
>;
|
|
4
12
|
|
|
5
13
|
/** Responses hints spanning the supported Pi versions, including newer flags. */
|
|
6
14
|
export interface MetaResponsesCompat {
|