orizu 0.5.24 → 0.5.26

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 (57) hide show
  1. package/README.md +23 -3
  2. package/dist/credentials.d.ts +15 -0
  3. package/dist/credentials.d.ts.map +1 -1
  4. package/dist/credentials.js +279 -59
  5. package/dist/help.d.ts.map +1 -1
  6. package/dist/help.js +5 -2
  7. package/dist/http.d.ts +20 -0
  8. package/dist/http.d.ts.map +1 -1
  9. package/dist/http.js +239 -19
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +153 -213
  12. package/dist/keyboard-select.d.ts +7 -0
  13. package/dist/keyboard-select.d.ts.map +1 -0
  14. package/dist/keyboard-select.js +72 -0
  15. package/dist/logout-diagnostic.d.ts +4 -0
  16. package/dist/logout-diagnostic.d.ts.map +1 -0
  17. package/dist/logout-diagnostic.js +26 -0
  18. package/dist/setup-account.d.ts +11 -0
  19. package/dist/setup-account.d.ts.map +1 -0
  20. package/dist/setup-account.js +106 -0
  21. package/dist/setup-onboarding.d.ts +27 -1
  22. package/dist/setup-onboarding.d.ts.map +1 -1
  23. package/dist/setup-onboarding.js +95 -11
  24. package/dist/setup-summary.d.ts +12 -0
  25. package/dist/setup-summary.d.ts.map +1 -0
  26. package/dist/setup-summary.js +56 -0
  27. package/package.json +1 -1
  28. package/src/credentials.ts +326 -57
  29. package/src/help.ts +7 -2
  30. package/src/http.ts +325 -20
  31. package/src/index.ts +193 -279
  32. package/src/keyboard-select.ts +97 -0
  33. package/src/logout-diagnostic.ts +39 -0
  34. package/src/setup-account.ts +129 -0
  35. package/src/setup-onboarding.ts +133 -10
  36. package/src/setup-summary.ts +77 -0
  37. package/vendor/skills/orizu/SKILL.md +51 -111
  38. package/vendor/skills/orizu/flows/expand.md +27 -0
  39. package/vendor/skills/orizu/flows/first-win.md +31 -0
  40. package/vendor/skills/orizu/flows/onboard.md +31 -0
  41. package/vendor/skills/orizu/flows/promote.md +33 -0
  42. package/vendor/skills/orizu/flows/recurse.md +23 -0
  43. package/vendor/skills/orizu/flows/triage.md +37 -0
  44. package/vendor/skills/orizu/references/assess-and-plan.md +5 -5
  45. package/vendor/skills/orizu/references/authority-map.md +20 -0
  46. package/vendor/skills/orizu/references/building-apps.md +7 -1
  47. package/vendor/skills/orizu/references/building-judges.md +15 -16
  48. package/vendor/skills/orizu/references/cli-reference.md +17 -8
  49. package/vendor/skills/orizu/references/dataset-design.md +16 -6
  50. package/vendor/skills/orizu/references/eval-strategy.md +18 -15
  51. package/vendor/skills/orizu/references/instructions-after-prompts.md +2 -2
  52. package/vendor/skills/orizu/references/migrate-existing-gepa-setup.md +10 -6
  53. package/vendor/skills/orizu/references/optimization-reports.md +11 -11
  54. package/vendor/skills/orizu/references/optimization-with-gepa.md +4 -2
  55. package/vendor/skills/orizu/references/primer.md +18 -22
  56. package/vendor/skills/orizu/references/prompt-control-plane.md +5 -5
  57. package/vendor/skills/orizu/references/vocabulary.md +27 -0
package/dist/http.js CHANGED
@@ -1,5 +1,6 @@
1
- import { getActiveBaseUrl, getServerCredentials, resolveEnvBearerToken, updateServerCredentials, } from './credentials.js';
1
+ import { clearServerCredentialsIfCurrent, credentialsEqual, getActiveBaseUrl, getServerCredentials, resolveEnvBearerToken, updateServerCredentialsIfCurrent, withCredentialsTransactionLockAsync, } from './credentials.js';
2
2
  import { getFlagBaseUrl, normalizeBaseUrl } from './global-flags.js';
3
+ import { describeLogoutHttpFailure, describeLogoutTransportFailure, } from './logout-diagnostic.js';
3
4
  let runtimeFlags = { local: false, server: null };
