@askalf/dario 6.0.35 → 6.0.36

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/cli.d.ts CHANGED
@@ -105,6 +105,42 @@ export declare function parseBooleanEnv(value: string | undefined): boolean | un
105
105
  * Set(['thinking','env']) — value "thinking,env" → preserve listed
106
106
  */
107
107
  export declare function resolvePreserveOrchestrationTags(args: string[], env: string | undefined): Set<string> | undefined;
108
+ /**
109
+ * The seat shape a running proxy returns from `/accounts`.
110
+ *
111
+ * Everything past `alias` is optional on purpose. A freshly installed CLI
112
+ * routinely queries a proxy still running the PREVIOUS release, whose seats
113
+ * predate whichever field the newest feature added — `sharesWindowWith`,
114
+ * `organizationId` and `grantedAt` all arrived that way (dario#1248 review).
115
+ * The payload is accepted on `mode` + `accounts` alone, so the renderer, not
116
+ * the fetch, is where a missing field would throw. Defaulting each one here
117
+ * keeps `accounts list --live` degrading to a thinner line instead of dying
118
+ * with a TypeError and skipping the on-disk fallback it advertises.
119
+ */
120
+ export interface LiveSeat {
121
+ alias?: string;
122
+ status?: string;
123
+ action?: 'none' | 'wait' | 'regrant';
124
+ util5h?: number;
125
+ util7d?: number;
126
+ utilAgeMs?: number | null;
127
+ resetInMs?: number | null;
128
+ requestCount?: number;
129
+ rejectedCount?: number;
130
+ organizationId?: string | null;
131
+ sharesWindowWith?: string[];
132
+ grantedAt?: number | null;
133
+ }
134
+ export interface LivePayload {
135
+ mode?: string;
136
+ accounts?: LiveSeat[];
137
+ distinctWindows?: number;
138
+ }
139
+ /**
140
+ * Render the live pool listing. Pure — returns the lines rather than printing
141
+ * them, so a legacy payload can be driven straight through it in a test.
142
+ */
143
+ export declare function formatLiveAccountsListing(payload: LivePayload, port: number, now: number): string[];
108
144
  /**
109
145
  * Decide whether this module is being invoked as the CLI entry point or
110
146
  * imported as a library. Pure, exported for tests; the file-bottom uses
package/dist/cli.js CHANGED
@@ -894,6 +894,52 @@ function parsePositiveIntFlag(prefix) {
894
894
  }
895
895
  return n;
896
896
  }
897
+ /**
898
+ * Render the live pool listing. Pure — returns the lines rather than printing
899
+ * them, so a legacy payload can be driven straight through it in a test.
900
+ */
901
+ export function formatLiveAccountsListing(payload, port, now) {
902
+ const seats = Array.isArray(payload.accounts) ? payload.accounts : [];
903
+ const num = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : 0);
904
+ const pct = (n) => `${Math.round(num(n) * 100)}%`;
905
+ const mins = (ms) => {
906
+ const m = Math.max(1, Math.round(ms / 60_000));
907
+ return m >= 60 ? `${Math.floor(m / 60)}h ${m % 60}m` : `${m}m`;
908
+ };
909
+ const age = (ms) => typeof ms !== 'number' || !Number.isFinite(ms)
910
+ ? 'never measured'
911
+ : ms < 60_000 ? `read ${Math.round(ms / 1000)}s ago` : `read ${mins(ms)} ago`;
912
+ const lines = [];
913
+ lines.push('');
914
+ lines.push(` dario — Accounts (live, from http://127.0.0.1:${port})`);
915
+ lines.push(' ────────────────');
916
+ lines.push('');
917
+ const windows = typeof payload.distinctWindows === 'number' ? payload.distinctWindows : seats.length;
918
+ lines.push(` Pool of ${seats.length} (${seats.length === 1 ? '1 seat' : seats.length + ' seats'} on ${windows} distinct window${windows === 1 ? '' : 's'})`);
919
+ lines.push('');
920
+ for (const s of seats) {
921
+ const alias = typeof s.alias === 'string' ? s.alias : '(unnamed)';
922
+ const rawStatus = typeof s.status === 'string' ? s.status : 'unknown';
923
+ const status = rawStatus === 'rejected' && typeof s.resetInMs === 'number' ? `rejected, back in ${mins(s.resetInMs)}` : rawStatus;
924
+ lines.push(` ${alias.padEnd(20)} ${status.padEnd(26)} 5h ${pct(s.util5h).padEnd(6)} 7d ${pct(s.util7d).padEnd(6)} ${age(s.utilAgeMs)}`);
925
+ // The one-word next step (dario#1244): a parked seat wants nothing from
926
+ // the operator; an auth-failure streak wants a re-grant.
927
+ const next = s.action === 'regrant' ? 'next: re-grant this seat (dario accounts remove + add)'
928
+ : s.action === 'wait' ? 'next: nothing, it comes back on its own'
929
+ : null;
930
+ const facts = [
931
+ `served ${num(s.requestCount)}`,
932
+ `429s ${num(s.rejectedCount)}`,
933
+ ...(next ? [next] : []),
934
+ typeof s.organizationId === 'string' && s.organizationId ? `org ${s.organizationId.slice(0, 8)}…` : 'org not yet observed',
935
+ ...(Array.isArray(s.sharesWindowWith) && s.sharesWindowWith.length > 0 ? [`shares its window with ${s.sharesWindowWith.join(', ')}`] : []),
936
+ ];
937
+ lines.push(` ${''.padEnd(20)} ${facts.join(' · ')}`);
938
+ lines.push(` ${''.padEnd(20)} ${describeGrantAge(grantAge(typeof s.grantedAt === 'number' ? s.grantedAt : undefined, now))}`);
939
+ }
940
+ lines.push('');
941
+ return lines;
942
+ }
897
943
  /**
898
944
  * `dario accounts list --live` — the running proxy's view of the pool
899
945
  * (dario#1244): status with its countdown, the reading and its age, 429s
@@ -924,40 +970,8 @@ async function accountsListLive() {
924
970
  }
925
971
  if (!payload || payload.mode !== 'pool' || !Array.isArray(payload.accounts))
926
972
  return false;
927
- const seats = payload.accounts;
928
- const now = Date.now();
929
- const pct = (n) => `${Math.round(n * 100)}%`;
930
- const mins = (ms) => {
931
- const m = Math.max(1, Math.round(ms / 60_000));
932
- return m >= 60 ? `${Math.floor(m / 60)}h ${m % 60}m` : `${m}m`;
933
- };
934
- const age = (ms) => ms === null ? 'never measured' : ms < 60_000 ? `read ${Math.round(ms / 1000)}s ago` : `read ${mins(ms)} ago`;
935
- console.log('');
936
- console.log(` dario — Accounts (live, from http://127.0.0.1:${port})`);
937
- console.log(' ────────────────');
938
- console.log('');
939
- const windows = payload.distinctWindows ?? seats.length;
940
- console.log(` Pool of ${seats.length} (${seats.length === 1 ? '1 seat' : seats.length + ' seats'} on ${windows} distinct window${windows === 1 ? '' : 's'})`);
941
- console.log('');
942
- for (const s of seats) {
943
- const status = s.status === 'rejected' && typeof s.resetInMs === 'number' ? `rejected, back in ${mins(s.resetInMs)}` : s.status;
944
- console.log(` ${s.alias.padEnd(20)} ${status.padEnd(26)} 5h ${pct(s.util5h).padEnd(6)} 7d ${pct(s.util7d).padEnd(6)} ${age(s.utilAgeMs)}`);
945
- // The one-word next step (dario#1244): a parked seat wants nothing from
946
- // the operator; an auth-failure streak wants a re-grant.
947
- const next = s.action === 'regrant' ? 'next: re-grant this seat (dario accounts remove + add)'
948
- : s.action === 'wait' ? 'next: nothing, it comes back on its own'
949
- : null;
950
- const facts = [
951
- `served ${s.requestCount}`,
952
- `429s ${s.rejectedCount}`,
953
- ...(next ? [next] : []),
954
- s.organizationId ? `org ${s.organizationId.slice(0, 8)}…` : 'org not yet observed',
955
- ...(s.sharesWindowWith.length > 0 ? [`shares its window with ${s.sharesWindowWith.join(', ')}`] : []),
956
- ];
957
- console.log(` ${''.padEnd(20)} ${facts.join(' · ')}`);
958
- console.log(` ${''.padEnd(20)} ${describeGrantAge(grantAge(s.grantedAt ?? undefined, now))}`);
959
- }
960
- console.log('');
973
+ for (const line of formatLiveAccountsListing(payload, port, Date.now()))
974
+ console.log(line);
961
975
  return true;
962
976
  }
963
977
  async function accounts() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askalf/dario",
3
- "version": "6.0.35",
3
+ "version": "6.0.36",
4
4
  "description": "Use your Claude and ChatGPT subscriptions in Cursor, Cline, Aider, Claude Code and the Agent SDK — at subscription pricing, not per-token API bills. One local Anthropic + OpenAI-compatible endpoint: either plan answers either wire shape, with automatic failover when one hits its limit.",
5
5
  "type": "module",
6
6
  "bin": {