@cortexkit/opencode-openai-auth 1.0.4 → 1.1.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/dist/bin.js +9 -8
- package/dist/{pool-lifecycle-DqKq8Eej.d.ts → codex-request-BXDXToyt.d.ts} +158 -27
- package/dist/index-0yh85bwb.js +69 -0
- package/dist/{index-jye6vccc.js → index-4c8yw54k.js} +2 -2
- package/dist/{index-k4eacw7x.js → index-7c6sxtd6.js} +2 -2
- package/dist/index-7eb3dw37.js +90 -0
- package/dist/index-8re8rprk.js +2 -0
- package/dist/{index-bgq9d21h.js → index-csky2xpg.js} +1 -1
- package/dist/index-edbdscy7.js +65 -0
- package/dist/{index-45qgkb0m.js → index-f2fmb596.js} +1 -1
- package/dist/{index-envpbt0k.js → index-j8v2xwq6.js} +1 -1
- package/dist/{index-egrty6tz.js → index-k6cydkgt.js} +2 -2
- package/dist/{index-dryydk6b.js → index-m5qmvr6c.js} +2 -2
- package/dist/index-msx2m78k.js +8 -0
- package/dist/{index-yeyp8t0k.js → index-zpm95mt5.js} +1 -1
- package/dist/index.d.ts +15 -7
- package/dist/index.js +1 -1
- package/dist/{map-CCbSZIhT.d.ts → map-Buq-GW7l.d.ts} +1 -1
- package/dist/{protocol-CSIDDBDf.d.ts → model-YRg7Uowo.d.ts} +67 -25
- package/dist/{port-file-CZ9MIdy2.d.ts → port-file-CxEpgSHY.d.ts} +1 -1
- package/dist/protocol-CDV1Pc6h.d.ts +29 -0
- package/dist/rpc/port-file.d.ts +1 -1
- package/dist/rpc/port-file.js +1 -1
- package/dist/rpc/protocol.d.ts +1 -1
- package/dist/rpc/protocol.js +1 -1
- package/dist/rpc/rpc-client.d.ts +3 -3
- package/dist/rpc/rpc-client.js +1 -1
- package/dist/rpc/rpc-dir.js +1 -1
- package/dist/{sidebar-state-BCxFpH68.d.ts → sidebar-state-DjTWUbmf.d.ts} +15 -9
- package/dist/sidebar-state.d.ts +1 -1
- package/dist/sidebar-state.js +1 -1
- package/dist/{token-vykm9bxh.js → token-rz4t3ay6.js} +1 -1
- package/dist/token-util-tpy2c7h6.js +1 -0
- package/dist/tui-preferences.js +1 -1
- package/dist/tui.d.ts +20 -2
- package/dist/v2/server.d.ts +73 -1
- package/dist/v2/server.js +3 -3
- package/package.json +2 -2
- package/src/tui-compiled/raw/shared/0332701351b3eeec-format.js +78 -42
- package/src/tui-compiled/raw/shared/0f77ebc3c914c9c6-custody.js +354 -0
- package/src/tui-compiled/raw/shared/134b008900698fe0-backoff.js +92 -0
- package/src/tui-compiled/raw/shared/169b2b4bf30909bc-consumer.js +393 -0
- package/src/tui-compiled/raw/shared/2577ec69f9973ae9-quota-normalize.js +154 -0
- package/src/tui-compiled/raw/shared/2dce6e9613993329-quota-manager.js +473 -0
- package/src/tui-compiled/raw/shared/30f5bc480f31f51c-roster.js +566 -0
- package/src/tui-compiled/raw/shared/43fc9e6b994519c7-refresh-lock.js +0 -1
- package/src/tui-compiled/raw/shared/4593a4d71edf69fd-tombstone.js +25 -0
- package/src/tui-compiled/raw/shared/4d05c2d7a531aece-oauth.js +500 -0
- package/src/tui-compiled/raw/shared/6371b24a0573d79e-any-signal.js +42 -0
- package/src/tui-compiled/raw/shared/7c140680fc81be92-interlock.js +29 -0
- package/src/tui-compiled/raw/shared/85512cc5263ba259-provider.js +87 -0
- package/src/tui-compiled/raw/shared/abd062d633e29fd2-pool-routing.js +192 -0
- package/src/tui-compiled/raw/shared/b9588ad4725c1d10-enrollment.js +806 -0
- package/src/tui-compiled/raw/shared/bfbdd0f9aae56b47-pool-quota.js +85 -0
- package/src/tui-compiled/raw/shared/c852a15cfe489d12-pool.js +0 -1
- package/src/tui-compiled/raw/shared/ca0fccaba1d87af4-errors.js +8 -0
- package/src/tui-compiled/raw/shared/d0f0b6be200311af-insula.js +152 -0
- package/src/tui-compiled/raw/shared/d5a8f1061ee2f114-index.js +1 -1
- package/src/tui-compiled/raw/shared/d9d2892a4c371301-host-slot.js +72 -0
- package/src/tui-compiled/raw/shared/dbc61cd405941828-accounts.js +1470 -0
- package/src/tui-compiled/raw/shared/e8cd4929507508d8-refresh-file-lock.js +26 -12
- package/src/tui-compiled/raw/shared/e8d3c8da776ef9cd-index.js +24 -0
- package/src/tui-compiled/raw/sidebar-state.js +41 -14
- package/src/tui-compiled/raw/tui/command-dialogs.tsx +3 -34
- package/src/tui-compiled/raw/tui/section-text.js +21 -0
- package/src/tui-compiled/raw/tui/v2-dialog.js +137 -0
- package/src/tui-compiled/raw/tui/v2.tsx +133 -0
- package/src/tui-compiled/raw/tui.tsx +146 -40
- package/src/tui-compiled/runtime/shared/0332701351b3eeec-format.js +78 -42
- package/src/tui-compiled/runtime/shared/0f77ebc3c914c9c6-custody.js +354 -0
- package/src/tui-compiled/runtime/shared/134b008900698fe0-backoff.js +92 -0
- package/src/tui-compiled/runtime/shared/169b2b4bf30909bc-consumer.js +393 -0
- package/src/tui-compiled/runtime/shared/2577ec69f9973ae9-quota-normalize.js +154 -0
- package/src/tui-compiled/runtime/shared/2dce6e9613993329-quota-manager.js +473 -0
- package/src/tui-compiled/runtime/shared/30f5bc480f31f51c-roster.js +566 -0
- package/src/tui-compiled/runtime/shared/43fc9e6b994519c7-refresh-lock.js +0 -1
- package/src/tui-compiled/runtime/shared/4593a4d71edf69fd-tombstone.js +25 -0
- package/src/tui-compiled/runtime/shared/4d05c2d7a531aece-oauth.js +500 -0
- package/src/tui-compiled/runtime/shared/6371b24a0573d79e-any-signal.js +42 -0
- package/src/tui-compiled/runtime/shared/7c140680fc81be92-interlock.js +29 -0
- package/src/tui-compiled/runtime/shared/85512cc5263ba259-provider.js +87 -0
- package/src/tui-compiled/runtime/shared/abd062d633e29fd2-pool-routing.js +192 -0
- package/src/tui-compiled/runtime/shared/b9588ad4725c1d10-enrollment.js +806 -0
- package/src/tui-compiled/runtime/shared/bfbdd0f9aae56b47-pool-quota.js +85 -0
- package/src/tui-compiled/runtime/shared/c852a15cfe489d12-pool.js +0 -1
- package/src/tui-compiled/runtime/shared/ca0fccaba1d87af4-errors.js +8 -0
- package/src/tui-compiled/runtime/shared/d0f0b6be200311af-insula.js +152 -0
- package/src/tui-compiled/runtime/shared/d5a8f1061ee2f114-index.js +1 -1
- package/src/tui-compiled/runtime/shared/d9d2892a4c371301-host-slot.js +72 -0
- package/src/tui-compiled/runtime/shared/dbc61cd405941828-accounts.js +1470 -0
- package/src/tui-compiled/runtime/shared/e8cd4929507508d8-refresh-file-lock.js +26 -12
- package/src/tui-compiled/runtime/shared/e8d3c8da776ef9cd-index.js +24 -0
- package/src/tui-compiled/runtime/sidebar-state.js +41 -14
- package/src/tui-compiled/runtime/tui/command-dialogs.js +3 -15
- package/src/tui-compiled/runtime/tui/section-text.js +21 -0
- package/src/tui-compiled/runtime/tui/v2-dialog.js +137 -0
- package/src/tui-compiled/runtime/tui/v2.js +95 -0
- package/src/tui-compiled/runtime/tui.js +255 -163
- package/dist/index-2hch7e8n.js +0 -8
- package/dist/index-5dg931q0.js +0 -65
- package/dist/index-99esnzg0.js +0 -157
- package/dist/index-9reyazwh.js +0 -2
- package/dist/index-yf6arn5w.js +0 -2
- package/dist/token-util-gmn5yaqc.js +0 -1
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { mkdir, open, readFile, rename, rm, stat
|
|
2
|
+
import { mkdir, open, readFile, rename, rm, stat } from "node:fs/promises";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { createPrivateBytes, createPrivateDirectoryExclusive, ensurePrivateDirectory, PrivateFileError, privateOperation, readPrivateText, verifyPrivate, windowsPrivateFiles, writePrivateAtomic, } from "./375d9cca2def037d-private-files.js";
|
|
5
5
|
import { LockOwnershipError, lockPathFor } from "./4825e03209d6ae3e-with-lock.js";
|
|
6
|
+
/**
|
|
7
|
+
* Create a new owner-only file and write `text` into it, failing with EEXIST if
|
|
8
|
+
* the file already exists. Writing through the handle rather than path-based
|
|
9
|
+
* `writeFile(path, …, { flag: 'wx' })` matters: Bun's path form also truncates
|
|
10
|
+
* the file it has just created, and on some Linux filesystems (XFS under load)
|
|
11
|
+
* that redundant ftruncate stalls for hundreds of milliseconds, inside every
|
|
12
|
+
* lock acquisition.
|
|
13
|
+
*/
|
|
14
|
+
async function createExclusiveText(path, text) {
|
|
15
|
+
const handle = await open(path, 'wx', 0o600);
|
|
16
|
+
try {
|
|
17
|
+
await handle.writeFile(text, 'utf8');
|
|
18
|
+
}
|
|
19
|
+
finally {
|
|
20
|
+
await handle.close();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
6
23
|
const setRefreshLockRenewalTimeout = globalThis.setTimeout.bind(globalThis);
|
|
7
24
|
const clearRefreshLockRenewalTimeout = globalThis.clearTimeout.bind(globalThis);
|
|
8
25
|
// A concurrent contender renaming the freshly-created eviction-marker directory
|
|
@@ -14,7 +31,12 @@ export function isLostMarkerRaceError(error) {
|
|
|
14
31
|
return (code === 'ENOENT' ||
|
|
15
32
|
(!windowsPrivateFiles && (code === 'EINVAL' || code === 'ENOTDIR')));
|
|
16
33
|
}
|
|
17
|
-
|
|
34
|
+
/** Filesystem leases share wall time across all callers and processes. */
|
|
35
|
+
export function acquireRefreshFileLock(options) {
|
|
36
|
+
return acquireRefreshFileLockWithClock({ ...options, now: Date.now });
|
|
37
|
+
}
|
|
38
|
+
/** @internal Clock seam for lease tests; not exported by the package's fs entry point. */
|
|
39
|
+
export async function acquireRefreshFileLockWithClock(options) {
|
|
18
40
|
const lockPath = lockPathFor(options.path, options.name);
|
|
19
41
|
const legacyOwnerPath = join(lockPath, 'owner.json');
|
|
20
42
|
const ownerId = randomUUID();
|
|
@@ -152,11 +174,7 @@ export async function acquireRefreshFileLock(options) {
|
|
|
152
174
|
if (windowsPrivateFiles)
|
|
153
175
|
await createPrivateBytes(lockPath, bytes);
|
|
154
176
|
else
|
|
155
|
-
await
|
|
156
|
-
encoding: 'utf8',
|
|
157
|
-
mode: 0o600,
|
|
158
|
-
flag: 'wx',
|
|
159
|
-
});
|
|
177
|
+
await createExclusiveText(lockPath, bytes);
|
|
160
178
|
}
|
|
161
179
|
async function leaseContention(error) {
|
|
162
180
|
const code = error.code;
|
|
@@ -304,11 +322,7 @@ export async function acquireRefreshFileLock(options) {
|
|
|
304
322
|
if (windowsPrivateFiles)
|
|
305
323
|
await createPrivateBytes(evictOwnerPath, bytes);
|
|
306
324
|
else
|
|
307
|
-
await
|
|
308
|
-
encoding: 'utf8',
|
|
309
|
-
mode: 0o600,
|
|
310
|
-
flag: 'wx',
|
|
311
|
-
});
|
|
325
|
+
await createExclusiveText(evictOwnerPath, bytes);
|
|
312
326
|
}
|
|
313
327
|
catch (error) {
|
|
314
328
|
// A competing contender can rename our just-created marker directory
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Claustrum vault custody shared by the auth plugins: scoped enrollment (run
|
|
2
|
+
// from setup), discovery of this consumer's vault accounts as rows for this
|
|
3
|
+
// package's `/routing` subpath, per-send authorization, 401 reporting, the
|
|
4
|
+
// declined-account interlock and the host-slot guard. Needs the optional
|
|
5
|
+
// peer `@cortexkit/claustrum-client`.
|
|
6
|
+
import { ClaustrumClient, } from "@cortexkit/claustrum-client";
|
|
7
|
+
import { insulaUsageTransport } from "./d0f0b6be200311af-insula.js";
|
|
8
|
+
export { ClaustrumCredentialError, } from "@cortexkit/claustrum-client";
|
|
9
|
+
export { ClaustrumConsumer, } from "./169b2b4bf30909bc-consumer.js";
|
|
10
|
+
export { ClaustrumScopedCustody, decideScopedRetryAfter401, isScopedCredentialRotation, SERVING_MARGIN_MS, } from "./0f77ebc3c914c9c6-custody.js";
|
|
11
|
+
export { ClaustrumEnrollmentManager, classifyEnrollmentError, connectClaustrumEnrollmentClient, disconnectClaustrumEnrollment, enrollmentAuthority, enrollmentName, getClaustrumEnrollmentPaths, hostEnrollmentPaths, RETRYABLE_ENROLLMENT_CODES, readClaustrumEnrollmentStatus, readClaustrumEnrollmentToken, resetClaustrumEnrollmentState, TERMINAL_ENROLLMENT_CODES, } from "./b9588ad4725c1d10-enrollment.js";
|
|
12
|
+
export { ClaustrumConsumerError, } from "./ca0fccaba1d87af4-errors.js";
|
|
13
|
+
export { assertHostSlotMatchesMode, assertNotCustodyPlaceholder, CUSTODY_PLACEHOLDER_PREFIX, classifyHostSlot, custodyPlaceholder, custodyPlaceholderKey, isCustodyPlaceholder, isCustodyPlaceholderValue, } from "./d9d2892a4c371301-host-slot.js";
|
|
14
|
+
export { acceptAccount, declineAccount, isDeclined, } from "./7c140680fc81be92-interlock.js";
|
|
15
|
+
export { acceptVaultRoute, DEFAULT_ROUTE_PREFIX, declineVaultRoute, mutateVaultRoster, projectVaultRoster, readVaultRoster, recordVaultAccountQuota, recordVaultQuota, refreshVaultRoster, resolveVaultPrimary, vaultRoutingRows, } from "./30f5bc480f31f51c-roster.js";
|
|
16
|
+
/**
|
|
17
|
+
* Connect the client that lists and fetches this consumer's vault credentials
|
|
18
|
+
* on the request path. `connectionFile` is required: this library
|
|
19
|
+
* reads no environment, so the plugin resolves the vault's connection file.
|
|
20
|
+
*/
|
|
21
|
+
export async function connectClaustrumScopedClient(options) {
|
|
22
|
+
const client = await ClaustrumClient.connect(options);
|
|
23
|
+
return Object.assign(client, { insula: insulaUsageTransport(options) });
|
|
24
|
+
}
|
|
@@ -3,6 +3,11 @@ import {
|
|
|
3
3
|
projectQuota,
|
|
4
4
|
quotaWindowName
|
|
5
5
|
} from "./shared/d5a8f1061ee2f114-index.js";
|
|
6
|
+
import { orderForPlacement } from "./shared/3e7d45a77c6ad4d5-index.js";
|
|
7
|
+
import {
|
|
8
|
+
orderedPlacement,
|
|
9
|
+
vaultMainRouteId
|
|
10
|
+
} from "./shared/abd062d633e29fd2-pool-routing.js";
|
|
6
11
|
const QUOTA_WINDOW_KEYS = ["primary", "secondary"], LEGACY_WINDOW_MINUTES = {
|
|
7
12
|
primary: 300,
|
|
8
13
|
secondary: 10080
|
|
@@ -167,11 +172,14 @@ function normalizeAccountList(raw) {
|
|
|
167
172
|
return [];
|
|
168
173
|
return raw.filter((entry) => entry !== null && typeof entry === "object" && !Array.isArray(entry) && typeof entry.id === "string").map((e) => ({
|
|
169
174
|
id: e.id,
|
|
175
|
+
...typeof e.credentialId === "string" ? { credentialId: e.credentialId } : {},
|
|
170
176
|
label: typeof e.label === "string" ? e.label : void 0,
|
|
171
177
|
...typeof e.accountId === "string" ? { accountId: e.accountId } : {},
|
|
172
178
|
quota: "quota" in e ? e.quota : null,
|
|
173
179
|
killed: typeof e.killed === "boolean" ? e.killed : !1,
|
|
174
180
|
enabled: typeof e.enabled === "boolean" ? e.enabled : !0,
|
|
181
|
+
...typeof e.routable === "boolean" ? { routable: e.routable } : {},
|
|
182
|
+
...["needs sign-in", "turned off in vault", "unavailable"].includes(e.vaultState) ? { vaultState: e.vaultState } : {},
|
|
175
183
|
...resetCreditsField(e.resetCredits)
|
|
176
184
|
}));
|
|
177
185
|
}
|
|
@@ -192,7 +200,7 @@ export function isUsableRoutingEntry(entry, accounts, now = Date.now()) {
|
|
|
192
200
|
return !1;
|
|
193
201
|
if (accounts === void 0)
|
|
194
202
|
return !0;
|
|
195
|
-
return entry.activeId === "main" || accounts.some((account) => account.enabled !== !1 && account.killed !== !0 && account.id === entry.activeId);
|
|
203
|
+
return entry.activeId === "main" || accounts.some((account) => account.enabled !== !1 && account.routable !== !1 && account.killed !== !0 && account.id === entry.activeId);
|
|
196
204
|
}
|
|
197
205
|
export function spendControlExhaustedResetAt(quota, now = Date.now()) {
|
|
198
206
|
const spendControl = quota?.spendControl;
|
|
@@ -229,28 +237,40 @@ export function resolveSessionStickyAccount(state, sessionId, now = Date.now())
|
|
|
229
237
|
const assignment = state.stickyAssignments?.[hashSidebarSessionId(sessionId)];
|
|
230
238
|
if (!assignment || assignment.lastSeenAt < now - STICKY_ASSIGNMENT_MAX_AGE_MS)
|
|
231
239
|
return;
|
|
232
|
-
if (assignment.accountId === "main")
|
|
240
|
+
if (assignment.accountId === "main" && state.vaultAccounts === void 0)
|
|
233
241
|
return state.main?.killed || isQuotaExhausted(state.main?.quota, now) ? void 0 : "main";
|
|
234
242
|
const fallback = routingAccountsOf(state).find((account) => account.id === assignment.accountId);
|
|
235
|
-
if (!fallback || fallback.enabled === !1 || fallback.killed === !0 || isQuotaExhausted(fallback.quota, now))
|
|
243
|
+
if (!fallback || fallback.enabled === !1 || fallback.routable === !1 || fallback.killed === !0 || isQuotaExhausted(fallback.quota, now))
|
|
236
244
|
return;
|
|
237
245
|
return fallback.id;
|
|
238
246
|
}
|
|
239
247
|
export function resolveSessionSidebarRouting(state, sessionId, now = Date.now()) {
|
|
240
|
-
if (!sessionId)
|
|
241
|
-
|
|
248
|
+
if (!sessionId) {
|
|
249
|
+
if (state.vaultAccounts === void 0)
|
|
250
|
+
return { activeId: state.activeId ?? "main", route: state.route };
|
|
251
|
+
const active = state.vaultAccounts.find((account) => account.id === state.activeId && account.enabled && account.routable !== !1 && !account.killed);
|
|
252
|
+
if (active && !isQuotaExhausted(active.quota, now))
|
|
253
|
+
return { activeId: active.id, route: state.route };
|
|
254
|
+
}
|
|
242
255
|
const own = sessionId ? state.activeRouting?.[sessionId] : void 0, accounts = routingAccountsOf(state), ownQuota = own?.activeId === "main" ? state.main.quota : accounts.find((account) => account.id === own?.activeId)?.quota;
|
|
243
|
-
if (own && isUsableRoutingEntry(own, accounts, now) && !isQuotaExhausted(ownQuota, now))
|
|
256
|
+
if (own && (state.vaultAccounts === void 0 || own.activeId.startsWith("vault:")) && isUsableRoutingEntry(own, accounts, now) && !isQuotaExhausted(ownQuota, now))
|
|
244
257
|
return { activeId: own.activeId, route: own.route };
|
|
245
258
|
const stickyAccountId = resolveSessionStickyAccount(state, sessionId, now);
|
|
246
259
|
if (stickyAccountId)
|
|
247
260
|
return { activeId: stickyAccountId, route: state.route };
|
|
248
|
-
const vaultMode = state.vaultAccounts !== void 0, enabledFallbacks = (vaultMode ? state
|
|
261
|
+
const vaultMode = state.vaultAccounts !== void 0, enabledFallbacks = (vaultMode ? vaultAccountsInPlacementOrder(state) : state.fallbacks).filter((account) => account.enabled && account.routable !== !1 && !account.killed), fallback = enabledFallbacks.find((account) => !isQuotaExhausted(account.quota, now)) ?? enabledFallbacks[0];
|
|
249
262
|
return {
|
|
250
|
-
activeId:
|
|
263
|
+
activeId: vaultMode ? fallback?.id ?? "vault" : state.route === "fallback-first" && fallback ? fallback.id : "main",
|
|
251
264
|
route: state.route
|
|
252
265
|
};
|
|
253
266
|
}
|
|
267
|
+
function vaultAccountsInPlacementOrder(state) {
|
|
268
|
+
const accounts = state.vaultAccounts ?? [], byId = new Map(accounts.map((account) => [account.id, account]));
|
|
269
|
+
return orderForPlacement(accounts.map((account) => account.id), state.route === "sticky-balanced" ? "roster" : orderedPlacement(state.route), vaultMainRouteId(accounts) ?? "").flatMap((id) => {
|
|
270
|
+
const account = byId.get(id);
|
|
271
|
+
return account ? [account] : [];
|
|
272
|
+
});
|
|
273
|
+
}
|
|
254
274
|
export function pruneActiveRouting(activeRouting, accounts, now = Date.now(), removedSessionId) {
|
|
255
275
|
if (!activeRouting)
|
|
256
276
|
return;
|
|
@@ -407,7 +427,14 @@ function writeMergedSidebarState(file, merge, hooks) {
|
|
|
407
427
|
if (hooks?.canWrite && !hooks.canWrite())
|
|
408
428
|
return;
|
|
409
429
|
const next = merge(latest);
|
|
410
|
-
|
|
430
|
+
if (next === void 0)
|
|
431
|
+
return;
|
|
432
|
+
if (next.vaultAccounts !== void 0) {
|
|
433
|
+
next.activeId = next.activeId?.startsWith("vault:") ? next.activeId : void 0;
|
|
434
|
+
next.activeRouting = Object.fromEntries(Object.entries(next.activeRouting ?? {}).filter(([, entry]) => entry.activeId.startsWith("vault:")));
|
|
435
|
+
next.stickyAssignments = Object.fromEntries(Object.entries(next.stickyAssignments ?? {}).filter(([, pin]) => pin.accountId.startsWith("vault:") && !next.vaultAccounts?.some((account) => account.id === pin.accountId && account.killed)));
|
|
436
|
+
}
|
|
437
|
+
return normalizeSidebarState(next);
|
|
411
438
|
}, {
|
|
412
439
|
beforeRecheck: async (...args) => {
|
|
413
440
|
await hooks?.beforeRecheck?.(...args);
|
|
@@ -493,7 +520,7 @@ function mergeQuotaByWindow(incoming, existing) {
|
|
|
493
520
|
export function setSidebarMachineState(machineState, file = getSidebarStateFile(), hooks) {
|
|
494
521
|
return enqueueSidebarWrite(async () => {
|
|
495
522
|
await writeMergedSidebarState(file, (latest) => {
|
|
496
|
-
const latestFallbacks = new Map(latest.fallbacks.map((account) => [account.id, account])), vaultMode = machineState.vaultAccounts !== void 0, mainSameIdentity = sameAccountIdentity(machineState.main.mainAccountId, latest.main.mainAccountId), mergedMainQuota = mainSameIdentity ? mergeQuotaByWindow(machineState.main.quota, latest.main.quota) : freshestQuota(machineState.main.quota, latest.main.quota), now = Date.now(), stickyAssignments = pruneStickyAssignments(latest.stickyAssignments, usableRoutingAccountIds(routingAccountsOf(machineState)), now);
|
|
523
|
+
const latestFallbacks = new Map(latest.fallbacks.map((account) => [account.id, account])), vaultMode = machineState.vaultAccounts !== void 0, mainSameIdentity = sameAccountIdentity(machineState.main.mainAccountId, latest.main.mainAccountId), mergedMainQuota = mainSameIdentity ? mergeQuotaByWindow(machineState.main.quota, latest.main.quota) : freshestQuota(machineState.main.quota, latest.main.quota), now = Date.now(), stickyAssignments = pruneStickyAssignments(latest.stickyAssignments, vaultMode ? void 0 : usableRoutingAccountIds(routingAccountsOf(machineState)), now);
|
|
497
524
|
return {
|
|
498
525
|
...latest,
|
|
499
526
|
...machineState,
|
|
@@ -530,7 +557,7 @@ export function upsertSidebarActiveRouting(input, accounts, file = getSidebarSta
|
|
|
530
557
|
route: input.route,
|
|
531
558
|
updatedAt: input.updatedAt
|
|
532
559
|
}
|
|
533
|
-
}, accounts, Date.now()), stickyAssignments = pruneStickyAssignments(latest.stickyAssignments, usableRoutingAccountIds(accounts), Date.now());
|
|
560
|
+
}, accounts, Date.now()), stickyAssignments = pruneStickyAssignments(latest.stickyAssignments, latest.vaultAccounts !== void 0 ? void 0 : usableRoutingAccountIds(accounts), Date.now());
|
|
534
561
|
return {
|
|
535
562
|
...latest,
|
|
536
563
|
activeId: input.activeId,
|
|
@@ -555,7 +582,7 @@ export function setSidebarLegacyRouting(input, file = getSidebarStateFile(), hoo
|
|
|
555
582
|
export function removeSidebarActiveRouting(sessionId, accounts, file = getSidebarStateFile(), hooks) {
|
|
556
583
|
return enqueueSidebarWrite(async () => {
|
|
557
584
|
await writeMergedSidebarState(file, (latest) => {
|
|
558
|
-
const now = Date.now(), activeRouting = pruneActiveRouting(latest.activeRouting, accounts, now, sessionId), stickyAssignments = pruneStickyAssignments(latest.stickyAssignments, usableRoutingAccountIds(accounts), now, hashSidebarSessionId(sessionId));
|
|
585
|
+
const now = Date.now(), activeRouting = pruneActiveRouting(latest.activeRouting, accounts, now, sessionId), stickyAssignments = pruneStickyAssignments(latest.stickyAssignments, latest.vaultAccounts !== void 0 ? void 0 : usableRoutingAccountIds(accounts), now, hashSidebarSessionId(sessionId));
|
|
559
586
|
return {
|
|
560
587
|
...latest,
|
|
561
588
|
activeRouting,
|
|
@@ -652,7 +679,7 @@ export function persistSidebarStickyAssignment(input, file = getSidebarStateFile
|
|
|
652
679
|
const sessionHash = hashSidebarSessionId(input.sessionId), validPinnedAccountIds = new Set(input.validPinnedAccountIds);
|
|
653
680
|
return enqueueSidebarWrite(async () => {
|
|
654
681
|
await writeMergedSidebarState(file, (latest) => {
|
|
655
|
-
const now = Date.now(), stickyAssignments = pruneStickyAssignments(latest.stickyAssignments, validPinnedAccountIds, now), current = stickyAssignments?.[sessionHash];
|
|
682
|
+
const now = Date.now(), stickyAssignments = pruneStickyAssignments(latest.stickyAssignments, latest.vaultAccounts !== void 0 ? void 0 : validPinnedAccountIds, now), current = stickyAssignments?.[sessionHash];
|
|
656
683
|
if (current && current.assignedAt > input.assignment.assignedAt) {
|
|
657
684
|
if (stickyAssignmentsEqual(latest.stickyAssignments, stickyAssignments))
|
|
658
685
|
return;
|
|
@@ -875,7 +902,7 @@ export function createSidebarBookkeepingQueue(options) {
|
|
|
875
902
|
export function resolveActiveAccount(state) {
|
|
876
903
|
const activeId = state.activeId;
|
|
877
904
|
if (state.vaultAccounts !== void 0) {
|
|
878
|
-
const enabled = state.
|
|
905
|
+
const enabled = vaultAccountsInPlacementOrder(state).filter((account) => account.enabled && account.routable !== !1), vaultAccount = enabled.find((account) => account.id === activeId) ?? enabled[0];
|
|
879
906
|
return vaultAccount ? {
|
|
880
907
|
id: vaultAccount.id,
|
|
881
908
|
name: vaultAccount.label ?? vaultAccount.id,
|
|
@@ -14,6 +14,7 @@ import { createComponent as _$createComponent } from "opentui:runtime-module:%40
|
|
|
14
14
|
// result carries.
|
|
15
15
|
|
|
16
16
|
import { createLogger } from "../logger.js";
|
|
17
|
+
import { factLines, sectionTitle } from "./section-text.js";
|
|
17
18
|
const log = createLogger('rpc-tui');
|
|
18
19
|
|
|
19
20
|
/** Sends one action to the plugin; the session id is added by the caller. */
|
|
@@ -30,13 +31,9 @@ export function sectionListOptions(menu) {
|
|
|
30
31
|
} : {})
|
|
31
32
|
}));
|
|
32
33
|
}
|
|
33
|
-
function factLines(facts) {
|
|
34
|
-
return Object.entries(facts ?? {}).map(([name, value]) => `${name}: ${typeof value === 'string' ? value : JSON.stringify(value)}`);
|
|
35
|
-
}
|
|
36
34
|
|
|
37
|
-
/**
|
|
35
|
+
/** Every option navigates or runs an action; read-only context is in the title. */
|
|
38
36
|
export function sectionOptions(section) {
|
|
39
|
-
const header = [...section.lines, ...factLines(section.facts), ...section.items.filter(item => item.actions.length === 0).map(item => [item.group, [item.label, item.status].filter(Boolean).join(' · '), item.detail, ...factLines(item.facts)].filter(Boolean).join('\n'))];
|
|
40
37
|
const options = [...section.items.filter(item => item.actions.length > 0).map(item => ({
|
|
41
38
|
title: item.label,
|
|
42
39
|
value: `item:${item.id}`,
|
|
@@ -62,15 +59,6 @@ export function sectionOptions(section) {
|
|
|
62
59
|
title: 'Back',
|
|
63
60
|
value: BACK
|
|
64
61
|
}];
|
|
65
|
-
const first = options[0];
|
|
66
|
-
if (first && header.length > 0) {
|
|
67
|
-
const category = first.category;
|
|
68
|
-
const text = [...header, category].filter(Boolean).join('\n');
|
|
69
|
-
for (const option of options) {
|
|
70
|
-
if (option.category !== category) break;
|
|
71
|
-
option.category = text;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
62
|
return options;
|
|
75
63
|
}
|
|
76
64
|
|
|
@@ -158,7 +146,7 @@ function showSection(context, command, menu, sectionId) {
|
|
|
158
146
|
api.ui.dialog.setSize('xlarge');
|
|
159
147
|
api.ui.dialog.replace(() => _$createComponent(DialogSelect, {
|
|
160
148
|
get title() {
|
|
161
|
-
return section
|
|
149
|
+
return sectionTitle(section);
|
|
162
150
|
},
|
|
163
151
|
get options() {
|
|
164
152
|
return sectionOptions(section);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function factLines(facts) {
|
|
2
|
+
return Object.entries(facts ?? {}).map(([name, value]) => `${name}: ${typeof value === "string" ? value : JSON.stringify(value)}`);
|
|
3
|
+
}
|
|
4
|
+
export function sectionText(section) {
|
|
5
|
+
return [
|
|
6
|
+
...section.lines,
|
|
7
|
+
...factLines(section.facts),
|
|
8
|
+
...section.items.filter((item) => item.actions.length === 0).map((item) => [
|
|
9
|
+
item.group,
|
|
10
|
+
[item.label, item.status].filter(Boolean).join(" \xB7 "),
|
|
11
|
+
item.detail,
|
|
12
|
+
...factLines(item.facts)
|
|
13
|
+
].filter(Boolean).join(`
|
|
14
|
+
`))
|
|
15
|
+
].filter(Boolean).join(`
|
|
16
|
+
`);
|
|
17
|
+
}
|
|
18
|
+
export function sectionTitle(section) {
|
|
19
|
+
return [section.title, sectionText(section)].filter(Boolean).join(`
|
|
20
|
+
`);
|
|
21
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { sectionText, sectionTitle } from "./section-text.js";
|
|
2
|
+
function inlineDescription(...parts) {
|
|
3
|
+
return parts.filter(Boolean).join(" \xB7 ").replace(/\r?\n/g, " \xB7 ");
|
|
4
|
+
}
|
|
5
|
+
export async function openControlDialog(dialog, initial, apply, active = () => !0) {
|
|
6
|
+
let payload = initial;
|
|
7
|
+
menu:
|
|
8
|
+
while (active()) {
|
|
9
|
+
const sectionId = await dialog.select({
|
|
10
|
+
title: payload.menu.title,
|
|
11
|
+
options: payload.menu.sections.map((section) => ({
|
|
12
|
+
title: section.title,
|
|
13
|
+
value: section.id,
|
|
14
|
+
description: inlineDescription(...section.lines)
|
|
15
|
+
}))
|
|
16
|
+
});
|
|
17
|
+
if (!active() || sectionId === void 0)
|
|
18
|
+
return;
|
|
19
|
+
const section = payload.menu.sections.find((entry) => entry.id === sectionId);
|
|
20
|
+
if (!section)
|
|
21
|
+
return;
|
|
22
|
+
const choices = [
|
|
23
|
+
...section.items.filter((item) => item.actions.length > 0).map((item) => ({
|
|
24
|
+
title: item.label,
|
|
25
|
+
description: inlineDescription(item.status, item.detail),
|
|
26
|
+
value: { itemId: item.id, actions: item.actions }
|
|
27
|
+
})),
|
|
28
|
+
...section.actions.map((action) => ({
|
|
29
|
+
title: action.label,
|
|
30
|
+
description: inlineDescription(action.description),
|
|
31
|
+
value: { itemId: void 0, actions: [action] }
|
|
32
|
+
}))
|
|
33
|
+
];
|
|
34
|
+
if (choices.length === 0) {
|
|
35
|
+
await dialog.alert({
|
|
36
|
+
title: section.title,
|
|
37
|
+
message: sectionText(section)
|
|
38
|
+
});
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const choice = await dialog.select({
|
|
42
|
+
title: sectionTitle(section),
|
|
43
|
+
options: [...choices, { title: "Back", value: void 0 }]
|
|
44
|
+
});
|
|
45
|
+
if (!active())
|
|
46
|
+
return;
|
|
47
|
+
if (!choice)
|
|
48
|
+
continue;
|
|
49
|
+
let actionId;
|
|
50
|
+
if (choice.itemId !== void 0) {
|
|
51
|
+
if (choice.actions.length === 0)
|
|
52
|
+
continue;
|
|
53
|
+
actionId = await dialog.select({
|
|
54
|
+
title: section.items.find((item) => item.id === choice.itemId)?.label ?? section.title,
|
|
55
|
+
options: [
|
|
56
|
+
...choice.actions.map((action) => ({
|
|
57
|
+
title: action.label,
|
|
58
|
+
value: action.id
|
|
59
|
+
})),
|
|
60
|
+
{ title: "Back", value: void 0 }
|
|
61
|
+
]
|
|
62
|
+
});
|
|
63
|
+
} else
|
|
64
|
+
actionId = choice.actions[0]?.id;
|
|
65
|
+
if (!active())
|
|
66
|
+
return;
|
|
67
|
+
if (actionId === void 0)
|
|
68
|
+
continue;
|
|
69
|
+
const action = choice.actions.find((entry) => entry.id === actionId);
|
|
70
|
+
if (!action)
|
|
71
|
+
continue;
|
|
72
|
+
try {
|
|
73
|
+
const values = {};
|
|
74
|
+
for (const knob of action.knobs) {
|
|
75
|
+
let value;
|
|
76
|
+
if (knob.kind === "choice")
|
|
77
|
+
value = await dialog.select({
|
|
78
|
+
title: knob.label,
|
|
79
|
+
options: knob.choices.map((entry) => ({
|
|
80
|
+
title: entry.label,
|
|
81
|
+
value: entry.value
|
|
82
|
+
}))
|
|
83
|
+
});
|
|
84
|
+
else if (knob.kind === "toggle")
|
|
85
|
+
value = await dialog.select({
|
|
86
|
+
title: knob.label,
|
|
87
|
+
options: [
|
|
88
|
+
{ title: `On${knob.value ? " (current)" : ""}`, value: !0 },
|
|
89
|
+
{ title: `Off${!knob.value ? " (current)" : ""}`, value: !1 }
|
|
90
|
+
]
|
|
91
|
+
});
|
|
92
|
+
else {
|
|
93
|
+
const text = await dialog.prompt({
|
|
94
|
+
title: knob.label,
|
|
95
|
+
...knob.value !== void 0 ? { value: String(knob.value) } : {},
|
|
96
|
+
...knob.kind === "text" && knob.placeholder ? { placeholder: knob.placeholder } : {}
|
|
97
|
+
});
|
|
98
|
+
value = text === void 0 ? void 0 : text === "" ? null : knob.kind === "number" && Number.isFinite(Number(text)) ? Number(text) : text;
|
|
99
|
+
}
|
|
100
|
+
if (!active())
|
|
101
|
+
return;
|
|
102
|
+
if (value === void 0)
|
|
103
|
+
continue menu;
|
|
104
|
+
values[knob.id] = value;
|
|
105
|
+
}
|
|
106
|
+
if (action.confirm) {
|
|
107
|
+
const confirmed = await dialog.confirm({
|
|
108
|
+
title: action.label,
|
|
109
|
+
message: action.confirm.message
|
|
110
|
+
});
|
|
111
|
+
if (!active())
|
|
112
|
+
return;
|
|
113
|
+
if (confirmed !== !0)
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
const result = await apply({
|
|
117
|
+
command: payload.command,
|
|
118
|
+
sectionId,
|
|
119
|
+
actionId,
|
|
120
|
+
...choice.itemId !== void 0 ? { itemId: choice.itemId } : {},
|
|
121
|
+
...action.knobs.length ? { values } : {},
|
|
122
|
+
...action.confirm ? { confirmed: !0 } : {}
|
|
123
|
+
});
|
|
124
|
+
if (!active())
|
|
125
|
+
return;
|
|
126
|
+
payload = { command: result.command, menu: result.menu };
|
|
127
|
+
await dialog.alert({ title: payload.menu.title, message: result.text });
|
|
128
|
+
} catch (error) {
|
|
129
|
+
if (!active())
|
|
130
|
+
return;
|
|
131
|
+
await dialog.alert({
|
|
132
|
+
title: payload.menu.title,
|
|
133
|
+
message: error instanceof Error ? error.message : String(error)
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { insert as _$insert } from "opentui:runtime-module:%40opentui%2Fsolid";
|
|
2
|
+
import { createElement as _$createElement } from "opentui:runtime-module:%40opentui%2Fsolid";
|
|
3
|
+
/** @jsxImportSource @opentui/solid */
|
|
4
|
+
|
|
5
|
+
import { isNotifyPayload } from "../rpc/protocol.js";
|
|
6
|
+
import { createRpcClient } from "../rpc/rpc-client.js";
|
|
7
|
+
import { resolveRpcDir } from "../rpc/rpc-dir.js";
|
|
8
|
+
import { openControlDialog } from "./v2-dialog.js";
|
|
9
|
+
const KEY = Symbol.for('cortexkit.openai-auth.v2.tui');
|
|
10
|
+
/** Uses OpenCode 2's transport without running the OpenCode 1 UI registration. */
|
|
11
|
+
export async function setupTui(context, options = {}) {
|
|
12
|
+
const processState = globalThis;
|
|
13
|
+
processState[KEY] ??= {
|
|
14
|
+
lastIds: new Map()
|
|
15
|
+
};
|
|
16
|
+
const state = processState[KEY];
|
|
17
|
+
state.mount?.dispose();
|
|
18
|
+
const openDialog = options.openDialog ?? ((payload, apply) => openControlDialog(context.ui.dialog, payload, apply, () => !disposed));
|
|
19
|
+
let disposed = false;
|
|
20
|
+
let inFlight = false;
|
|
21
|
+
let dialogOpen = false;
|
|
22
|
+
let directory = '';
|
|
23
|
+
let client;
|
|
24
|
+
const unmount = options.sidebarText === undefined ? options.sidebar ? context.ui.slot({
|
|
25
|
+
append: 'sidebar.content',
|
|
26
|
+
render: options.sidebar.render
|
|
27
|
+
}) : undefined : context.ui.slot({
|
|
28
|
+
append: 'sidebar.content',
|
|
29
|
+
render: () => (() => {
|
|
30
|
+
var _el$ = _$createElement("text");
|
|
31
|
+
_$insert(_el$, () => options.sidebarText);
|
|
32
|
+
return _el$;
|
|
33
|
+
})()
|
|
34
|
+
});
|
|
35
|
+
const poll = async () => {
|
|
36
|
+
if (disposed || inFlight || dialogOpen) return;
|
|
37
|
+
const route = context.ui.router.current();
|
|
38
|
+
if (route.type !== 'session') return;
|
|
39
|
+
const sessionId = route.sessionID;
|
|
40
|
+
const session = context.data.session.get(sessionId);
|
|
41
|
+
if (!session) return;
|
|
42
|
+
inFlight = true;
|
|
43
|
+
try {
|
|
44
|
+
if (directory !== session.location.directory) {
|
|
45
|
+
directory = session.location.directory;
|
|
46
|
+
const resolved = await resolveRpcDir(directory);
|
|
47
|
+
if (disposed) return;
|
|
48
|
+
client = createRpcClient(resolved.dir, process.pid);
|
|
49
|
+
}
|
|
50
|
+
const rpc = client;
|
|
51
|
+
if (!rpc) return;
|
|
52
|
+
const messages = await rpc.pending(state.lastIds.get(sessionId) ?? 0, sessionId);
|
|
53
|
+
if (disposed || context.ui.router.current().type !== 'session' || context.ui.router.current().sessionID !== sessionId) return;
|
|
54
|
+
for (const message of [...messages].sort((a, b) => a.id - b.id)) {
|
|
55
|
+
state.lastIds.set(sessionId, Math.max(state.lastIds.get(sessionId) ?? 0, message.id));
|
|
56
|
+
if (isNotifyPayload(message.payload)) context.ui.toast.show({
|
|
57
|
+
message: message.payload.notify.message,
|
|
58
|
+
sessionID: sessionId
|
|
59
|
+
});else {
|
|
60
|
+
dialogOpen = true;
|
|
61
|
+
await openDialog(message.payload, request => {
|
|
62
|
+
if (disposed) throw new Error('The OpenAI dialog was closed');
|
|
63
|
+
return rpc.apply({
|
|
64
|
+
...request,
|
|
65
|
+
sessionId
|
|
66
|
+
}, 90_000);
|
|
67
|
+
}).finally(() => {
|
|
68
|
+
dialogOpen = false;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
} catch {
|
|
73
|
+
// The server can be absent while a location starts or is reloaded.
|
|
74
|
+
} finally {
|
|
75
|
+
inFlight = false;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const timer = setInterval(() => {
|
|
79
|
+
void poll();
|
|
80
|
+
}, 500);
|
|
81
|
+
const mount = {
|
|
82
|
+
dispose() {
|
|
83
|
+
if (disposed) return;
|
|
84
|
+
disposed = true;
|
|
85
|
+
clearInterval(timer);
|
|
86
|
+
unmount?.();
|
|
87
|
+
options.sidebar?.dispose();
|
|
88
|
+
if (dialogOpen) context.ui.dialog.clear();
|
|
89
|
+
if (state.mount === mount) state.mount = undefined;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
state.mount = mount;
|
|
93
|
+
void poll();
|
|
94
|
+
return () => mount.dispose();
|
|
95
|
+
}
|