4
5
  export function setGlobalFlags(flags) {
5
6
  runtimeFlags = flags;
@@ -65,24 +66,79 @@ function isExpired(expiresAt) {
65
66
  const nowUnix = Math.floor(Date.now() / 1000);
66
67
  return expiresAt <= nowUnix + 30;
67
68
  }
69
+ const DEFAULT_CREDENTIALS_REFRESH_TIMEOUT_MS = 10_000;
70
+ const MAX_CREDENTIALS_REFRESH_TIMEOUT_MS = 30_000;
71
+ const DEFAULT_LOGOUT_TIMEOUT_MS = 10_000;
72
+ const MAX_LOGOUT_TIMEOUT_MS = 30_000;
73
+ function getCredentialsRefreshTimeoutMs() {
74
+ const configured = Number(process.env.ORIZU_CREDENTIALS_REFRESH_TIMEOUT_MS);
75
+ if (!Number.isSafeInteger(configured) || configured <= 0) {
76
+ return DEFAULT_CREDENTIALS_REFRESH_TIMEOUT_MS;
77
+ }
78
+ return Math.min(configured, MAX_CREDENTIALS_REFRESH_TIMEOUT_MS);
79
+ }
80
+ function getLogoutTimeoutMs() {
81
+ const configured = Number(process.env.ORIZU_LOGOUT_TIMEOUT_MS);
82
+ if (!Number.isSafeInteger(configured) || configured <= 0) {
83
+ return DEFAULT_LOGOUT_TIMEOUT_MS;
84
+ }
85
+ return Math.min(configured, MAX_LOGOUT_TIMEOUT_MS);
86
+ }
87
+ async function withAbortDeadline(operation, callerSignal, timeoutMs, timeoutError) {
88
+ const controller = new AbortController();
89
+ const handleCallerAbort = () => controller.abort(callerSignal?.reason);
90
+ if (callerSignal?.aborted)
91
+ handleCallerAbort();
92
+ else
93
+ callerSignal?.addEventListener('abort', handleCallerAbort, { once: true });
94
+ const timer = setTimeout(() => controller.abort(timeoutError), timeoutMs);
95
+ const cancellation = new Promise((_resolve, reject) => {
96
+ const handleAbort = () => reject(controller.signal.reason ?? new Error('Request aborted.'));
97
+ if (controller.signal.aborted)
98
+ handleAbort();
99
+ else
100
+ controller.signal.addEventListener('abort', handleAbort, { once: true });
101
+ });
102
+ try {
103
+ return await Promise.race([operation(controller.signal), cancellation]);
104
+ }
105
+ finally {
106
+ clearTimeout(timer);
107
+ callerSignal?.removeEventListener('abort', handleCallerAbort);
108
+ }
109
+ }
110
+ async function withCredentialsRefreshDeadline(operation, callerSignal) {
111
+ const timeoutMs = getCredentialsRefreshTimeoutMs();
112
+ const timeoutError = new Error(`ORIZU_CREDENTIALS_REFRESH_TIMEOUT: session refresh exceeded ${timeoutMs}ms.`);
113
+ return await withAbortDeadline(operation, callerSignal, timeoutMs, timeoutError);
114
+ }
115
+ async function withLogoutDeadline(operation, callerSignal) {
116
+ const timeoutMs = getLogoutTimeoutMs();
117
+ const timeoutError = new Error(`ORIZU_LOGOUT_TIMEOUT: remote logout exceeded ${timeoutMs}ms.`);
118
+ timeoutError.name = 'TimeoutError';
119
+ return await withAbortDeadline(operation, callerSignal, timeoutMs, timeoutError);
120
+ }
68
121
  export function credentialRequestRedirectPolicy() {
69
122
  return process.env.ORIZU_CLI_AUTH_REDIRECT === 'error' ? { redirect: 'error' } : {};
70
123
  }
71
- async function refreshCredentials(baseUrl, credentials) {
124
+ async function refreshCredentials(baseUrl, credentials, callerSignal) {
72
125
  if (!isSessionCredentials(credentials)) {
73
126
  throw new Error('API key credentials do not refresh. Run `orizu login` again if access fails.');
74
127
  }
75
128
  assertSecureTokenTransport(baseUrl);
76
- const response = await fetch(`${baseUrl}/api/cli/auth/refresh`, {
77
- method: 'POST',
78
- headers: { 'Content-Type': 'application/json' },
79
- body: JSON.stringify({ refreshToken: credentials.refreshToken }),
80
- ...credentialRequestRedirectPolicy(),
81
- });
82
- if (!response.ok) {
83
- throw new Error('Session expired. Run `orizu login` again.');
84
- }
85
- const data = await response.json();
129
+ const data = await withCredentialsRefreshDeadline(async (signal) => {
130
+ const response = await fetch(`${baseUrl}/api/cli/auth/refresh`, {
131
+ method: 'POST',
132
+ headers: { 'Content-Type': 'application/json' },
133
+ body: JSON.stringify({ refreshToken: credentials.refreshToken }),
134
+ ...credentialRequestRedirectPolicy(),
135
+ signal,
136
+ });
137
+ if (!response.ok) {
138
+ throw new Error('Session expired. Run `orizu login` again.');
139
+ }
140
+ return await response.json();
141
+ }, callerSignal);
86
142
  if (!data.accessToken || !data.refreshToken || !data.expiresAt) {
87
143
  throw new Error('Server returned invalid refresh credentials. Run `orizu login` again.');
88
144
  }
@@ -92,9 +148,169 @@ async function refreshCredentials(baseUrl, credentials) {
92
148
  refreshToken: data.refreshToken,
93
149
  expiresAt: data.expiresAt,
94
150
  };
95
- updateServerCredentials(baseUrl, refreshed);
151
+ if (isExpired(refreshed.expiresAt)) {
152
+ throw new Error('ORIZU_CREDENTIALS_REFRESH_UNUSABLE: refreshed session expires within the 30-second safety window.');
153
+ }
96
154
  return refreshed;
97
155
  }
156
+ async function refreshStoredCredentialsIfCurrent(baseUrl, expected, callerSignal) {
157
+ return await withCredentialsTransactionLockAsync(async () => {
158
+ const current = getServerCredentials(baseUrl);
159
+ if (!current || !credentialsEqual(current, expected)) {
160
+ return { status: 'changed', credentials: current };
161
+ }
162
+ const refreshed = await refreshCredentials(baseUrl, expected, callerSignal);
163
+ if (!updateServerCredentialsIfCurrent(baseUrl, expected, refreshed)) {
164
+ return { status: 'changed', credentials: getServerCredentials(baseUrl) };
165
+ }
166
+ return { status: 'refreshed', credentials: refreshed };
167
+ });
168
+ }
169
+ function requireCurrentCredentials(baseUrl, credentials) {
170
+ if (credentials)
171
+ return credentials;
172
+ throw new Error(`Not logged in for ${baseUrl}. Run \`orizu login --server ${baseUrl}\` (or \`--local\`) first.`);
173
+ }
174
+ async function refreshExpiredStoredCredentials(baseUrl, initial, callerSignal) {
175
+ if (!isSessionCredentials(initial) || !isExpired(initial.expiresAt)) {
176
+ return { credentials: initial, didRefresh: false };
177
+ }
178
+ const result = await refreshStoredCredentialsIfCurrent(baseUrl, initial, callerSignal);
179
+ if (result.status === 'changed') {
180
+ throw new Error('ORIZU_AUTH_CONTEXT_CHANGED: stored credentials changed before this request began; the request was not sent.');
181
+ }
182
+ return {
183
+ credentials: requireCurrentCredentials(baseUrl, result.credentials),
184
+ didRefresh: true,
185
+ };
186
+ }
187
+ /**
188
+ * Captures one authenticated server context for a multi-step operation. The
189
+ * returned interface intentionally exposes no credential material: its closure
190
+ * pins the selected origin and bearer/session so later global config changes
191
+ * cannot move an in-flight operation to another account.
192
+ */
193
+ export function captureAuthenticatedRequestContext(options = {}) {
194
+ const baseUrl = resolveBaseUrl();
195
+ const allowRefresh = options.allowRefresh ?? true;
196
+ assertSecureTokenTransport(baseUrl);
197
+ const envBearer = resolveEnvBearerToken();
198
+ if (envBearer) {
199
+ const explicitBearer = process.env.ORIZU_TOKEN?.trim() || null;
200
+ const source = explicitBearer
201
+ ? 'ORIZU_TOKEN'
202
+ : 'ORIZU_TOKEN_FILE';
203
+ const resolveRequestBearer = () => {
204
+ if (explicitBearer)
205
+ return explicitBearer;
206
+ try {
207
+ const currentBearer = resolveEnvBearerToken();
208
+ if (currentBearer)
209
+ return currentBearer;
210
+ }
211
+ catch (error) {
212
+ const detail = error instanceof Error ? error.message : String(error);
213
+ throw new Error(`${detail} Fix, rotate, or unset ORIZU_TOKEN_FILE and rerun \`orizu setup\`.`);
214
+ }
215
+ throw new Error('ORIZU_TOKEN_FILE no longer supplies a token. Fix, rotate, or unset ORIZU_TOKEN_FILE and rerun `orizu setup`.');
216
+ };
217
+ return {
218
+ baseUrl,
219
+ source,
220
+ async fetch(path, init = {}) {
221
+ return await fetch(`${baseUrl}${path}`, {
222
+ ...init,
223
+ headers: {
224
+ ...(init.headers || {}),
225
+ Authorization: `Bearer ${resolveRequestBearer()}`,
226
+ },
227
+ ...credentialRequestRedirectPolicy(),
228
+ });
229
+ },
230
+ async logout() {
231
+ throw new Error(`Authentication is supplied by ${source}; stored logout is unavailable.`);
232
+ },
233
+ };
234
+ }
235
+ const capturedCredentials = getServerCredentials(baseUrl);
236
+ if (!capturedCredentials) {
237
+ throw new Error(`Not logged in for ${baseUrl}. Run \`orizu login --server ${baseUrl}\` (or \`--local\`) first.`);
238
+ }
239
+ let activeCredentials = capturedCredentials;
240
+ async function refreshCapturedCredentials(callerSignal) {
241
+ const result = await refreshStoredCredentialsIfCurrent(baseUrl, activeCredentials, callerSignal);
242
+ if (result.status === 'changed') {
243
+ throw new Error('ORIZU_CREDENTIALS_CHANGED: stored credentials changed before the captured session could refresh. Reconfirm the account and retry.');
244
+ }
245
+ activeCredentials = requireCurrentCredentials(baseUrl, result.credentials);
246
+ }
247
+ return {
248
+ baseUrl,
249
+ source: 'stored',
250
+ async fetch(path, init = {}) {
251
+ let didRefresh = false;
252
+ if (allowRefresh
253
+ && isSessionCredentials(activeCredentials)
254
+ && isExpired(activeCredentials.expiresAt)) {
255
+ await refreshCapturedCredentials(init.signal);
256
+ didRefresh = true;
257
+ }
258
+ let response = await fetch(`${baseUrl}${path}`, {
259
+ ...init,
260
+ headers: {
261
+ ...(init.headers || {}),
262
+ Authorization: `Bearer ${getAuthorizationToken(activeCredentials)}`,
263
+ },
264
+ ...credentialRequestRedirectPolicy(),
265
+ });
266
+ if (allowRefresh
267
+ && response.status === 401
268
+ && isSessionCredentials(activeCredentials)
269
+ && !didRefresh) {
270
+ await refreshCapturedCredentials(init.signal);
271
+ response = await fetch(`${baseUrl}${path}`, {
272
+ ...init,
273
+ headers: {
274
+ ...(init.headers || {}),
275
+ Authorization: `Bearer ${getAuthorizationToken(activeCredentials)}`,
276
+ },
277
+ ...credentialRequestRedirectPolicy(),
278
+ });
279
+ }
280
+ return response;
281
+ },
282
+ async logout(callerSignal) {
283
+ const authorizationToken = getAuthorizationToken(activeCredentials);
284
+ const secrets = [
285
+ authorizationToken,
286
+ ...(isSessionCredentials(activeCredentials) ? [activeCredentials.refreshToken] : []),
287
+ ];
288
+ let remoteLogoutReason = null;
289
+ try {
290
+ remoteLogoutReason = await withLogoutDeadline(async (signal) => {
291
+ const response = await fetch(`${baseUrl}/api/cli/auth/logout`, {
292
+ method: 'POST',
293
+ headers: {
294
+ 'Content-Type': 'application/json',
295
+ Authorization: `Bearer ${authorizationToken}`,
296
+ },
297
+ body: isSessionCredentials(activeCredentials)
298
+ ? JSON.stringify({ refreshToken: activeCredentials.refreshToken })
299
+ : undefined,
300
+ ...credentialRequestRedirectPolicy(),
301
+ signal,
302
+ });
303
+ return response.ok ? null : await describeLogoutHttpFailure(response, secrets);
304
+ }, callerSignal);
305
+ }
306
+ catch (error) {
307
+ remoteLogoutReason = describeLogoutTransportFailure(error, secrets);
308
+ }
309
+ const localCredentialCleared = clearServerCredentialsIfCurrent(baseUrl, activeCredentials);
310
+ return { remoteError: remoteLogoutReason, localCredentialCleared };
311
+ },
312
+ };
313
+ }
98
314
  export async function authedFetch(path, init = {}) {
99
315
  const baseUrl = resolveBaseUrl();
100
316
  assertSecureTokenTransport(baseUrl);
@@ -118,10 +334,8 @@ export async function authedFetch(path, init = {}) {
118
334
  if (!credentials) {
119
335
  throw new Error(`Not logged in for ${baseUrl}. Run \`orizu login --server ${baseUrl}\` (or \`--local\`) first.`);
120
336
  }
121
- let activeCredentials = credentials;
122
- if (isSessionCredentials(activeCredentials) && isExpired(activeCredentials.expiresAt)) {
123
- activeCredentials = await refreshCredentials(baseUrl, activeCredentials);
124
- }
337
+ const resolved = await refreshExpiredStoredCredentials(baseUrl, credentials, init.signal);
338
+ let activeCredentials = resolved.credentials;
125
339
  let response = await fetch(`${baseUrl}${path}`, {
126
340
  ...init,
127
341
  headers: {
@@ -130,8 +344,14 @@ export async function authedFetch(path, init = {}) {
130
344
  },
131
345
  ...credentialRequestRedirectPolicy(),
132
346
  });
133
- if (response.status === 401 && isSessionCredentials(activeCredentials)) {
134
- activeCredentials = await refreshCredentials(baseUrl, activeCredentials);
347
+ if (response.status === 401
348
+ && isSessionCredentials(activeCredentials)
349
+ && !resolved.didRefresh) {
350
+ const refreshResult = await refreshStoredCredentialsIfCurrent(baseUrl, activeCredentials, init.signal);
351
+ if (refreshResult.status === 'changed') {
352
+ throw new Error('ORIZU_AUTH_CONTEXT_CHANGED: stored credentials changed after this request began; the request was not replayed.');
353
+ }
354
+ activeCredentials = requireCurrentCredentials(baseUrl, refreshResult.credentials);
135
355
  response = await fetch(`${baseUrl}${path}`, {
136
356
  ...init,
137
357
  headers: {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAuGgF,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAoChJ,eAAO,MAAM,wBAAwB,GAAI,iBAAiB,MAAM;;;EACJ,CAAA;AAE5D,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAgU5E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAUD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOnD;AAyBD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE5D;AAsBD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,GAAG,CAqB5E;AA6BD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAgBtD;AAylKD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CA0B1G;AAED,eAAO,MAAM,iBAAiB,SAAU,CAAA;AACxC,eAAO,MAAM,uBAAuB,QAAkB,CAAA;AACtD,eAAO,MAAM,oBAAoB,aAgB/B,CAAA;AAEF,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,CAaxH;AAmMD,wBAAsB,IAAI,CAAC,OAAO,WAAwB,iBAwgBzD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAwHgF,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAoChJ,eAAO,MAAM,wBAAwB,GAAI,iBAAiB,MAAM;;;EACJ,CAAA;AAE5D,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAgU5E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAUD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOnD;AAyBD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE5D;AAsBD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,GAAG,CAqB5E;AA6BD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAgBtD;AAk/JD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CA0B1G;AAED,eAAO,MAAM,iBAAiB,SAAU,CAAA;AACxC,eAAO,MAAM,uBAAuB,QAAkB,CAAA;AACtD,eAAO,MAAM,oBAAoB,aAgB/B,CAAA;AAEF,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,CAaxH;AAmMD,wBAAsB,IAAI,CAAC,OAAO,WAAwB,iBAwgBzD"}