@oh-my-pi/pi-ai 18.3.4 → 18.4.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.
Files changed (62) hide show
  1. package/CHANGELOG.md +35 -48
  2. package/dist/types/auth/blocks.d.ts +10 -19
  3. package/dist/types/auth/cascade.d.ts +1 -1
  4. package/dist/types/auth/rotation.d.ts +6 -3
  5. package/dist/types/auth/types.d.ts +23 -3
  6. package/dist/types/auth/usage-cache.d.ts +14 -2
  7. package/dist/types/auth/usage.d.ts +2 -2
  8. package/dist/types/auth-broker/client.d.ts +2 -1
  9. package/dist/types/auth-broker/remote-store.d.ts +5 -6
  10. package/dist/types/auth-broker/types.d.ts +3 -1
  11. package/dist/types/auth-broker/wire-schemas.d.ts +2 -1
  12. package/dist/types/auth-retry.d.ts +22 -13
  13. package/dist/types/images/types.d.ts +9 -0
  14. package/dist/types/index.d.ts +1 -0
  15. package/dist/types/judgment/types.d.ts +3 -2
  16. package/dist/types/providers/cursor/exec-modern.d.ts +1 -1
  17. package/dist/types/providers/cursor-pi-args.d.ts +10 -0
  18. package/dist/types/providers/cursor.d.ts +6 -2
  19. package/dist/types/providers/google-shared.d.ts +13 -2
  20. package/dist/types/types.d.ts +0 -10
  21. package/dist/types/usage/claude-reset.d.ts +6 -0
  22. package/dist/types/usage/claude.d.ts +6 -0
  23. package/dist/types/usage.d.ts +2 -0
  24. package/dist/types/utils/proxy.d.ts +2 -2
  25. package/package.json +6 -6
  26. package/src/auth/blocks.ts +79 -37
  27. package/src/auth/cascade.ts +5 -4
  28. package/src/auth/resets.ts +104 -40
  29. package/src/auth/rotation.ts +62 -21
  30. package/src/auth/select.ts +9 -2
  31. package/src/auth/types.ts +28 -3
  32. package/src/auth/usage-cache.ts +45 -2
  33. package/src/auth/usage.ts +52 -25
  34. package/src/auth-broker/client.ts +13 -0
  35. package/src/auth-broker/remote-store.ts +128 -27
  36. package/src/auth-broker/server.ts +20 -0
  37. package/src/auth-broker/types.ts +4 -1
  38. package/src/auth-broker/wire-schemas.ts +7 -0
  39. package/src/auth-retry.ts +50 -28
  40. package/src/images/openai-hosted.ts +18 -3
  41. package/src/images/types.ts +9 -0
  42. package/src/index.ts +1 -0
  43. package/src/judgment/types.ts +10 -6
  44. package/src/judgment/typesafe.ts +2 -2
  45. package/src/providers/anthropic.ts +54 -123
  46. package/src/providers/cursor/exec-modern.ts +1 -0
  47. package/src/providers/cursor/proto/agent.proto +5 -0
  48. package/src/providers/cursor-pi-args.ts +14 -0
  49. package/src/providers/cursor.ts +66 -28
  50. package/src/providers/google-gemini-cli.ts +2 -19
  51. package/src/providers/google-shared.ts +30 -22
  52. package/src/providers/ollama.ts +2 -0
  53. package/src/registry/oauth/oauth.html +206 -185
  54. package/src/stream.ts +2 -284
  55. package/src/types.ts +0 -10
  56. package/src/usage/claude-reset.ts +45 -11
  57. package/src/usage/claude.ts +79 -49
  58. package/src/usage.ts +2 -0
  59. package/src/utils/proxy.ts +3 -3
  60. package/src/utils/schema/CONSTRAINTS.md +30 -28
  61. package/src/utils/schema/normalize.ts +1 -1
  62. package/src/utils/schema/wire.ts +3 -3
