orizu 0.5.50 → 0.5.52

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 (61) hide show
  1. package/dist/app-commands.d.ts.map +1 -1
  2. package/dist/app-commands.js +4 -12
  3. package/dist/auth-commands.d.ts.map +1 -1
  4. package/dist/auth-commands.js +13 -112
  5. package/dist/cli-write-commands.d.ts +143 -0
  6. package/dist/cli-write-commands.d.ts.map +1 -0
  7. package/dist/cli-write-commands.js +123 -0
  8. package/dist/credentials-transaction.d.ts +7 -0
  9. package/dist/credentials-transaction.d.ts.map +1 -0
  10. package/dist/credentials-transaction.js +60 -0
  11. package/dist/credentials.d.ts +19 -1
  12. package/dist/credentials.d.ts.map +1 -1
  13. package/dist/credentials.js +118 -73
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +44 -50
  16. package/dist/last-error-record.d.ts +7 -0
  17. package/dist/last-error-record.d.ts.map +1 -1
  18. package/dist/last-error-record.js +14 -2
  19. package/dist/login-flow.d.ts +37 -0
  20. package/dist/login-flow.d.ts.map +1 -0
  21. package/dist/login-flow.js +295 -0
  22. package/dist/logout-diagnostic.d.ts.map +1 -1
  23. package/dist/logout-diagnostic.js +2 -1
  24. package/dist/manifests-cli.d.ts.map +1 -1
  25. package/dist/manifests-cli.js +36 -22
  26. package/dist/one-request-write.d.ts +59 -0
  27. package/dist/one-request-write.d.ts.map +1 -0
  28. package/dist/one-request-write.js +157 -0
  29. package/dist/provider-write-workflow.d.ts +12 -0
  30. package/dist/provider-write-workflow.d.ts.map +1 -0
  31. package/dist/provider-write-workflow.js +110 -0
  32. package/dist/providers-cli.d.ts.map +1 -1
  33. package/dist/providers-cli.js +27 -23
  34. package/dist/scores-accept-cli.d.ts +1 -1
  35. package/dist/scores-accept-cli.d.ts.map +1 -1
  36. package/dist/scores-accept-cli.js +4 -28
  37. package/dist/stored-session-requests.d.ts.map +1 -1
  38. package/dist/stored-session-requests.js +8 -4
  39. package/dist/task-commands.d.ts.map +1 -1
  40. package/dist/task-commands.js +26 -62
  41. package/package.json +1 -1
  42. package/src/app-commands.ts +3 -18
  43. package/src/auth-commands.ts +17 -131
  44. package/src/cli-write-commands.ts +143 -0
  45. package/src/credentials-transaction.ts +73 -0
  46. package/src/credentials.ts +128 -86
  47. package/src/index.ts +23 -57
  48. package/src/last-error-record.ts +16 -2
  49. package/src/login-flow.ts +368 -0
  50. package/src/logout-diagnostic.ts +2 -1
  51. package/src/manifests-cli.ts +36 -23
  52. package/src/one-request-write.ts +192 -0
  53. package/src/provider-write-workflow.ts +117 -0
  54. package/src/providers-cli.ts +24 -17
  55. package/src/scores-accept-cli.ts +4 -46
  56. package/src/stored-session-requests.ts +7 -4
  57. package/src/task-commands.ts +22 -115
  58. package/dist/headless-login.d.ts +0 -19
  59. package/dist/headless-login.d.ts.map +0 -1
  60. package/dist/headless-login.js +0 -108
  61. package/src/headless-login.ts +0 -192
@@ -9,6 +9,11 @@ import { canonicalServerUrl, normalizeBaseUrl } from './global-flags.js';
9
9
  function isNodeError(error) {
10
10
  return error instanceof Error;
11
11
  }
