Skip to content

Commit 3968217

Browse files
committed
updated n stuff
1 parent 4aec3ca commit 3968217

7 files changed

Lines changed: 265 additions & 90 deletions

File tree

build.zig.zon

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
.minimum_zig_version = "0.16.0-dev",
66
.dependencies = .{
77
.webui = .{
8-
.url = "git+https://github.com/SmallThingz/webui#bb6c02da7e85d14af311bebc12d224b7eb89bda0",
9-
.hash = "webui-0.0.0-NV0cf7JIDAClGgztfCK1Vh6HmAOxwcNPhFzg_THjQHg6",
8+
.url = "git+https://github.com/SmallThingz/webui#3d17287c513c3a90a658abc732516f24a8f21124",
9+
.hash = "webui-0.0.0-NV0cf75IDACe_AfUVI3lVSXaPPo9KUYl3Ub8OVpeAs7l",
1010
},
1111
},
1212
.paths = .{

frontend/src/lib/codexAuth.ts

Lines changed: 154 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ type WebuiRpcBridge = {
8686
cm_rpc: (request: Record<string, unknown>) => Promise<unknown> | unknown;
8787
};
8888

89+
type WebuiFrontendBridge = {
90+
register: (name: string, handler: (...args: unknown[]) => unknown) => boolean;
91+
};
92+
8993
type BackendApis = {
9094
invoke: <T>(command: string, payload?: Record<string, unknown>) => Promise<T>;
9195
openUrl: (url: string) => Promise<void>;
@@ -94,6 +98,14 @@ type BackendApis = {
9498
let backendApisPromise: Promise<BackendApis> | null = null;
9599
let pendingBrowserLogin: PendingBrowserLogin | null = null;
96100
const inflightRefreshByAccountId = new Map<string, Promise<CreditsInfo>>();
101+
const pendingUsageRefreshByAccountId = new Map<
102+
string,
103+
{
104+
promise: Promise<CreditsInfo>;
105+
resolve: (credits: CreditsInfo) => void;
106+
}
107+
>();
108+
let usageRefreshPushRegistered = false;
97109

98110
// Now epoch.
99111
const nowEpoch = (): number => Math.floor(Date.now() / 1000);
@@ -399,6 +411,115 @@ const waitForWebuiBridge = async (): Promise<WebuiRpcBridge> => {
399411
throw new Error("WebUI bridge is unavailable (webuiRpc.cm_rpc missing).");
400412
};
401413

414+
// Returns get webui frontend push bridge.
415+
const getWebuiFrontendBridge = (): WebuiFrontendBridge | null => {
416+
const bridge = (globalThis as { webuiFrontend?: WebuiFrontendBridge }).webuiFrontend;
417+
if (!bridge || typeof bridge.register !== "function") {
418+
return null;
419+
}
420+
return bridge;
421+
};
422+
423+
// Wait for webui frontend push bridge.
424+
const waitForWebuiFrontendBridge = async (): Promise<WebuiFrontendBridge> => {
425+
const immediate = getWebuiFrontendBridge();
426+
if (immediate) {
427+
return immediate;
428+
}
429+
430+
await waitForWebuiBridge();
431+
for (let attempt = 0; attempt < 40; attempt += 1) {
432+
await new Promise((resolve) => window.setTimeout(resolve, 25));
433+
const bridge = getWebuiFrontendBridge();
434+
if (bridge) {
435+
return bridge;
436+
}
437+
}
438+
439+
throw new Error("WebUI frontend push bridge is unavailable.");
440+
};
441+
442+
// Clears pending usage refresh waiter.
443+
const clearPendingUsageRefresh = (accountId: string) => {
444+
pendingUsageRefreshByAccountId.delete(accountId);
445+
};
446+
447+
// Creates create pending usage refresh waiter.
448+
const createPendingUsageRefresh = (accountId: string) => {
449+
const existing = pendingUsageRefreshByAccountId.get(accountId);
450+
if (existing) {
451+
return existing;
452+
}
453+
454+
let resolvePromise: (credits: CreditsInfo) => void = () => {};
455+
const promise = new Promise<CreditsInfo>((resolve) => {
456+
resolvePromise = resolve;
457+
});
458+
459+
const pending = {
460+
promise,
461+
resolve: (credits: CreditsInfo) => {
462+
clearPendingUsageRefresh(accountId);
463+
resolvePromise(credits);
464+
},
465+
};
466+
pendingUsageRefreshByAccountId.set(accountId, pending);
467+
return pending;
468+
};
469+
470+
// Parses as pushed usage refresh result.
471+
const asPushedUsageRefreshResult = (value: unknown): { accountId: string; credits: CreditsInfo } | null => {
472+
let parsedValue = value;
473+
if (typeof parsedValue === "string") {
474+
try {
475+
parsedValue = JSON.parse(parsedValue) as unknown;
476+
} catch {
477+
return null;
478+
}
479+
}
480+
481+
const parsed = asRecord(parsedValue);
482+
if (!parsed || typeof parsed.accountId !== "string") {
483+
return null;
484+
}
485+
486+
const credits = asCreditsInfo(parsed.credits);
487+
if (!credits) {
488+
return null;
489+
}
490+
491+
return {
492+
accountId: parsed.accountId,
493+
credits,
494+
};
495+
};
496+
497+
// Registers the pushed usage-refresh completion handler exposed to backend websocket RPC.
498+
const ensureUsageRefreshPushHandler = async (): Promise<void> => {
499+
if (usageRefreshPushRegistered) {
500+
return;
501+
}
502+
503+
const frontendBridge = await waitForWebuiFrontendBridge();
504+
if (usageRefreshPushRegistered) {
505+
return;
506+
}
507+
508+
frontendBridge.register("cm.handleUsageRefreshCompletion", (payload: unknown) => {
509+
const pushed = asPushedUsageRefreshResult(payload);
510+
if (!pushed) {
511+
return null;
512+
}
513+
514+
const pending = pendingUsageRefreshByAccountId.get(pushed.accountId);
515+
if (pending) {
516+
pending.resolve(pushed.credits);
517+
}
518+
return null;
519+
});
520+
usageRefreshPushRegistered = true;
521+
};
522+
402523
// Sends a request through the injected webui bridge and decodes the backend response.
403524
const callBridge = async <T>(op: string, payload: Record<string, unknown> = {}): Promise<T> => {
404525
const request = { op, ...payload };
@@ -410,13 +531,16 @@ const callBridge = async <T>(op: string, payload: Record<string, unknown> = {}):
410531
// Loads load backend apis.
411532
const loadBackendApis = async (): Promise<BackendApis> => {
412533
if (!backendApisPromise) {
413-
backendApisPromise = Promise.resolve({
414-
invoke: async <T>(command: string, payload: Record<string, unknown> = {}) =>
415-
callBridge<T>(`invoke:${command}`, payload),
416-
openUrl: async (url: string) => {
417-
await callBridge<null>("shell:open_url", { url });
418-
},
419-
});
534+
backendApisPromise = (async () => {
535+
await ensureUsageRefreshPushHandler();
536+
return {
537+
invoke: async <T>(command: string, payload: Record<string, unknown> = {}) =>
538+
callBridge<T>(`invoke:${command}`, payload),
539+
openUrl: async (url: string) => {
540+
await callBridge<null>("shell:open_url", { url });
541+
},
542+
};
543+
})();
420544
}
421545

422546
return backendApisPromise;
@@ -748,33 +872,35 @@ export const getRemainingCreditsForAccount = async (id: string): Promise<Credits
748872
const pending = (async (): Promise<CreditsInfo> => {
749873
try {
750874
const tauri = await loadBackendApis();
875+
const pushedRefresh = createPendingUsageRefresh(id);
751876

752-
while (true) {
753-
let payload: unknown;
754-
try {
755-
payload = await tauri.invoke<unknown>("refresh_account_usage", { accountId: id });
756-
} catch (invokeError) {
757-
const rendered = invokeError instanceof Error ? invokeError.message : String(invokeError);
758-
return errorCreditsInfo(rendered);
759-
}
877+
let payload: unknown;
878+
try {
879+
payload = await tauri.invoke<unknown>("refresh_account_usage", { accountId: id });
880+
} catch (invokeError) {
881+
clearPendingUsageRefresh(id);
882+
const rendered = invokeError instanceof Error ? invokeError.message : String(invokeError);
883+
return errorCreditsInfo(rendered);
884+
}
760885

761-
const record = asRecord(payload);
762-
if (record) {
763-
const inFlight = valueAsBoolean(record.inFlight) ?? false;
764-
if (inFlight) {
765-
await new Promise((resolve) => window.setTimeout(resolve, 500));
766-
continue;
767-
}
768-
769-
const credits = asCreditsInfo(record.credits);
770-
if (credits) {
771-
return credits;
772-
}
886+
const record = asRecord(payload);
887+
if (record) {
888+
const inFlight = valueAsBoolean(record.inFlight) ?? false;
889+
if (inFlight) {
890+
return await pushedRefresh.promise;
773891
}
774892

775-
return errorCreditsInfo("Usage refresh returned an invalid response.");
893+
clearPendingUsageRefresh(id);
894+
const credits = asCreditsInfo(record.credits);
895+
if (credits) {
896+
return credits;
897+
}
776898
}
899+
900+
clearPendingUsageRefresh(id);
901+
return errorCreditsInfo("Usage refresh returned an invalid response.");
777902
} catch (refreshError) {
903+
clearPendingUsageRefresh(id);
778904
const rendered = refreshError instanceof Error ? refreshError.message : String(refreshError);
779905
return errorCreditsInfo(rendered);
780906
}

frontend/vite.config.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,6 @@ function inlineHtmlAssetsPlugin(): Plugin {
4343
let html = readFileSync(indexPath, "utf8");
4444
const filesToDelete = new Set<string>();
4545

46-
if (!html.includes('src="/webui.js"') && !html.includes("src='/webui.js'")) {
47-
html = html.replace("</head>", ' <script src="/webui.js"></script>\n</head>');
48-
}
49-
5046
html = html.replace(/<link\b[^>]*>/g, (tag) => {
5147
if (!/\brel=(["'])stylesheet\1/.test(tag)) {
5248
return tag;

src/embedded_index.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ fn canonicalizeBootstrapJson(allocator: std.mem.Allocator, bootstrap_json: []con
154154

155155
/// Injects the webui bridge script tag when the built frontend omitted it.
156156
fn ensureModuleWebUiScript(allocator: std.mem.Allocator, html: []const u8) ![]u8 {
157-
const script_tag = "<script src=\"/webui_bridge.js\"></script>";
157+
const script_tag = "<script src=\"/webui/bridge.js\"></script>";
158158

159159
if (std.mem.indexOf(u8, html, script_tag) != null) {
160160
return allocator.dupe(u8, html);

src/main.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ fn runModeService(
134134
},
135135
});
136136
defer service.deinit();
137+
rpc_webui.setService(&service);
138+
defer rpc_webui.setService(null);
137139

138140
if (strict_native_only) {
139141
try ensureNativeWebviewAvailable(&service, allocator);

0 commit comments

Comments
 (0)