@cortexkit/pi-anthropic-auth 1.19.1 → 1.20.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/README.md +16 -2
- package/dist/commands.js +20 -3
- package/dist/convert.js +65 -1
- package/dist/index.js +11 -2
- package/dist/stream.js +4 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Pi package for CortexKit Anthropic OAuth support. It overrides Pi's built-in `anthropic` provider with a CortexKit provider extension backed by the shared `@cortexkit/anthropic-auth-core` package.
|
|
4
4
|
|
|
5
|
-
The Pi provider catalog includes Claude Fable 5 (`claude-fable-5`), limited-access Claude Mythos 5 (`claude-mythos-5`), Claude Opus 4.8, Claude Opus 4.5, Claude Sonnet 4.5, and Claude Sonnet 5 (`claude-sonnet-5`). Fable/Mythos reasoning uses Anthropic adaptive thinking with `thinking.display: "summarized"` and `output_config.effort`; the package does not send rejected manual `thinking.budget_tokens` for those models.
|
|
5
|
+
The Pi provider catalog includes Claude Fable 5 (`claude-fable-5`), limited-access Claude Mythos 5 (`claude-mythos-5`), Claude Opus 5 (`claude-opus-5`), Claude Opus 4.8, Claude Opus 4.5, Claude Sonnet 4.5, and Claude Sonnet 5 (`claude-sonnet-5`). Fable/Mythos reasoning uses Anthropic adaptive thinking with `thinking.display: "summarized"` and `output_config.effort`; the package does not send rejected manual `thinking.budget_tokens` for those models.
|
|
6
6
|
|
|
7
7
|
This package is part of the CortexKit Anthropic Auth monorepo, which supports both OpenCode (`@cortexkit/opencode-anthropic-auth`) and Pi (`@cortexkit/pi-anthropic-auth`) through the same shared core logic.
|
|
8
8
|
|
|
@@ -42,7 +42,7 @@ Pi state is stored separately from OpenCode at:
|
|
|
42
42
|
|
|
43
43
|
Override the path with `PI_ANTHROPIC_AUTH_FILE`. The package also respects `PI_AGENT_DIR` when deriving the default sidecar path.
|
|
44
44
|
|
|
45
|
-
The sidecar uses the same JSON shape as the OpenCode package, including `routing`, `claudeCache`, `cacheKeep`, `claudeFast`, `dump`, `relay`, and fallback `accounts` blocks. Runtime OAuth/quota state is stored in `anthropic-auth-state.json`; sticky session assignments are stored separately in `anthropic-auth-routing-state.json` with SHA-256-hashed session IDs.
|
|
45
|
+
The sidecar uses the same JSON shape as the OpenCode package, including `routing`, `claudeCache`, `cacheKeep`, `prime`, `claudeFast`, `dump`, `relay`, and fallback `accounts` blocks. Runtime OAuth/quota state is stored in `anthropic-auth-state.json`; sticky session assignments are stored separately in `anthropic-auth-routing-state.json` with SHA-256-hashed session IDs.
|
|
46
46
|
|
|
47
47
|
## Commands
|
|
48
48
|
|
|
@@ -59,6 +59,10 @@ The sidecar uses the same JSON shape as the OpenCode package, including `routing
|
|
|
59
59
|
/claude-cachekeep 09-23
|
|
60
60
|
/claude-cachekeep off
|
|
61
61
|
|
|
62
|
+
/claude-prime
|
|
63
|
+
/claude-prime on
|
|
64
|
+
/claude-prime off
|
|
65
|
+
|
|
62
66
|
/claude-dump
|
|
63
67
|
/claude-dump on
|
|
64
68
|
/claude-dump off
|
|
@@ -78,6 +82,16 @@ The sidecar uses the same JSON shape as the OpenCode package, including `routing
|
|
|
78
82
|
|
|
79
83
|
`/claude-quota` reports sidecar OAuth fallback quota state from `~/.pi/agent/anthropic-auth.json`. `/claude-routing fallback-first` prefers usable OAuth fallback accounts before the main account; `/claude-routing main-first` restores the default. `/claude-routing sticky-balanced` assigns each Pi session to an OAuth account according to current 5-hour, 7-day, and matching model-scoped quota headroom, then persists that assignment across transient failures and process restarts. `/claude-routing reset` clears the current Pi session's assignment. Direct Opus sessions prefer usable accounts whose Fable quota is exhausted. API-key routes use the same sidecar shape as OpenCode and are sent directly to their configured Anthropic-compatible base URL, such as Kie's `https://api.kie.ai/claude`, but Pi only uses them after the main OAuth model response reports HTTP 429 or a streaming rate-limit error and a live quota check confirms 0% remaining. `/claude-cachekeep always` keeps active hybrid caches warm while Pi remains open; `/claude-cachekeep HH-HH` limits prewarms to a local time window. Both send `max_tokens: 0` pre-warm requests about five minutes before the 1-hour TTL expires. Running `/claude-cachekeep` without arguments lists live tracked sessions across Pi processes through a temporary lease registry that stores only session IDs and cache timing. `/claude-fast on` adds Anthropic `speed: "fast"` plus the `fast-mode-2026-02-01` beta header for supported Opus models (`claude-opus-4-6`, `claude-opus-4-7`, `claude-opus-4-8`, and `claude-opus-5`).
|
|
80
84
|
|
|
85
|
+
### Claude quota window priming
|
|
86
|
+
|
|
87
|
+
Priming is off by default. When enabled from OpenCode with `/claude-prime on`, it watches each OAuth account's 5-hour quota reset and sends one minimal `claude-haiku-4-5` request about one minute after a confirmed reset. This starts the next window without waiting for a normal prompt. Usage is measured from response accounting.
|
|
88
|
+
|
|
89
|
+
For an idle account with no cached reset time, one bootstrap request establishes the first observed window. Atomic temporary-file claims limit multiple processes sharing an account config to one request per account and reset.
|
|
90
|
+
|
|
91
|
+
Prime marker identities live in `anthropic-auth-state.json`. Plugin-owned refresh rotations preserve the main account's lineage, while a host credential replacement creates a new lineage. An existing main lineage without a refresh-token binding attaches to the current credential on its first check without changing identity. On upgrade, an existing fallback account receives an identity during its first prime check; that one-time marker change can send one extra request in the current window.
|
|
92
|
+
|
|
93
|
+
Pi's `/claude-prime` command displays status only. The `on` and `off` arguments are ignored; toggling priming requires OpenCode.
|
|
94
|
+
|
|
81
95
|
## Relay
|
|
82
96
|
|
|
83
97
|
The Pi package can use the same user-owned Cloudflare relay config as the OpenCode package. The relay setup helper currently lives in the OpenCode package CLI:
|
package/dist/commands.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { buildClaudeQuotaSummary, buildFallbackQuotaSummaries, CLAUDE_ACCOUNT_COMMAND_NAME, CLAUDE_CACHE_KEEP_COMMAND_NAME, CLAUDE_ROUTING_COMMAND_NAME, createEmptyStorage, executeAccountCommand, executeCache1hCommand, executeCacheKeepCommand, executeDumpCommand, executeFastModeCommand, executeLoggingCommand, executeRoutingCommand, getCache1hPersistentMode, getCacheKeepWindow, getPersistedLogLevel, getRoutingMode, isCache1hPersistentlyEnabled, isCacheKeepAlways, isCacheKeepHybridActive, isCacheKeepPersistentlyEnabled, isDumpPersistentlyEnabled, isFastModePersistentlyEnabled, loadAccounts, parseCache1hCommandAction, parseCacheKeepCommandAction, parseDumpCommandAction, parseFastModeCommandAction, parseLoggingCommandAction, parseRoutingCommandAction, removeAccountPersistent, reorderAccountsPersistent, setAccountEnabledPersistent, setCache1hPersistentEnabled, setCache1hPersistentMode, setCacheKeepPersistentAlways, setCacheKeepPersistentEnabled, setCacheKeepPersistentWindow, setCacheKeepSubagentsEnabled, setDumpEnabled, setDumpPersistentEnabled, setFastModePersistentEnabled, setLogLevelPersistent, setRoutingMode, } from '@cortexkit/anthropic-auth-core';
|
|
2
|
-
import { getPiAccountStoragePath } from
|
|
3
|
-
import { clearPiStickyRoutingSession, getPiTrackedCacheKeepSessions, } from
|
|
1
|
+
import { buildClaudeQuotaSummary, buildFallbackQuotaSummaries, buildPrimeAccountStatuses, CLAUDE_ACCOUNT_COMMAND_NAME, CLAUDE_CACHE_KEEP_COMMAND_NAME, CLAUDE_PRIME_COMMAND_NAME, CLAUDE_ROUTING_COMMAND_NAME, createEmptyStorage, executeAccountCommand, executeCache1hCommand, executeCacheKeepCommand, executeDumpCommand, executeFastModeCommand, executeLoggingCommand, executePrimeCommand, executeRoutingCommand, getCache1hPersistentMode, getCacheKeepWindow, getPersistedLogLevel, getRoutingMode, isCache1hPersistentlyEnabled, isCacheKeepAlways, isCacheKeepHybridActive, isCacheKeepPersistentlyEnabled, isDumpPersistentlyEnabled, isFastModePersistentlyEnabled, isPrimePersistentlyEnabled, loadAccounts, parseCache1hCommandAction, parseCacheKeepCommandAction, parseDumpCommandAction, parseFastModeCommandAction, parseLoggingCommandAction, parseRoutingCommandAction, removeAccountPersistent, reorderAccountsPersistent, setAccountEnabledPersistent, setCache1hPersistentEnabled, setCache1hPersistentMode, setCacheKeepPersistentAlways, setCacheKeepPersistentEnabled, setCacheKeepPersistentWindow, setCacheKeepSubagentsEnabled, setDumpEnabled, setDumpPersistentEnabled, setFastModePersistentEnabled, setLogLevelPersistent, setRoutingMode, } from '@cortexkit/anthropic-auth-core';
|
|
2
|
+
import { getPiAccountStoragePath } from './paths.js';
|
|
3
|
+
import { clearPiStickyRoutingSession, getPiTrackedCacheKeepSessions, } from './stream.js';
|
|
4
4
|
function notify(ctx, message, kind = 'info') {
|
|
5
5
|
ctx.ui.notify(message, kind);
|
|
6
6
|
}
|
|
@@ -200,4 +200,21 @@ export function registerCommands(pi) {
|
|
|
200
200
|
notify(ctx, executeLoggingCommand({ argumentsText: args ?? '', level }));
|
|
201
201
|
},
|
|
202
202
|
});
|
|
203
|
+
pi.registerCommand(CLAUDE_PRIME_COMMAND_NAME, {
|
|
204
|
+
description: "Show each OAuth account's five-hour quota window state",
|
|
205
|
+
handler: async (_args, ctx) => {
|
|
206
|
+
// Pi has no live PrimeManager; render persisted counters and the
|
|
207
|
+
// next-due timestamp from stored quota snapshots. Always pass
|
|
208
|
+
// `argumentsText: 'status'` to the pure executor so on/off args
|
|
209
|
+
// are display-only — Pi cannot toggle the setting.
|
|
210
|
+
const path = getPiAccountStoragePath();
|
|
211
|
+
const storage = await loadAccounts(path);
|
|
212
|
+
const accounts = buildPrimeAccountStatuses(storage, { now: Date.now() });
|
|
213
|
+
notify(ctx, executePrimeCommand({
|
|
214
|
+
argumentsText: 'status',
|
|
215
|
+
enabled: isPrimePersistentlyEnabled(storage),
|
|
216
|
+
accounts,
|
|
217
|
+
}).text);
|
|
218
|
+
},
|
|
219
|
+
});
|
|
203
220
|
}
|
package/dist/convert.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { applyClaudeCodeMetadata, buildBillingHeaderValue, CLAUDE_CODE_ENTRYPOINT, CLAUDE_CODE_IDENTITY, CLAUDE_FABLE_MYTHOS_5_SUMMARIZED_THINKING, CLAUDE_OPUS_5_ADAPTIVE_THINKING, CLAUDE_SONNET_5_ADAPTIVE_THINKING, isClaudeFableOrMythos5Model, isClaudeOpus5Model, isClaudeSonnet5Model, isFastModeSupportedModel, isOpenAIReasoningSignature, orderClaudeCodeBody, signRequestBody, } from '@cortexkit/anthropic-auth-core';
|
|
2
|
+
// Anchor identifying Pi's documentation paragraph — the only part of the prompt
|
|
3
|
+
// that Anthropic currently rejects in system[]. Unknown prompt shapes take the
|
|
4
|
+
// service-preserving fallback below instead of returning the full prompt there.
|
|
5
|
+
const PI_DOCS_ANCHOR = 'Pi documentation';
|
|
2
6
|
const CLAUDE_CODE_TOOLS = new Map([
|
|
3
7
|
'Read',
|
|
4
8
|
'Write',
|
|
@@ -253,6 +257,38 @@ function addEphemeralCacheControl(body) {
|
|
|
253
257
|
break;
|
|
254
258
|
}
|
|
255
259
|
}
|
|
260
|
+
function splitPiSystemPrompt(prompt) {
|
|
261
|
+
const sanitized = sanitize(prompt);
|
|
262
|
+
const paragraphs = sanitized.split(/\n\n+/);
|
|
263
|
+
const docs = paragraphs.filter((paragraph) => paragraph.includes(PI_DOCS_ANCHOR));
|
|
264
|
+
// If Pi changes the heading or prompt layout, fail away from the request shape
|
|
265
|
+
// known to trigger billing rejection: carry the complete host prompt in the
|
|
266
|
+
// first user message until the new shape can be classified precisely.
|
|
267
|
+
if (docs.length === 0)
|
|
268
|
+
return { messageText: sanitized };
|
|
269
|
+
const keep = paragraphs.filter((paragraph) => !paragraph.includes(PI_DOCS_ANCHOR));
|
|
270
|
+
return {
|
|
271
|
+
...(keep.length ? { systemText: keep.join('\n\n') } : {}),
|
|
272
|
+
messageText: docs.join('\n\n'),
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
function prependCachedPromptBlock(messages, text) {
|
|
276
|
+
const firstUser = messages.find((message) => message.role === 'user');
|
|
277
|
+
if (!firstUser)
|
|
278
|
+
return;
|
|
279
|
+
const promptBlock = {
|
|
280
|
+
type: 'text',
|
|
281
|
+
text,
|
|
282
|
+
cache_control: { type: 'ephemeral' },
|
|
283
|
+
};
|
|
284
|
+
if (typeof firstUser.content === 'string') {
|
|
285
|
+
firstUser.content = [promptBlock, { type: 'text', text: firstUser.content }];
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (Array.isArray(firstUser.content)) {
|
|
289
|
+
firstUser.content.unshift(promptBlock);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
256
292
|
function applyCacheMode(body, enabled, mode) {
|
|
257
293
|
if (!enabled)
|
|
258
294
|
return;
|
|
@@ -296,7 +332,35 @@ export async function buildAnthropicRequest(modelId, context, options, cache, fa
|
|
|
296
332
|
{ type: 'text', text: CLAUDE_CODE_IDENTITY },
|
|
297
333
|
];
|
|
298
334
|
if (context.systemPrompt?.trim()) {
|
|
299
|
-
system
|
|
335
|
+
// Pi's prompt cannot sit whole in the top-level system[] array: two lines of
|
|
336
|
+
// its documentation paragraph (the docs/*.md enumeration and the "follow .md
|
|
337
|
+
// cross-references" instruction) are each independently sufficient to make
|
|
338
|
+
// Anthropic reject the request with 400 "You're out of extra usage". Entry
|
|
339
|
+
// count and payload size are ruled out — 2697 bytes of neutral filler in the
|
|
340
|
+
// same position is accepted, and the same text is accepted inside messages[].
|
|
341
|
+
//
|
|
342
|
+
// For the recognized Pi prompt, keep the identity, tool contract, and
|
|
343
|
+
// guidelines in system[], where they carry system weight and survive context
|
|
344
|
+
// compaction, and carry only the documentation paragraph in messages[]. An
|
|
345
|
+
// unknown future prompt shape is moved whole rather than risking this 400.
|
|
346
|
+
//
|
|
347
|
+
// It goes in as its own content block ahead of the user's text, not merged
|
|
348
|
+
// into it and not as a separate message. A cache prefix matches contiguously
|
|
349
|
+
// from the start of the request, so a block boundary here lets the prefix end
|
|
350
|
+
// before the user's words: a new conversation with a different first message
|
|
351
|
+
// still reads the paragraph from cache instead of re-writing ~1.1k tokens.
|
|
352
|
+
// A role: "system" message cannot be used at messages[0] — Anthropic rejects
|
|
353
|
+
// that — and placing one after the first user message puts it behind content
|
|
354
|
+
// that varies, which defeats the caching.
|
|
355
|
+
//
|
|
356
|
+
// cache_control is set explicitly because addEphemeralCacheControl's
|
|
357
|
+
// message-level breakpoint only fires for array content on the *last* user
|
|
358
|
+
// message, which is not this one after the first turn.
|
|
359
|
+
const prompt = splitPiSystemPrompt(context.systemPrompt);
|
|
360
|
+
if (prompt.systemText) {
|
|
361
|
+
system.push({ type: 'text', text: prompt.systemText });
|
|
362
|
+
}
|
|
363
|
+
prependCachedPromptBlock(messages, prompt.messageText);
|
|
300
364
|
}
|
|
301
365
|
const body = {
|
|
302
366
|
model: modelId,
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { authorize, CLAUDE_FABLE_MYTHOS_5_CONTEXT_WINDOW, CLAUDE_FABLE_MYTHOS_5_MAX_OUTPUT_TOKENS, CLAUDE_FABLE_MYTHOS_5_MODEL_SPECS, CLAUDE_FABLE_MYTHOS_5_PRICING, exchange, refreshClaudeOAuthToken, } from '@cortexkit/anthropic-auth-core';
|
|
2
|
-
import { registerCommands } from
|
|
3
|
-
import { streamCortexKitAnthropic } from
|
|
2
|
+
import { registerCommands } from './commands.js';
|
|
3
|
+
import { streamCortexKitAnthropic } from './stream.js';
|
|
4
4
|
async function loginAnthropic(callbacks) {
|
|
5
5
|
const auth = await authorize('max');
|
|
6
6
|
callbacks.onAuth({ url: auth.url });
|
|
@@ -51,6 +51,15 @@ export default function cortexKitPiAnthropicAuth(pi) {
|
|
|
51
51
|
contextWindow: CLAUDE_FABLE_MYTHOS_5_CONTEXT_WINDOW,
|
|
52
52
|
maxTokens: CLAUDE_FABLE_MYTHOS_5_MAX_OUTPUT_TOKENS,
|
|
53
53
|
})),
|
|
54
|
+
{
|
|
55
|
+
id: 'claude-opus-5',
|
|
56
|
+
name: 'Claude Opus 5',
|
|
57
|
+
reasoning: true,
|
|
58
|
+
input: textImageInput(),
|
|
59
|
+
cost: { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
|
|
60
|
+
contextWindow: 1_000_000,
|
|
61
|
+
maxTokens: 128_000,
|
|
62
|
+
},
|
|
54
63
|
{
|
|
55
64
|
id: 'claude-opus-4-8',
|
|
56
65
|
name: 'Claude Opus 4.8',
|
package/dist/stream.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { applyClaudeCodeHeaders, CACHE_KEEP_EXTENDED_TTL_BETA, CacheKeepManager, CacheKeepSessionRegistry, createStickyNoRouteResponse, decideStickyQuotaFailure, dumpDirectRequest, FAST_MODE_BETA, FallbackAccountManager, getCache1hPersistentMode, getDefaultCacheKeepRegistryDirectory, getRelayConfig, getRoutingMode, getStickyRoutingStatePath, isApiKeyAccount, isCache1hPersistentlyEnabled, isCacheKeepHybridActive, isDumpPersistentlyEnabled, isFastModePersistentlyEnabled, isKillswitchEnabled, isOAuthAccount, isPermanentRefreshError, isValidApiBaseURL, killswitchPassesPolicy, loadAccounts, mergeAnthropicBetas, QuotaManager, quotaSnapshotModelScopeIsExhausted, quotaSnapshotPassesModelScope, quotaSnapshotPassesPolicy, resolveClaudeCodeIdentity, STICKY_ROUTING_MAIN_ACCOUNT_ID, StickySessionRouter, sendViaRelay, setDumpEnabled, shouldFallbackStatus, stickyQuotaSnapshotIsFresh, stickyRetryAfterWithJitter, stickyRouteFamilyForModel, } from '@cortexkit/anthropic-auth-core';
|
|
1
|
+
import { applyClaudeCodeHeaders, CACHE_KEEP_EXTENDED_TTL_BETA, CacheKeepManager, CacheKeepSessionRegistry, createStickyNoRouteResponse, decideStickyQuotaFailure, dumpDirectRequest, FAST_MODE_BETA, FallbackAccountManager, getCache1hPersistentMode, getDefaultCacheKeepRegistryDirectory, getFallbackReauthLabels, getRelayConfig, getRoutingMode, getStickyRoutingStatePath, isApiKeyAccount, isCache1hPersistentlyEnabled, isCacheKeepHybridActive, isDumpPersistentlyEnabled, isFastModePersistentlyEnabled, isKillswitchEnabled, isOAuthAccount, isPermanentRefreshError, isValidApiBaseURL, killswitchPassesPolicy, loadAccounts, mergeAnthropicBetas, QuotaManager, quotaSnapshotModelScopeIsExhausted, quotaSnapshotPassesModelScope, quotaSnapshotPassesPolicy, resolveClaudeCodeIdentity, STICKY_ROUTING_MAIN_ACCOUNT_ID, StickySessionRouter, sendViaRelay, setDumpEnabled, shouldFallbackStatus, stickyQuotaSnapshotIsFresh, stickyRetryAfterWithJitter, stickyRouteFamilyForModel, } from '@cortexkit/anthropic-auth-core';
|
|
2
2
|
import { calculateCost, createAssistantMessageEventStream, } from '@earendil-works/pi-ai';
|
|
3
|
-
import { buildAnthropicRequest, fromClaudeCodeToolName } from
|
|
4
|
-
import { getPiAccountStoragePath } from
|
|
3
|
+
import { buildAnthropicRequest, fromClaudeCodeToolName } from './convert.js';
|
|
4
|
+
import { getPiAccountStoragePath } from './paths.js';
|
|
5
5
|
function errorText(error) {
|
|
6
6
|
return error instanceof Error ? error.message : String(error);
|
|
7
7
|
}
|
|
@@ -504,6 +504,7 @@ async function executeWithFallback(options) {
|
|
|
504
504
|
if (!resolution) {
|
|
505
505
|
return createStickyNoRouteResponse({
|
|
506
506
|
mainRefreshError: storage?.refresh?.mainLastRefreshError,
|
|
507
|
+
fallbackReauthLabels: getFallbackReauthLabels(storage),
|
|
507
508
|
routeQuotas: routes.allRoutes.flatMap((route) => route.quota ? [route.quota] : []),
|
|
508
509
|
modelId: options.model.id,
|
|
509
510
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/pi-anthropic-auth",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"prepublishOnly": "bun run build"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@cortexkit/anthropic-auth-core": "1.
|
|
39
|
+
"@cortexkit/anthropic-auth-core": "1.20.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"@earendil-works/pi-ai": "*",
|