@centerforagenticai/pi-multi-account 0.1.6 → 0.1.7
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/package.json +5 -5
- package/src/logical-provider.ts +37 -6
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.1.
|
|
2
|
+
"version": "0.1.7",
|
|
3
3
|
"description": "Global Anthropic and OpenAI Codex multi-account OAuth routing for Pi.",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
@@ -59,11 +59,11 @@
|
|
|
59
59
|
"undici": "^8.10.0"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
|
-
"@earendil-works/pi-ai": "^1.0.
|
|
63
|
-
"@earendil-works/pi-coding-agent": "^1.0.
|
|
64
|
-
"@earendil-works/pi-tui": "^1.0.
|
|
62
|
+
"@earendil-works/pi-ai": "^1.0.4",
|
|
63
|
+
"@earendil-works/pi-coding-agent": "^1.0.4",
|
|
64
|
+
"@earendil-works/pi-tui": "^1.0.4",
|
|
65
65
|
"@types/node": "^24.0.0",
|
|
66
|
-
"typebox": "1.3.
|
|
66
|
+
"typebox": "1.3.35",
|
|
67
67
|
"typescript": "^5.9.0",
|
|
68
68
|
"vitest": "^4.1.11"
|
|
69
69
|
},
|
package/src/logical-provider.ts
CHANGED
|
@@ -456,6 +456,14 @@ const VISIBLE_FAILURE_CAUSES: Readonly<Record<LogicalFailureEvidence["category"]
|
|
|
456
456
|
export const HOST_STALE_INSTALL_MESSAGE =
|
|
457
457
|
"pi's installed files changed while this session was running; restart the pi session to load the current install.";
|
|
458
458
|
|
|
459
|
+
/**
|
|
460
|
+
* Public text when the missing module is a bare package rather than one of
|
|
461
|
+
* pi's own bundle files: the dependency is absent from the install, so a
|
|
462
|
+
* restart loads the same broken tree and only a reinstall helps.
|
|
463
|
+
*/
|
|
464
|
+
export const HOST_MISSING_DEPENDENCY_MESSAGE =
|
|
465
|
+
"pi could not load a package it depends on; reinstall pi (or the extension that needs the package), then restart the pi session.";
|
|
466
|
+
|
|
459
467
|
/**
|
|
460
468
|
* Node's own phrasing, anchored at the start: pi-ai `lazyStream` keeps
|
|
461
469
|
* `error.message` unprefixed. An unanchored match would misread a provider or
|
|
@@ -491,7 +499,7 @@ function hasStructuredFailureEvidence(failure: ProviderFailureSignal | undefined
|
|
|
491
499
|
* Bounded diagnostic cause for a stale install: the Node error code and the
|
|
492
500
|
* missing file's base name. Directories are dropped so no local path is kept.
|
|
493
501
|
*/
|
|
494
|
-
function
|
|
502
|
+
export function hostLoadFault(error: unknown): { cause: string; missingDependency: boolean } {
|
|
495
503
|
let text = "";
|
|
496
504
|
let code: unknown;
|
|
497
505
|
try {
|
|
@@ -504,9 +512,25 @@ function hostStaleInstallCause(error: unknown): string {
|
|
|
504
512
|
} catch {
|
|
505
513
|
// Fall through with whatever was read.
|
|
506
514
|
}
|
|
507
|
-
const file = /['"]?([^'"\s]*\.(?:m?js|cjs|json|node))['"]?/.exec(text)?.[1]?.split(/[\\/]/).at(-1);
|
|
508
515
|
const kind = code === "ERR_MODULE_NOT_FOUND" || /ERR_MODULE_NOT_FOUND|Cannot find/.test(text) ? "ERR_MODULE_NOT_FOUND" : "dynamic-import-failed";
|
|
509
|
-
|
|
516
|
+
// Node names the missing specifier first ("Cannot find package 'x' imported
|
|
517
|
+
// from /dir/importer.js"); the importer is not what is missing.
|
|
518
|
+
// Node does not escape quotes, so a path may contain one: read up to the
|
|
519
|
+
// quote that ends the specifier (before " imported from", a CJS
|
|
520
|
+
// ". Please verify…"/"Require stack" line, or the end of the text).
|
|
521
|
+
const specifier = /Cannot find (?:module|package) '(.+?)'(?=$| imported from |\.\s|\r?\n)/.exec(text)?.[1];
|
|
522
|
+
if (specifier !== undefined && specifier.length > 0) {
|
|
523
|
+
if (!/^(?:\.{1,2}[\\/]|[\\/]|file:|[A-Za-z]:[\\/])/.test(specifier)) {
|
|
524
|
+
// A bare specifier names a package (keep "@scope/name" or "name").
|
|
525
|
+
const parts = specifier.split("/");
|
|
526
|
+
const name = (specifier.startsWith("@") ? parts.slice(0, 2) : parts.slice(0, 1)).join("/");
|
|
527
|
+
return { cause: `${kind} package ${name.slice(0, 120)}`, missingDependency: true };
|
|
528
|
+
}
|
|
529
|
+
const base = specifier.split(/[\\/]/).at(-1) ?? "";
|
|
530
|
+
return { cause: base.length === 0 ? kind : `${kind} ${base.slice(0, 120)}`, missingDependency: false };
|
|
531
|
+
}
|
|
532
|
+
const file = /['"]?([^'"\s]*\.(?:m?js|cjs|json|node))['"]?/.exec(text)?.[1]?.split(/[\\/]/).at(-1);
|
|
533
|
+
return { cause: file === undefined || file.length === 0 ? kind : `${kind} ${file.slice(0, 120)}`, missingDependency: false };
|
|
510
534
|
}
|
|
511
535
|
|
|
512
536
|
type SetupFailureDisposition = "context-overflow" | "retryable" | "host-final";
|
|
@@ -1167,13 +1191,15 @@ export function createLogicalProvider(
|
|
|
1167
1191
|
account: LogicalPhysicalAccount,
|
|
1168
1192
|
cause: unknown,
|
|
1169
1193
|
): void => {
|
|
1194
|
+
const fault = hostLoadFault(cause);
|
|
1170
1195
|
box.hostStaleInstall = true;
|
|
1196
|
+
box.hostMissingDependency = fault.missingDependency;
|
|
1171
1197
|
box.overflow = false;
|
|
1172
1198
|
box.preStartRetryable = false;
|
|
1173
1199
|
try {
|
|
1174
1200
|
deps.onDiagnostic?.(
|
|
1175
1201
|
`logical dispatch for ${account.providerId} failed loading host code (host_stale_install: ` +
|
|
1176
|
-
`${
|
|
1202
|
+
`${fault.cause}); ${fault.missingDependency ? "reinstall the missing package" : "restart the pi session"}`,
|
|
1177
1203
|
);
|
|
1178
1204
|
} catch {
|
|
1179
1205
|
// A diagnostic sink failure cannot replace a provider result.
|
|
@@ -1247,6 +1273,8 @@ export function createLogicalProvider(
|
|
|
1247
1273
|
preStartRetryable: boolean;
|
|
1248
1274
|
/** Setup failed because this pi process could not load its own code. */
|
|
1249
1275
|
hostStaleInstall?: boolean;
|
|
1276
|
+
/** The missing module is a bare package, so a reinstall, not a restart, helps. */
|
|
1277
|
+
hostMissingDependency?: boolean;
|
|
1250
1278
|
/** Whether the caller or provider shutdown aborted this physical request. */
|
|
1251
1279
|
cancelled?: () => boolean;
|
|
1252
1280
|
/**
|
|
@@ -1283,7 +1311,10 @@ export function createLogicalProvider(
|
|
|
1283
1311
|
let sawTerminal = false;
|
|
1284
1312
|
// Whether any event other than a leading `start` arrived. A `start`
|
|
1285
1313
|
// only reports that response headers arrived, so a failure after it is
|
|
1286
|
-
// still the stream's first real event.
|
|
1314
|
+
// still the stream's first real event. Every other non-terminal event
|
|
1315
|
+
// is output (it also sets `box.sawOutput`), and a terminal ends the
|
|
1316
|
+
// loop, so on the thrown path `!sawEvent` means "no content yet": this
|
|
1317
|
+
// is the signal the setup-only classifications key on.
|
|
1287
1318
|
let sawEvent = false;
|
|
1288
1319
|
const recordFailureOnce = (error: unknown): HostRetryCooldownReceipt => {
|
|
1289
1320
|
if (box.hostStaleInstall === true) box.receipt ??= hostFaultReceipt();
|
|
@@ -1779,7 +1810,7 @@ export function createLogicalProvider(
|
|
|
1779
1810
|
return {
|
|
1780
1811
|
...syntheticErrorMessage(
|
|
1781
1812
|
modelId,
|
|
1782
|
-
`${stale ? HOST_STALE_INSTALL_MESSAGE : errorMessage} ${evidence}`,
|
|
1813
|
+
`${stale ? (box?.hostMissingDependency === true ? HOST_MISSING_DEPENDENCY_MESSAGE : HOST_STALE_INSTALL_MESSAGE) : errorMessage} ${evidence}`,
|
|
1783
1814
|
physical === undefined ? undefined : projectTerminalUsage(physical),
|
|
1784
1815
|
physical === undefined ? undefined : finiteNonNegative(physical.timestamp),
|
|
1785
1816
|
),
|