@psnext/slingcli 2.5.20260725-1 → 2.5.20260727-1
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/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js +8 -3
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/radius.js +37 -35
- package/node_modules/@earendil-works/pi-ai/dist/auth/resolve.js +11 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/anthropic.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/github-copilot.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/nvidia.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.js +8 -3
- package/node_modules/@earendil-works/pi-ai/package.json +1 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js +13 -3
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/remote-catalog-provider.js +19 -1
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/resource-loader.js +3 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/core/system-prompt.js +4 -4
- package/node_modules/@earendil-works/pi-coding-agent/dist/extensions/llama/provider.js +6 -0
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/custom-message.js +10 -2
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js +22 -13
- package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +38 -27
- package/node_modules/@earendil-works/pi-coding-agent/npm-shrinkwrap.json +12 -12
- package/node_modules/@earendil-works/pi-coding-agent/package.json +6 -5
- package/node_modules/@earendil-works/pi-server/package.json +2 -2
- package/node_modules/@earendil-works/pi-tui/dist/tui.js +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
- package/slingshot/index.js +164 -164
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@earendil-works/pi-agent-core",
|
|
3
|
-
"version": "0.82.
|
|
3
|
+
"version": "0.82.1",
|
|
4
4
|
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"prepublishOnly": "npm run build"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@earendil-works/pi-ai": "^0.82.
|
|
32
|
+
"@earendil-works/pi-ai": "^0.82.1",
|
|
33
33
|
"diff": "8.0.4",
|
|
34
34
|
"ignore": "7.0.5",
|
|
35
35
|
"typebox": "1.1.38",
|
|
@@ -432,13 +432,18 @@ function supportsAdaptiveThinking(modelId, modelName) {
|
|
|
432
432
|
return candidates.some((s) => s.includes("opus-4-6") ||
|
|
433
433
|
s.includes("opus-4-7") ||
|
|
434
434
|
s.includes("opus-4-8") ||
|
|
435
|
+
s.includes("opus-5") ||
|
|
435
436
|
s.includes("sonnet-4-6") ||
|
|
436
437
|
s.includes("sonnet-5") ||
|
|
437
438
|
s.includes("fable-5"));
|
|
438
439
|
}
|
|
439
440
|
function supportsNativeXhighEffort(model) {
|
|
440
441
|
const candidates = getModelMatchCandidates(model.id, model.name);
|
|
441
|
-
return candidates.some((s) => s.includes("opus-4-7") ||
|
|
442
|
+
return candidates.some((s) => s.includes("opus-4-7") ||
|
|
443
|
+
s.includes("opus-4-8") ||
|
|
444
|
+
s.includes("opus-5") ||
|
|
445
|
+
s.includes("sonnet-5") ||
|
|
446
|
+
s.includes("fable-5"));
|
|
442
447
|
}
|
|
443
448
|
function mapThinkingLevelToEffort(model, level) {
|
|
444
449
|
if (level === "xhigh" && supportsNativeXhighEffort(model))
|
|
@@ -507,8 +512,8 @@ function supportsPromptCaching(model, env) {
|
|
|
507
512
|
return true;
|
|
508
513
|
return false;
|
|
509
514
|
}
|
|
510
|
-
// Claude 5 models (fable-5, sonnet-5)
|
|
511
|
-
if (candidates.some((s) => s.includes("fable-5") || s.includes("sonnet-5")))
|
|
515
|
+
// Claude 5 models (fable-5, opus-5, sonnet-5)
|
|
516
|
+
if (candidates.some((s) => s.includes("fable-5") || s.includes("opus-5") || s.includes("sonnet-5")))
|
|
512
517
|
return true;
|
|
513
518
|
// Claude 4.x models (opus-4, sonnet-4, haiku-4)
|
|
514
519
|
if (candidates.some((s) => s.includes("-4-")))
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Radius gateway OAuth flow.
|
|
3
3
|
*
|
|
4
|
-
* Radius is a pi-messages gateway. OAuth
|
|
5
|
-
* gateway
|
|
4
|
+
* Radius is a pi-messages gateway. OAuth client APIs live on the configured
|
|
5
|
+
* gateway; only the interactive browser authorization endpoint is discovered.
|
|
6
|
+
* Model catalog loading is owned by the Radius provider.
|
|
6
7
|
*
|
|
7
8
|
* NOTE: This module uses node:http for the OAuth callback server.
|
|
8
9
|
* It is only intended for CLI use, not browser environments.
|
|
@@ -25,14 +26,21 @@ const REDIRECT_URI = `http://${CALLBACK_HOST}:${CALLBACK_PORT}${CALLBACK_PATH}`;
|
|
|
25
26
|
const TOKEN_EXPIRY_SKEW_MS = 60_000;
|
|
26
27
|
const LOGIN_METHOD_BROWSER = "browser";
|
|
27
28
|
const LOGIN_METHOD_DEVICE_CODE = "device-code";
|
|
28
|
-
|
|
29
|
+
const OAUTH_CLIENT_ID = "pi-gateway";
|
|
30
|
+
const OAUTH_SCOPE = "gateway offline_access";
|
|
31
|
+
const OAUTH_DEVICE_CODE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
|
|
32
|
+
async function loadRadiusOAuthDiscovery(gateway) {
|
|
29
33
|
const response = await fetch(new URL("/v1/oauth", gateway), {
|
|
30
34
|
headers: { accept: "application/json" },
|
|
31
35
|
});
|
|
32
36
|
if (!response.ok) {
|
|
33
37
|
throw new Error(`Could not load Radius OAuth config from ${gateway}: ${response.status} ${await response.text()}`);
|
|
34
38
|
}
|
|
35
|
-
|
|
39
|
+
const discovery = (await response.json());
|
|
40
|
+
if (typeof discovery.authorizationEndpoint !== "string") {
|
|
41
|
+
throw new Error(`Invalid Radius OAuth config from ${gateway}`);
|
|
42
|
+
}
|
|
43
|
+
return { authorizationEndpoint: discovery.authorizationEndpoint };
|
|
36
44
|
}
|
|
37
45
|
class OAuthResponseError extends Error {
|
|
38
46
|
status;
|
|
@@ -64,10 +72,10 @@ async function readOAuthResponseError(response, message) {
|
|
|
64
72
|
}
|
|
65
73
|
return new OAuthResponseError(response.status, oauthError, description, message);
|
|
66
74
|
}
|
|
67
|
-
async function requestOAuthToken(
|
|
75
|
+
async function requestOAuthToken(gateway, body, signal) {
|
|
68
76
|
let response;
|
|
69
77
|
try {
|
|
70
|
-
response = await fetch(oauth
|
|
78
|
+
response = await fetch(new URL("/v1/oauth/token", gateway), {
|
|
71
79
|
method: "POST",
|
|
72
80
|
headers: { accept: "application/json", "content-type": "application/x-www-form-urlencoded" },
|
|
73
81
|
body,
|
|
@@ -157,15 +165,15 @@ function startOAuthCallbackServer(expectedState, signal) {
|
|
|
157
165
|
});
|
|
158
166
|
});
|
|
159
167
|
}
|
|
160
|
-
async function loginWithBrowser(
|
|
168
|
+
async function loginWithBrowser(gateway, authorizationEndpoint, interaction) {
|
|
161
169
|
const { verifier, challenge } = await generatePKCE();
|
|
162
170
|
const state = crypto.randomUUID();
|
|
163
|
-
const authorizeUrl = new URL(
|
|
171
|
+
const authorizeUrl = new URL(authorizationEndpoint);
|
|
164
172
|
authorizeUrl.search = new URLSearchParams({
|
|
165
173
|
response_type: "code",
|
|
166
|
-
client_id:
|
|
174
|
+
client_id: OAUTH_CLIENT_ID,
|
|
167
175
|
redirect_uri: REDIRECT_URI,
|
|
168
|
-
scope:
|
|
176
|
+
scope: OAUTH_SCOPE,
|
|
169
177
|
code_challenge: challenge,
|
|
170
178
|
code_challenge_method: "S256",
|
|
171
179
|
handoff: "url",
|
|
@@ -186,9 +194,9 @@ async function loginWithBrowser(oauth, interaction) {
|
|
|
186
194
|
}
|
|
187
195
|
throw new Error("OAuth callback did not complete.");
|
|
188
196
|
}
|
|
189
|
-
return await requestOAuthToken(
|
|
197
|
+
return await requestOAuthToken(gateway, new URLSearchParams({
|
|
190
198
|
grant_type: "authorization_code",
|
|
191
|
-
client_id:
|
|
199
|
+
client_id: OAUTH_CLIENT_ID,
|
|
192
200
|
redirect_uri: REDIRECT_URI,
|
|
193
201
|
code,
|
|
194
202
|
code_verifier: verifier,
|
|
@@ -198,13 +206,13 @@ async function loginWithBrowser(oauth, interaction) {
|
|
|
198
206
|
callbackServer.close();
|
|
199
207
|
}
|
|
200
208
|
}
|
|
201
|
-
async function requestDeviceAuthorization(
|
|
209
|
+
async function requestDeviceAuthorization(gateway, signal) {
|
|
202
210
|
let response;
|
|
203
211
|
try {
|
|
204
|
-
response = await fetch(oauth
|
|
212
|
+
response = await fetch(new URL("/v1/oauth/device", gateway), {
|
|
205
213
|
method: "POST",
|
|
206
214
|
headers: { accept: "application/json", "content-type": "application/x-www-form-urlencoded" },
|
|
207
|
-
body: new URLSearchParams({ client_id:
|
|
215
|
+
body: new URLSearchParams({ client_id: OAUTH_CLIENT_ID, scope: OAUTH_SCOPE }),
|
|
208
216
|
signal,
|
|
209
217
|
});
|
|
210
218
|
}
|
|
@@ -218,24 +226,23 @@ async function requestDeviceAuthorization(oauth, signal) {
|
|
|
218
226
|
throw await readOAuthResponseError(response, "Radius OAuth device authorization failed");
|
|
219
227
|
}
|
|
220
228
|
const data = (await response.json());
|
|
221
|
-
if (!data.device_code || !data.user_code || !data.expires_in) {
|
|
229
|
+
if (!data.device_code || !data.user_code || !data.verification_uri || !data.expires_in) {
|
|
222
230
|
throw new Error("Radius OAuth device authorization response is missing required fields");
|
|
223
231
|
}
|
|
224
232
|
return {
|
|
225
233
|
device_code: data.device_code,
|
|
226
234
|
user_code: data.user_code,
|
|
227
235
|
verification_uri: data.verification_uri,
|
|
228
|
-
verification_uri_complete: data.verification_uri_complete,
|
|
229
236
|
expires_in: data.expires_in,
|
|
230
237
|
interval: data.interval,
|
|
231
238
|
};
|
|
232
239
|
}
|
|
233
|
-
async function loginWithDeviceCode(
|
|
234
|
-
const device = await requestDeviceAuthorization(
|
|
240
|
+
async function loginWithDeviceCode(gateway, interaction) {
|
|
241
|
+
const device = await requestDeviceAuthorization(gateway, interaction.signal);
|
|
235
242
|
interaction.notify({
|
|
236
243
|
type: "device_code",
|
|
237
244
|
userCode: device.user_code,
|
|
238
|
-
verificationUri: device.verification_uri
|
|
245
|
+
verificationUri: device.verification_uri,
|
|
239
246
|
intervalSeconds: device.interval,
|
|
240
247
|
expiresInSeconds: device.expires_in,
|
|
241
248
|
});
|
|
@@ -245,9 +252,9 @@ async function loginWithDeviceCode(oauth, interaction) {
|
|
|
245
252
|
signal: interaction.signal,
|
|
246
253
|
poll: async () => {
|
|
247
254
|
try {
|
|
248
|
-
const credentials = await requestOAuthToken(
|
|
249
|
-
grant_type:
|
|
250
|
-
client_id:
|
|
255
|
+
const credentials = await requestOAuthToken(gateway, new URLSearchParams({
|
|
256
|
+
grant_type: OAUTH_DEVICE_CODE_GRANT_TYPE,
|
|
257
|
+
client_id: OAUTH_CLIENT_ID,
|
|
251
258
|
device_code: device.device_code,
|
|
252
259
|
}), interaction.signal);
|
|
253
260
|
return { status: "complete", value: credentials };
|
|
@@ -277,7 +284,6 @@ export function createRadiusOAuth(options) {
|
|
|
277
284
|
return {
|
|
278
285
|
name: options.name,
|
|
279
286
|
async login(interaction) {
|
|
280
|
-
const oauth = await loadRadiusOAuthConfig(gateway);
|
|
281
287
|
const loginMethod = await interaction.prompt({
|
|
282
288
|
type: "select",
|
|
283
289
|
message: `Sign in to ${options.name}:`,
|
|
@@ -289,23 +295,19 @@ export function createRadiusOAuth(options) {
|
|
|
289
295
|
},
|
|
290
296
|
],
|
|
291
297
|
});
|
|
292
|
-
let credential;
|
|
293
298
|
if (loginMethod === LOGIN_METHOD_DEVICE_CODE) {
|
|
294
|
-
|
|
295
|
-
}
|
|
296
|
-
else if (loginMethod === LOGIN_METHOD_BROWSER) {
|
|
297
|
-
credential = await loginWithBrowser(oauth, interaction);
|
|
299
|
+
return loginWithDeviceCode(gateway, interaction);
|
|
298
300
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
+
if (loginMethod === LOGIN_METHOD_BROWSER) {
|
|
302
|
+
const discovery = await loadRadiusOAuthDiscovery(gateway);
|
|
303
|
+
return loginWithBrowser(gateway, discovery.authorizationEndpoint, interaction);
|
|
301
304
|
}
|
|
302
|
-
|
|
305
|
+
throw new Error(`Unknown ${options.name} sign-in method: ${loginMethod}`);
|
|
303
306
|
},
|
|
304
307
|
async refresh(credential, signal) {
|
|
305
|
-
const
|
|
306
|
-
const refreshed = await requestOAuthToken(oauth, new URLSearchParams({
|
|
308
|
+
const refreshed = await requestOAuthToken(gateway, new URLSearchParams({
|
|
307
309
|
grant_type: "refresh_token",
|
|
308
|
-
client_id:
|
|
310
|
+
client_id: OAUTH_CLIENT_ID,
|
|
309
311
|
refresh_token: credential.refresh,
|
|
310
312
|
}), signal);
|
|
311
313
|
return refreshed;
|
|
@@ -1,11 +1,21 @@
|
|
|
1
|
+
import { formatThrownValue } from "../utils/diagnostics.js";
|
|
1
2
|
export class ModelsError extends Error {
|
|
2
3
|
code;
|
|
3
4
|
constructor(code, message, options) {
|
|
4
|
-
super(message, options);
|
|
5
|
+
super(withCauseDetail(message, options?.cause), options);
|
|
5
6
|
this.name = "ModelsError";
|
|
6
7
|
this.code = code;
|
|
7
8
|
}
|
|
8
9
|
}
|
|
10
|
+
/** Callers surface `error.message` only, so keep the underlying reason in it. */
|
|
11
|
+
function withCauseDetail(message, cause) {
|
|
12
|
+
if (cause === undefined || cause === null)
|
|
13
|
+
return message;
|
|
14
|
+
const detail = formatThrownValue(cause).trim();
|
|
15
|
+
if (!detail || message.includes(detail))
|
|
16
|
+
return message;
|
|
17
|
+
return `${message}: ${detail}`;
|
|
18
|
+
}
|
|
9
19
|
/**
|
|
10
20
|
* Auth resolution shared by the `Models` and `ImagesModels` collections.
|
|
11
21
|
* A stored credential owns the provider: ambient/env is consulted only when
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"schemaVersion":3,"generatedAt":"2026-07-
|
|
1
|
+
{"schemaVersion":3,"generatedAt":"2026-07-27T03:47:01.896Z","structureHash":"af3ded7a9ebefec1131521259cd58cea6f00df4283da89abfb4de141e3519b7f","files":{"amazon-bedrock.json":"1aabd3227d32b332727eec7638cebdbd1323d2e7eedbf9b174226c5d38196df9","ant-ling.json":"4979fe79d99ed97382d7ce40170c6132e932e77906de1eb54464c042e7d63633","anthropic.json":"fea1226a62396c8d4a3d9c3b7f65d4dc527ac025d05719880418d3a2b0665a69","azure-openai-responses.json":"2d13ecc23826b5be73f3628fca5f70cc2c5c15ff609b41e4acba8167cd40934b","cerebras.json":"3fd3e19b83ab4be07d27817e25e5be0ffe4158b1394f0311d6a22d9f03847728","cloudflare-ai-gateway.json":"692b375a3bcb11743a9c1899442caecc3861163d9424356d3d073d9fa5c04091","cloudflare-workers-ai.json":"03b19d69433c843512e953390d51d4578e9b91f19098a9b14ff820c001623598","deepseek.json":"0dcc807a4e5827b488c6ceac87884ff6e735e01cf4f2ddfec9dd812e6fde041b","fireworks.json":"0ea306d50e72343439f71995dcda0776473e0e43a8500c4ff42769f085fee09d","github-copilot.json":"1215a0792908480ff709a26954b1c4b76bcfec5d31f31d0843d505770c764c1e","google-vertex.json":"99d0b89ac9d8dc460ed922236abffbe88c9381af4d35046d2ad3a8f08fa2560c","google.json":"bcf8a8d59ccbb75bc6fb4a261a37ac4b7c806f40ee7313b4411178964be5fa37","groq.json":"868c90a897f75f866e09a585f6aee5cb991e40a9c3281497b5842a23de7d9add","huggingface.json":"e001db7b5925939c5850f31cd08b323cd40eb586222e2f74d7890eb52a30f161","kimi-coding.json":"ba42a26a69e5cb2122c0b384ba572efae93f42239ad86f26c2b7320a14aa75a1","minimax-cn.json":"8ccd71ff838f4b78bb88809babb0dc32a4fcfaec2bc4f4083841000428df4f77","minimax.json":"253edd4c910a41c8bb252400ba099f3a4de0a8900ce154319288ab091b372987","mistral.json":"44531201e872713923b7544a1f75a048d31fa25292e3e1f9f896f608a52e412d","moonshotai-cn.json":"1e6146ff3477883636448c0f44222f0a52b3c4562ef8f4eecf7e941cdd59d12e","moonshotai.json":"d8d5209873058ddccd37c1f026833e2ea3f9476c3dc9628b4a940d407acc0b2e","nvidia.json":"79482f617066917a4525f78c19738c3b392e1e2e35c9d8ba3183b874bb504463","openai-codex.json":"c3313710bc6910e6bbcb06d5867247e97ec3fa6c2af9bc780f8a3eefb03e32e1","openai.json":"8e3852ef3567b23d2ab03b0f52f1caeb77e6e776f248e6111f851ccbe0fca8b6","opencode-go.json":"57a677764bc885c83fbbba46ad5d418baabe012a062424a0f6d8d488352102ce","opencode.json":"905b27bf3d7e48f35ca862991411b97a93782ad92b24129a2c3ee377fd1e3675","openrouter.json":"3dcc960f1fe6a84e6e1773425884195a70c43e5364bc34ae1f411d52072631e1","qwen-token-plan-cn.json":"a1495ccc21835c51d438207652f948aa9ecf4a3d784e81deaf313e484bdab1ec","qwen-token-plan.json":"597fae649f549a8ffb0b6a28426ea6761b7ece41a78b6fdef49ca970ac029970","together.json":"1d46a57e99da9be6f7e78d8f98f5610adbb266df3de7b0a13652d6fa175086e7","vercel-ai-gateway.json":"61242947db49d08e4a70ccf4cd16eea9d58758effc35c2f6abdd953f8da64013","xai.json":"4cbdbfbbf915246369b9bacbc6f1feeb56accdac9f3444456343f750547f2f38","xiaomi-token-plan-ams.json":"7ce8ed244672c45737d286ff3ca9c3a2686a5d4b2f0c5f71ab9348c14b14daf3","xiaomi-token-plan-cn.json":"684892b5fe6fa371d6040d2011c0fa33facd650d2eaf19f5dbee8efb33016200","xiaomi-token-plan-sgp.json":"b19e79b82ac4fdcb080e6d220106797504c885ed70b27fc2b3c1aa4810f32963","xiaomi.json":"810368d3828c8bb9498012a0d48639030c0c216b8c13e79da02daa515aa23448","zai-coding-cn.json":"28fcb04ce65b4c6612a6b866902e0c832e18b685e5374578c4e2872569131d39","zai.json":"474670a0cf1109f4c296ea93d2c5c6eb8f53541dc9c0bddbb0e6cb054b971ebe"}}
|