remote-codex 0.11.32 → 0.11.33

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.
@@ -10,7 +10,7 @@
10
10
  <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
11
11
  <link rel="manifest" href="/site.webmanifest" />
12
12
  <title>Remote Codex</title>
13
- <script type="module" crossorigin src="/assets/index-CGHHTNkM.js"></script>
13
+ <script type="module" crossorigin src="/assets/index-DIi8wTEY.js"></script>
14
14
  <link rel="modulepreload" crossorigin href="/assets/react-vendor-Dfg_6BLf.js">
15
15
  <link rel="modulepreload" crossorigin href="/assets/ui-vendor-CuR8GHb0.js">
16
16
  <link rel="modulepreload" crossorigin href="/assets/graph-vendor-DVQUpZ8C.js">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remote-codex",
3
- "version": "0.11.32",
3
+ "version": "0.11.33",
4
4
  "description": "Local web supervisor for Codex workspaces and threads.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -15,6 +15,7 @@ interface Query extends AsyncIterable<SDKMessage> {
15
15
  interrupt(): Promise<void>;
16
16
  supportedModels(): Promise<any[]>;
17
17
  mcpServerStatus(): Promise<any[]>;
18
+ usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET(): Promise<any>;
18
19
  }
19
20
 
20
21
  function wait(ms = 0) {
@@ -29,7 +30,7 @@ class FakeQuery implements Query {
29
30
 
30
31
  constructor(
31
32
  private readonly messages: SDKMessage[],
32
- private readonly options: { holdOpen?: boolean } = {},
33
+ private readonly options: { holdOpen?: boolean; usage?: Record<string, any> } = {},
33
34
  ) {}
34
35
 
35
36
  [Symbol.asyncIterator]() {
@@ -104,6 +105,12 @@ class FakeQuery implements Query {
104
105
  async getContextUsage(): Promise<any> {
105
106
  return {};
106
107
  }
108
+ async usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET(): Promise<any> {
109
+ if (!this.options.usage) {
110
+ throw new Error('Structured usage unavailable in this fixture.');
111
+ }
112
+ return this.options.usage;
113
+ }
107
114
  async readFile(): Promise<any> {
108
115
  return {};
109
116
  }
@@ -208,6 +215,55 @@ function makeAdapter(
208
215
  }
209
216
 
210
217
  describe('ClaudeRuntimeAdapter', () => {
218
+ it('actively reads Claude subscription windows from the structured usage API', async () => {
219
+ const adapter = makeAdapter(() => new FakeQuery(
220
+ [systemInit(), result()],
221
+ {
222
+ usage: {
223
+ subscription_type: 'max',
224
+ rate_limits_available: true,
225
+ rate_limits: {
226
+ five_hour: {
227
+ utilization: 23,
228
+ resets_at: '2027-01-15T08:00:00.000Z',
229
+ },
230
+ seven_day: {
231
+ utilization: 84,
232
+ resets_at: '2027-01-20T08:00:00.000Z',
233
+ },
234
+ },
235
+ },
236
+ },
237
+ ));
238
+
239
+ await adapter.start();
240
+ await adapter.startSession({
241
+ cwd: '/tmp/workspace',
242
+ model: 'sonnet',
243
+ approvalMode: 'guarded',
244
+ sandboxMode: 'workspace-write',
245
+ });
246
+
247
+ await expect(adapter.getSubscriptionUsage()).resolves.toMatchObject({
248
+ provider: 'claude',
249
+ authKind: 'subscription',
250
+ windows: [
251
+ {
252
+ id: 'five_hour',
253
+ label: '5h',
254
+ usedPercent: 23,
255
+ resetsAt: '2027-01-15T08:00:00.000Z',
256
+ },
257
+ {
258
+ id: 'seven_day',
259
+ label: '7d',
260
+ usedPercent: 84,
261
+ resetsAt: '2027-01-20T08:00:00.000Z',
262
+ },
263
+ ],
264
+ });
265
+ });
266
+
211
267
  it('captures Claude subscription rate-limit windows from SDK events', async () => {
212
268
  const adapter = makeAdapter(() => [
213
269
  systemInit(),
@@ -99,6 +99,19 @@ interface Query extends AsyncIterable<SDKMessage> {
99
99
  interrupt(): Promise<void>;
100
100
  supportedModels(): Promise<ModelInfo[]>;
101
101
  mcpServerStatus(): Promise<McpServerStatus[]>;
102
+ usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET?(): Promise<SDKUsageResponse>;
103
+ }
104
+ interface SDKUsageWindow {
105
+ utilization: number | null;
106
+ resets_at: string | null;
107
+ }
108
+ interface SDKUsageResponse {
109
+ subscription_type: string | null;
110
+ rate_limits_available: boolean;
111
+ rate_limits: {
112
+ five_hour?: SDKUsageWindow | null;
113
+ seven_day?: SDKUsageWindow | null;
114
+ } | null;
102
115
  }
103
116
  interface SDKMessage {
104
117
  type: string;
@@ -1239,6 +1252,7 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1239
1252
  'five_hour' | 'seven_day',
1240
1253
  { usedPercent: number; resetsAt: string | null }
1241
1254
  >();
1255
+ private subscriptionAuthKind: 'subscription' | 'apiKey' | 'unknown' = 'unknown';
1242
1256
  private subscriptionUsageObservedAt: string | null = null;
1243
1257
  private readonly clientApp: string;
1244
1258
  private sdkLoadError: string | null = null;
@@ -1271,7 +1285,7 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1271
1285
  }));
1272
1286
  return {
1273
1287
  provider: 'claude' as const,
1274
- authKind: windows.length > 0 ? 'subscription' as const : 'unknown' as const,
1288
+ authKind: this.subscriptionAuthKind,
1275
1289
  observedAt,
1276
1290
  stale: false,
1277
1291
  windows,
@@ -1297,9 +1311,56 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1297
1311
  ? new Date(info.resetsAt * 1000).toISOString()
1298
1312
  : null,
1299
1313
  });
1314
+ this.subscriptionAuthKind = 'subscription';
1300
1315
  this.subscriptionUsageObservedAt = new Date().toISOString();
1301
1316
  }
1302
1317
 
1318
+ private async captureStructuredSubscriptionUsage(query: Query) {
1319
+ const getUsage = query.usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET;
1320
+ if (!getUsage) {
1321
+ return;
1322
+ }
1323
+
1324
+ try {
1325
+ const usage = await getUsage.call(query);
1326
+ const nextWindows = new Map<
1327
+ 'five_hour' | 'seven_day',
1328
+ { usedPercent: number; resetsAt: string | null }
1329
+ >();
1330
+ const addWindow = (
1331
+ id: 'five_hour' | 'seven_day',
1332
+ window: SDKUsageWindow | null | undefined,
1333
+ ) => {
1334
+ if (
1335
+ typeof window?.utilization !== 'number'
1336
+ || !Number.isFinite(window.utilization)
1337
+ ) {
1338
+ return;
1339
+ }
1340
+ nextWindows.set(id, {
1341
+ usedPercent: Math.max(0, Math.min(100, window.utilization)),
1342
+ resetsAt: typeof window.resets_at === 'string' ? window.resets_at : null,
1343
+ });
1344
+ };
1345
+ addWindow('five_hour', usage.rate_limits?.five_hour);
1346
+ addWindow('seven_day', usage.rate_limits?.seven_day);
1347
+
1348
+ this.subscriptionUsageWindows.clear();
1349
+ for (const [id, window] of nextWindows) {
1350
+ this.subscriptionUsageWindows.set(id, window);
1351
+ }
1352
+ this.subscriptionAuthKind = usage.subscription_type
1353
+ ? 'subscription'
1354
+ : usage.rate_limits_available
1355
+ ? 'subscription'
1356
+ : 'apiKey';
1357
+ this.subscriptionUsageObservedAt = new Date().toISOString();
1358
+ } catch {
1359
+ // This SDK method is experimental. Rate-limit events remain the
1360
+ // compatibility fallback for Claude versions that do not expose it.
1361
+ }
1362
+ }
1363
+
1303
1364
  private updateToolboxItemsFromSystemInit(message: SDKMessage) {
1304
1365
  this.managementSchema.toolboxItems = buildClaudeToolboxItems(
1305
1366
  normalizeClaudeSlashCommands(message.slash_commands),
@@ -1463,9 +1524,14 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1463
1524
  let providerSessionId: string | null = null;
1464
1525
  let model: string | null = input.model;
1465
1526
  const rawMessages: SDKMessage[] = [];
1527
+ let capturedStructuredUsage = false;
1466
1528
  try {
1467
1529
  for await (const message of query) {
1468
1530
  rawMessages.push(message);
1531
+ if (!capturedStructuredUsage) {
1532
+ capturedStructuredUsage = true;
1533
+ await this.captureStructuredSubscriptionUsage(query);
1534
+ }
1469
1535
  this.captureRateLimit(message);
1470
1536
  if (message.type === 'system' && message.subtype === 'init') {
1471
1537
  this.updateToolboxItemsFromSystemInit(message);
@@ -1755,9 +1821,14 @@ export class ClaudeRuntimeAdapter extends EventEmitter implements AgentRuntime {
1755
1821
  const rawMessages: SDKMessage[] = [];
1756
1822
  let terminalStatus: AgentTurn['status'] | null = null;
1757
1823
  let terminalError: string | null = null;
1824
+ let capturedStructuredUsage = false;
1758
1825
  try {
1759
1826
  for await (const message of state.query) {
1760
1827
  rawMessages.push(message);
1828
+ if (!capturedStructuredUsage) {
1829
+ capturedStructuredUsage = true;
1830
+ await this.captureStructuredSubscriptionUsage(state.query);
1831
+ }
1761
1832
  this.captureRateLimit(message);
1762
1833
  this.consumeMessage(state, message);
1763
1834
  const status = queryResultStatus(message);