pi-bob 0.2.0 → 0.2.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/README.md +30 -10
- package/extensions/bob.ts +104 -9
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -8,8 +8,8 @@ The package registers Bob through Pi's built-in compatible provider APIs and dis
|
|
|
8
8
|
|
|
9
9
|
From the installed `bobshell@1.0.6` package and the local redacted Bob configuration:
|
|
10
10
|
|
|
11
|
-
- Bob Shell CLI:
|
|
12
|
-
- Bob Shell package:
|
|
11
|
+
- Bob Shell CLI: `bob`
|
|
12
|
+
- Bob Shell package: `bobshell`
|
|
13
13
|
- Installed auth method: `sso`
|
|
14
14
|
- Default Bob API host: `https://api.us-east.bob.ibm.com`
|
|
15
15
|
- OpenAI-compatible chat base URL: `https://api.us-east.bob.ibm.com/inference/v1`
|
|
@@ -43,7 +43,7 @@ The extension registers provider id `ibm-bob`. It uses an isolated `ibm-bob-comp
|
|
|
43
43
|
Model discovery is enabled by default:
|
|
44
44
|
|
|
45
45
|
- With `IBM_BOB_API_KEY` or `IBM_BOB_KEY`, the extension fetches `/inference/v1/model/info` during Pi's async extension startup. This makes current models available to `--list-models` and `/model` immediately.
|
|
46
|
-
- With `/login ibm-bob`, the extension fetches the catalog after login and on
|
|
46
|
+
- With `/login ibm-bob`, the extension fetches the catalog after login, and on token refresh only when no catalog is already cached (reusing a cached catalog avoids holding Pi's global auth-store lock across a network call). A sanitized, non-secret copy is cached with Pi's OAuth credentials so the models can be restored on later startups.
|
|
47
47
|
- Entries returned by the authenticated catalog are registered when `model_info.exposed` is omitted or `true`. Routes explicitly marked `exposed: false` are ignored.
|
|
48
48
|
- HTTP failures, timeouts, malformed responses, empty catalogs, and catalogs with no visible models retain the previous SSO catalog or fall back to `IBM_BOB_MODELS`. Bob still enforces route access during inference.
|
|
49
49
|
|
|
@@ -54,7 +54,7 @@ Discovered context limits, output limits, vision support, reasoning support, bac
|
|
|
54
54
|
Use Bob SSO through Pi:
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
|
-
pi -e
|
|
57
|
+
pi -e .
|
|
58
58
|
# inside Pi:
|
|
59
59
|
# /login ibm-bob
|
|
60
60
|
# /model ibm-bob/premium
|
|
@@ -67,8 +67,8 @@ For non-interactive use with an approved Bob API key, run:
|
|
|
67
67
|
```bash
|
|
68
68
|
export IBM_BOB_API_KEY="..." # IBM_BOB_KEY is also accepted; do not commit either
|
|
69
69
|
|
|
70
|
-
pi -e
|
|
71
|
-
pi -e
|
|
70
|
+
pi -e . --list-models
|
|
71
|
+
pi -e . --model ibm-bob/premium
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
API keys use Bob's `Authorization: Apikey ...` scheme by default. If your approved credential is instead a bearer token, set `IBM_BOB_AUTH_SCHEME=Bearer`. Pi resolves credentials in this order: runtime `--api-key`, a stored credential (including SSO), then the provider's environment-key fallback. The model catalog follows stored SSO metadata when SSO remains configured. Run `/logout ibm-bob` before switching from SSO to either `IBM_BOB_API_KEY` or runtime `--api-key`; runtime-only keys cannot drive startup discovery.
|
|
@@ -109,7 +109,7 @@ For SSO, do **not** copy a token out of Bob's local credential store unless IBM
|
|
|
109
109
|
| `IBM_BOB_READ_BOBSHELL_SETTINGS` | `true` | Read non-secret `instanceId`/`teamId` from `~/.bob/settings.json`. |
|
|
110
110
|
| `IBM_BOB_INSTANCE_ID` | Bob setting | Override `x-instance-id`. |
|
|
111
111
|
| `IBM_BOB_TEAM_ID` | Bob setting | Override `x-team-id`. |
|
|
112
|
-
| `IBM_BOB_USER_AGENT` | `pi-bob/0.
|
|
112
|
+
| `IBM_BOB_USER_AGENT` | `pi-bob/0.2.0` | User-Agent header sent to Bob endpoint. |
|
|
113
113
|
|
|
114
114
|
### Auth headers
|
|
115
115
|
|
|
@@ -141,6 +141,7 @@ export IBM_BOB_MAX_TOKENS_FIELD=max_tokens
|
|
|
141
141
|
```
|
|
142
142
|
|
|
143
143
|
Bob's OpenAI-compatible route currently rejects `tools[].function.strict`, so `IBM_BOB_SUPPORTS_STRICT_MODE=false` is the default.
|
|
144
|
+
It also rejects OpenAI-specific `store` and `prompt_cache_key` request properties with a bare `422`, so both are always disabled for the OpenAI adapters.
|
|
144
145
|
|
|
145
146
|
## Validation performed
|
|
146
147
|
|
|
@@ -197,24 +198,43 @@ Result:
|
|
|
197
198
|
pi-bob-ok
|
|
198
199
|
```
|
|
199
200
|
|
|
201
|
+
## Troubleshooting
|
|
202
|
+
|
|
203
|
+
**`/login ibm-bob` freezes after the browser returns to Pi.** Pi refreshes
|
|
204
|
+
model catalogs for built-in providers after every login by fetching
|
|
205
|
+
`https://pi.dev/api/models/providers/<id>` (the remote-catalog provider). If
|
|
206
|
+
pi.dev's API is unreachable or hangs from your network, that post-login fetch
|
|
207
|
+
blocks with no timeout for roughly 5 minutes, and Ctrl-C cannot cancel it.
|
|
208
|
+
Run Pi with `PI_OFFLINE=1` to skip Pi's network catalog refresh entirely; the
|
|
209
|
+
login then returns to the prompt immediately after SSO, and ibm-bob's own
|
|
210
|
+
model discovery still runs.
|
|
211
|
+
|
|
212
|
+
**Debugging the login flow.** Run with `IBM_BOB_DEBUG=1` and stderr
|
|
213
|
+
redirected to a file to see per-step timing for the SSO callback, token
|
|
214
|
+
exchange, and model discovery:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
IBM_BOB_DEBUG=1 pi 2>/tmp/pi-bob-debug.log
|
|
218
|
+
```
|
|
219
|
+
|
|
200
220
|
## Install options
|
|
201
221
|
|
|
202
222
|
Temporary test load:
|
|
203
223
|
|
|
204
224
|
```bash
|
|
205
|
-
pi -e
|
|
225
|
+
pi -e .
|
|
206
226
|
```
|
|
207
227
|
|
|
208
228
|
Install as a local Pi package:
|
|
209
229
|
|
|
210
230
|
```bash
|
|
211
|
-
pi install
|
|
231
|
+
pi install npm:pi-bob
|
|
212
232
|
```
|
|
213
233
|
|
|
214
234
|
Remove later:
|
|
215
235
|
|
|
216
236
|
```bash
|
|
217
|
-
pi remove
|
|
237
|
+
pi remove npm:pi-bob
|
|
218
238
|
```
|
|
219
239
|
|
|
220
240
|
## Next steps
|
package/extensions/bob.ts
CHANGED
|
@@ -239,7 +239,7 @@ function parseJsonHeaders(): Record<string, string> | undefined {
|
|
|
239
239
|
|
|
240
240
|
function buildHeaders(settings?: BobShellSettings): Record<string, string> | undefined {
|
|
241
241
|
const headers: Record<string, string> = {
|
|
242
|
-
"User-Agent": env("IBM_BOB_USER_AGENT") ?? "pi-bob/0.
|
|
242
|
+
"User-Agent": env("IBM_BOB_USER_AGENT") ?? "pi-bob/0.2.0",
|
|
243
243
|
};
|
|
244
244
|
|
|
245
245
|
const instanceId = env("IBM_BOB_INSTANCE_ID") ?? settings?.ibm?.instanceId;
|
|
@@ -282,6 +282,10 @@ function buildOpenAiCompat(api: CompatibleApi): Record<string, unknown> | undefi
|
|
|
282
282
|
supportsReasoningEffort: envBool("IBM_BOB_SUPPORTS_REASONING_EFFORT", false),
|
|
283
283
|
supportsUsageInStreaming: envBool("IBM_BOB_SUPPORTS_USAGE_IN_STREAMING", true),
|
|
284
284
|
supportsStrictMode: envBool("IBM_BOB_SUPPORTS_STRICT_MODE", false),
|
|
285
|
+
// pi-ai auto-detect defaults these to true for unknown endpoints; Bob's
|
|
286
|
+
// strict validator rejects `store` and `prompt_cache_key` with a bare 422.
|
|
287
|
+
supportsStore: false,
|
|
288
|
+
supportsLongCacheRetention: false,
|
|
285
289
|
};
|
|
286
290
|
|
|
287
291
|
const maxTokensField = env("IBM_BOB_MAX_TOKENS_FIELD") as MaxTokensField | undefined;
|
|
@@ -497,18 +501,23 @@ async function fetchBobModelCatalog(
|
|
|
497
501
|
authScheme: string,
|
|
498
502
|
settings?: BobShellSettings,
|
|
499
503
|
): Promise<BobDiscoveredModel[]> {
|
|
504
|
+
const startedAt = Date.now();
|
|
500
505
|
const controller = new AbortController();
|
|
501
506
|
const timeoutMs = envInt("IBM_BOB_MODEL_DISCOVERY_TIMEOUT_MS", DEFAULT_MODEL_DISCOVERY_TIMEOUT_MS);
|
|
502
507
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
503
508
|
const headers: Record<string, string> = {
|
|
504
509
|
Accept: "application/json",
|
|
505
510
|
Authorization: `${authScheme} ${accessToken}`,
|
|
506
|
-
"User-Agent": env("IBM_BOB_USER_AGENT") ?? "pi-bob/0.
|
|
511
|
+
"User-Agent": env("IBM_BOB_USER_AGENT") ?? "pi-bob/0.2.0",
|
|
507
512
|
};
|
|
508
|
-
const instanceId =
|
|
513
|
+
const instanceId =
|
|
514
|
+
env("IBM_BOB_INSTANCE_ID") ?? settings?.ibm?.instanceId ?? readInstanceFromJwt(accessToken);
|
|
509
515
|
const teamId = env("IBM_BOB_TEAM_ID") ?? settings?.ibm?.teamId;
|
|
510
516
|
if (instanceId) headers["x-instance-id"] = instanceId;
|
|
511
517
|
if (teamId) headers["x-team-id"] = teamId;
|
|
518
|
+
bobLog(
|
|
519
|
+
`model discovery → ${providerBaseUrl().replace(/\/+$/, "")}/model/info (instance=${instanceId ?? "none"}, timeout=${timeoutMs}ms)`,
|
|
520
|
+
);
|
|
512
521
|
|
|
513
522
|
try {
|
|
514
523
|
const response = await fetch(`${providerBaseUrl().replace(/\/+$/, "")}/model/info`, {
|
|
@@ -521,11 +530,16 @@ async function fetchBobModelCatalog(
|
|
|
521
530
|
`IBM Bob model discovery failed: ${response.status} ${truncateHttpBody(await readBoundedResponseBody(response))}`.trim(),
|
|
522
531
|
);
|
|
523
532
|
}
|
|
524
|
-
|
|
533
|
+
const catalog = parseBobModelCatalog(await readJsonResponse(response));
|
|
534
|
+
bobLog(`model discovery ok: ${catalog.length} models in ${Date.now() - startedAt}ms`);
|
|
535
|
+
return catalog;
|
|
525
536
|
} catch (error) {
|
|
526
537
|
if (controller.signal.aborted) {
|
|
527
538
|
throw new Error(`IBM Bob model discovery timed out after ${timeoutMs}ms.`);
|
|
528
539
|
}
|
|
540
|
+
bobLog(
|
|
541
|
+
`model discovery failed in ${Date.now() - startedAt}ms: ${error instanceof Error ? error.message : String(error)}`,
|
|
542
|
+
);
|
|
529
543
|
throw error;
|
|
530
544
|
} finally {
|
|
531
545
|
clearTimeout(timeout);
|
|
@@ -572,6 +586,21 @@ function jwtExpiry(accessToken: string): number | undefined {
|
|
|
572
586
|
return typeof exp === "number" ? exp * 1000 - 5 * 60 * 1000 : undefined;
|
|
573
587
|
}
|
|
574
588
|
|
|
589
|
+
function readInstanceFromJwt(accessToken: string | undefined): string | undefined {
|
|
590
|
+
if (!accessToken) return undefined;
|
|
591
|
+
const instances = jwtPayload(accessToken)?.instances;
|
|
592
|
+
if (!Array.isArray(instances) || instances.length === 0) return undefined;
|
|
593
|
+
for (const entry of instances) {
|
|
594
|
+
if (isRecord(entry) && typeof entry.id === "string" && entry.id) return entry.id;
|
|
595
|
+
}
|
|
596
|
+
return undefined;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function bobLog(message: string): void {
|
|
600
|
+
if (!envBool("IBM_BOB_DEBUG", false)) return;
|
|
601
|
+
console.warn(`[pi-bob] ${new Date().toISOString()} ${message}`);
|
|
602
|
+
}
|
|
603
|
+
|
|
575
604
|
export function applyBobAuthHeaders(
|
|
576
605
|
headers: Record<string, string | null | undefined>,
|
|
577
606
|
resolvedCredential?: string,
|
|
@@ -666,6 +695,7 @@ async function startCallbackServer(state: string): Promise<CallbackServer> {
|
|
|
666
695
|
|
|
667
696
|
const code = new Promise<string>((resolve, reject) => {
|
|
668
697
|
server.on("request", (req, res) => {
|
|
698
|
+
bobLog(`login: callback request received: ${(req.url ?? "").split("?")[0] || "(no path)"}`);
|
|
669
699
|
if (!req.url?.startsWith("/bob-callback")) {
|
|
670
700
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
671
701
|
res.end("Not Found");
|
|
@@ -679,6 +709,7 @@ async function startCallbackServer(state: string): Promise<CallbackServer> {
|
|
|
679
709
|
const error = url.searchParams.get("error");
|
|
680
710
|
|
|
681
711
|
if (returnedState !== state) {
|
|
712
|
+
bobLog(`login: callback state mismatch (expected ${state}, got ${returnedState})`);
|
|
682
713
|
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
683
714
|
res.end("Invalid state parameter");
|
|
684
715
|
reject(new Error("Invalid state parameter from IBM Bob SSO callback."));
|
|
@@ -686,6 +717,7 @@ async function startCallbackServer(state: string): Promise<CallbackServer> {
|
|
|
686
717
|
}
|
|
687
718
|
|
|
688
719
|
if (error) {
|
|
720
|
+
bobLog(`login: callback auth error: ${error}`);
|
|
689
721
|
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
690
722
|
res.end("Authentication failed");
|
|
691
723
|
reject(new Error(`IBM Bob SSO failed: ${error}`));
|
|
@@ -693,6 +725,7 @@ async function startCallbackServer(state: string): Promise<CallbackServer> {
|
|
|
693
725
|
}
|
|
694
726
|
|
|
695
727
|
if (!authCode) {
|
|
728
|
+
bobLog("login: callback missing authorization code");
|
|
696
729
|
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
697
730
|
res.end("Missing authorization code");
|
|
698
731
|
reject(new Error("IBM Bob SSO callback did not include an authorization code."));
|
|
@@ -712,7 +745,10 @@ async function startCallbackServer(state: string): Promise<CallbackServer> {
|
|
|
712
745
|
reject(error);
|
|
713
746
|
}
|
|
714
747
|
});
|
|
715
|
-
server.on("error",
|
|
748
|
+
server.on("error", (error) => {
|
|
749
|
+
bobLog(`login: callback server error: ${error instanceof Error ? error.message : String(error)}`);
|
|
750
|
+
reject(error);
|
|
751
|
+
});
|
|
716
752
|
});
|
|
717
753
|
|
|
718
754
|
await new Promise<void>((resolve, reject) => {
|
|
@@ -733,13 +769,15 @@ async function startCallbackServer(state: string): Promise<CallbackServer> {
|
|
|
733
769
|
}
|
|
734
770
|
|
|
735
771
|
async function postToken(path: "/authn/v1/auth/token" | "/authn/v1/auth/refresh", body: unknown): Promise<BobTokenResponse> {
|
|
772
|
+
const startedAt = Date.now();
|
|
736
773
|
const controller = new AbortController();
|
|
737
774
|
const timeoutMs = envInt("IBM_BOB_TOKEN_REQUEST_TIMEOUT_MS", DEFAULT_TOKEN_REQUEST_TIMEOUT_MS);
|
|
738
775
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
776
|
+
bobLog(`token request → ${bobOrigin()}${path} (timeout=${timeoutMs}ms)`);
|
|
739
777
|
try {
|
|
740
778
|
const response = await fetch(`${bobOrigin()}${path}`, {
|
|
741
779
|
method: "POST",
|
|
742
|
-
headers: { "Content-Type": "application/json", "User-Agent": env("IBM_BOB_USER_AGENT") ?? "pi-bob/0.
|
|
780
|
+
headers: { "Content-Type": "application/json", "User-Agent": env("IBM_BOB_USER_AGENT") ?? "pi-bob/0.2.0" },
|
|
743
781
|
body: JSON.stringify(body),
|
|
744
782
|
signal: controller.signal,
|
|
745
783
|
});
|
|
@@ -750,6 +788,7 @@ async function postToken(path: "/authn/v1/auth/token" | "/authn/v1/auth/refresh"
|
|
|
750
788
|
);
|
|
751
789
|
}
|
|
752
790
|
|
|
791
|
+
bobLog(`token request ok in ${Date.now() - startedAt}ms`);
|
|
753
792
|
return (await readJsonResponse(response)) as BobTokenResponse;
|
|
754
793
|
} catch (error) {
|
|
755
794
|
if (controller.signal.aborted) throw new Error(`IBM Bob SSO token request timed out after ${timeoutMs}ms.`);
|
|
@@ -760,26 +799,73 @@ async function postToken(path: "/authn/v1/auth/token" | "/authn/v1/auth/refresh"
|
|
|
760
799
|
}
|
|
761
800
|
|
|
762
801
|
async function loginBob(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {
|
|
802
|
+
const startedAt = Date.now();
|
|
803
|
+
const elapsed = () => `${((Date.now() - startedAt) / 1000).toFixed(1)}s`;
|
|
804
|
+
bobLog("login: starting IBM Bob SSO");
|
|
805
|
+
|
|
763
806
|
const state = randomBytes(16).toString("hex");
|
|
764
807
|
const callbackServer = await startCallbackServer(state);
|
|
808
|
+
bobLog(`login: callback server listening at ${callbackServer.callbackUri} (+${elapsed()})`);
|
|
809
|
+
|
|
765
810
|
const loginUrl = new URL(bobWebLoginUrl());
|
|
766
811
|
loginUrl.searchParams.set("callback_uri", callbackServer.callbackUri);
|
|
767
812
|
loginUrl.searchParams.set("state", state);
|
|
768
813
|
|
|
814
|
+
const signal = callbacks.signal;
|
|
769
815
|
let loginTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
816
|
+
let progressInterval: ReturnType<typeof setInterval> | undefined;
|
|
817
|
+
let onAbort: (() => void) | undefined;
|
|
770
818
|
try {
|
|
771
819
|
callbacks.onAuth({ url: loginUrl.toString() });
|
|
820
|
+
bobLog(`login: browser opened for SSO (+${elapsed()})`);
|
|
772
821
|
|
|
773
822
|
const timeoutMs = envInt("IBM_BOB_LOGIN_TIMEOUT_MS", 180_000);
|
|
774
823
|
const timeout = new Promise<never>((_resolve, reject) => {
|
|
775
|
-
loginTimeout = setTimeout(() =>
|
|
824
|
+
loginTimeout = setTimeout(() => {
|
|
825
|
+
bobLog(`login: SSO timed out after ${timeoutMs}ms (+${elapsed()})`);
|
|
826
|
+
reject(new Error("IBM Bob SSO timed out."));
|
|
827
|
+
}, timeoutMs);
|
|
776
828
|
});
|
|
777
829
|
|
|
778
|
-
const
|
|
830
|
+
const abort =
|
|
831
|
+
signal === undefined
|
|
832
|
+
? undefined
|
|
833
|
+
: new Promise<never>((_resolve, reject) => {
|
|
834
|
+
if (signal.aborted) {
|
|
835
|
+
bobLog("login: cancelled (abort signal already fired)");
|
|
836
|
+
reject(new Error("Login cancelled"));
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
onAbort = () => {
|
|
840
|
+
bobLog(`login: cancelled via abort signal (+${elapsed()})`);
|
|
841
|
+
reject(new Error("Login cancelled"));
|
|
842
|
+
};
|
|
843
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
progressInterval = setInterval(() => {
|
|
847
|
+
bobLog(`login: still waiting for SSO browser callback (+${elapsed()})`);
|
|
848
|
+
}, Math.min(15_000, Math.max(5_000, Math.floor(timeoutMs / 12))));
|
|
849
|
+
|
|
850
|
+
const races: Array<Promise<string | never>> = [callbackServer.code, timeout];
|
|
851
|
+
if (abort) races.push(abort);
|
|
852
|
+
const code = await Promise.race(races);
|
|
853
|
+
bobLog(`login: SSO callback received (+${elapsed()})`);
|
|
854
|
+
|
|
779
855
|
const credentials = credentialsFromTokenResponse(await postToken("/authn/v1/auth/token", { code }));
|
|
780
|
-
|
|
856
|
+
bobLog(`login: access token exchanged (+${elapsed()})`);
|
|
857
|
+
|
|
858
|
+
const withCatalog = await attachBobModelCatalog(credentials);
|
|
859
|
+
bobLog(`login: model catalog attached (+${elapsed()})`);
|
|
860
|
+
|
|
861
|
+
return withCatalog;
|
|
862
|
+
} catch (error) {
|
|
863
|
+
bobLog(`login: failed (+${elapsed()}): ${error instanceof Error ? error.message : String(error)}`);
|
|
864
|
+
throw error;
|
|
781
865
|
} finally {
|
|
782
866
|
if (loginTimeout) clearTimeout(loginTimeout);
|
|
867
|
+
if (progressInterval) clearInterval(progressInterval);
|
|
868
|
+
if (onAbort && signal) signal.removeEventListener("abort", onAbort);
|
|
783
869
|
callbackServer.close();
|
|
784
870
|
}
|
|
785
871
|
}
|
|
@@ -790,6 +876,15 @@ async function refreshBobToken(credentials: OAuthCredentials): Promise<OAuthCred
|
|
|
790
876
|
await postToken("/authn/v1/auth/refresh", { refresh_token: credentials.refresh }),
|
|
791
877
|
credentials.refresh,
|
|
792
878
|
);
|
|
879
|
+
// Token refresh runs inside pi-ai's locked credentials.modify (auth.json lock).
|
|
880
|
+
// Avoid holding that global lock across a model-discovery network call when a
|
|
881
|
+
// catalog is already cached; rediscovery still happens on login and whenever
|
|
882
|
+
// no cached catalog exists.
|
|
883
|
+
const existingCatalog = cachedCatalog(credentials);
|
|
884
|
+
if (existingCatalog) {
|
|
885
|
+
bobLog(`refresh: reusing cached catalog (${existingCatalog.length} models); skipping rediscovery`);
|
|
886
|
+
return { ...refreshed, bobModelCatalog: existingCatalog };
|
|
887
|
+
}
|
|
793
888
|
return attachBobModelCatalog(refreshed, credentials);
|
|
794
889
|
}
|
|
795
890
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-bob",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Pi provider extension for IBM Bob / IBM-approved compatible model endpoints.",
|
|
6
6
|
"keywords": [
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"repository": {
|
|
26
26
|
"type": "git",
|
|
27
|
-
"url": "https://github.com/songlining/pi-bob.git"
|
|
27
|
+
"url": "git+https://github.com/songlining/pi-bob.git"
|
|
28
28
|
},
|
|
29
29
|
"bugs": {
|
|
30
30
|
"url": "https://github.com/songlining/pi-bob/issues"
|