@@ -1,3 +1,4 @@
1
+ import { calculateCost } from "@oh-my-pi/pi-catalog/models";
1
2
  import { fetchWithRetry, parseStreamingJson, readJsonl } from "@oh-my-pi/pi-utils";
2
3
  import * as AIError from "../error";
3
4
  import { getEnvApiKey } from "../stream";
@@ -710,6 +711,7 @@ const streamOllamaOnce = (
710
711
  output.usage.input = (chunk.prompt_eval_count ?? 0) - output.usage.cacheRead;
711
712
  output.usage.output = chunk.eval_count ?? 0;
712
713
  output.usage.totalTokens = output.usage.input + output.usage.output + output.usage.cacheRead;
714
+ calculateCost(model, output.usage, output.timestamp);
713
715
  }
714
716
  }
715
717
  if (streamMarkupHealing) {
@@ -3,283 +3,304 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>oh my pi · authentication</title>
6
+ <meta name="color-scheme" content="dark light" />
7
+ <title>omp · authentication</title>
7
8
  <style>
8
9
  :root {
9
- /* dark, slightly purple-tinted neutrals — never #000 */
10
- --ink-0: oklch(0.135 0.018 295);
11
- --ink-1: oklch(0.172 0.022 295);
12
- --ink-2: oklch(0.21 0.024 295);
13
- --ink-4: oklch(0.36 0.024 295);
14
- --ink-5: oklch(0.5 0.022 290);
15
- --ink-6: oklch(0.66 0.018 290);
16
- --ink-7: oklch(0.82 0.014 285);
17
- --ink-9: oklch(0.97 0.008 90);
18
-
19
- /* brand: magenta lead, cyan reply */
20
- --magenta: oklch(0.7 0.24 340);
21
- --iris: oklch(0.62 0.21 295);
22
- --cyan: oklch(0.81 0.14 200);
23
-
24
- /* signal */
25
- --success: oklch(0.78 0.16 150);
26
- --error: oklch(0.66 0.22 25);
27
-
28
- --bg: var(--ink-0);
29
- --fg: var(--ink-9);
30
- --fg-dim: var(--ink-7);
31
- --fg-faint: var(--ink-5);
32
- --rule: oklch(1 0 0 / 0.07);
33
- --rule-strong: oklch(1 0 0 / 0.14);
34
- --accent: var(--magenta);
35
- --accent-2: var(--cyan);
36
-
10
+ --bg: #000;
11
+ --card: #0b0b0d;
12
+ --ink-1: #f4f4f5;
13
+ --ink-2: #b4b4bb;
14
+ --ink-3: #7a7a83;
15
+ --line-1: rgb(255 255 255 / 0.07);
16
+ --line-2: rgb(255 255 255 / 0.12);
17
+ --top-lit: rgb(255 255 255 / 0.22);
18
+ --accent-a: #22d3ee;
19
+ --accent-b: #f472b6;
20
+ --accent-gradient: linear-gradient(135deg, var(--accent-a), var(--accent-b));
21
+ --ok: #4ade80;
22
+ --ok-soft: rgb(74 222 128 / 0.12);
23
+ --bad: #f87171;
24
+ --bad-soft: rgb(248 113 113 / 0.12);
25
+ --pending: #a1a1aa;
26
+ --pending-soft: rgb(161 161 170 / 0.12);
27
+ --glow-a: rgb(34 211 238 / 0.1);
28
+ --glow-b: rgb(244 114 182 / 0.08);
29
+ --shadow: 0 30px 60px -20px rgb(0 0 0 / 0.8);
30
+ --btn-bg: #f4f4f5;
31
+ --btn-fg: #000;
32
+ --r-control: 8px;
33
+ --r-panel: 16px;
34
+ --sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
35
+ --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
37
36
  color-scheme: dark;
38
37
  }
39
38
 
39
+ @media (prefers-color-scheme: light) {
40
+ :root {
41
+ --bg: #fafafa;
42
+ --card: #fff;
43
+ --ink-1: #111113;
44
+ --ink-2: #4b4b52;
45
+ --ink-3: #83838b;
46
+ --line-1: rgb(0 0 0 / 0.08);
47
+ --line-2: rgb(0 0 0 / 0.12);
48
+ --top-lit: rgb(0 0 0 / 0.04);
49
+ --accent-a: #0891b2;
50
+ --accent-b: #db2777;
51
+ --ok: #16a34a;
52
+ --ok-soft: rgb(22 163 74 / 0.1);
53
+ --bad: #dc2626;
54
+ --bad-soft: rgb(220 38 38 / 0.1);
55
+ --pending: #71717a;
56
+ --pending-soft: rgb(113 113 122 / 0.1);
57
+ --glow-a: rgb(8 145 178 / 0.08);
58
+ --glow-b: rgb(219 39 119 / 0.06);
59
+ --shadow: 0 20px 50px -24px rgb(0 0 0 / 0.18);
60
+ --btn-bg: #111113;
61
+ --btn-fg: #fff;
62
+ color-scheme: light;
63
+ }
64
+ }
65
+
40
66
  * {
41
67
  box-sizing: border-box;
42
68
  }
43
69
 
44
70
  body {
45
- font-family: "Geist", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
46
- background:
47
- radial-gradient(ellipse 80vw 60vh at 18% -10%, oklch(0.7 0.24 340 / 0.13), transparent 60%),
48
- radial-gradient(ellipse 70vw 70vh at 110% 30%, oklch(0.81 0.14 200 / 0.09), transparent 60%),
49
- var(--bg);
50
- color: var(--fg);
71
+ margin: 0;
72
+ min-height: 100vh;
51
73
  display: flex;
52
74
  align-items: center;
53
75
  justify-content: center;
54
- min-height: 100vh;
55
- margin: 0;
56
76
  padding: 1.5rem;
77
+ font-family: var(--sans);
78
+ background: var(--bg);
79
+ color: var(--ink-1);
57
80
  -webkit-font-smoothing: antialiased;
58
- text-rendering: optimizeLegibility;
59
81
  overflow-x: clip;
60
82
  }
61
83
 
62
- /* ascii grid backdrop */
63
- .grid-bg {
84
+ /* ambient glow */
85
+ .glow {
64
86
  position: fixed;
65
87
  inset: 0;
66
- background-image:
67
- linear-gradient(to right, oklch(1 0 0 / 0.025) 1px, transparent 1px),
68
- linear-gradient(to bottom, oklch(1 0 0 / 0.025) 1px, transparent 1px);
69
- background-size: 56px 56px;
70
- background-position: -1px -1px;
71
- mask-image: radial-gradient(ellipse at 50% 30%, #000 30%, transparent 75%);
72
88
  pointer-events: none;
73
- z-index: 0;
89
+ background:
90
+ radial-gradient(ellipse 50vw 40vh at 38% 30%, var(--glow-a), transparent 70%),
91
+ radial-gradient(ellipse 50vw 40vh at 64% 64%, var(--glow-b), transparent 70%);
74
92
  }
75
93
 
76
94
  .container {
77
95
  position: relative;
78
- z-index: 1;
79
- background: oklch(0.172 0.022 295 / 0.72);
80
- backdrop-filter: blur(16px);
81
- -webkit-backdrop-filter: blur(16px);
82
- border: 1px solid var(--rule);
83
- border-radius: 16px;
84
- padding: 2.5rem 2.5rem 2rem;
85
96
  width: 100%;
86
- max-width: 420px;
87
- text-align: center;
88
- box-shadow:
89
- 0 24px 48px -12px oklch(0 0 0 / 0.55),
90
- 0 0 0 1px oklch(1 0 0 / 0.03) inset;
91
- opacity: 0;
92
- transform: translateY(10px);
93
- animation: rise 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;
97
+ max-width: 400px;
98
+ padding: 2rem 2rem 1.75rem;
99
+ background: var(--card);
100
+ border: 1px solid var(--line-1);
101
+ border-radius: var(--r-panel);
102
+ box-shadow: var(--shadow);
103
+ overflow: hidden;
104
+ animation: rise 0.5s cubic-bezier(0.16, 1, 0.3, 1) both;
105
+ }
106
+ /* top-lit hairline */
107
+ .container::before {
108
+ content: "";
109
+ position: absolute;
110
+ top: 0;
111
+ left: 12%;
112
+ right: 12%;
113
+ height: 1px;
114
+ background: linear-gradient(90deg, transparent, var(--top-lit), transparent);
94
115
  }
95
116
  @keyframes rise {
96
- to {
97
- opacity: 1;
98
- transform: none;
117
+ from {
118
+ opacity: 0;
119
+ transform: translateY(8px);
99
120
  }
100
121
  }
101
122
 
102
- /* brand header */
103
123
  .brand {
104
124
  display: flex;
105
125
  align-items: center;
106
- justify-content: center;
107
- gap: 0.5rem;
108
- margin-bottom: 2rem;
126
+ gap: 0.6rem;
127
+ margin-bottom: 1.75rem;
109
128
  }
110
- .brand svg {
111
- width: 22px;
112
- height: 22px;
129
+ .omp-mark {
130
+ flex: none;
131
+ display: block;
113
132
  }
114
- .brand .wordmark {
133
+ .wordmark {
115
134
  font-size: 13px;
116
- font-weight: 500;
135
+ font-weight: 600;
117
136
  letter-spacing: -0.01em;
118
- color: var(--fg);
137
+ }
138
+ .wordmark-sub {
139
+ margin-left: auto;
140
+ font-family: var(--mono);
141
+ font-size: 12px;
142
+ color: var(--ink-3);
119
143
  }
120
144
 
121
- /* status mark */
122
- .mark-wrap {
123
- position: relative;
124
- width: 72px;
125
- height: 72px;
126
- margin: 0 auto 1.5rem;
127
- display: flex;
145
+ .status {
146
+ display: inline-flex;
128
147
  align-items: center;
129
- justify-content: center;
148
+ gap: 0.45rem;
149
+ padding: 0.22rem 0.6rem 0.22rem 0.5rem;
150
+ margin-bottom: 1rem;
151
+ border-radius: 999px;
152
+ font-size: 12px;
153
+ font-weight: 500;
154
+ color: var(--pending);
155
+ background: var(--pending-soft);
130
156
  }
131
- .mark-wrap::before {
132
- content: "";
133
- position: absolute;
134
- inset: 0;
157
+ .status-dot {
158
+ width: 7px;
159
+ height: 7px;
135
160
  border-radius: 50%;
136
- background: oklch(1 0 0 / 0.05);
137
- z-index: 0;
138
- }
139
- .mark {
140
- width: 40px;
141
- height: 40px;
142
- z-index: 2;
161
+ background: currentColor;
143
162
  }
144
- .timer-svg {
145
- position: absolute;
146
- top: -2px;
147
- left: -2px;
148
- width: 76px;
149
- height: 76px;
150
- transform: rotate(-90deg);
151
- z-index: 1;
152
- pointer-events: none;
163
+ .status-word {
164
+ display: none;
153
165
  }
154
- .timer-circle {
155
- fill: none;
156
- stroke-width: 2;
157
- stroke-linecap: round;
158
- stroke-dasharray: 226;
159
- stroke-dashoffset: 0;
166
+ .container:not(.success):not(.error) .status-pending,
167
+ .success .status-success,
168
+ .error .status-error {
169
+ display: inline;
160
170
  }
161
- .countdown .timer-circle {
162
- animation: countdown 3s linear forwards;
171
+ .container:not(.success):not(.error) .status-dot {
172
+ animation: pulse 1.4s ease-in-out infinite;
163
173
  }
164
- @keyframes countdown {
165
- to {
166
- stroke-dashoffset: 226;
174
+ @keyframes pulse {
175
+ 50% {
176
+ opacity: 0.35;
167
177
  }
168
178
  }
179
+ .success .status {
180
+ color: var(--ok);
181
+ background: var(--ok-soft);
182
+ }
183
+ .error .status {
184
+ color: var(--bad);
185
+ background: var(--bad-soft);
186
+ }
169
187
 
170
188
  h1 {
171
- font-family: "Fraunces", "Iowan Old Style", Georgia, serif;
172
- font-size: 1.6rem;
173
- font-weight: 500;
174
- letter-spacing: -0.022em;
175
- line-height: 1.15;
176
- margin: 0 0 0.75rem;
189
+ margin: 0 0 0.5rem;
190
+ font-size: 1.35rem;
191
+ font-weight: 600;
192
+ letter-spacing: -0.02em;
193
+ line-height: 1.25;
177
194
  text-wrap: balance;
178
195
  }
179
196
  p {
180
- color: var(--fg-dim);
197
+ margin: 0 0 1.5rem;
198
+ font-size: 0.9rem;
181
199
  line-height: 1.6;
182
- margin: 0 0 1.75rem;
183
- font-size: 0.95rem;
200
+ color: var(--ink-2);
201
+ }
202
+ p:last-of-type:not(:has(+ .btn)) {
203
+ margin-bottom: 0.25rem;
204
+ }
205
+ .error p {
206
+ font-family: var(--mono);
207
+ font-size: 0.8rem;
208
+ padding: 0.6rem 0.75rem;
209
+ border: 1px solid var(--line-1);
210
+ border-radius: var(--r-control);
211
+ overflow-wrap: anywhere;
184
212
  }
185
213
 
186
214
  .btn {
187
215
  display: inline-flex;
188
216
  align-items: center;
189
- gap: 0.5rem;
190
- background: var(--fg);
191
- color: var(--bg);
192
- font-family: inherit;
217
+ justify-content: center;
218
+ width: 100%;
219
+ padding: 0.6rem 1rem;
220
+ font: inherit;
221
+ font-size: 0.875rem;
193
222
  font-weight: 500;
194
- padding: 0.65rem 1.4rem;
195
- border-radius: 8px;
196
- text-decoration: none;
197
- font-size: 0.9rem;
198
- transition: opacity 0.2s, transform 0.2s;
199
- cursor: pointer;
223
+ color: var(--btn-fg);
224
+ background: var(--btn-bg);
200
225
  border: none;
226
+ border-radius: var(--r-control);
227
+ cursor: pointer;
228
+ transition: opacity 0.15s;
201
229
  }
202
230
  .btn:hover {
203
- opacity: 0.88;
204
- transform: translateY(-1px);
205
- }
206
- .btn:active {
207
- transform: translateY(0);
208
- }
209
-
210
- /* State: success */
211
- .success .mark-wrap::before {
212
- background: oklch(0.78 0.16 150 / 0.12);
231
+ opacity: 0.86;
213
232
  }
214
- .success .mark {
215
- color: var(--success);
233
+ .btn:focus-visible {
234
+ outline: 2px solid var(--accent-a);
235
+ outline-offset: 2px;
216
236
  }
217
- .success .timer-circle {
218
- stroke: var(--success);
219
- }
220
- .success .icon-error {
221
- display: none;
237
+ .error .btn {
238
+ color: var(--ink-1);
239
+ background: transparent;
240
+ border: 1px solid var(--line-2);
222
241
  }
223
242
 
224
- /* State: error */
225
- .error .mark-wrap::before {
226
- background: oklch(0.66 0.22 25 / 0.12);
227
- }
228
- .error .mark {
229
- color: var(--error);
243
+ /* auto-close progress */
244
+ .timer {
245
+ position: absolute;
246
+ left: 0;
247
+ bottom: 0;
248
+ height: 2px;
249
+ width: 100%;
250
+ background: var(--accent-gradient);
251
+ transform-origin: left;
252
+ transform: scaleX(0);
230
253
  }
231
- .error .timer-circle {
232
- stroke: var(--error);
254
+ .countdown .timer {
255
+ animation: countdown 3s linear forwards;
233
256
  }
234
- .error .icon-success,
235
- .error .timer-svg {
236
- display: none;
257
+ @keyframes countdown {
258
+ from {
259
+ transform: scaleX(1);
260
+ }
261
+ to {
262
+ transform: scaleX(0);
263
+ }
237
264
  }
238
265
 
239
266
  @media (prefers-reduced-motion: reduce) {
240
267
  .container,
241
- .countdown .timer-circle {
242
- animation: none;
243
- }
244
- .container {
245
- opacity: 1;
246
- transform: none;
268
+ .countdown .timer,
269
+ .status-dot {
270
+ animation: none !important;
247
271
  }
248
272
  }
249
273
  </style>
250
274
  </head>
251
275
  <body>
252
- <span aria-hidden="true" class="grid-bg"></span>
276
+ <span aria-hidden="true" class="glow"></span>
253
277
  <div id="app" class="container">
254
278
  <div class="brand">
255
- <svg viewBox="0 0 64 64" aria-hidden="true">
279
+ <svg class="omp-mark" viewBox="0 0 64 64" width="24" height="24" aria-hidden="true">
256
280
  <defs>
257
- <linearGradient id="pi-grad" x1="0" y1="0" x2="1" y2="1">
281
+ <linearGradient id="omp-mark-grad" x1="0" y1="0" x2="1" y2="1">
258
282
  <stop offset="0" stop-color="oklch(0.7 0.24 340)" />
259
283
  <stop offset=".5" stop-color="oklch(0.62 0.21 295)" />
260
284
  <stop offset="1" stop-color="oklch(0.81 0.14 200)" />
261
285
  </linearGradient>
262
286
  </defs>
263
- <path fill="url(#pi-grad)" d="M10 14h44v9H43v33h-9V23h-9v22h-9V23H10z" />
287
+ <path fill="url(#omp-mark-grad)" d="M10 14h44v9H43v33h-9V23h-9v22h-9V23H10z" />
264
288
  </svg>
265
- <span class="wordmark">oh my pi</span>
289
+ <span class="wordmark">omp</span>
290
+ <span class="wordmark-sub">oauth</span>
266
291
  </div>
267
292
 
268
- <div class="mark-wrap">
269
- <svg class="timer-svg" viewBox="0 0 76 76">
270
- <circle class="timer-circle" cx="38" cy="38" r="36"></circle>
271
- </svg>
272
- <svg class="mark icon-success" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
273
- <path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" />
274
- </svg>
275
- <svg class="mark icon-error" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
276
- <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
277
- </svg>
293
+ <div class="status" role="status">
294
+ <span aria-hidden="true" class="status-dot"></span>
295
+ <span class="status-word status-pending">Waiting</span>
296
+ <span class="status-word status-success">Connected</span>
297
+ <span class="status-word status-error">Failed</span>
278
298
  </div>
279
299
 
280
300
  <h1 id="title">Authentication</h1>
281
301
  <p id="message"></p>
282
- <button onclick="window.close()" class="btn">Close Window</button>
302
+ <button onclick="window.close()" class="btn">Close window</button>
303
+ <span aria-hidden="true" class="timer"></span>
283
304
  </div>
284
305
 
285
306
  <script id="server-state" type="application/json">
@@ -305,7 +326,7 @@
305
326
  if (serverState.ok) {
306
327
  const closeButton = document.querySelector(".btn");
307
328
  app.classList.add("success", "countdown");
308
- title.textContent = "Authentication Successful";
329
+ title.textContent = "Authentication successful";
309
330
  message.textContent = "You have successfully logged in.";
310
331
  window.close();
311
332
  setTimeout(() => {
@@ -315,7 +336,7 @@
315
336
  }, 300);
316
337
  } else {
317
338
  app.classList.add("error");
318
- title.textContent = "Authentication Failed";
339
+ title.textContent = "Authentication failed";
319
340
  message.textContent = serverState.error || "An error occurred";
320
341
  }
321
342
  </script>