@cortexkit/opencode-openai-auth 0.1.2 → 0.1.4
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 +27 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +14 -0
- package/dist/commands.d.ts +49 -0
- package/dist/core/accounts.d.ts +227 -0
- package/dist/core/atomic-write.d.ts +1 -0
- package/dist/core/backoff.d.ts +53 -0
- package/dist/core/cachekeep.d.ts +95 -0
- package/dist/core/oauth.d.ts +126 -0
- package/dist/core/provider.d.ts +59 -0
- package/dist/core/quota-manager.d.ts +136 -0
- package/dist/core/refresh-all-quota.d.ts +52 -0
- package/dist/core/refresh-file-lock.d.ts +15 -0
- package/dist/index-0s5tftne.js +3 -0
- package/dist/index-6qjg8qfw.js +2 -0
- package/dist/index-dz7tcrxb.js +2 -0
- package/dist/index-eks9xm6s.js +1 -0
- package/dist/index-hrg6bdgb.js +2 -0
- package/dist/index-jshhys8t.js +2 -0
- package/dist/index-m9dtkj3q.js +92 -0
- package/dist/index-sgfft5d3.js +2 -0
- package/dist/index.d.ts +3 -19
- package/dist/index.js +118 -124
- package/dist/logger.d.ts +11 -1
- package/dist/quota-normalize-qh5n0p7v.js +1 -0
- package/dist/quota-normalize.d.ts +33 -0
- package/dist/rpc/notifications.d.ts +5 -0
- package/dist/rpc/port-file.d.ts +12 -0
- package/dist/rpc/port-file.js +1 -0
- package/dist/rpc/protocol.d.ts +20 -0
- package/dist/rpc/protocol.js +0 -0
- package/dist/rpc/rpc-client.d.ts +7 -0
- package/dist/rpc/rpc-client.js +1 -0
- package/dist/rpc/rpc-dir.d.ts +3 -0
- package/dist/rpc/rpc-dir.js +1 -0
- package/dist/rpc/rpc-server.d.ts +13 -0
- package/dist/sidebar-state.d.ts +83 -0
- package/dist/sidebar-state.js +1 -0
- package/dist/token-util-sqwtzxz0.js +1 -0
- package/dist/token-zzzp7w2y.js +1 -0
- package/dist/tui/command-dialogs.d.ts +14 -0
- package/dist/tui-preferences.d.ts +40 -0
- package/dist/tui-preferences.js +6 -0
- package/dist/tui.d.ts +9 -0
- package/dist/util/open-url.d.ts +6 -0
- package/dist/version.d.ts +1 -1
- package/dist/ws-pool.d.ts +8 -0
- package/dist/ws.d.ts +4 -0
- package/package.json +25 -4
- package/src/rpc/notifications.ts +54 -0
- package/src/rpc/port-file.ts +70 -0
- package/src/rpc/protocol.ts +31 -0
- package/src/rpc/rpc-client.ts +78 -0
- package/src/rpc/rpc-dir.ts +22 -0
- package/src/rpc/rpc-server.ts +129 -0
- package/src/sidebar-state.ts +312 -0
- package/src/tui/command-dialogs.tsx +742 -0
- package/src/tui-preferences.ts +328 -0
- package/src/tui.tsx +837 -0
- package/dist/index-6z8w05g9.js +0 -2
- package/dist/index-xqe8xm96.js +0 -2
- package/dist/token-util-fs3z8kkp.js +0 -1
- package/dist/token-yyjm093c.js +0 -1
package/dist/logger.d.ts
CHANGED
|
@@ -1 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
type Level = 'error' | 'warn' | 'info' | 'debug' | 'trace';
|
|
2
|
+
export declare function setLogLevel(l: Level | undefined): void;
|
|
3
|
+
export declare function createLogger(channel: string): {
|
|
4
|
+
error: (m: string, d?: unknown) => void;
|
|
5
|
+
warn: (m: string, d?: unknown) => void;
|
|
6
|
+
info: (m: string, d?: unknown) => void;
|
|
7
|
+
debug: (m: string, d?: unknown) => void;
|
|
8
|
+
trace: (m: string, d?: unknown) => void;
|
|
9
|
+
};
|
|
10
|
+
export declare function flushForTest(): Promise<void>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c as a,d as b,e as c,f as d}from"./index-sgfft5d3.js";import"./index-hrg6bdgb.js";export{a as toResetIso,c as normalizeWsFrame,d as normalizeWham,b as normalizeQuotaHeaders};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { OAuthQuotaSnapshot } from './core/accounts.ts';
|
|
2
|
+
export declare function toResetIso(raw: string | number | undefined): string | undefined;
|
|
3
|
+
export declare function normalizeQuotaHeaders(h: Headers): OAuthQuotaSnapshot;
|
|
4
|
+
interface WsRateLimitWindow {
|
|
5
|
+
used_percent: number;
|
|
6
|
+
window_minutes: number;
|
|
7
|
+
reset_at?: string | number;
|
|
8
|
+
}
|
|
9
|
+
interface WsRateLimits {
|
|
10
|
+
primary?: WsRateLimitWindow;
|
|
11
|
+
secondary?: WsRateLimitWindow;
|
|
12
|
+
}
|
|
13
|
+
interface WsRateLimitsFrame {
|
|
14
|
+
type: string;
|
|
15
|
+
rate_limits: WsRateLimits;
|
|
16
|
+
plan_type?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function normalizeWsFrame(event: WsRateLimitsFrame): OAuthQuotaSnapshot;
|
|
19
|
+
interface WhamRateLimitWindow {
|
|
20
|
+
used_percent: number;
|
|
21
|
+
limit_window_seconds: number;
|
|
22
|
+
reset_at?: string | number;
|
|
23
|
+
}
|
|
24
|
+
interface WhamRateLimits {
|
|
25
|
+
primary_window?: WhamRateLimitWindow;
|
|
26
|
+
secondary_window?: WhamRateLimitWindow;
|
|
27
|
+
}
|
|
28
|
+
interface WhamUsageResponse {
|
|
29
|
+
plan_type?: string;
|
|
30
|
+
rate_limit: WhamRateLimits;
|
|
31
|
+
}
|
|
32
|
+
export declare function normalizeWham(json: WhamUsageResponse): OAuthQuotaSnapshot;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { OpenDialogPayload, RpcNotification } from './protocol';
|
|
2
|
+
export declare function pushNotification(payload: OpenDialogPayload, sessionId?: string): void;
|
|
3
|
+
export declare function drainNotifications(lastReceivedId?: number, sessionId?: string): RpcNotification[];
|
|
4
|
+
export declare function isTuiConnected(sessionId?: string): boolean;
|
|
5
|
+
export declare function resetNotificationsForTest(): void;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface PortFileEntry {
|
|
2
|
+
port: number;
|
|
3
|
+
token: string;
|
|
4
|
+
pid: number;
|
|
5
|
+
startedAt: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function writePortFile(dir: string, entry: {
|
|
8
|
+
port: number;
|
|
9
|
+
token: string;
|
|
10
|
+
pid: number;
|
|
11
|
+
}): Promise<string>;
|
|
12
|
+
export declare function discoverPortFile(dir: string, expectedPid?: number): Promise<PortFileEntry | null>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Z as a,_ as b}from"../index-dz7tcrxb.js";import"../index-hrg6bdgb.js";export{a as writePortFile,b as discoverPortFile};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type CommandModalName = 'openai-quota' | 'openai-account' | 'openai-routing' | 'openai-killswitch' | 'openai-dump' | 'openai-logging' | 'openai-cachekeep';
|
|
2
|
+
export interface OpenDialogPayload {
|
|
3
|
+
command: CommandModalName;
|
|
4
|
+
text: string;
|
|
5
|
+
knobs: Record<string, unknown>;
|
|
6
|
+
}
|
|
7
|
+
export interface RpcNotification {
|
|
8
|
+
id: number;
|
|
9
|
+
type: 'open-dialog';
|
|
10
|
+
payload: OpenDialogPayload;
|
|
11
|
+
sessionId?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ApplyRequest {
|
|
14
|
+
command: CommandModalName;
|
|
15
|
+
arguments: string;
|
|
16
|
+
}
|
|
17
|
+
export interface ApplyResult {
|
|
18
|
+
text: string;
|
|
19
|
+
knobs: Record<string, unknown>;
|
|
20
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type PortFileEntry } from './port-file';
|
|
2
|
+
import type { ApplyRequest, ApplyResult, RpcNotification } from './protocol';
|
|
3
|
+
export interface RpcClient {
|
|
4
|
+
pending: (lastReceivedId: number, sessionId?: string) => Promise<RpcNotification[]>;
|
|
5
|
+
apply: (request: ApplyRequest) => Promise<ApplyResult>;
|
|
6
|
+
}
|
|
7
|
+
export declare function createRpcClient(dir: string, expectedPid?: number, onSelected?: (entry: PortFileEntry | null) => void): RpcClient;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{_ as c}from"../index-dz7tcrxb.js";import"../index-hrg6bdgb.js";async function p(n,r,l,o,i){let t=await c(n,r);if(l?.(t),!t)return null;let e=new AbortController,s=setTimeout(()=>e.abort(),2000);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:e.signal});if(!a.ok)return null;return await a.json()}catch{return null}finally{clearTimeout(s)}}function y(n,r,l){let o=!1,i=(t)=>{if(o)return;o=!0,l?.(t)};return{async pending(t,e){return(await p(n,r,i,"pending-notifications",{lastReceivedId:t,sessionId:e}))?.messages??[]},async apply(t){return await p(n,r,i,"apply",{...t})??{text:"apply failed",knobs:{}}}}}export{y as createRpcClient};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{$ as a,aa as b}from"../index-eks9xm6s.js";import"../index-hrg6bdgb.js";export{a as tmpdir,b as getRpcDir};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { drainNotifications } from './notifications';
|
|
2
|
+
import type { ApplyRequest, ApplyResult } from './protocol';
|
|
3
|
+
export interface RpcServerHandle {
|
|
4
|
+
port: number;
|
|
5
|
+
token: string;
|
|
6
|
+
stop: () => Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
export interface RpcServerOptions {
|
|
9
|
+
dir: string;
|
|
10
|
+
drain: typeof drainNotifications;
|
|
11
|
+
apply: (request: ApplyRequest) => Promise<ApplyResult>;
|
|
12
|
+
}
|
|
13
|
+
export declare function startRpcServer(options: RpcServerOptions): Promise<RpcServerHandle>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export interface QuotaWindow {
|
|
2
|
+
usedPercent: number;
|
|
3
|
+
remainingPercent: number;
|
|
4
|
+
resetsAt?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface AccountQuota {
|
|
7
|
+
primary?: QuotaWindow;
|
|
8
|
+
secondary?: QuotaWindow;
|
|
9
|
+
}
|
|
10
|
+
export interface SidebarAccountState {
|
|
11
|
+
id: string;
|
|
12
|
+
label: string | undefined;
|
|
13
|
+
quota: AccountQuota | null;
|
|
14
|
+
killed: boolean;
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface SidebarState {
|
|
18
|
+
main: {
|
|
19
|
+
quota: AccountQuota | null;
|
|
20
|
+
killed: boolean;
|
|
21
|
+
quotaBackedOff?: boolean;
|
|
22
|
+
quotaBackoffUntil?: number;
|
|
23
|
+
refreshBackedOff?: boolean;
|
|
24
|
+
refreshBackoffUntil?: number;
|
|
25
|
+
};
|
|
26
|
+
fallbacks: SidebarAccountState[];
|
|
27
|
+
activeId: string | undefined;
|
|
28
|
+
route: string;
|
|
29
|
+
planType?: string;
|
|
30
|
+
credits?: number;
|
|
31
|
+
lastUpdated: number;
|
|
32
|
+
}
|
|
33
|
+
export declare function getSidebarStateFile(): string;
|
|
34
|
+
export declare const DEFAULT_SIDEBAR_STATE: SidebarState;
|
|
35
|
+
/**
|
|
36
|
+
* Normalize an arbitrary parsed value into a well-formed SidebarState.
|
|
37
|
+
*
|
|
38
|
+
* JSON.parse + `as SidebarState` is an unchecked cast — a partial, old, or
|
|
39
|
+
* malformed state file passes through and the TUI's `state().main.quota` /
|
|
40
|
+
* `state().fallbacks.filter(...)` throw at runtime. This helper guarantees
|
|
41
|
+
* every required field is present and correctly typed before the value leaves
|
|
42
|
+
* the I/O boundary, so a bad file can never crash the host TUI.
|
|
43
|
+
*/
|
|
44
|
+
export declare function normalizeSidebarState(raw: unknown): SidebarState;
|
|
45
|
+
export declare function getSidebarState(): Promise<SidebarState>;
|
|
46
|
+
/**
|
|
47
|
+
* Write sidebar state to disk, serialized through a promise chain so
|
|
48
|
+
* concurrent callers never interleave or let a stale write land last.
|
|
49
|
+
*
|
|
50
|
+
* @param state The state to persist.
|
|
51
|
+
* @param file Explicit path override — callers that bind the path at init
|
|
52
|
+
* time (e.g. the index.ts loader) pass this so late callbacks
|
|
53
|
+
* always write to the path that was current when the loader ran,
|
|
54
|
+
* even if the env changes underneath them during tests.
|
|
55
|
+
* Defaults to getSidebarStateFile() (per-call resolution).
|
|
56
|
+
*/
|
|
57
|
+
export declare function setSidebarState(state: SidebarState, file?: string): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Await all pending sidebar writes. Tests call this before restoring env
|
|
60
|
+
* vars in teardown so no in-flight write can re-resolve getSidebarStateFile()
|
|
61
|
+
* after the env is changed.
|
|
62
|
+
*/
|
|
63
|
+
export declare function drainSidebarWrites(): Promise<void>;
|
|
64
|
+
export declare function resolveActiveAccount(state: SidebarState): {
|
|
65
|
+
id: string;
|
|
66
|
+
name: string;
|
|
67
|
+
quota: AccountQuota | null;
|
|
68
|
+
killed: boolean;
|
|
69
|
+
};
|
|
70
|
+
export declare function getCollapsedQuotaSummary(quota: AccountQuota | null): {
|
|
71
|
+
primaryUsedPercent: number | null;
|
|
72
|
+
secondaryUsedPercent: number | null;
|
|
73
|
+
text: string | null;
|
|
74
|
+
};
|
|
75
|
+
export declare const FIVE_HOUR_MS: number;
|
|
76
|
+
export declare const SEVEN_DAY_MS: number;
|
|
77
|
+
export interface QuotaPacing {
|
|
78
|
+
pacePercent: number;
|
|
79
|
+
deltaPercent: number;
|
|
80
|
+
state: 'deficit' | 'reserve' | 'on-pace';
|
|
81
|
+
runsOutAt: string | null;
|
|
82
|
+
}
|
|
83
|
+
export declare function computeQuotaPacing(window: QuotaWindow, windowMs: number, now: number): QuotaPacing | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{M as a,N as b,O as c,P as d,Q as e,R as f,S as g,T as h,U as i,V as j,W as k}from"./index-jshhys8t.js";import"./index-0s5tftne.js";import"./index-hrg6bdgb.js";export{e as setSidebarState,g as resolveActiveAccount,c as normalizeSidebarState,a as getSidebarStateFile,d as getSidebarState,h as getCollapsedQuotaSummary,f as drainSidebarWrites,k as computeQuotaPacing,j as SEVEN_DAY_MS,i as FIVE_HOUR_MS,b as DEFAULT_SIDEBAR_STATE};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{b as a}from"./index-6qjg8qfw.js";import"./index-hrg6bdgb.js";export default a();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as U,b as V}from"./index-6qjg8qfw.js";import{ca as R}from"./index-hrg6bdgb.js";var E=R((M,Q)=>{var{defineProperty:K,getOwnPropertyDescriptor:W,getOwnPropertyNames:X}=Object,Y=Object.prototype.hasOwnProperty,Z=(q,v)=>{for(var z in v)K(q,z,{get:v[z],enumerable:!0})},$=(q,v,z,F)=>{if(v&&typeof v==="object"||typeof v==="function"){for(let G of X(v))if(!Y.call(q,G)&&G!==z)K(q,G,{get:()=>v[G],enumerable:!(F=W(v,G))||F.enumerable})}return q},A=(q)=>$(K({},"__esModule",{value:!0}),q),L={};Z(L,{refreshToken:()=>D});Q.exports=A(L);var H=U(),B=V();async function D(){let{projectId:q,teamId:v}=(0,B.findProjectInfo)(),z=(0,B.loadToken)(q);if(!z||(0,B.isExpired)((0,B.getTokenPayload)(z.token))){let F=await(0,B.getVercelCliToken)();if(!F)throw new H.VercelOidcTokenError("Failed to refresh OIDC token: Log in to Vercel CLI and link your project with `vc link`");if(!q)throw new H.VercelOidcTokenError("Failed to refresh OIDC token: Try re-linking your project with `vc link`");if(z=await(0,B.getVercelOidcToken)(F,q,v),!z)throw new H.VercelOidcTokenError("Failed to refresh OIDC token");(0,B.saveToken)(z,q)}process.env.VERCEL_OIDC_TOKEN=z.token;return}});export default E();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
import type { TuiPluginApi } from '@opencode-ai/plugin/tui';
|
|
3
|
+
import type { OpenDialogPayload } from '../rpc/protocol.js';
|
|
4
|
+
type ApplyFn = (command: OpenDialogPayload['command'], args: string) => Promise<{
|
|
5
|
+
text: string;
|
|
6
|
+
knobs: Record<string, unknown>;
|
|
7
|
+
}>;
|
|
8
|
+
export declare function buildCachekeepDialogOptions(payload: OpenDialogPayload): {
|
|
9
|
+
title: string;
|
|
10
|
+
value: string;
|
|
11
|
+
description: string;
|
|
12
|
+
}[];
|
|
13
|
+
export declare function openCommandDialog(api: TuiPluginApi, payload: OpenDialogPayload, apply: ApplyFn): void;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export declare const TUI_PREFS_FILE_ENV = "OPENCODE_TUI_PREFERENCES_FILE";
|
|
2
|
+
export declare function getTuiPreferencesFile(): string;
|
|
3
|
+
export declare function readTuiPreferencesFile(): Promise<Record<string, unknown>>;
|
|
4
|
+
export declare const PLUGIN_KEY = "openai-auth";
|
|
5
|
+
export declare const DEFAULT_SLOT_ORDER = 160;
|
|
6
|
+
export interface OpenaiAuthTuiPrefs {
|
|
7
|
+
forceToTop: boolean;
|
|
8
|
+
order: number;
|
|
9
|
+
startCollapsed: boolean;
|
|
10
|
+
rememberCollapsed: boolean;
|
|
11
|
+
collapsed: boolean | null;
|
|
12
|
+
pollMs: number;
|
|
13
|
+
refreshDebounceMs: number;
|
|
14
|
+
header: {
|
|
15
|
+
label: string;
|
|
16
|
+
showVersion: boolean;
|
|
17
|
+
};
|
|
18
|
+
sections: {
|
|
19
|
+
quota: boolean;
|
|
20
|
+
fallbackAccounts: boolean;
|
|
21
|
+
routing: boolean;
|
|
22
|
+
health: boolean;
|
|
23
|
+
pacing: boolean;
|
|
24
|
+
};
|
|
25
|
+
appearance: {
|
|
26
|
+
barWidth: number;
|
|
27
|
+
barFilledChar: string;
|
|
28
|
+
barEmptyChar: string;
|
|
29
|
+
warnThreshold: number;
|
|
30
|
+
errorThreshold: number;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export type AppearancePrefs = OpenaiAuthTuiPrefs['appearance'];
|
|
34
|
+
export declare const DEFAULT_PREFS: OpenaiAuthTuiPrefs;
|
|
35
|
+
export declare function resolveOpenaiAuthPrefs(root: Record<string, unknown>): OpenaiAuthTuiPrefs;
|
|
36
|
+
export declare function computeEffectiveOrder(root: Record<string, unknown>, pluginKey: string, defaultOrder: number): number;
|
|
37
|
+
type JsonValue = string | number | boolean | null;
|
|
38
|
+
export declare function queueTuiPreferenceUpdate(pluginKey: string, path: string[], value: JsonValue): Promise<void>;
|
|
39
|
+
export declare function watchTuiPreferences(onChange: () => void): () => void;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import{ba as P,ca as C,ea as _}from"./index-hrg6bdgb.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
|
+
// One top-level key per plugin (short name). See each plugin's README for
|
|
3
|
+
// its supported settings. This file is safe to hand-edit; plugins update
|
|
4
|
+
// individual keys in place and preserve comments.
|
|
5
|
+
{}
|
|
6
|
+
`;async function a(Q,J,X){let Z=w();await c(v(Z),{recursive:!0});let G;try{G=await O(Z,"utf8")}catch{G=""}if(G.trim()==="")G=t;let $=M.modify(G,[Q,...J],X,{formattingOptions:{insertSpaces:!0,tabSize:2}}),W=M.applyEdits(G,$),H=`${Z}.${process.pid}.tmp`;await S(H,W,"utf8"),await y(H,Z)}var R=Promise.resolve();function Gz(Q,J,X){return R=R.then(()=>a(Q,J,X)).catch(()=>{}),R}var o=150;function Hz(Q){let J=w(),X=l(J),Z=null,G=null;O(J,"utf8").then(($)=>{if(G===null)G=$}).catch(()=>{});try{let $=K(v(J),(W,H)=>{let q=H===X||H?.startsWith(`${X}.`)&&H.endsWith(".tmp");if(H!=null&&!q)return;if(Z)clearTimeout(Z);Z=setTimeout(()=>{Z=null,O(J,"utf8").catch(()=>null).then((D)=>{if(D===null)return;if(D===G)return;G=D,Q()})},o)});return()=>{if(Z)clearTimeout(Z);$.close()}}catch{return()=>{}}}export{Hz as watchTuiPreferences,Zz as resolveOpenaiAuthPrefs,Xz as readTuiPreferencesFile,Gz as queueTuiPreferenceUpdate,w as getTuiPreferencesFile,$z as computeEffectiveOrder,p as TUI_PREFS_FILE_ENV,i as PLUGIN_KEY,s as DEFAULT_SLOT_ORDER,b as DEFAULT_PREFS};
|
package/dist/tui.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
import type { TuiPluginModule } from '@opencode-ai/plugin/tui';
|
|
3
|
+
import { type SidebarState } from './sidebar-state.js';
|
|
4
|
+
export declare function formatResetIn(resetsAt: string | undefined): string;
|
|
5
|
+
export declare function readStateFromFile(): Promise<SidebarState>;
|
|
6
|
+
declare const plugin: TuiPluginModule & {
|
|
7
|
+
id: string;
|
|
8
|
+
};
|
|
9
|
+
export default plugin;
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PackageVersion = "0.1.
|
|
1
|
+
export declare const PackageVersion = "0.1.4";
|
package/dist/ws-pool.d.ts
CHANGED
|
@@ -10,6 +10,14 @@ export interface CreateWebSocketFetchOptions {
|
|
|
10
10
|
firstEventGraceMs?: number;
|
|
11
11
|
/** Use the hand-rolled raw TCP/TLS WebSocket client instead of native WebSocket. */
|
|
12
12
|
rawWebSocket?: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Push per-turn quota from a codex.rate_limits in-band frame.
|
|
15
|
+
*
|
|
16
|
+
* Receives the snapshot plus the per-request account identity (access token
|
|
17
|
+
* and accountId) captured at send time so the frame is attributed to the
|
|
18
|
+
* connection's own account, not a shared mutable global.
|
|
19
|
+
*/
|
|
20
|
+
onQuota?: (s: Record<string, unknown>, accessToken: string, accountId: string | undefined) => void;
|
|
13
21
|
}
|
|
14
22
|
interface PoolEntry {
|
|
15
23
|
socket?: WebSocket;
|
package/dist/ws.d.ts
CHANGED
|
@@ -20,6 +20,10 @@ export interface StreamResponsesWebSocketOptions {
|
|
|
20
20
|
onRetryableTerminal?: (event: Record<string, unknown>) => Promise<WebSocket | undefined>;
|
|
21
21
|
onConnectionInvalid?: (error: ResponseStreamError) => void;
|
|
22
22
|
onAbort?: (error: Error) => void;
|
|
23
|
+
/** Push per-turn quota from a codex.rate_limits in-band frame. */
|
|
24
|
+
onQuota?: (s: Record<string, unknown>) => void;
|
|
25
|
+
/** Display+classify only: called when response.failed carries a rate_limit_reached_type. */
|
|
26
|
+
onRateLimitReached?: (window: string) => void;
|
|
23
27
|
}
|
|
24
28
|
export interface WrappedError {
|
|
25
29
|
status: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/opencode-openai-auth",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -10,25 +10,42 @@
|
|
|
10
10
|
},
|
|
11
11
|
"main": "./dist/index.js",
|
|
12
12
|
"types": "./dist/index.d.ts",
|
|
13
|
+
"bin": {
|
|
14
|
+
"openai-auth": "./dist/cli.js"
|
|
15
|
+
},
|
|
13
16
|
"exports": {
|
|
14
17
|
".": {
|
|
15
18
|
"import": "./dist/index.js",
|
|
16
19
|
"types": "./dist/index.d.ts"
|
|
20
|
+
},
|
|
21
|
+
"./tui": {
|
|
22
|
+
"types": "./dist/tui.d.ts",
|
|
23
|
+
"import": "./src/tui.tsx"
|
|
24
|
+
},
|
|
25
|
+
"./tui-prefs": {
|
|
26
|
+
"types": "./dist/tui-preferences.d.ts",
|
|
27
|
+
"import": "./dist/tui-preferences.js"
|
|
17
28
|
}
|
|
18
29
|
},
|
|
19
30
|
"oc-plugin": [
|
|
20
|
-
"server"
|
|
31
|
+
"server",
|
|
32
|
+
"tui"
|
|
21
33
|
],
|
|
22
34
|
"engines": {
|
|
23
35
|
"bun": "1.3.14"
|
|
24
36
|
},
|
|
25
37
|
"files": [
|
|
26
38
|
"dist",
|
|
39
|
+
"src/tui.tsx",
|
|
40
|
+
"src/tui/command-dialogs.tsx",
|
|
41
|
+
"src/sidebar-state.ts",
|
|
42
|
+
"src/tui-preferences.ts",
|
|
43
|
+
"src/rpc",
|
|
27
44
|
"README.md",
|
|
28
45
|
"LICENSE"
|
|
29
46
|
],
|
|
30
47
|
"scripts": {
|
|
31
|
-
"build": "rm -rf dist && bun build src/index.ts --outdir dist --target node --format esm --splitting --external @opencode-ai/plugin --minify && tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
48
|
+
"build": "rm -rf dist && bun build src/index.ts src/cli.ts src/sidebar-state.ts src/tui-preferences.ts src/rpc/rpc-client.ts src/rpc/port-file.ts src/rpc/protocol.ts src/rpc/rpc-dir.ts --outdir dist --target node --format esm --splitting --external @opencode-ai/plugin --minify && tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
32
49
|
"build:dev": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
33
50
|
"test": "bun test src/tests",
|
|
34
51
|
"typecheck": "tsc",
|
|
@@ -39,6 +56,10 @@
|
|
|
39
56
|
"@opencode-ai/plugin": "*"
|
|
40
57
|
},
|
|
41
58
|
"dependencies": {
|
|
42
|
-
"
|
|
59
|
+
"@opentui/core": ">=0.4.0",
|
|
60
|
+
"@opentui/solid": ">=0.4.0",
|
|
61
|
+
"ai": "^5.0.88",
|
|
62
|
+
"jsonc-parser": "^3.3.1",
|
|
63
|
+
"solid-js": "^1.9.10"
|
|
43
64
|
}
|
|
44
65
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { OpenDialogPayload, RpcNotification } from './protocol'
|
|
2
|
+
|
|
3
|
+
const QUEUE_CAP = 100
|
|
4
|
+
const TUI_CONNECTED_WINDOW_MS = 3_000
|
|
5
|
+
|
|
6
|
+
let queue: RpcNotification[] = []
|
|
7
|
+
let nextId = 1
|
|
8
|
+
let lastDrainAtAny = 0
|
|
9
|
+
const lastDrainAtBySession = new Map<string, number>()
|
|
10
|
+
|
|
11
|
+
export function pushNotification(
|
|
12
|
+
payload: OpenDialogPayload,
|
|
13
|
+
sessionId?: string,
|
|
14
|
+
): void {
|
|
15
|
+
queue.push({ id: nextId++, type: 'open-dialog', payload, sessionId })
|
|
16
|
+
if (queue.length > QUEUE_CAP) queue = queue.slice(queue.length - QUEUE_CAP)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function drainNotifications(
|
|
20
|
+
lastReceivedId = 0,
|
|
21
|
+
sessionId?: string,
|
|
22
|
+
): RpcNotification[] {
|
|
23
|
+
const now = Date.now()
|
|
24
|
+
lastDrainAtAny = now
|
|
25
|
+
if (sessionId !== undefined) lastDrainAtBySession.set(sessionId, now)
|
|
26
|
+
const matches = (n: RpcNotification) =>
|
|
27
|
+
sessionId === undefined ||
|
|
28
|
+
n.sessionId === undefined ||
|
|
29
|
+
n.sessionId === sessionId
|
|
30
|
+
if (lastReceivedId > 0) {
|
|
31
|
+
queue = queue.filter((n) => {
|
|
32
|
+
if (n.id > lastReceivedId) return true
|
|
33
|
+
if (sessionId === undefined) return false
|
|
34
|
+
return n.sessionId !== sessionId
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
return queue.filter((n) => n.id > lastReceivedId && matches(n))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function isTuiConnected(sessionId?: string): boolean {
|
|
41
|
+
const now = Date.now()
|
|
42
|
+
if (sessionId !== undefined) {
|
|
43
|
+
const at = lastDrainAtBySession.get(sessionId) ?? 0
|
|
44
|
+
return at > 0 && now - at < TUI_CONNECTED_WINDOW_MS
|
|
45
|
+
}
|
|
46
|
+
return lastDrainAtAny > 0 && now - lastDrainAtAny < TUI_CONNECTED_WINDOW_MS
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function resetNotificationsForTest(): void {
|
|
50
|
+
queue = []
|
|
51
|
+
nextId = 1
|
|
52
|
+
lastDrainAtAny = 0
|
|
53
|
+
lastDrainAtBySession.clear()
|
|
54
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mkdir,
|
|
3
|
+
readdir,
|
|
4
|
+
readFile,
|
|
5
|
+
rename,
|
|
6
|
+
unlink,
|
|
7
|
+
writeFile,
|
|
8
|
+
} from 'node:fs/promises'
|
|
9
|
+
import { join } from 'node:path'
|
|
10
|
+
|
|
11
|
+
export interface PortFileEntry {
|
|
12
|
+
port: number
|
|
13
|
+
token: string
|
|
14
|
+
pid: number
|
|
15
|
+
startedAt: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function pidAlive(pid: number): boolean {
|
|
19
|
+
try {
|
|
20
|
+
process.kill(pid, 0)
|
|
21
|
+
return true
|
|
22
|
+
} catch (error) {
|
|
23
|
+
return (error as NodeJS.ErrnoException).code === 'EPERM'
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function writePortFile(
|
|
28
|
+
dir: string,
|
|
29
|
+
entry: { port: number; token: string; pid: number },
|
|
30
|
+
): Promise<string> {
|
|
31
|
+
await mkdir(dir, { recursive: true })
|
|
32
|
+
const full: PortFileEntry = { ...entry, startedAt: Date.now() }
|
|
33
|
+
const target = join(dir, `port-${entry.pid}.json`)
|
|
34
|
+
const tmp = `${target}.${process.pid}.tmp`
|
|
35
|
+
await writeFile(tmp, JSON.stringify(full), { encoding: 'utf8', mode: 0o600 })
|
|
36
|
+
await rename(tmp, target)
|
|
37
|
+
return target
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function discoverPortFile(
|
|
41
|
+
dir: string,
|
|
42
|
+
expectedPid?: number,
|
|
43
|
+
): Promise<PortFileEntry | null> {
|
|
44
|
+
let names: string[]
|
|
45
|
+
try {
|
|
46
|
+
names = await readdir(dir)
|
|
47
|
+
} catch {
|
|
48
|
+
return null
|
|
49
|
+
}
|
|
50
|
+
const live: PortFileEntry[] = []
|
|
51
|
+
for (const name of names) {
|
|
52
|
+
if (!name.startsWith('port-') || !name.endsWith('.json')) continue
|
|
53
|
+
try {
|
|
54
|
+
const parsed = JSON.parse(
|
|
55
|
+
await readFile(join(dir, name), 'utf8'),
|
|
56
|
+
) as PortFileEntry
|
|
57
|
+
if (Number.isFinite(parsed.port)) {
|
|
58
|
+
if (pidAlive(parsed.pid)) live.push(parsed)
|
|
59
|
+
else unlink(join(dir, name)).catch(() => {})
|
|
60
|
+
}
|
|
61
|
+
} catch {}
|
|
62
|
+
}
|
|
63
|
+
if (live.length === 0) return null
|
|
64
|
+
const candidates =
|
|
65
|
+
expectedPid !== undefined && expectedPid >= 1
|
|
66
|
+
? live.filter((entry) => entry.pid === expectedPid)
|
|
67
|
+
: []
|
|
68
|
+
const entries = candidates.length > 0 ? candidates : live
|
|
69
|
+
return entries.sort((a, b) => b.startedAt - a.startedAt)[0] ?? null
|
|
70
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export type CommandModalName =
|
|
2
|
+
| 'openai-quota'
|
|
3
|
+
| 'openai-account'
|
|
4
|
+
| 'openai-routing'
|
|
5
|
+
| 'openai-killswitch'
|
|
6
|
+
| 'openai-dump'
|
|
7
|
+
| 'openai-logging'
|
|
8
|
+
| 'openai-cachekeep'
|
|
9
|
+
|
|
10
|
+
export interface OpenDialogPayload {
|
|
11
|
+
command: CommandModalName
|
|
12
|
+
text: string
|
|
13
|
+
knobs: Record<string, unknown>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface RpcNotification {
|
|
17
|
+
id: number
|
|
18
|
+
type: 'open-dialog'
|
|
19
|
+
payload: OpenDialogPayload
|
|
20
|
+
sessionId?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ApplyRequest {
|
|
24
|
+
command: CommandModalName
|
|
25
|
+
arguments: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ApplyResult {
|
|
29
|
+
text: string
|
|
30
|
+
knobs: Record<string, unknown>
|
|
31
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { discoverPortFile, type PortFileEntry } from './port-file'
|
|
2
|
+
import type { ApplyRequest, ApplyResult, RpcNotification } from './protocol'
|
|
3
|
+
|
|
4
|
+
export interface RpcClient {
|
|
5
|
+
pending: (
|
|
6
|
+
lastReceivedId: number,
|
|
7
|
+
sessionId?: string,
|
|
8
|
+
) => Promise<RpcNotification[]>
|
|
9
|
+
apply: (request: ApplyRequest) => Promise<ApplyResult>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function call<T>(
|
|
13
|
+
dir: string,
|
|
14
|
+
expectedPid: number | undefined,
|
|
15
|
+
onSelected: ((entry: PortFileEntry | null) => void) | undefined,
|
|
16
|
+
method: string,
|
|
17
|
+
params: Record<string, unknown>,
|
|
18
|
+
): Promise<T | null> {
|
|
19
|
+
const entry = await discoverPortFile(dir, expectedPid)
|
|
20
|
+
onSelected?.(entry)
|
|
21
|
+
if (!entry) return null
|
|
22
|
+
const controller = new AbortController()
|
|
23
|
+
const timer = setTimeout(() => controller.abort(), 2_000)
|
|
24
|
+
try {
|
|
25
|
+
const res = await fetch(`http://127.0.0.1:${entry.port}/rpc/${method}`, {
|
|
26
|
+
method: 'POST',
|
|
27
|
+
headers: {
|
|
28
|
+
'content-type': 'application/json',
|
|
29
|
+
authorization: `Bearer ${entry.token}`,
|
|
30
|
+
},
|
|
31
|
+
body: JSON.stringify(params),
|
|
32
|
+
signal: controller.signal,
|
|
33
|
+
})
|
|
34
|
+
if (!res.ok) return null
|
|
35
|
+
return (await res.json()) as T
|
|
36
|
+
} catch {
|
|
37
|
+
return null
|
|
38
|
+
} finally {
|
|
39
|
+
clearTimeout(timer)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createRpcClient(
|
|
44
|
+
dir: string,
|
|
45
|
+
expectedPid?: number,
|
|
46
|
+
onSelected?: (entry: PortFileEntry | null) => void,
|
|
47
|
+
): RpcClient {
|
|
48
|
+
let reportedSelection = false
|
|
49
|
+
const reportSelected = (entry: PortFileEntry | null) => {
|
|
50
|
+
if (reportedSelection) return
|
|
51
|
+
reportedSelection = true
|
|
52
|
+
onSelected?.(entry)
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
async pending(lastReceivedId, sessionId) {
|
|
56
|
+
const out = await call<{ messages: RpcNotification[] }>(
|
|
57
|
+
dir,
|
|
58
|
+
expectedPid,
|
|
59
|
+
reportSelected,
|
|
60
|
+
'pending-notifications',
|
|
61
|
+
{ lastReceivedId, sessionId },
|
|
62
|
+
)
|
|
63
|
+
return out?.messages ?? []
|
|
64
|
+
},
|
|
65
|
+
async apply(request) {
|
|
66
|
+
const out = await call<ApplyResult>(
|
|
67
|
+
dir,
|
|
68
|
+
expectedPid,
|
|
69
|
+
reportSelected,
|
|
70
|
+
'apply',
|
|
71
|
+
{
|
|
72
|
+
...request,
|
|
73
|
+
},
|
|
74
|
+
)
|
|
75
|
+
return out ?? { text: 'apply failed', knobs: {} }
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
}
|