12
+ // The system's short error code (EACCES, EPERM), never the message: messages
13
+ // from fs calls carry paths, and this one goes to the terminal.
14
+ function errorCode(error) {
15
+ return isNodeError(error) && typeof error.code === 'string' ? error.code : 'unknown error';
16
+ }
12
17
  export function getConfigDir() {
13
18
  if (process.env.ORIZU_CONFIG_DIR) {
14
19
  return process.env.ORIZU_CONFIG_DIR;
@@ -20,7 +25,7 @@ function getCredentialsPath() {
20
25
  }
21
26
  const DEFAULT_CREDENTIALS_LOCK_TIMEOUT_MS = 2_000;
22
27
  const MAX_CREDENTIALS_LOCK_TIMEOUT_MS = 10_000;
23
- const CREDENTIALS_LOCK_RETRY_MS = 20;
28
+ export const CREDENTIALS_LOCK_RETRY_MS = 20;
24
29
  let heldCredentialsLock = null;
25
30
  const credentialsTransactionOwner = new AsyncLocalStorage();
26
31
  function getCredentialsLockPath() {
@@ -38,58 +43,74 @@ function readLockDuration(name, fallback, maximum = Number.MAX_SAFE_INTEGER) {
38
43
  function sleepSync(milliseconds) {
39
44
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
40
45
  }
41
- function acquireCredentialsLock() {
46
+ export function credentialsLockTimeoutMs() {
47
+ return readLockDuration('ORIZU_CREDENTIALS_LOCK_TIMEOUT_MS', DEFAULT_CREDENTIALS_LOCK_TIMEOUT_MS, MAX_CREDENTIALS_LOCK_TIMEOUT_MS);
48
+ }
49
+ export function credentialsLockTimeoutError(timeoutMs) {
50
+ return new Error(`ORIZU_CREDENTIALS_LOCK_TIMEOUT: credentials mutation did not acquire the lock within ${timeoutMs}ms. Verify no Orizu process is running, then remove credentials.json.lock manually.`);
51
+ }
52
+ /**
53
+ * One try at the cross-process lock. Returns null when another owner holds
54
+ * it. The waits between tries belong to the caller: a blocking one below for
55
+ * synchronous writers, an Effect sleep in credentials-transaction.ts.
56
+ */
57
+ export function tryAcquireCredentialsLock() {
42
58
  const dir = getConfigDir();
43
59
  mkdirSync(dir, { recursive: true, mode: 0o700 });
44
60
  chmodSync(dir, 0o700);
45
61
  const lockPath = getCredentialsLockPath();
46
- const timeoutMs = readLockDuration('ORIZU_CREDENTIALS_LOCK_TIMEOUT_MS', DEFAULT_CREDENTIALS_LOCK_TIMEOUT_MS, MAX_CREDENTIALS_LOCK_TIMEOUT_MS);
47
- const deadline = Date.now() + timeoutMs;
48
- while (true) {
49
- const metadata = JSON.stringify({
50
- version: 1,
51
- pid: process.pid,
52
- hostname: hostname(),
53
- createdAt: Date.now(),
54
- instanceId: randomBytes(16).toString('hex'),
55
- });
56
- const tempPath = join(dir, `.credentials.json.lock.${process.pid}.${randomBytes(8).toString('hex')}.tmp`);
62
+ const metadata = JSON.stringify({
63
+ version: 1,
64
+ pid: process.pid,
65
+ hostname: hostname(),
66
+ createdAt: Date.now(),
67
+ instanceId: randomBytes(16).toString('hex'),
68
+ });
69
+ const tempPath = join(dir, `.credentials.json.lock.${process.pid}.${randomBytes(8).toString('hex')}.tmp`);
70
+ try {
71
+ const fd = openSync(tempPath, 'wx', 0o600);
57
72
  try {
58
- const fd = openSync(tempPath, 'wx', 0o600);
59
- try {
60
- writeFileSync(fd, metadata, 'utf8');
61
- fsyncSync(fd);
62
- chmodSync(tempPath, 0o600);
63
- }
64
- finally {
65
- closeSync(fd);
66
- }
67
- try {
68
- // link(2) publishes the already-complete inode without replacing an
69
- // existing owner. No contender ever unlinks the canonical lock.
70
- linkSync(tempPath, lockPath);
71
- }
72
- catch (error) {
73
- if (!(isNodeError(error) && error.code === 'EEXIST'))
74
- throw error;
75
- if (Date.now() >= deadline) {
76
- throw new Error(`ORIZU_CREDENTIALS_LOCK_TIMEOUT: credentials mutation did not acquire the lock within ${timeoutMs}ms. Verify no Orizu process is running, then remove credentials.json.lock manually.`);
77
- }
78
- sleepSync(Math.min(CREDENTIALS_LOCK_RETRY_MS, Math.max(1, deadline - Date.now())));
79
- continue;
80
- }
81
- let resolveReleased = () => { };
82
- const released = new Promise(resolve => { resolveReleased = resolve; });
83
- return { path: lockPath, metadata, depth: 1, released, resolveReleased };
73
+ writeFileSync(fd, metadata, 'utf8');
74
+ fsyncSync(fd);
75
+ chmodSync(tempPath, 0o600);
84
76
  }
85
77
  finally {
86
- // The unique owner-only preparation link is never retained, whether
87
- // publication succeeds, loses to an owner, or fails unexpectedly.
88
- rmSync(tempPath, { force: true });
78
+ closeSync(fd);
79
+ }
80
+ try {
81
+ // link(2) publishes the already-complete inode without replacing an
82
+ // existing owner. No contender ever unlinks the canonical lock.
83
+ linkSync(tempPath, lockPath);
84
+ }
85
+ catch (error) {
86
+ if (isNodeError(error) && error.code === 'EEXIST')
87
+ return null;
88
+ throw error;
89
89
  }
90
+ let resolveReleased = () => { };
91
+ const released = new Promise(resolve => { resolveReleased = resolve; });
92
+ return { path: lockPath, metadata, depth: 1, released, resolveReleased };
93
+ }
94
+ finally {
95
+ // The unique owner-only preparation link is never retained, whether
96
+ // publication succeeds, loses to an owner, or fails unexpectedly.
97
+ rmSync(tempPath, { force: true });
90
98
  }
91
99
  }
92
- function releaseCredentialsLock(lock) {
100
+ /** Blocks the process between tries; only for synchronous writers. */
101
+ function acquireCredentialsLock() {
102
+ const timeoutMs = credentialsLockTimeoutMs();
103
+ const deadline = Date.now() + timeoutMs;
104
+ while (true) {
105
+ const lock = tryAcquireCredentialsLock();
106
+ if (lock)
107
+ return lock;
108
+ if (Date.now() >= deadline)
109
+ throw credentialsLockTimeoutError(timeoutMs);
110
+ sleepSync(Math.min(CREDENTIALS_LOCK_RETRY_MS, Math.max(1, deadline - Date.now())));
111
+ }
112
+ }
113
+ export function releaseCredentialsLock(lock) {
93
114
  let currentMetadata;
94
115
  try {
95
116
  currentMetadata = readFileSync(lock.path, 'utf8');
@@ -129,17 +150,30 @@ export function withCredentialsTransactionLock(operation) {
129
150
  }
130
151
  const lock = acquireCredentialsLock();
131
152
  heldCredentialsLock = lock;
153
+ let failed = false;
132
154
  try {
133
155
  return operation();
134
156
  }
157
+ catch (error) {
158
+ failed = true;
159
+ throw error;
160
+ }
135
161
  finally {
136
162
  heldCredentialsLock = null;
137
- try {
138
- releaseCredentialsLock(lock);
139
- }
140
- finally {
141
- lock.resolveReleased();
142
- }
163
+ endTransaction(lock, failed);
164
+ }
165
+ }
166
+ /** Release the lock if it is still ours. A release error never hides the transaction's own error. */
167
+ function endTransaction(lock, failed) {
168
+ try {
169
+ releaseCredentialsLock(lock);
170
+ }
171
+ catch (releaseError) {
172
+ if (!failed)
173
+ throw releaseError;
174
+ }
175
+ finally {
176
+ lock.resolveReleased();
143
177
  }
144
178
  }
145
179
  /**
@@ -147,7 +181,10 @@ export function withCredentialsTransactionLock(operation) {
147
181
  * synchronous credential writes are admitted only from this transaction's
148
182
  * async execution context; unrelated same-process work must wait or fail.
149
183
  */
150
- export async function withCredentialsTransactionLockAsync(operation) {
184
+ export async function withCredentialsTransactionLockAsync(operation,
185
+ // How to wait for the cross-process lock. The default blocks the process
186
+ // between tries; stored-login commands pass credentials-transaction.ts's.
187
+ acquire = acquireCredentialsLock) {
151
188
  const lockPath = getCredentialsLockPath();
152
189
  const currentOwner = credentialsTransactionOwner.getStore();
153
190
  if (heldCredentialsLock) {
@@ -163,7 +200,7 @@ export async function withCredentialsTransactionLockAsync(operation) {
163
200
  heldCredentialsLock.depth -= 1;
164
201
  }
165
202
  }
166
- const timeoutMs = readLockDuration('ORIZU_CREDENTIALS_LOCK_TIMEOUT_MS', DEFAULT_CREDENTIALS_LOCK_TIMEOUT_MS, MAX_CREDENTIALS_LOCK_TIMEOUT_MS);
203
+ const timeoutMs = credentialsLockTimeoutMs();
167
204
  let timer;
168
205
  const didRelease = await Promise.race([
169
206
  heldCredentialsLock.released.then(() => true),
@@ -171,26 +208,25 @@ export async function withCredentialsTransactionLockAsync(operation) {
171
208
  ]);
172
209
  if (timer)
173
210
  clearTimeout(timer);
174
- if (!didRelease) {
175
- throw new Error(`ORIZU_CREDENTIALS_LOCK_TIMEOUT: credentials mutation did not acquire the lock within ${timeoutMs}ms. Verify no Orizu process is running, then remove credentials.json.lock manually.`);
176
- }
177
- return await withCredentialsTransactionLockAsync(operation);
211
+ if (!didRelease)
212
+ throw credentialsLockTimeoutError(timeoutMs);
213
+ return await withCredentialsTransactionLockAsync(operation, acquire);
178
214
  }
179
- const lock = acquireCredentialsLock();
215
+ const lock = await acquire();
180
216
  const owner = Symbol('credentials-transaction-owner');
181
217
  lock.asyncOwner = owner;
182
218
  heldCredentialsLock = lock;
219
+ let failed = false;
183
220
  try {
184
221
  return await credentialsTransactionOwner.run(owner, operation);
185
222
  }
223
+ catch (error) {
224
+ failed = true;
225
+ throw error;
226
+ }
186
227
  finally {
187
228
  heldCredentialsLock = null;
188
- try {
189
- releaseCredentialsLock(lock);
190
- }
191
- finally {
192
- lock.resolveReleased();
193
- }
229
+ endTransaction(lock, failed);
194
230
  }
195
231
  }
196
232
  function assertCredentialsLockHeld() {
@@ -244,18 +280,24 @@ function writeCredentials(config) {
244
280
  const tempPath = join(dir, `.credentials.json.${process.pid}.${randomBytes(8).toString('hex')}.tmp`);
245
281
  const payload = JSON.stringify(config, null, 2) + '\n';
246
282
  try {
247
- const fd = openSync(tempPath, 'wx', 0o600);
248
283
  try {
249
- writeFileSync(fd, payload, 'utf-8');
250
- fsyncSync(fd);
284
+ const fd = openSync(tempPath, 'wx', 0o600);
285
+ try {
286
+ writeFileSync(fd, payload, 'utf-8');
287
+ fsyncSync(fd);
288
+ }
289
+ finally {
290
+ closeSync(fd);
291
+ }
292
+ chmodSync(tempPath, 0o600);
293
+ // Never delete the destination to emulate replacement. If this platform
294
+ // cannot atomically replace it, preserve the old file and surface the error.
295
+ renameSync(tempPath, path);
251
296
  }
252
- finally {
253
- closeSync(fd);
297
+ catch (error) {
298
+ // Until the rename succeeds the saved file is untouched.
299
+ throw new Error(`ORIZU_CREDENTIALS_WRITE_FAILED: could not save sign-ins to ${path} (${errorCode(error)}). The saved file is unchanged.`, { cause: error });
254
300
  }
255
- chmodSync(tempPath, 0o600);
256
- // Never delete the destination to emulate replacement. If this platform
257
- // cannot atomically replace it, preserve the old file and surface the error.
258
- renameSync(tempPath, path);
259
301
  chmodSync(path, 0o600);
260
302
  }
261
303
  finally {
@@ -271,12 +313,15 @@ function createEmptyCredentialsConfig() {
271
313
  servers: {},
272
314
  };
273
315
  }
316
+ // A missing file, invalid JSON or an unknown format start empty (the "log in
317
+ // again" recovery). A file that exists but cannot be read is never replaced:
318
+ // that would delete every other saved sign-in.
274
319
  function loadCredentialsConfigForWrite() {
275
320
  try {
276
321
  return loadCredentialsConfig() || createEmptyCredentialsConfig();
277
322
  }
278
- catch {
279
- return createEmptyCredentialsConfig();
323
+ catch (error) {
324
+ throw new Error(`ORIZU_CREDENTIALS_READ_FAILED: could not read ${getCredentialsPath()} (${errorCode(error)}). Saved sign-ins were not changed. Move it aside, then run \`orizu login\` again.`, { cause: error });
280
325
  }
281
326
  }
282
327
  export function loadCredentialsConfig() {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAsLA,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAEhE,eAAO,MAAM,wBAAwB,GAAI,iBAAiB,MAAM;;;EACJ,CAAA;AAE5D,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AA+sCtD,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,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,CAexH;AAmMD,wBAAsB,IAAI,CAAC,OAAO,WAAwB,iBAiiBzD;AA8DD,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAEhF,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAuLA,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAEhE,eAAO,MAAM,wBAAwB,GAAI,iBAAiB,MAAM;;;EACJ,CAAA;AAE5D,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAmqCtD,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,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,CAexH;AAmMD,wBAAsB,IAAI,CAAC,OAAO,WAAwB,iBAiiBzD;AAuED,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAEhF,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA"}
package/dist/index.js CHANGED
@@ -62,7 +62,8 @@ import { killSwitchCommand } from './kill-switch-cli.js';
62
62
  import { egressAllowlistCommand } from './egress-allowlist-cli.js';
63
63
  import { teamConnectorsCommand } from './team-connectors-cli.js';
64
64
  import { inspectExistingWorkspaceTeam, getWorkspaceRoot } from './workspace.js';
65
- import { writeLastErrorRecord, extractLastErrorCode } from './last-error-record.js';
65
+ import { errorOutputSecrets, writeLastErrorRecord, extractLastErrorCode } from './last-error-record.js';
66
+ import { redactSecrets } from './secret-redaction.js';
66
67
  export { loadInstructionSet } from './instruction-set-loader.js';
67
68
  export const materializeRunnerVersion = (runnerVersionId) => materializeRegisteredRunnerVersion(runnerVersionId, false);
68
69
  export { parseJsonResponse, sanitizeTerminalText } from './json-response.js';
@@ -123,26 +124,21 @@ async function logOptimizationEvent() {
123
124
  if (!eventType || eventType.startsWith('--') || !runId || !Number.isInteger(sequence) || sequence <= 0) {
124
125
  throw new Error('Usage: orizu log <event_type> --run-id <id> --sequence <n> --payload @event.json');
125
126
  }
126
- const response = await authedFetch(`/api/cli/optimization-runs/${encodeURIComponent(runId)}/events`, {
127
- method: 'POST',
128
- headers: { 'Content-Type': 'application/json' },
129
- body: JSON.stringify({
130
- eventId: getArg('--event-id') || randomUUID(),
131
- sequence,
132
- eventType,
133
- eventLayer: getArg('--event-layer') || 'core',
134
- optimizerFamily: getArg('--optimizer-family'),
135
- iteration: getArg('--iteration') ? Number(getArg('--iteration')) : undefined,
136
- candidateId: getArg('--candidate-id'),
137
- parentCandidateId: getArg('--parent-candidate-id'),
138
- childCandidateId: getArg('--child-candidate-id'),
139
- payload,
140
- }),
141
- });
142
- if (!response.ok) {
143
- throw new Error(`Failed to log event: ${await response.text()}`);
144
- }
145
- const data = await parseJsonResponse(response, 'Optimization event log');
127
+ const writes = await import('./cli-write-commands.js');
128
+ const data = await writes.sendCommandWrite(writes.optimizationEvent(runId, {
129
+ eventId: getArg('--event-id') || randomUUID(),
130
+ sequence,
131
+ eventType,
132
+ eventLayer: getArg('--event-layer') || 'core',
133
+ optimizerFamily: getArg('--optimizer-family'),
134
+ iteration: getArg('--iteration') ? Number(getArg('--iteration')) : undefined,
135
+ candidateId: getArg('--candidate-id'),
136
+ parentCandidateId: getArg('--parent-candidate-id'),
137
+ childCandidateId: getArg('--child-candidate-id'),
138
+ payload,
139
+ }), { json: hasJsonFlag(), print: printLine });
140
+ if (!data)
141
+ return;
146
142
  if (hasJsonFlag()) {
147
143
  printJson({ eventId: data.eventId });
148
144
  return;
@@ -822,25 +818,20 @@ async function startOptimizationRun() {
822
818
  trackedScope: 'best_candidate',
823
819
  })),
824
820
  ];
825
- const response = await authedFetch(`/api/cli/optimization-runs?project=${encodeURIComponent(project)}`, {
826
- method: 'POST',
827
- headers: { 'Content-Type': 'application/json' },
828
- body: JSON.stringify({
829
- optimizerVersionId,
830
- promptVersionIds,
831
- judgeVersionIds,
832
- scorers,
833
- datasetVersionId,
834
- splitSetId,
835
- trainSplitName: getArg('--train-split') || undefined,
836
- validationSplitName: getArg('--validation-split') || undefined,
837
- metadata,
838
- }),
839
- });
840
- if (!response.ok) {
841
- throw new Error(`Failed to start optimization run: ${await response.text()}`);
842
- }
843
- const data = await parseJsonResponse(response, 'Optimization run start');
821
+ const writes = await import('./cli-write-commands.js');
822
+ const data = await writes.sendCommandWrite(writes.optimizationStart(project, {
823
+ optimizerVersionId,
824
+ promptVersionIds,
825
+ judgeVersionIds,
826
+ scorers,
827
+ datasetVersionId,
828
+ splitSetId,
829
+ trainSplitName: getArg('--train-split') || undefined,
830
+ validationSplitName: getArg('--validation-split') || undefined,
831
+ metadata,
832
+ }), { json: hasJsonFlag(), print: printLine });
833
+ if (!data)
834
+ return;
844
835
  if (hasJsonFlag()) {
845
836
  printJson(data);
846
837
  return;
@@ -897,15 +888,10 @@ async function updateOptimizationRunLifecycle(action) {
897
888
  body.resultPromptVersionId = resultPromptVersionId;
898
889
  }
899
890
  }
900
- const response = await authedFetch(`/api/cli/optimization-runs/${encodeURIComponent(runId)}`, {
901
- method: 'PATCH',
902
- headers: { 'Content-Type': 'application/json' },
903
- body: JSON.stringify(body),
904
- });
905
- if (!response.ok) {
906
- throw new Error(`Failed to update optimization run: ${await response.text()}`);
907
- }
908
- const data = await parseJsonResponse(response, 'Optimization run update');
891
+ const writes = await import('./cli-write-commands.js');
892
+ const data = await writes.sendCommandWrite(writes.optimizationLifecycle(runId, body), { json: hasJsonFlag(), print: printLine });
893
+ if (!data)
894
+ return;
909
895
  if (hasJsonFlag()) {
910
896
  printJson(data);
911
897
  return;
@@ -1805,9 +1791,17 @@ function isCliEntrypoint() {
1805
1791
  return false;
1806
1792
  }
1807
1793
  }
1794
+ // A secret this process holds (a login code, its verifier, a poll token, a
1795
+ // bearer, a forwarded runner key) is never printed, even when a server echoes
1796
+ // it back in an error. last-error.json hides the same values. Scrubbed again
1797
+ // after control characters are stripped, so one inside a secret can't hide it.
1798
+ function printErrorWithoutSecrets(message) {
1799
+ const secrets = errorOutputSecrets();
1800
+ printError(redactSecrets(sanitizeTerminalText(redactSecrets(message, { secrets })), { secrets }));
1801
+ }
1808
1802
  if (isCliEntrypoint()) {
1809
1803
  const rawArgs = process.argv.slice(2);
1810
- withPassiveUpdateNotice(rawArgs, getCliVersion, printError, args =>
1804
+ withPassiveUpdateNotice(rawArgs, getCliVersion, printErrorWithoutSecrets, args =>
1811
1805
  // ORI-2429: the process-boundary adapter stays a Promise (ADR-038 guide); the record write is Effect-owned and never replaces this error.
1812
1806
  main(args).catch(async (error) => {
1813
1807
  const message = error instanceof Error ? error.message : 'Unknown error';
@@ -29,6 +29,13 @@ export interface LastErrorRecordInput {
29
29
  export interface WriteLastErrorRecordOptions {
30
30
  configDir?: string;
31
31
  }
32
+ /**
33
+ * The values no error output may show: this process's remembered secrets, the
34
+ * current bearer and runner-forwarded keys. last-error.json and the terminal's
35
+ * error line both use this list. Longest first, so a secret inside another
36
+ * can't leave part of the longer one behind.
37
+ */
38
+ export declare function errorOutputSecrets(): string[];
32
39
  export declare function extractLastErrorCode(error: unknown): string | null;
33
40
  export declare function isStringWithinCap(value: unknown, maximumCharacters: number, wasTruncated: boolean): value is string;
34
41
  export declare function boundedServerBaseUrl(value: string | null | undefined): string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"last-error-record.d.ts","sourceRoot":"","sources":["../src/last-error-record.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,CAAC,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,aAAa,EAAE,OAAO,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,gBAAgB,EAAE,OAAO,CAAA;CAC1B;AAED,eAAO,MAAM,yBAAyB,IAAI,CAAA;AAC1C,eAAO,MAAM,mBAAmB,oBAAoB,CAAA;AACpD,eAAO,MAAM,4BAA4B,WAAa,CAAA;AACtD,eAAO,MAAM,4BAA4B,gBAAgB,CAAA;AAezD,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3B,GAAG,CAAC,EAAE,IAAI,CAAA;CACX;AAED,MAAM,WAAW,2BAA2B;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA8ID,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAKlE;AAUD,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,EACd,iBAAiB,EAAE,MAAM,EACzB,YAAY,EAAE,OAAO,GACpB,KAAK,IAAI,MAAM,CAMjB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAIpF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,oBAAoB,GAAG,eAAe,CA0DjF;AAED,wBAAgB,sBAAsB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,oBAAoB,EAC3B,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED,wBAAsB,mBAAmB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAQ7F"}
1
+ {"version":3,"file":"last-error-record.d.ts","sourceRoot":"","sources":["../src/last-error-record.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,CAAC,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,aAAa,EAAE,OAAO,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,gBAAgB,EAAE,OAAO,CAAA;CAC1B;AAED,eAAO,MAAM,yBAAyB,IAAI,CAAA;AAC1C,eAAO,MAAM,mBAAmB,oBAAoB,CAAA;AACpD,eAAO,MAAM,4BAA4B,WAAa,CAAA;AACtD,eAAO,MAAM,4BAA4B,gBAAgB,CAAA;AAezD,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3B,GAAG,CAAC,EAAE,IAAI,CAAA;CACX;AAED,MAAM,WAAW,2BAA2B;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA6HD;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,CAI7C;AAsBD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAKlE;AAUD,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,EACd,iBAAiB,EAAE,MAAM,EACzB,YAAY,EAAE,OAAO,GACpB,KAAK,IAAI,MAAM,CAMjB;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAIpF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,oBAAoB,GAAG,eAAe,CA0DjF;AAED,wBAAgB,sBAAsB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,oBAAoB,EAC3B,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,wBAAsB,mBAAmB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAQ7F"}
@@ -116,6 +116,19 @@ function serializeRecord(record) {
116
116
  function isSerializedRecordTooLarge(record) {
117
117
  return Buffer.byteLength(serializeRecord(record), 'utf8') > LAST_ERROR_FILE_MAX_BYTES;
118
118
  }
119
+ /** Shorter values are left alone: replacing them would garble ordinary text. */
120
+ const MIN_ERROR_OUTPUT_SECRET_LENGTH = 8;
121
+ /**
122
+ * The values no error output may show: this process's remembered secrets, the
123
+ * current bearer and runner-forwarded keys. last-error.json and the terminal's
124
+ * error line both use this list. Longest first, so a secret inside another
125
+ * can't leave part of the longer one behind.
126
+ */
127
+ export function errorOutputSecrets() {
128
+ return [...new Set([...resolveActiveProcessSecrets(), ...runnerForwardedSecretValues()])]
129
+ .filter(secret => typeof secret === 'string' && secret.length >= MIN_ERROR_OUTPUT_SECRET_LENGTH)
130
+ .sort((left, right) => right.length - left.length);
131
+ }
119
132
  function resolveActiveProcessSecrets() {
120
133
  const rememberedSecrets = getSecretsSeenThisProcess();
121
134
  try {
@@ -230,8 +243,7 @@ export async function writeLastErrorRecord(input, options = {}) {
230
243
  ...input,
231
244
  homeDir: input.homeDir === undefined ? resolveHomeDir() : input.homeDir,
232
245
  secrets: [
233
- ...resolveActiveProcessSecrets(),
234
- ...runnerForwardedSecretValues(),
246
+ ...errorOutputSecrets(),
235
247
  ...(input.secrets ?? []),
236
248
  ],
237
249
  });
@@ -0,0 +1,37 @@
1
+ export interface LoginFlowDependencies {
2
+ parseJsonResponse: <T>(response: Response, context: string) => Promise<T>;
3
+ printProgress: (message: string) => void;
4
+ sanitizeTerminalText: (text: string) => string;
5
+ validateBrowserUrl: (url: string, expectedOrigin?: string) => URL;
6
+ sleep?: (milliseconds: number) => Promise<void>;
7
+ }
8
+ export interface CompletedLogin {
9
+ apiKey: string;
10
+ user: {
11
+ id: string;
12
+ email: string | null;
13
+ };
14
+ }
15
+ export declare function shouldUseHeadlessLogin({ isForced, platform, environment, }: {
16
+ isForced: boolean;
17
+ platform?: NodeJS.Platform;
18
+ environment?: Record<string, string | undefined>;
19
+ }): boolean;
20
+ export declare const POLL_INTERVAL_MS = 2000;
21
+ /** For tests: the headless wait alone, returning the authorization code. */
22
+ export declare function waitForHeadlessAuthorization(input: {
23
+ baseUrl: string;
24
+ codeChallenge: string;
25
+ }, dependencies: LoginFlowDependencies, signal?: AbortSignal): Promise<string>;
26
+ /**
27
+ * Waits for the person to approve the login (in the browser, or by polling
28
+ * when headless), then exchanges the code. `callbackPort` undefined means
29
+ * headless. `signal` stops it, closing the loopback server.
30
+ */
31
+ export declare function completeLogin(input: {
32
+ baseUrl: string;
33
+ codeVerifier: string;
34
+ codeChallenge: string;
35
+ callbackPort?: number;
36
+ }, dependencies: LoginFlowDependencies, signal?: AbortSignal): Promise<CompletedLogin>;
37
+ //# sourceMappingURL=login-flow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login-flow.d.ts","sourceRoot":"","sources":["../src/login-flow.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,qBAAqB;IACpC,iBAAiB,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;IACzE,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,oBAAoB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;IAC9C,kBAAkB,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,KAAK,GAAG,CAAA;IACjE,KAAK,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAChD;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAA;CAC3C;AAED,wBAAgB,sBAAsB,CAAC,EACrC,QAAQ,EACR,QAA2B,EAC3B,WAAyB,GAC1B,EAAE;IACD,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAA;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;CACjD,GAAG,OAAO,CAMV;AA2ID,eAAO,MAAM,gBAAgB,OAAQ,CAAA;AA2JrC,4EAA4E;AAC5E,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,EACjD,YAAY,EAAE,qBAAqB,EACnC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,MAAM,CAAC,CAEjB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,EAC9F,YAAY,EAAE,qBAAqB,EACnC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,CAAC,CAOzB"}