@cortexkit/opencode-openai-auth 0.5.2 → 0.6.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 +2 -1
- package/dist/cli.js +1 -1
- package/dist/commands.d.ts +16 -0
- package/dist/core/accounts.d.ts +22 -0
- package/dist/core/oauth.d.ts +1 -1
- package/dist/core/refresh-all-quota.d.ts +3 -0
- package/dist/core/reset-credits.d.ts +114 -0
- package/dist/index-87145jkw.js +2 -0
- package/dist/{index-rfgyxxxq.js → index-dz9a4xwy.js} +1 -1
- package/dist/{index-zkzb0twr.js → index-e08r33g5.js} +2 -2
- package/dist/{index-s5fbqqwx.js → index-hxpv25e8.js} +1 -1
- package/dist/index-meg0ng0z.js +2 -0
- package/dist/{index-vrdc90yd.js → index-n00ybbbq.js} +1 -1
- package/dist/{index-6rnw2cgs.js → index-nwy7ec7p.js} +1 -1
- package/dist/{index-wpd7mg3p.js → index-s2vhsb2v.js} +1 -1
- package/dist/index-zddpw2sa.js +89 -0
- package/dist/index.d.ts +30 -1
- package/dist/index.js +120 -70
- package/dist/quota-normalize-vf2pj6n7.js +1 -0
- package/dist/quota-normalize.d.ts +1 -0
- package/dist/rpc/port-file.js +1 -1
- package/dist/rpc/protocol.d.ts +1 -1
- package/dist/rpc/rpc-client.d.ts +2 -1
- package/dist/rpc/rpc-client.js +1 -1
- package/dist/rpc/rpc-dir.js +1 -1
- package/dist/rpc/rpc-server.d.ts +1 -0
- package/dist/sidebar-state.d.ts +2 -0
- package/dist/sidebar-state.js +1 -1
- package/dist/{token-5hdnazxp.js → token-esc14v6e.js} +1 -1
- package/dist/token-util-qkpkvy5t.js +1 -0
- package/dist/tui-preferences.js +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +3 -3
- package/src/rpc/protocol.ts +1 -0
- package/src/rpc/rpc-client.ts +7 -3
- package/src/rpc/rpc-server.ts +24 -4
- package/src/sidebar-state.ts +62 -3
- package/src/tui/command-dialogs.tsx +245 -0
- package/src/tui-compiled/rpc/protocol.ts +1 -0
- package/src/tui-compiled/rpc/rpc-client.ts +7 -3
- package/src/tui-compiled/sidebar-state.ts +62 -3
- package/src/tui-compiled/tui/command-dialogs.tsx +162 -1
- package/src/tui-compiled/tui.tsx +1 -1
- package/src/tui-compiled/util/error.ts +9 -0
- package/src/tui.tsx +4 -1
- package/dist/index-2ng03cby.js +0 -2
- package/dist/index-qhvsskdz.js +0 -89
- package/dist/index-wnxfzjfr.js +0 -2
- package/dist/quota-normalize-n02dfdx0.js +0 -1
- package/dist/token-util-3d2nj45d.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c as a,d as b,e as c,f as d,g as e}from"./index-87145jkw.js";import"./index-s2vhsb2v.js";export{a as toResetIso,d as normalizeWsFrame,e as normalizeWham,b as normalizeQuotaHeaders,c as isCompleteQuotaHeaderFrame};
|
|
@@ -36,6 +36,7 @@ interface WhamUsageResponse {
|
|
|
36
36
|
rate_limit: WhamRateLimits;
|
|
37
37
|
rate_limit_reset_credits?: {
|
|
38
38
|
available_count?: number;
|
|
39
|
+
applicable_available_count?: number;
|
|
39
40
|
} | null;
|
|
40
41
|
}
|
|
41
42
|
export declare function normalizeWham(json: WhamUsageResponse): OAuthQuotaSnapshot;
|
package/dist/rpc/port-file.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{va as a,wa as b,xa as c}from"../index-e08r33g5.js";import"../index-hxpv25e8.js";import"../index-s2vhsb2v.js";export{a as writePortFile,b as sweepRpcState,c as discoverPortFile};
|
package/dist/rpc/protocol.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type CommandModalName = 'openai-quota' | 'openai-account' | 'openai-routing' | 'openai-killswitch' | 'openai-dump' | 'openai-logging' | 'openai-cachekeep';
|
|
1
|
+
export type CommandModalName = 'openai-quota' | 'openai-account' | 'openai-routing' | 'openai-killswitch' | 'openai-dump' | 'openai-logging' | 'openai-cachekeep' | 'openai-reset';
|
|
2
2
|
export interface OpenDialogPayload {
|
|
3
3
|
command: CommandModalName;
|
|
4
4
|
text: string;
|
package/dist/rpc/rpc-client.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type PortFileEntry } from './port-file';
|
|
|
2
2
|
import type { ApplyRequest, ApplyResult, RpcNotification } from './protocol';
|
|
3
3
|
export interface RpcClient {
|
|
4
4
|
pending: (lastReceivedId: number, sessionId?: string) => Promise<RpcNotification[]>;
|
|
5
|
-
apply: (request: ApplyRequest) => Promise<ApplyResult>;
|
|
5
|
+
apply: (request: ApplyRequest, timeoutMs?: number) => Promise<ApplyResult>;
|
|
6
6
|
}
|
|
7
|
+
export declare const DEFAULT_RPC_TIMEOUT_MS = 2000;
|
|
7
8
|
export declare function createRpcClient(dir: string, expectedPid?: number, onSelected?: (entry: PortFileEntry | null) => void): RpcClient;
|
package/dist/rpc/rpc-client.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{xa as c}from"../index-e08r33g5.js";import"../index-hxpv25e8.js";import"../index-s2vhsb2v.js";var y=2000;async function p(n,r,s,o,i,e=y){let t=await c(n,r);if(s?.(t),!t)return null;let l=new AbortController,u=setTimeout(()=>l.abort(),e);try{let a=await fetch(`http://127.0.0.1:${t.port}/rpc/${o}`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${t.token}`},body:JSON.stringify(i),signal:l.signal});if(!a.ok)return null;return await a.json()}catch{return null}finally{clearTimeout(u)}}function m(n,r,s){let o=!1,i=(e)=>{if(o)return;o=!0,s?.(e)};return{async pending(e,t){return(await p(n,r,i,"pending-notifications",{lastReceivedId:e,sessionId:t}))?.messages??[]},async apply(e,t){return await p(n,r,i,"apply",{...e},t)??{text:"apply failed",knobs:{}}}}}export{m as createRpcClient,y as DEFAULT_RPC_TIMEOUT_MS};
|
package/dist/rpc/rpc-dir.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{Ba as a,Ca as b,Da as c}from"../index-dz9a4xwy.js";import"../index-s2vhsb2v.js";export{a as tmpdir,c as resolveRpcDir,b as getRpcDir};
|
package/dist/rpc/rpc-server.d.ts
CHANGED
|
@@ -12,5 +12,6 @@ export interface RpcServerOptions {
|
|
|
12
12
|
drain: typeof drainNotifications;
|
|
13
13
|
apply: (request: ApplyRequest) => Promise<ApplyResult>;
|
|
14
14
|
timeoutMs?: number;
|
|
15
|
+
receiptTimeoutMs?: number;
|
|
15
16
|
}
|
|
16
17
|
export declare function startRpcServer(options: RpcServerOptions): Promise<RpcServerHandle>;
|
package/dist/sidebar-state.d.ts
CHANGED
|
@@ -38,6 +38,7 @@ export interface ActiveRoutingEntry {
|
|
|
38
38
|
export type ActiveRoutingMap = Record<string, ActiveRoutingEntry>;
|
|
39
39
|
export interface StickyAssignment {
|
|
40
40
|
accountId: string;
|
|
41
|
+
wireAccountId?: string;
|
|
41
42
|
assignedAt: number;
|
|
42
43
|
lastSeenAt: number;
|
|
43
44
|
inputBytes: number;
|
|
@@ -55,6 +56,7 @@ export interface ResolveStickyAssignmentInput {
|
|
|
55
56
|
validPinnedAccountIds: readonly string[];
|
|
56
57
|
excludeAccountIds?: readonly string[];
|
|
57
58
|
quotaCheckedAtByAccount: Readonly<Record<string, number | undefined>>;
|
|
59
|
+
wireAccountIdByAccount?: Readonly<Record<string, string | undefined>>;
|
|
58
60
|
choose: (pendingBytes: ReadonlyMap<string, number>) => StickyAssignmentChoice | undefined;
|
|
59
61
|
}
|
|
60
62
|
export interface SidebarState {
|
package/dist/sidebar-state.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{$ as
|
|
1
|
+
import{$ as i,T as a,U as b,V as c,W as d,X as e,Y as f,Z as g,_ as h,aa as j,ba as k,ca as l,da as m,ea as n,fa as o,ga as p,ha as q,ia as r,ja as s,ka as t,la as u,ma as v,na as w,oa as x,pa as y,qa as z,ra as A,sa as B,ta as C}from"./index-meg0ng0z.js";import"./index-n00ybbbq.js";import"./index-hxpv25e8.js";import"./index-s2vhsb2v.js";export{u as upsertSidebarActiveRouting,s as setSidebarState,t as setSidebarMachineState,v as setSidebarLegacyRouting,x as resolveSidebarStickyAssignment,o as resolveSessionStickyAccount,p as resolveSessionSidebarRouting,A as resolveActiveAccount,w as removeSidebarActiveRouting,r as pruneStickyAssignments,q as pruneActiveRouting,h as normalizeSidebarState,l as isUsableRoutingEntry,n as isQuotaExhausted,e as hashSidebarSessionId,f as getSidebarStateFile,i as getSidebarState,b as getPresentQuotaWindows,B as getCollapsedQuotaSummary,a as formatWindowLabel,m as exhaustedQuotaResetAt,z as drainSidebarWrites,C as computeQuotaPacing,y as clearSidebarStickyAssignment,d as STICKY_ASSIGNMENT_MAX_ENTRIES,c as STICKY_ASSIGNMENT_MAX_AGE_MS,g as DEFAULT_SIDEBAR_STATE,k as ACTIVE_ROUTING_MAX_ENTRIES,j as ACTIVE_ROUTING_MAX_AGE_MS};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as U,b as V}from"./index-
|
|
1
|
+
import{a as U,b as V}from"./index-nwy7ec7p.js";import{Fa as R}from"./index-s2vhsb2v.js";var E=R((M,Q)=>{var{defineProperty:H,getOwnPropertyDescriptor:W,getOwnPropertyNames:X}=Object,Y=Object.prototype.hasOwnProperty,Z=(z,q)=>{for(var B in q)H(z,B,{get:q[B],enumerable:!0})},$=(z,q,B,F)=>{if(q&&typeof q==="object"||typeof q==="function"){for(let v of X(q))if(!Y.call(z,v)&&v!==B)H(z,v,{get:()=>q[v],enumerable:!(F=W(q,v))||F.enumerable})}return z},A=(z)=>$(H({},"__esModule",{value:!0}),z),L={};Z(L,{refreshToken:()=>D});Q.exports=A(L);var K=U(),G=V();async function D(z){let q=z?.project,B=z?.team;if(!q&&!B){let v=(0,G.findProjectInfo)();q=v.projectId,B=v.teamId}else if(!q||!B){let v=(0,G.findProjectInfo)();q=q??v.projectId,B=B??v.teamId}if(!q)throw new K.VercelOidcTokenError("Failed to refresh OIDC token: No project specified. Try re-linking your project with `vc link`");let F=(0,G.loadToken)(q);if(!F||(0,G.isExpired)((0,G.getTokenPayload)(F.token),z?.expirationBufferMs)){let v=await(0,G.getVercelToken)({expirationBufferMs:z?.expirationBufferMs});if(F=await(0,G.getVercelOidcToken)(v,q,B),!F)throw new K.VercelOidcTokenError("Failed to refresh OIDC token");(0,G.saveToken)(F,q)}process.env.VERCEL_OIDC_TOKEN=F.token;return}});export default E();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{b as a}from"./index-nwy7ec7p.js";import"./index-s2vhsb2v.js";export default a();
|
package/dist/tui-preferences.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{Ea as P,Fa as C,Ha as _}from"./index-s2vhsb2v.js";var g=C((T,L)=>{(function(Q){if(typeof L==="object"&&typeof L.exports==="object"){var J=Q(_,T);if(J!==void 0)L.exports=J}else if(typeof define==="function"&&define.amd)define(["require","exports","./impl/format","./impl/edit","./impl/scanner","./impl/parser"],Q)})(function(Q,J){Object.defineProperty(J,"__esModule",{value:!0}),J.applyEdits=J.modify=J.format=J.printParseErrorCode=J.ParseErrorCode=J.stripComments=J.visit=J.getNodeValue=J.getNodePath=J.findNodeAtOffset=J.findNodeAtLocation=J.parseTree=J.parse=J.getLocation=J.SyntaxKind=J.ScanError=J.createScanner=void 0;let X=Q("./impl/format"),Z=Q("./impl/edit"),G=Q("./impl/scanner"),$=Q("./impl/parser");J.createScanner=G.createScanner;var W;(function(z){z[z.None=0]="None",z[z.UnexpectedEndOfComment=1]="UnexpectedEndOfComment",z[z.UnexpectedEndOfString=2]="UnexpectedEndOfString",z[z.UnexpectedEndOfNumber=3]="UnexpectedEndOfNumber",z[z.InvalidUnicode=4]="InvalidUnicode",z[z.InvalidEscapeCharacter=5]="InvalidEscapeCharacter",z[z.InvalidCharacter=6]="InvalidCharacter"})(W||(J.ScanError=W={}));var H;(function(z){z[z.OpenBraceToken=1]="OpenBraceToken",z[z.CloseBraceToken=2]="CloseBraceToken",z[z.OpenBracketToken=3]="OpenBracketToken",z[z.CloseBracketToken=4]="CloseBracketToken",z[z.CommaToken=5]="CommaToken",z[z.ColonToken=6]="ColonToken",z[z.NullKeyword=7]="NullKeyword",z[z.TrueKeyword=8]="TrueKeyword",z[z.FalseKeyword=9]="FalseKeyword",z[z.StringLiteral=10]="StringLiteral",z[z.NumericLiteral=11]="NumericLiteral",z[z.LineCommentTrivia=12]="LineCommentTrivia",z[z.BlockCommentTrivia=13]="BlockCommentTrivia",z[z.LineBreakTrivia=14]="LineBreakTrivia",z[z.Trivia=15]="Trivia",z[z.Unknown=16]="Unknown",z[z.EOF=17]="EOF"})(H||(J.SyntaxKind=H={})),J.getLocation=$.getLocation,J.parse=$.parse,J.parseTree=$.parseTree,J.findNodeAtLocation=$.findNodeAtLocation,J.findNodeAtOffset=$.findNodeAtOffset,J.getNodePath=$.getNodePath,J.getNodeValue=$.getNodeValue,J.visit=$.visit,J.stripComments=$.stripComments;var q;(function(z){z[z.InvalidSymbol=1]="InvalidSymbol",z[z.InvalidNumberFormat=2]="InvalidNumberFormat",z[z.PropertyNameExpected=3]="PropertyNameExpected",z[z.ValueExpected=4]="ValueExpected",z[z.ColonExpected=5]="ColonExpected",z[z.CommaExpected=6]="CommaExpected",z[z.CloseBraceExpected=7]="CloseBraceExpected",z[z.CloseBracketExpected=8]="CloseBracketExpected",z[z.EndOfFileExpected=9]="EndOfFileExpected",z[z.InvalidCommentToken=10]="InvalidCommentToken",z[z.UnexpectedEndOfComment=11]="UnexpectedEndOfComment",z[z.UnexpectedEndOfString=12]="UnexpectedEndOfString",z[z.UnexpectedEndOfNumber=13]="UnexpectedEndOfNumber",z[z.InvalidUnicode=14]="InvalidUnicode",z[z.InvalidEscapeCharacter=15]="InvalidEscapeCharacter",z[z.InvalidCharacter=16]="InvalidCharacter"})(q||(J.ParseErrorCode=q={}));function D(z){switch(z){case 1:return"InvalidSymbol";case 2:return"InvalidNumberFormat";case 3:return"PropertyNameExpected";case 4:return"ValueExpected";case 5:return"ColonExpected";case 6:return"CommaExpected";case 7:return"CloseBraceExpected";case 8:return"CloseBracketExpected";case 9:return"EndOfFileExpected";case 10:return"InvalidCommentToken";case 11:return"UnexpectedEndOfComment";case 12:return"UnexpectedEndOfString";case 13:return"UnexpectedEndOfNumber";case 14:return"InvalidUnicode";case 15:return"InvalidEscapeCharacter";case 16:return"InvalidCharacter"}return"<unknown ParseErrorCode>"}J.printParseErrorCode=D;function m(z,U,j){return X.format(z,U,j)}J.format=m;function f(z,U,j,A){return Z.setProperty(z,U,j,A)}J.modify=f;function u(z,U){let j=U.slice(0).sort((I,Y)=>{let F=I.offset-Y.offset;if(F===0)return I.length-Y.length;return F}),A=z.length;for(let I=j.length-1;I>=0;I--){let Y=j[I];if(Y.offset+Y.length<=A)z=Z.applyEdit(z,Y);else throw Error("Overlapping edit");A=Y.offset}return z}J.applyEdits=u})});var M=P(g(),1);import{watch as K}from"node:fs";import{mkdir as c,readFile as O,rename as y,writeFile as S}from"node:fs/promises";import{homedir as E}from"node:os";import{basename as l,dirname as v,join as k}from"node:path";var p="OPENCODE_TUI_PREFERENCES_FILE",x="tui-preferences.jsonc";function w(){let Q=process.env[p];if(Q)return Q;let J=process.env.OPENCODE_CONFIG_DIR||k(process.env.XDG_CONFIG_HOME||k(E(),".config"),"opencode");return k(J,x)}function N(Q){return typeof Q==="object"&&Q!==null&&!Array.isArray(Q)}async function Xz(){try{let Q=await O(w(),"utf8"),J=[],X=M.parse(Q,J,{allowTrailingComma:!0});if(J.length>0)return{};return N(X)?X:{}}catch{return{}}}var i="openai-auth",s=160,b={forceToTop:!1,order:s,startCollapsed:!1,rememberCollapsed:!0,collapsed:null,pollMs:1500,refreshDebounceMs:200,header:{label:"OPENAI",showVersion:!0},sections:{quota:!0,fallbackAccounts:!0,routing:!0,health:!0,pacing:!0},appearance:{barWidth:10,barFilledChar:"█",barEmptyChar:"░",warnThreshold:50,errorThreshold:80}};function V(Q,J){return typeof Q==="boolean"?Q:J}function B(Q,J,X,Z){if(typeof Q!=="number"||!Number.isFinite(Q))return J;return Math.min(Math.max(Math.round(Q),X),Z)}function d(Q,J,X){if(typeof Q!=="string"||Q.length===0)return J;return Q.slice(0,X)}function h(Q,J){if(typeof Q!=="string")return J;return[...Q][0]??J}function Zz(Q){let J=Q[i];if(!N(J))return structuredClone(b);let X=b,Z=N(J.header)?J.header:{},G=N(J.sections)?J.sections:{},$=N(J.appearance)?J.appearance:{},W=B($.warnThreshold,X.appearance.warnThreshold,0,99),H=Math.max(B($.errorThreshold,X.appearance.errorThreshold,0,100),Math.min(W+1,100));return{forceToTop:V(J.forceToTop,X.forceToTop),order:B(J.order,X.order,-1e4,1e4),startCollapsed:V(J.startCollapsed,X.startCollapsed),rememberCollapsed:V(J.rememberCollapsed,X.rememberCollapsed),collapsed:typeof J.collapsed==="boolean"?J.collapsed:null,pollMs:B(J.pollMs,X.pollMs,500,30000),refreshDebounceMs:B(J.refreshDebounceMs,X.refreshDebounceMs,50,5000),header:{label:d(Z.label,X.header.label,20),showVersion:V(Z.showVersion,X.header.showVersion)},sections:{quota:V(G.quota,X.sections.quota),fallbackAccounts:V(G.fallbackAccounts,X.sections.fallbackAccounts),routing:V(G.routing,X.sections.routing),health:V(G.health,X.sections.health),pacing:V(G.pacing,X.sections.pacing)},appearance:{barWidth:B($.barWidth,X.appearance.barWidth,4,40),barFilledChar:h($.barFilledChar,X.appearance.barFilledChar),barEmptyChar:h($.barEmptyChar,X.appearance.barEmptyChar),warnThreshold:W,errorThreshold:H}}}var n=-1e5;function $z(Q,J,X){let Z=Q[J];if(!N(Z))return X;if(Z.forceToTop===!0)return n+Object.keys(Q).indexOf(J);return B(Z.order,X,-1e4,1e4)}var t=`// Shared preferences for opencode TUI plugins.
|
|
2
2
|
// One top-level key per plugin (short name). See each plugin's README for
|
|
3
3
|
// its supported settings. This file is safe to hand-edit; plugins update
|
|
4
4
|
// individual keys in place and preserve comments.
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PackageVersion = "0.
|
|
1
|
+
export declare const PackageVersion = "0.6.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/opencode-openai-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -61,8 +61,8 @@
|
|
|
61
61
|
"@opencode-ai/plugin": "*"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@opentui/core": ">=0.
|
|
65
|
-
"@opentui/solid": ">=0.
|
|
64
|
+
"@opentui/core": ">=0.5.1",
|
|
65
|
+
"@opentui/solid": ">=0.5.1",
|
|
66
66
|
"ai": "^7.0.48",
|
|
67
67
|
"jsonc-parser": "^3.3.1",
|
|
68
68
|
"solid-js": "1.9.12"
|
package/src/rpc/protocol.ts
CHANGED
package/src/rpc/rpc-client.ts
CHANGED
|
@@ -6,21 +6,24 @@ export interface RpcClient {
|
|
|
6
6
|
lastReceivedId: number,
|
|
7
7
|
sessionId?: string,
|
|
8
8
|
) => Promise<RpcNotification[]>
|
|
9
|
-
apply: (request: ApplyRequest) => Promise<ApplyResult>
|
|
9
|
+
apply: (request: ApplyRequest, timeoutMs?: number) => Promise<ApplyResult>
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
export const DEFAULT_RPC_TIMEOUT_MS = 2_000
|
|
13
|
+
|
|
12
14
|
async function call<T>(
|
|
13
15
|
dir: string,
|
|
14
16
|
expectedPid: number | undefined,
|
|
15
17
|
onSelected: ((entry: PortFileEntry | null) => void) | undefined,
|
|
16
18
|
method: string,
|
|
17
19
|
params: Record<string, unknown>,
|
|
20
|
+
timeoutMs = DEFAULT_RPC_TIMEOUT_MS,
|
|
18
21
|
): Promise<T | null> {
|
|
19
22
|
const entry = await discoverPortFile(dir, expectedPid)
|
|
20
23
|
onSelected?.(entry)
|
|
21
24
|
if (!entry) return null
|
|
22
25
|
const controller = new AbortController()
|
|
23
|
-
const timer = setTimeout(() => controller.abort(),
|
|
26
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs)
|
|
24
27
|
try {
|
|
25
28
|
const res = await fetch(`http://127.0.0.1:${entry.port}/rpc/${method}`, {
|
|
26
29
|
method: 'POST',
|
|
@@ -62,7 +65,7 @@ export function createRpcClient(
|
|
|
62
65
|
)
|
|
63
66
|
return out?.messages ?? []
|
|
64
67
|
},
|
|
65
|
-
async apply(request) {
|
|
68
|
+
async apply(request, timeoutMs) {
|
|
66
69
|
const out = await call<ApplyResult>(
|
|
67
70
|
dir,
|
|
68
71
|
expectedPid,
|
|
@@ -71,6 +74,7 @@ export function createRpcClient(
|
|
|
71
74
|
{
|
|
72
75
|
...request,
|
|
73
76
|
},
|
|
77
|
+
timeoutMs,
|
|
74
78
|
)
|
|
75
79
|
return out ?? { text: 'apply failed', knobs: {} }
|
|
76
80
|
},
|
package/src/rpc/rpc-server.ts
CHANGED
|
@@ -25,7 +25,10 @@ export interface RpcServerOptions {
|
|
|
25
25
|
sweepRoot?: string
|
|
26
26
|
drain: typeof drainNotifications
|
|
27
27
|
apply: (request: ApplyRequest) => Promise<ApplyResult>
|
|
28
|
+
// Bounds handler execution via the socket inactivity timer.
|
|
28
29
|
timeoutMs?: number
|
|
30
|
+
// Bounds request delivery only (requestTimeout/headersTimeout).
|
|
31
|
+
receiptTimeoutMs?: number
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
function readBody(req: IncomingMessage): Promise<string> {
|
|
@@ -57,15 +60,32 @@ export async function startRpcServer(
|
|
|
57
60
|
options: RpcServerOptions,
|
|
58
61
|
): Promise<RpcServerHandle> {
|
|
59
62
|
const token = randomBytes(32).toString('hex')
|
|
60
|
-
|
|
63
|
+
// Two different bounds, deliberately not one value.
|
|
64
|
+
//
|
|
65
|
+
// requestTimeout/headersTimeout bound how long a client may take to DELIVER
|
|
66
|
+
// a request; they do not bound handler execution. A receipt bound of 500ms
|
|
67
|
+
// still returns 200 for a 3s handler. Over loopback with a 1 MiB body cap,
|
|
68
|
+
// seconds is already generous.
|
|
69
|
+
//
|
|
70
|
+
// The inactivity timer is the one that can kill a working handler: it
|
|
71
|
+
// destroys the socket out from under it. It must therefore outlast the
|
|
72
|
+
// slowest legitimate apply — a reset redemption is bounded by a 60s consume
|
|
73
|
+
// call — or the response is lost server-side no matter what deadline the
|
|
74
|
+
// client passed. Fast commands respond in milliseconds regardless.
|
|
75
|
+
//
|
|
76
|
+
// Collapsing these back into a single value reintroduces one of two bugs:
|
|
77
|
+
// a receipt bound that aborts a slow reset, or a socket timer that leaves
|
|
78
|
+
// every endpoint holding a dead connection for 90s.
|
|
79
|
+
const handlerTimeoutMs = options.timeoutMs ?? 90_000
|
|
80
|
+
const receiptTimeoutMs = options.receiptTimeoutMs ?? 2_000
|
|
61
81
|
const server = createServer((req, res) => {
|
|
62
|
-
req.setTimeout(
|
|
82
|
+
req.setTimeout(handlerTimeoutMs, () => {
|
|
63
83
|
req.socket.destroy()
|
|
64
84
|
})
|
|
65
85
|
void dispatch(req, res)
|
|
66
86
|
})
|
|
67
|
-
server.requestTimeout =
|
|
68
|
-
server.headersTimeout =
|
|
87
|
+
server.requestTimeout = receiptTimeoutMs
|
|
88
|
+
server.headersTimeout = receiptTimeoutMs
|
|
69
89
|
|
|
70
90
|
async function dispatch(req: IncomingMessage, res: ServerResponse) {
|
|
71
91
|
const json = (status: number, value: unknown) => {
|
package/src/sidebar-state.ts
CHANGED
|
@@ -100,6 +100,7 @@ export type ActiveRoutingMap = Record<string, ActiveRoutingEntry>
|
|
|
100
100
|
|
|
101
101
|
export interface StickyAssignment {
|
|
102
102
|
accountId: string
|
|
103
|
+
wireAccountId?: string
|
|
103
104
|
assignedAt: number
|
|
104
105
|
lastSeenAt: number
|
|
105
106
|
inputBytes: number
|
|
@@ -120,6 +121,7 @@ export interface ResolveStickyAssignmentInput {
|
|
|
120
121
|
validPinnedAccountIds: readonly string[]
|
|
121
122
|
excludeAccountIds?: readonly string[]
|
|
122
123
|
quotaCheckedAtByAccount: Readonly<Record<string, number | undefined>>
|
|
124
|
+
wireAccountIdByAccount?: Readonly<Record<string, string | undefined>>
|
|
123
125
|
choose: (
|
|
124
126
|
pendingBytes: ReadonlyMap<string, number>,
|
|
125
127
|
) => StickyAssignmentChoice | undefined
|
|
@@ -256,12 +258,14 @@ function normalizeStickyAssignments(
|
|
|
256
258
|
) {
|
|
257
259
|
continue
|
|
258
260
|
}
|
|
261
|
+
const wireAccountId = assignment.wireAccountId
|
|
259
262
|
normalized[sessionHash] = {
|
|
260
263
|
accountId: assignment.accountId,
|
|
261
264
|
assignedAt: assignment.assignedAt,
|
|
262
265
|
lastSeenAt: assignment.lastSeenAt,
|
|
263
266
|
inputBytes: assignment.inputBytes,
|
|
264
267
|
...(quotaCheckedAt === undefined ? {} : { quotaCheckedAt }),
|
|
268
|
+
...(typeof wireAccountId === 'string' ? { wireAccountId } : {}),
|
|
265
269
|
}
|
|
266
270
|
}
|
|
267
271
|
|
|
@@ -652,10 +656,32 @@ function stickyAssignmentNeedsMetadataUpdate(
|
|
|
652
656
|
assignment: StickyAssignment,
|
|
653
657
|
requestBytes: number,
|
|
654
658
|
now: number,
|
|
659
|
+
wireAccountId: string | undefined,
|
|
655
660
|
): boolean {
|
|
656
661
|
return (
|
|
657
662
|
requestBytes > assignment.inputBytes ||
|
|
658
|
-
now - assignment.lastSeenAt >= STICKY_ASSIGNMENT_LAST_SEEN_TOUCH_MS
|
|
663
|
+
now - assignment.lastSeenAt >= STICKY_ASSIGNMENT_LAST_SEEN_TOUCH_MS ||
|
|
664
|
+
(assignment.wireAccountId === undefined && wireAccountId !== undefined)
|
|
665
|
+
)
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
function hasStickyIdentityMismatch(
|
|
669
|
+
assignment: StickyAssignment,
|
|
670
|
+
wireAccountId: string | undefined,
|
|
671
|
+
): boolean {
|
|
672
|
+
// True only when both sides know their ChatGPT account and those accounts
|
|
673
|
+
// differ, which means the slot this session was placed on now holds a
|
|
674
|
+
// different account than it did at placement.
|
|
675
|
+
//
|
|
676
|
+
// An unknown on either side deliberately reports no mismatch. A session is
|
|
677
|
+
// pinned to keep its prompt cache warm, and treating missing data as a
|
|
678
|
+
// change would place sessions again for no reason and throw those caches
|
|
679
|
+
// away — worse than leaving a rare wrong pin in place until real evidence
|
|
680
|
+
// arrives.
|
|
681
|
+
return (
|
|
682
|
+
typeof assignment.wireAccountId === 'string' &&
|
|
683
|
+
typeof wireAccountId === 'string' &&
|
|
684
|
+
assignment.wireAccountId !== wireAccountId
|
|
659
685
|
)
|
|
660
686
|
}
|
|
661
687
|
|
|
@@ -692,9 +718,11 @@ function readonlyPendingBytes(
|
|
|
692
718
|
function pendingBytesForAssignments(
|
|
693
719
|
assignments: StickyAssignmentMap | undefined,
|
|
694
720
|
quotaCheckedAtByAccount: Readonly<Record<string, number | undefined>>,
|
|
721
|
+
excludedSessionHash?: string,
|
|
695
722
|
): ReadonlyMap<string, number> {
|
|
696
723
|
const pendingBytes = new Map<string, number>()
|
|
697
|
-
for (const assignment of Object.
|
|
724
|
+
for (const [sessionHash, assignment] of Object.entries(assignments ?? {})) {
|
|
725
|
+
if (sessionHash === excludedSessionHash) continue
|
|
698
726
|
if (
|
|
699
727
|
assignment.quotaCheckedAt !==
|
|
700
728
|
quotaCheckedAtByAccount[assignment.accountId]
|
|
@@ -1178,10 +1206,15 @@ export async function resolveSidebarStickyAssignment(
|
|
|
1178
1206
|
excludedAccountIds,
|
|
1179
1207
|
input.now,
|
|
1180
1208
|
) &&
|
|
1209
|
+
!hasStickyIdentityMismatch(
|
|
1210
|
+
existing,
|
|
1211
|
+
input.wireAccountIdByAccount?.[existing.accountId],
|
|
1212
|
+
) &&
|
|
1181
1213
|
!stickyAssignmentNeedsMetadataUpdate(
|
|
1182
1214
|
existing,
|
|
1183
1215
|
input.requestBytes,
|
|
1184
1216
|
input.now,
|
|
1217
|
+
input.wireAccountIdByAccount?.[existing.accountId],
|
|
1185
1218
|
)
|
|
1186
1219
|
) {
|
|
1187
1220
|
return existing
|
|
@@ -1202,23 +1235,38 @@ export async function resolveSidebarStickyAssignment(
|
|
|
1202
1235
|
stickyAssignments,
|
|
1203
1236
|
)
|
|
1204
1237
|
const current = stickyAssignments?.[sessionHash]
|
|
1238
|
+
const currentIdentityMismatch =
|
|
1239
|
+
current !== undefined &&
|
|
1240
|
+
hasStickyIdentityMismatch(
|
|
1241
|
+
current,
|
|
1242
|
+
input.wireAccountIdByAccount?.[current.accountId],
|
|
1243
|
+
)
|
|
1205
1244
|
if (
|
|
1206
1245
|
isValidStickyAssignment(
|
|
1207
1246
|
current,
|
|
1208
1247
|
validPinnedAccountIds,
|
|
1209
1248
|
excludedAccountIds,
|
|
1210
1249
|
input.now,
|
|
1211
|
-
)
|
|
1250
|
+
) &&
|
|
1251
|
+
!currentIdentityMismatch
|
|
1212
1252
|
) {
|
|
1213
1253
|
const metadataNeedsUpdate = stickyAssignmentNeedsMetadataUpdate(
|
|
1214
1254
|
current,
|
|
1215
1255
|
input.requestBytes,
|
|
1216
1256
|
input.now,
|
|
1257
|
+
input.wireAccountIdByAccount?.[current.accountId],
|
|
1217
1258
|
)
|
|
1218
1259
|
const assignment = metadataNeedsUpdate
|
|
1219
1260
|
? {
|
|
1220
1261
|
...current,
|
|
1221
1262
|
inputBytes: Math.max(current.inputBytes, input.requestBytes),
|
|
1263
|
+
...(current.wireAccountId === undefined &&
|
|
1264
|
+
input.wireAccountIdByAccount?.[current.accountId] !== undefined
|
|
1265
|
+
? {
|
|
1266
|
+
wireAccountId:
|
|
1267
|
+
input.wireAccountIdByAccount?.[current.accountId],
|
|
1268
|
+
}
|
|
1269
|
+
: {}),
|
|
1222
1270
|
...(input.now - current.lastSeenAt >=
|
|
1223
1271
|
STICKY_ASSIGNMENT_LAST_SEEN_TOUCH_MS
|
|
1224
1272
|
? { lastSeenAt: input.now }
|
|
@@ -1241,6 +1289,12 @@ export async function resolveSidebarStickyAssignment(
|
|
|
1241
1289
|
pendingBytesForAssignments(
|
|
1242
1290
|
stickyAssignments,
|
|
1243
1291
|
input.quotaCheckedAtByAccount,
|
|
1292
|
+
// Pending bytes weigh how much traffic each account is already
|
|
1293
|
+
// committed to, so the session being placed must not weigh its own
|
|
1294
|
+
// stale entry: that entry belongs to the account it is moving off,
|
|
1295
|
+
// and counting it would bias placement away from a perfectly good
|
|
1296
|
+
// destination. Other sessions' entries still count.
|
|
1297
|
+
currentIdentityMismatch ? sessionHash : undefined,
|
|
1244
1298
|
),
|
|
1245
1299
|
)
|
|
1246
1300
|
if (!choice) {
|
|
@@ -1261,6 +1315,11 @@ export async function resolveSidebarStickyAssignment(
|
|
|
1261
1315
|
...(choice.quotaCheckedAt === undefined
|
|
1262
1316
|
? {}
|
|
1263
1317
|
: { quotaCheckedAt: choice.quotaCheckedAt }),
|
|
1318
|
+
...(input.wireAccountIdByAccount?.[choice.accountId] === undefined
|
|
1319
|
+
? {}
|
|
1320
|
+
: {
|
|
1321
|
+
wireAccountId: input.wireAccountIdByAccount?.[choice.accountId],
|
|
1322
|
+
}),
|
|
1264
1323
|
}
|
|
1265
1324
|
return {
|
|
1266
1325
|
...latest,
|