@f5-sales-demo/pi-ai 21.12.2 → 21.13.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/CHANGELOG.md +6 -0
- package/package.json +2 -2
- package/src/auth-storage.ts +2 -2
- package/src/providers/cursor.ts +30 -23
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,12 @@
|
|
|
10
10
|
|
|
11
11
|
### Fixed
|
|
12
12
|
|
|
13
|
+
- Prevented case-insensitive Windows environment lookup from replacing literal stored credentials,
|
|
14
|
+
and delayed Cursor success until the HTTP/2/CONNECT protocol terminates cleanly so late stream
|
|
15
|
+
errors remain visible. Semantically ported from
|
|
16
|
+
[can1357/oh-my-pi#7362](https://github.com/can1357/oh-my-pi/pull/7362) and
|
|
17
|
+
[#5636](https://github.com/can1357/oh-my-pi/pull/5636)
|
|
18
|
+
([#3711](https://github.com/f5-sales-demo/xcsh/issues/3711)).
|
|
13
19
|
- Exported the canonical provider credential-shape matcher so xcsh can protect otherwise
|
|
14
20
|
unconfigured tokens reversibly before provider submission, preserving exact-match local edits.
|
|
15
21
|
Semantically ported from
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5-sales-demo/pi-ai",
|
|
4
|
-
"version": "21.
|
|
4
|
+
"version": "21.13.1",
|
|
5
5
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
6
6
|
"homepage": "https://github.com/f5-sales-demo/xcsh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@anthropic-ai/sdk": "^0.115",
|
|
47
47
|
"@aws-sdk/client-bedrock-runtime": "^3",
|
|
48
48
|
"@bufbuild/protobuf": "^2.11",
|
|
49
|
-
"@f5-sales-demo/pi-utils": "21.
|
|
49
|
+
"@f5-sales-demo/pi-utils": "21.13.1",
|
|
50
50
|
"@google/genai": "^2.15",
|
|
51
51
|
"@sinclair/typebox": "^0.34",
|
|
52
52
|
"@smithy/node-http-handler": "^4.4",
|
package/src/auth-storage.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { Database, type Statement } from "bun:sqlite";
|
|
11
11
|
import * as fs from "node:fs/promises";
|
|
12
12
|
import * as path from "node:path";
|
|
13
|
-
import { getAgentDbPath, logger } from "@f5-sales-demo/pi-utils";
|
|
13
|
+
import { $envExact, getAgentDbPath, logger } from "@f5-sales-demo/pi-utils";
|
|
14
14
|
import { getEnvApiKey } from "./stream";
|
|
15
15
|
import type { Provider } from "./types";
|
|
16
16
|
import type {
|
|
@@ -149,7 +149,7 @@ export type AuthStorageOptions = {
|
|
|
149
149
|
* Does NOT support "!command" syntax (that requires pi-natives).
|
|
150
150
|
*/
|
|
151
151
|
async function defaultConfigValueResolver(config: string): Promise<string | undefined> {
|
|
152
|
-
const envValue =
|
|
152
|
+
const envValue = $envExact(config);
|
|
153
153
|
return envValue || config;
|
|
154
154
|
}
|
|
155
155
|
|
package/src/providers/cursor.ts
CHANGED
|
@@ -325,6 +325,27 @@ export const streamCursor: StreamFunction<"cursor-agent"> = (
|
|
|
325
325
|
let h2Client: http2.ClientHttp2Session | null = null;
|
|
326
326
|
let h2Request: http2.ClientHttp2Stream | null = null;
|
|
327
327
|
let heartbeatTimer: NodeJS.Timeout | null = null;
|
|
328
|
+
const h2Completion = Promise.withResolvers<void>();
|
|
329
|
+
let h2Settled = false;
|
|
330
|
+
let sawTurnEnded = false;
|
|
331
|
+
let endStreamError: Error | null = null;
|
|
332
|
+
const settleH2 = (error?: unknown): void => {
|
|
333
|
+
if (h2Settled) return;
|
|
334
|
+
h2Settled = true;
|
|
335
|
+
if (error !== undefined) {
|
|
336
|
+
h2Completion.reject(error);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (endStreamError) {
|
|
340
|
+
h2Completion.reject(endStreamError);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
if (!sawTurnEnded) {
|
|
344
|
+
h2Completion.reject(new Error("Cursor stream ended before turnEnded"));
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
h2Completion.resolve();
|
|
348
|
+
};
|
|
328
349
|
|
|
329
350
|
try {
|
|
330
351
|
const apiKey = options?.apiKey;
|
|
@@ -346,6 +367,7 @@ export const streamCursor: StreamFunction<"cursor-agent"> = (
|
|
|
346
367
|
|
|
347
368
|
const baseUrl = model.baseUrl || CURSOR_API_URL;
|
|
348
369
|
h2Client = http2.connect(baseUrl);
|
|
370
|
+
h2Client.on("error", error => settleH2(error));
|
|
349
371
|
|
|
350
372
|
h2Request = h2Client.request({
|
|
351
373
|
":method": "POST",
|
|
@@ -363,7 +385,6 @@ export const streamCursor: StreamFunction<"cursor-agent"> = (
|
|
|
363
385
|
stream.push({ type: "start", partial: output });
|
|
364
386
|
|
|
365
387
|
let pendingBuffer = Buffer.alloc(0);
|
|
366
|
-
let endStreamError: Error | null = null;
|
|
367
388
|
let currentTextBlock: (TextContent & { index: number }) | null = null;
|
|
368
389
|
let currentThinkingBlock: (ThinkingContent & { index: number }) | null = null;
|
|
369
390
|
let currentToolCall: ToolCallState | null = null;
|
|
@@ -400,8 +421,6 @@ export const streamCursor: StreamFunction<"cursor-agent"> = (
|
|
|
400
421
|
conversationStateCache.set(conversationId, checkpoint);
|
|
401
422
|
};
|
|
402
423
|
|
|
403
|
-
let resolveH2: (() => void) | undefined;
|
|
404
|
-
|
|
405
424
|
h2Request.on("data", (chunk: Buffer) => {
|
|
406
425
|
pendingBuffer = Buffer.concat([pendingBuffer, chunk]);
|
|
407
426
|
|
|
@@ -417,6 +436,7 @@ export const streamCursor: StreamFunction<"cursor-agent"> = (
|
|
|
417
436
|
const endError = parseConnectEndStream(messageBytes);
|
|
418
437
|
if (endError) {
|
|
419
438
|
endStreamError = endError;
|
|
439
|
+
settleH2(endError);
|
|
420
440
|
h2Request?.close();
|
|
421
441
|
}
|
|
422
442
|
continue;
|
|
@@ -443,13 +463,8 @@ export const streamCursor: StreamFunction<"cursor-agent"> = (
|
|
|
443
463
|
log("error", "handleServerMessage", { error: String(error) });
|
|
444
464
|
});
|
|
445
465
|
|
|
446
|
-
//
|
|
447
|
-
|
|
448
|
-
if (isTurnEnded && resolveH2) {
|
|
449
|
-
const r = resolveH2;
|
|
450
|
-
resolveH2 = undefined;
|
|
451
|
-
r();
|
|
452
|
-
}
|
|
466
|
+
// Application completion is not protocol success; wait for a clean HTTP/2 end.
|
|
467
|
+
if (isTurnEnded) sawTurnEnded = true;
|
|
453
468
|
} catch (e) {
|
|
454
469
|
log("error", "parseServerMessage", { error: String(e) });
|
|
455
470
|
}
|
|
@@ -471,32 +486,24 @@ export const streamCursor: StreamFunction<"cursor-agent"> = (
|
|
|
471
486
|
|
|
472
487
|
heartbeatTimer = setInterval(sendHeartbeat, 5000);
|
|
473
488
|
|
|
474
|
-
const h2Completion = Promise.withResolvers<void>();
|
|
475
|
-
resolveH2 = h2Completion.resolve;
|
|
476
|
-
|
|
477
489
|
h2Request!.on("trailers", trailers => {
|
|
478
490
|
const status = trailers["grpc-status"];
|
|
479
491
|
const msg = trailers["grpc-message"];
|
|
480
|
-
if (status && status !== "0") {
|
|
481
|
-
|
|
492
|
+
if (status && status !== "0" && !endStreamError) {
|
|
493
|
+
endStreamError = new Error(`gRPC error ${status}: ${decodeURIComponent(String(msg || ""))}`);
|
|
482
494
|
}
|
|
483
495
|
});
|
|
484
496
|
|
|
485
497
|
h2Request!.on("end", () => {
|
|
486
|
-
|
|
487
|
-
if (endStreamError) {
|
|
488
|
-
h2Completion.reject(endStreamError);
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
|
-
h2Completion.resolve();
|
|
498
|
+
settleH2();
|
|
492
499
|
});
|
|
493
500
|
|
|
494
|
-
h2Request!.on("error",
|
|
501
|
+
h2Request!.on("error", error => settleH2(error));
|
|
495
502
|
|
|
496
503
|
if (options?.signal) {
|
|
497
504
|
options.signal.addEventListener("abort", () => {
|
|
498
505
|
h2Request?.close();
|
|
499
|
-
|
|
506
|
+
settleH2(new Error("Request was aborted"));
|
|
500
507
|
});
|
|
501
508
|
}
|
|
502
509
|
await h2Completion.promise;
|