@clickraft/cli 0.8.4 → 0.8.6
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/CHANGELOG.md +1 -1
- package/dist/cli.js +681 -660
- package/dist/cli.js.map +1 -1
- package/dist/index.js +681 -660
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10,6 +10,10 @@ import { z } from "zod";
|
|
|
10
10
|
import { z as z2 } from "zod";
|
|
11
11
|
import { z as z3 } from "zod";
|
|
12
12
|
import { z as z4 } from "zod";
|
|
13
|
+
import { request as undiciRequest } from "undici";
|
|
14
|
+
import { z as z6 } from "zod";
|
|
15
|
+
import { createHash, randomUUID } from "crypto";
|
|
16
|
+
import { z as z5 } from "zod";
|
|
13
17
|
var TOKEN_FIELD_PATTERN = /"(access_token|refresh_token|device_code|user_code|code_verifier|client_secret)"\s*:\s*"[^"]*"/gi;
|
|
14
18
|
var BARE_TOKEN_PATTERN = /\bck_(?:live|dev|test)_[A-Za-z0-9_-]+/gi;
|
|
15
19
|
function redactTokenLikeFields(input) {
|
|
@@ -202,432 +206,6 @@ function composeIterationSignal(userSignal, remainingMs) {
|
|
|
202
206
|
const deadlineSignal = AbortSignal.timeout(Math.max(remainingMs, 0));
|
|
203
207
|
return userSignal ? AbortSignal.any([userSignal, deadlineSignal]) : deadlineSignal;
|
|
204
208
|
}
|
|
205
|
-
|
|
206
|
-
// src/api/client.ts
|
|
207
|
-
import { request as undiciRequest } from "undici";
|
|
208
|
-
import { z as z6 } from "zod";
|
|
209
|
-
|
|
210
|
-
// src/errors/codes.ts
|
|
211
|
-
var LOGIN_HINT = "Run `clickraft login` to authenticate.";
|
|
212
|
-
var RELOGIN_HINT = "Run `clickraft login` to refresh.";
|
|
213
|
-
var EXIT_CODE_MAP = {
|
|
214
|
-
// --- 37 Phase 8 Q7 codes ----------------------------------------------------
|
|
215
|
-
AUTH_TOKEN_MISSING: {
|
|
216
|
-
exitCode: 3,
|
|
217
|
-
category: "auth",
|
|
218
|
-
retryable: false,
|
|
219
|
-
hint: "Set `CLICKRAFT_TOKEN` or run `clickraft login`."
|
|
220
|
-
},
|
|
221
|
-
AUTH_TOKEN_INVALID: {
|
|
222
|
-
exitCode: 3,
|
|
223
|
-
category: "auth",
|
|
224
|
-
retryable: false,
|
|
225
|
-
hint: "The token is malformed or unknown. Run `clickraft login`."
|
|
226
|
-
},
|
|
227
|
-
AUTH_TOKEN_EXPIRED: {
|
|
228
|
-
exitCode: 3,
|
|
229
|
-
category: "auth",
|
|
230
|
-
retryable: false,
|
|
231
|
-
hint: RELOGIN_HINT
|
|
232
|
-
},
|
|
233
|
-
AUTH_TOKEN_REVOKED: {
|
|
234
|
-
exitCode: 3,
|
|
235
|
-
category: "auth",
|
|
236
|
-
retryable: false,
|
|
237
|
-
hint: "Token was revoked. Run `clickraft login` to mint a new one."
|
|
238
|
-
},
|
|
239
|
-
AUTH_TOKEN_SCOPE_INSUFFICIENT: {
|
|
240
|
-
exitCode: 4,
|
|
241
|
-
category: "permission",
|
|
242
|
-
retryable: false,
|
|
243
|
-
hint: "This token does not have the required scope. Re-run `clickraft login` selecting the needed scopes."
|
|
244
|
-
},
|
|
245
|
-
AUTH_ORG_MISMATCH: {
|
|
246
|
-
exitCode: 4,
|
|
247
|
-
category: "permission",
|
|
248
|
-
retryable: false,
|
|
249
|
-
hint: "This token's org no longer exists or you have been removed."
|
|
250
|
-
},
|
|
251
|
-
AUTH_DEVICE_CODE_PENDING: {
|
|
252
|
-
exitCode: 0,
|
|
253
|
-
category: "success",
|
|
254
|
-
retryable: false,
|
|
255
|
-
hint: "Continue polling."
|
|
256
|
-
},
|
|
257
|
-
AUTH_DEVICE_CODE_EXPIRED: {
|
|
258
|
-
exitCode: 3,
|
|
259
|
-
category: "auth",
|
|
260
|
-
retryable: false,
|
|
261
|
-
hint: "Device code expired. Re-run `clickraft login`."
|
|
262
|
-
},
|
|
263
|
-
AUTH_DEVICE_CODE_SLOW_DOWN: {
|
|
264
|
-
exitCode: 0,
|
|
265
|
-
category: "success",
|
|
266
|
-
retryable: false,
|
|
267
|
-
hint: "Increase polling interval (Retry-After)."
|
|
268
|
-
},
|
|
269
|
-
IDEMPOTENCY_KEY_MISSING: {
|
|
270
|
-
exitCode: 2,
|
|
271
|
-
category: "usage",
|
|
272
|
-
retryable: false,
|
|
273
|
-
hint: "Add `X-Idempotency-Key` header (or `--idempotency-key`)."
|
|
274
|
-
},
|
|
275
|
-
IDEMPOTENCY_KEY_CONFLICT: {
|
|
276
|
-
exitCode: 7,
|
|
277
|
-
category: "conflict",
|
|
278
|
-
retryable: false,
|
|
279
|
-
hint: "Same key was used with a different request body. Use a new key."
|
|
280
|
-
},
|
|
281
|
-
PLAN_FEATURE_LOCKED: {
|
|
282
|
-
exitCode: 4,
|
|
283
|
-
category: "permission",
|
|
284
|
-
retryable: false,
|
|
285
|
-
hint: "API access is not enabled on your plan. Visit billing settings."
|
|
286
|
-
},
|
|
287
|
-
PLAN_QUOTA_EXCEEDED: {
|
|
288
|
-
exitCode: 5,
|
|
289
|
-
category: "quota",
|
|
290
|
-
retryable: false,
|
|
291
|
-
hint: "Daily API quota exhausted. Wait, or upgrade your plan."
|
|
292
|
-
},
|
|
293
|
-
INSUFFICIENT_CREDITS: {
|
|
294
|
-
exitCode: 5,
|
|
295
|
-
category: "quota",
|
|
296
|
-
retryable: false,
|
|
297
|
-
hint: "Top up credits to retry."
|
|
298
|
-
},
|
|
299
|
-
CREDIT_DEDUCTION_FAILED: {
|
|
300
|
-
exitCode: 1,
|
|
301
|
-
category: "runtime",
|
|
302
|
-
retryable: true,
|
|
303
|
-
hint: "Could not charge credits. Retry; no charge was made."
|
|
304
|
-
},
|
|
305
|
-
RATE_LIMITED: {
|
|
306
|
-
exitCode: 5,
|
|
307
|
-
category: "quota",
|
|
308
|
-
retryable: true,
|
|
309
|
-
hint: "Slow down. Honor Retry-After."
|
|
310
|
-
},
|
|
311
|
-
INPUT_INVALID_FORMAT: {
|
|
312
|
-
exitCode: 2,
|
|
313
|
-
category: "usage",
|
|
314
|
-
retryable: false,
|
|
315
|
-
hint: "Check input format and try again."
|
|
316
|
-
},
|
|
317
|
-
INPUT_TOO_LONG: {
|
|
318
|
-
exitCode: 2,
|
|
319
|
-
category: "usage",
|
|
320
|
-
retryable: false,
|
|
321
|
-
hint: "Field exceeded max length."
|
|
322
|
-
},
|
|
323
|
-
WORKFLOW_VALIDATION_FAILED: {
|
|
324
|
-
exitCode: 2,
|
|
325
|
-
category: "usage",
|
|
326
|
-
retryable: false,
|
|
327
|
-
hint: "Run `workflow validate` for the full list of errors."
|
|
328
|
-
},
|
|
329
|
-
WORKFLOW_NODE_TYPE_UNKNOWN: {
|
|
330
|
-
exitCode: 2,
|
|
331
|
-
category: "usage",
|
|
332
|
-
retryable: false,
|
|
333
|
-
hint: "Run `clickraft nodes list` for the current set of node types."
|
|
334
|
-
},
|
|
335
|
-
CANVAS_REV_MISMATCH: {
|
|
336
|
-
exitCode: 7,
|
|
337
|
-
category: "conflict",
|
|
338
|
-
retryable: false,
|
|
339
|
-
hint: "The workflow was updated elsewhere. Re-fetch and retry with the current canvas_rev."
|
|
340
|
-
},
|
|
341
|
-
CANVAS_REV_REQUIRED: {
|
|
342
|
-
exitCode: 2,
|
|
343
|
-
category: "usage",
|
|
344
|
-
retryable: false,
|
|
345
|
-
hint: "Include --canvas-rev or use --auto-rev."
|
|
346
|
-
},
|
|
347
|
-
WORKER_UNREACHABLE: {
|
|
348
|
-
exitCode: 6,
|
|
349
|
-
category: "network",
|
|
350
|
-
retryable: true,
|
|
351
|
-
hint: "Collaboration service temporarily unavailable. Retry."
|
|
352
|
-
},
|
|
353
|
-
IDEMPOTENCY_IN_PROGRESS: {
|
|
354
|
-
exitCode: 7,
|
|
355
|
-
category: "conflict",
|
|
356
|
-
retryable: false,
|
|
357
|
-
hint: "A request with this idempotency key is currently in-flight. Retry in a moment."
|
|
358
|
-
},
|
|
359
|
-
TOKEN_NAME_INVALID: {
|
|
360
|
-
exitCode: 2,
|
|
361
|
-
category: "usage",
|
|
362
|
-
retryable: false,
|
|
363
|
-
hint: "Agent token name is malformed."
|
|
364
|
-
},
|
|
365
|
-
WORKFLOW_NOT_FOUND: {
|
|
366
|
-
exitCode: 2,
|
|
367
|
-
category: "usage",
|
|
368
|
-
retryable: false,
|
|
369
|
-
hint: "No workflow with that ID in your org."
|
|
370
|
-
},
|
|
371
|
-
NOT_FOUND: {
|
|
372
|
-
exitCode: 2,
|
|
373
|
-
category: "usage",
|
|
374
|
-
retryable: false,
|
|
375
|
-
hint: "Resource not found."
|
|
376
|
-
},
|
|
377
|
-
NODE_TYPE_NOT_FOUND: {
|
|
378
|
-
exitCode: 2,
|
|
379
|
-
category: "usage",
|
|
380
|
-
retryable: false,
|
|
381
|
-
hint: "Run `clickraft nodes list` for the current set of node types."
|
|
382
|
-
},
|
|
383
|
-
FEATURE_DISABLED: {
|
|
384
|
-
exitCode: 2,
|
|
385
|
-
category: "usage",
|
|
386
|
-
retryable: false,
|
|
387
|
-
hint: "This endpoint is not enabled. Contact support if you believe this is in error."
|
|
388
|
-
},
|
|
389
|
-
FORBIDDEN: {
|
|
390
|
-
exitCode: 4,
|
|
391
|
-
category: "permission",
|
|
392
|
-
retryable: false,
|
|
393
|
-
hint: "Your role does not permit this action."
|
|
394
|
-
},
|
|
395
|
-
MODEL_NOT_FOUND: {
|
|
396
|
-
exitCode: 2,
|
|
397
|
-
category: "usage",
|
|
398
|
-
retryable: false,
|
|
399
|
-
hint: "Unknown `modelSlug`. Run `brand-model list` for the current set."
|
|
400
|
-
},
|
|
401
|
-
PROVIDER_UNAVAILABLE: {
|
|
402
|
-
exitCode: 6,
|
|
403
|
-
category: "network",
|
|
404
|
-
retryable: true,
|
|
405
|
-
hint: "Provider is temporarily unavailable. Retry shortly."
|
|
406
|
-
},
|
|
407
|
-
STORAGE_UPLOAD_FAILED: {
|
|
408
|
-
exitCode: 1,
|
|
409
|
-
category: "runtime",
|
|
410
|
-
retryable: true,
|
|
411
|
-
hint: "Upload failed mid-stream. Retry."
|
|
412
|
-
},
|
|
413
|
-
STORAGE_QUOTA_EXCEEDED: {
|
|
414
|
-
exitCode: 5,
|
|
415
|
-
category: "quota",
|
|
416
|
-
retryable: false,
|
|
417
|
-
hint: "Org storage quota exceeded. Free space or upgrade."
|
|
418
|
-
},
|
|
419
|
-
GEN_CONTENT_REFUSAL: {
|
|
420
|
-
exitCode: 1,
|
|
421
|
-
category: "runtime",
|
|
422
|
-
retryable: false,
|
|
423
|
-
hint: "The model declined the request via safety policy."
|
|
424
|
-
},
|
|
425
|
-
GEN_OUTPUT_SCHEMA_MISMATCH: {
|
|
426
|
-
exitCode: 1,
|
|
427
|
-
category: "runtime",
|
|
428
|
-
retryable: true,
|
|
429
|
-
hint: "Provider returned malformed output. Full refund issued. Retry."
|
|
430
|
-
},
|
|
431
|
-
GEN_EMPTY_OUTPUT: {
|
|
432
|
-
exitCode: 1,
|
|
433
|
-
category: "runtime",
|
|
434
|
-
retryable: true,
|
|
435
|
-
hint: "Provider returned no usable output. Full refund issued. Retry."
|
|
436
|
-
},
|
|
437
|
-
GEN_ITEM_COUNT_MISMATCH: {
|
|
438
|
-
exitCode: 0,
|
|
439
|
-
category: "success",
|
|
440
|
-
retryable: false,
|
|
441
|
-
hint: "Partial output kept; no refund."
|
|
442
|
-
},
|
|
443
|
-
AGENT_FINGERPRINT_REJECTED: {
|
|
444
|
-
exitCode: 4,
|
|
445
|
-
category: "permission",
|
|
446
|
-
retryable: false,
|
|
447
|
-
hint: "This client identification is not permitted. Contact support."
|
|
448
|
-
},
|
|
449
|
-
INTERNAL_ERROR: {
|
|
450
|
-
exitCode: 1,
|
|
451
|
-
category: "runtime",
|
|
452
|
-
retryable: true,
|
|
453
|
-
hint: "Server error. Retry; if it persists, contact support with the request ID."
|
|
454
|
-
},
|
|
455
|
-
CREDENTIALS_LOCKED: {
|
|
456
|
-
exitCode: 1,
|
|
457
|
-
category: "runtime",
|
|
458
|
-
retryable: true,
|
|
459
|
-
hint: "Another CLI invocation holds the credentials lock. Retry shortly."
|
|
460
|
-
},
|
|
461
|
-
CREDENTIALS_PERMISSIONS_LOOSE: {
|
|
462
|
-
exitCode: 0,
|
|
463
|
-
category: "success",
|
|
464
|
-
retryable: false,
|
|
465
|
-
hint: "Credentials file mode is wider than 0600; continuing."
|
|
466
|
-
},
|
|
467
|
-
// --- 5 additional codes introduced by 1A.1 ---------------------------------
|
|
468
|
-
AUTH_ACCESS_DENIED: {
|
|
469
|
-
exitCode: 3,
|
|
470
|
-
category: "auth",
|
|
471
|
-
retryable: false,
|
|
472
|
-
hint: "You denied the OAuth grant. Re-run `clickraft login` to retry."
|
|
473
|
-
},
|
|
474
|
-
AUTH_FLOW_CANCELLED: {
|
|
475
|
-
exitCode: 3,
|
|
476
|
-
category: "auth",
|
|
477
|
-
retryable: false,
|
|
478
|
-
hint: "Authentication was cancelled. Re-run `clickraft login`."
|
|
479
|
-
},
|
|
480
|
-
CREDENTIALS_CORRUPT: {
|
|
481
|
-
exitCode: 1,
|
|
482
|
-
category: "runtime",
|
|
483
|
-
retryable: false,
|
|
484
|
-
hint: "Credentials file is unreadable. Remove it and run `clickraft login`."
|
|
485
|
-
},
|
|
486
|
-
CREDENTIALS_NOT_FOUND: {
|
|
487
|
-
exitCode: 3,
|
|
488
|
-
category: "auth",
|
|
489
|
-
retryable: false,
|
|
490
|
-
hint: LOGIN_HINT
|
|
491
|
-
},
|
|
492
|
-
CREDENTIALS_PROFILE_NOT_FOUND: {
|
|
493
|
-
exitCode: 2,
|
|
494
|
-
category: "usage",
|
|
495
|
-
retryable: false,
|
|
496
|
-
hint: "No such profile. Use `clickraft auth profiles` to list available profiles."
|
|
497
|
-
},
|
|
498
|
-
// --- 8 brand model resolution codes -----------------------------------------
|
|
499
|
-
BRAND_MODEL_NOT_FOUND: {
|
|
500
|
-
exitCode: 1,
|
|
501
|
-
category: "runtime",
|
|
502
|
-
retryable: false,
|
|
503
|
-
hint: "No brand model with that ID. Run `clickraft brand-model list`."
|
|
504
|
-
},
|
|
505
|
-
BRAND_MODEL_NOT_A_MODEL: {
|
|
506
|
-
exitCode: 1,
|
|
507
|
-
category: "runtime",
|
|
508
|
-
retryable: false,
|
|
509
|
-
hint: "The specified asset is not a brand model."
|
|
510
|
-
},
|
|
511
|
-
BRAND_MODEL_NOT_ACTIVE: {
|
|
512
|
-
exitCode: 1,
|
|
513
|
-
category: "runtime",
|
|
514
|
-
retryable: false,
|
|
515
|
-
hint: "Brand model is not active. Check its status in the dashboard."
|
|
516
|
-
},
|
|
517
|
-
BRAND_MODEL_NO_REFERENCES: {
|
|
518
|
-
exitCode: 1,
|
|
519
|
-
category: "runtime",
|
|
520
|
-
retryable: false,
|
|
521
|
-
hint: "Brand model has no reference images. Upload references first."
|
|
522
|
-
},
|
|
523
|
-
BRAND_MODEL_POSE_NOT_FOUND: {
|
|
524
|
-
exitCode: 1,
|
|
525
|
-
category: "runtime",
|
|
526
|
-
retryable: false,
|
|
527
|
-
hint: "Requested pose not found. Valid: front, 3/4-right, right, left, 3/4-left, back, approved."
|
|
528
|
-
},
|
|
529
|
-
BRAND_MODEL_DUPLICATE_ID: {
|
|
530
|
-
exitCode: 2,
|
|
531
|
-
category: "usage",
|
|
532
|
-
retryable: false,
|
|
533
|
-
hint: "Same brand model ID appears more than once."
|
|
534
|
-
},
|
|
535
|
-
MODEL_DOES_NOT_SUPPORT_REFERENCES: {
|
|
536
|
-
exitCode: 2,
|
|
537
|
-
category: "usage",
|
|
538
|
-
retryable: false,
|
|
539
|
-
hint: "This model does not support brand model references."
|
|
540
|
-
},
|
|
541
|
-
REFERENCE_LIMIT_EXCEEDED: {
|
|
542
|
-
exitCode: 2,
|
|
543
|
-
category: "usage",
|
|
544
|
-
retryable: false,
|
|
545
|
-
hint: "Too many reference images. Check model limits."
|
|
546
|
-
},
|
|
547
|
-
// --- 8 product reference resolver codes -------------------------------------
|
|
548
|
-
PRODUCT_NOT_FOUND: {
|
|
549
|
-
exitCode: 1,
|
|
550
|
-
category: "runtime",
|
|
551
|
-
retryable: false,
|
|
552
|
-
hint: "No product with that ID in your org. Run `clickraft product list`."
|
|
553
|
-
},
|
|
554
|
-
PRODUCT_DUPLICATE_REFERENCE: {
|
|
555
|
-
exitCode: 2,
|
|
556
|
-
category: "usage",
|
|
557
|
-
retryable: false,
|
|
558
|
-
hint: "Same (productId, imageId) tuple appears twice in the request."
|
|
559
|
-
},
|
|
560
|
-
PRODUCT_IMAGE_NOT_FOUND: {
|
|
561
|
-
exitCode: 1,
|
|
562
|
-
category: "runtime",
|
|
563
|
-
retryable: false,
|
|
564
|
-
hint: "imageId does not exist in the product images."
|
|
565
|
-
},
|
|
566
|
-
PRODUCT_IMAGE_MISMATCH: {
|
|
567
|
-
exitCode: 2,
|
|
568
|
-
category: "usage",
|
|
569
|
-
retryable: false,
|
|
570
|
-
hint: "imageId belongs to a different product."
|
|
571
|
-
},
|
|
572
|
-
PRODUCT_SYNC_ERROR: {
|
|
573
|
-
exitCode: 1,
|
|
574
|
-
category: "runtime",
|
|
575
|
-
retryable: false,
|
|
576
|
-
hint: "Product sync status is 'error'. Re-sync or use a different product."
|
|
577
|
-
},
|
|
578
|
-
PRODUCT_DELETED_UPSTREAM: {
|
|
579
|
-
exitCode: 1,
|
|
580
|
-
category: "runtime",
|
|
581
|
-
retryable: false,
|
|
582
|
-
hint: "Product was deleted upstream. Remove it or re-sync."
|
|
583
|
-
},
|
|
584
|
-
PRODUCT_NO_PRIMARY_IMAGE: {
|
|
585
|
-
exitCode: 1,
|
|
586
|
-
category: "runtime",
|
|
587
|
-
retryable: false,
|
|
588
|
-
hint: "Product has no primary image. Specify --product <id>:<imageId> explicitly."
|
|
589
|
-
},
|
|
590
|
-
PRODUCT_SOURCE_MANAGED: {
|
|
591
|
-
exitCode: 4,
|
|
592
|
-
category: "permission",
|
|
593
|
-
retryable: false,
|
|
594
|
-
hint: "Product is managed by an external integration."
|
|
595
|
-
},
|
|
596
|
-
// --- 4 CLI-synthetic codes --------------------------------------------------
|
|
597
|
-
NETWORK_ERROR: {
|
|
598
|
-
exitCode: 6,
|
|
599
|
-
category: "network",
|
|
600
|
-
retryable: true,
|
|
601
|
-
hint: "Network connection failed. Check connectivity and retry."
|
|
602
|
-
},
|
|
603
|
-
TIMEOUT: {
|
|
604
|
-
exitCode: 6,
|
|
605
|
-
category: "network",
|
|
606
|
-
retryable: true,
|
|
607
|
-
hint: "Request timed out. Retry; consider increasing the timeout."
|
|
608
|
-
},
|
|
609
|
-
PARSE_ERROR: {
|
|
610
|
-
exitCode: 1,
|
|
611
|
-
category: "runtime",
|
|
612
|
-
retryable: false,
|
|
613
|
-
hint: "Server returned an unexpected response. Contact support with the request ID."
|
|
614
|
-
},
|
|
615
|
-
SCHEMA_MISMATCH: {
|
|
616
|
-
exitCode: 1,
|
|
617
|
-
category: "runtime",
|
|
618
|
-
retryable: false,
|
|
619
|
-
hint: "Server response did not match the expected schema. Update the CLI."
|
|
620
|
-
}
|
|
621
|
-
};
|
|
622
|
-
function getExitCode(code) {
|
|
623
|
-
return EXIT_CODE_MAP[code]?.exitCode ?? 1;
|
|
624
|
-
}
|
|
625
|
-
function toEnvelopeCode(code) {
|
|
626
|
-
return code.startsWith("E_") ? code : `E_${code}`;
|
|
627
|
-
}
|
|
628
|
-
var SIGINT_EXIT_CODE = 130;
|
|
629
|
-
|
|
630
|
-
// src/errors/schema-mismatch.ts
|
|
631
209
|
var MAX_ISSUES = 5;
|
|
632
210
|
function formatSchemaIssues(error) {
|
|
633
211
|
const issues = error.issues;
|
|
@@ -651,9 +229,6 @@ function formatPath(path) {
|
|
|
651
229
|
if (path.length === 0) return "(root)";
|
|
652
230
|
return path.map(String).join(".");
|
|
653
231
|
}
|
|
654
|
-
|
|
655
|
-
// src/api/idempotency.ts
|
|
656
|
-
import { createHash, randomUUID } from "crypto";
|
|
657
232
|
function generateIdempotencyKey() {
|
|
658
233
|
return randomUUID();
|
|
659
234
|
}
|
|
@@ -661,8 +236,6 @@ function generateUploadIdempotencyKey(content, scope) {
|
|
|
661
236
|
const hash = createHash("sha256").update(content).digest("hex");
|
|
662
237
|
return `upload-${scope}-${hash}`;
|
|
663
238
|
}
|
|
664
|
-
|
|
665
|
-
// src/api/retry.ts
|
|
666
239
|
var DEFAULT_RETRY_OPTIONS = {
|
|
667
240
|
maxAttempts: 3,
|
|
668
241
|
baseDelayMs: 200,
|
|
@@ -676,7 +249,7 @@ function computeBackoffMs(attempt, baseDelayMs, maxDelayMs, jitterFraction) {
|
|
|
676
249
|
return Math.max(0, Math.round(exp + jitter));
|
|
677
250
|
}
|
|
678
251
|
function decideRetry(params) {
|
|
679
|
-
const { attempt, options, serverRetryAfterMs, isNetworkError, isRetryable } = params;
|
|
252
|
+
const { attempt, options, serverRetryAfterMs, isNetworkError, isRetryable, onWarning: onWarning2 } = params;
|
|
680
253
|
if (options.signal?.aborted) {
|
|
681
254
|
return { shouldRetry: false, delayMs: 0, reason: "aborted" };
|
|
682
255
|
}
|
|
@@ -689,7 +262,7 @@ function decideRetry(params) {
|
|
|
689
262
|
if (typeof serverRetryAfterMs === "number" && serverRetryAfterMs >= 0) {
|
|
690
263
|
let delayMs2 = serverRetryAfterMs;
|
|
691
264
|
if (delayMs2 > ABSOLUTE_MAX_RETRY_AFTER_MS) {
|
|
692
|
-
|
|
265
|
+
onWarning2?.(`Server Retry-After exceeded ${ABSOLUTE_MAX_RETRY_AFTER_MS}ms; clipping.`);
|
|
693
266
|
delayMs2 = ABSOLUTE_MAX_RETRY_AFTER_MS;
|
|
694
267
|
}
|
|
695
268
|
return { shouldRetry: true, delayMs: delayMs2, reason: "rate_limited" };
|
|
@@ -729,9 +302,6 @@ function signalToAbortError(signal) {
|
|
|
729
302
|
err.name = "AbortError";
|
|
730
303
|
return err;
|
|
731
304
|
}
|
|
732
|
-
|
|
733
|
-
// src/api/types.ts
|
|
734
|
-
import { z as z5 } from "zod";
|
|
735
305
|
var ServerSuccessSchema = (payload) => z5.object({ data: payload }).strict();
|
|
736
306
|
var ServerErrorBodySchema = z5.object({
|
|
737
307
|
error: z5.object({
|
|
@@ -742,8 +312,6 @@ var ServerErrorBodySchema = z5.object({
|
|
|
742
312
|
details: z5.unknown().optional()
|
|
743
313
|
}).strict()
|
|
744
314
|
}).strict();
|
|
745
|
-
|
|
746
|
-
// src/api/client.ts
|
|
747
315
|
var REDACT_BODY_LIMIT = 1024;
|
|
748
316
|
var REDACT_INPUT_CAP = 100 * 1024;
|
|
749
317
|
var DEFAULT_USER_AGENT = "clickraft-cli";
|
|
@@ -800,7 +368,8 @@ var ApiClient = class {
|
|
|
800
368
|
options: { ...retry, signal },
|
|
801
369
|
serverRetryAfterMs: err.meta.retryAfterMs,
|
|
802
370
|
isNetworkError,
|
|
803
|
-
isRetryable
|
|
371
|
+
isRetryable,
|
|
372
|
+
onWarning: this.options.onWarning
|
|
804
373
|
});
|
|
805
374
|
if (!decision.shouldRetry) throw err;
|
|
806
375
|
try {
|
|
@@ -888,178 +457,654 @@ var ApiClient = class {
|
|
|
888
457
|
bodyText,
|
|
889
458
|
statusCode,
|
|
890
459
|
requestId,
|
|
891
|
-
retryAfterHeader: pickHeader(responseHeaders, "retry-after")
|
|
460
|
+
retryAfterHeader: pickHeader(responseHeaders, "retry-after"),
|
|
461
|
+
onWarning: this.options.onWarning,
|
|
462
|
+
isRetryableServerCode: this.options.isRetryableServerCode
|
|
892
463
|
});
|
|
893
464
|
}
|
|
894
465
|
};
|
|
895
|
-
function
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
code =
|
|
977
|
-
safeMessage =
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
return
|
|
466
|
+
function createClient(options) {
|
|
467
|
+
return new ApiClient(options);
|
|
468
|
+
}
|
|
469
|
+
function parseSuccessBody(args) {
|
|
470
|
+
let parsed;
|
|
471
|
+
try {
|
|
472
|
+
parsed = JSON.parse(args.bodyText);
|
|
473
|
+
} catch (err) {
|
|
474
|
+
throw new ApiError(
|
|
475
|
+
"PARSE_ERROR",
|
|
476
|
+
"Server returned a non-JSON response.",
|
|
477
|
+
{
|
|
478
|
+
status: args.statusCode,
|
|
479
|
+
requestId: args.requestId,
|
|
480
|
+
retryable: false,
|
|
481
|
+
redactedBody: redactAndTruncate(args.bodyText)
|
|
482
|
+
},
|
|
483
|
+
err
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
const envelope = ServerSuccessSchema(args.responseSchema ?? z6.unknown()).safeParse(parsed);
|
|
487
|
+
if (!envelope.success) {
|
|
488
|
+
const issues = formatSchemaIssues(envelope.error);
|
|
489
|
+
throw new ApiError(
|
|
490
|
+
"SCHEMA_MISMATCH",
|
|
491
|
+
`Server response did not match the expected schema. Issues: ${issues}`,
|
|
492
|
+
{
|
|
493
|
+
status: args.statusCode,
|
|
494
|
+
requestId: args.requestId,
|
|
495
|
+
retryable: false,
|
|
496
|
+
redactedBody: redactAndTruncate(args.bodyText)
|
|
497
|
+
}
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
return envelope.data.data;
|
|
501
|
+
}
|
|
502
|
+
function parseErrorBody(args) {
|
|
503
|
+
const redactedBody = redactAndTruncate(args.bodyText);
|
|
504
|
+
let parsed;
|
|
505
|
+
try {
|
|
506
|
+
parsed = JSON.parse(args.bodyText);
|
|
507
|
+
} catch {
|
|
508
|
+
return new ApiError(
|
|
509
|
+
"PARSE_ERROR",
|
|
510
|
+
`Server returned a ${args.statusCode} response with a non-JSON body.`,
|
|
511
|
+
{
|
|
512
|
+
status: args.statusCode,
|
|
513
|
+
requestId: args.requestId,
|
|
514
|
+
retryable: args.statusCode >= 500,
|
|
515
|
+
redactedBody
|
|
516
|
+
}
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
const envelope = ServerErrorBodySchema.safeParse(parsed);
|
|
520
|
+
if (!envelope.success) {
|
|
521
|
+
return new ApiError(
|
|
522
|
+
"PARSE_ERROR",
|
|
523
|
+
`Server returned ${args.statusCode} but body did not match the error envelope.`,
|
|
524
|
+
{
|
|
525
|
+
status: args.statusCode,
|
|
526
|
+
requestId: args.requestId,
|
|
527
|
+
retryable: args.statusCode >= 500,
|
|
528
|
+
redactedBody
|
|
529
|
+
}
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
return apiErrorFromServerBody({
|
|
533
|
+
body: envelope.data,
|
|
534
|
+
statusCode: args.statusCode,
|
|
535
|
+
requestId: args.requestId,
|
|
536
|
+
retryAfterHeader: args.retryAfterHeader,
|
|
537
|
+
redactedBody,
|
|
538
|
+
onWarning: args.onWarning,
|
|
539
|
+
isRetryableServerCode: args.isRetryableServerCode
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
function apiErrorFromServerBody(args) {
|
|
543
|
+
const { error } = args.body;
|
|
544
|
+
let code;
|
|
545
|
+
let safeMessage;
|
|
546
|
+
if (KNOWN_SERVER_CODES.has(error.code)) {
|
|
547
|
+
code = error.code;
|
|
548
|
+
safeMessage = redactTokenLikeFields(error.message);
|
|
549
|
+
} else {
|
|
550
|
+
const redactedRawCode = redactTokenLikeFields(error.code);
|
|
551
|
+
args.onWarning?.(`Unknown server error code: ${redactedRawCode}. Treating as INTERNAL_ERROR.`);
|
|
552
|
+
code = "INTERNAL_ERROR";
|
|
553
|
+
safeMessage = `Unknown server error code: ${redactedRawCode}.`;
|
|
554
|
+
}
|
|
555
|
+
const retryable = args.isRetryableServerCode?.(code) ?? (args.statusCode >= 500 || args.statusCode === 429);
|
|
556
|
+
const retryAfterMs = typeof error.retryAfter === "number" ? error.retryAfter * 1e3 : parseRetryAfterHeader(args.retryAfterHeader);
|
|
557
|
+
return new ApiError(code, safeMessage, {
|
|
558
|
+
status: args.statusCode,
|
|
559
|
+
requestId: error.correlationId ?? args.requestId,
|
|
560
|
+
retryable,
|
|
561
|
+
retryAfterMs,
|
|
562
|
+
redactedBody: args.redactedBody,
|
|
563
|
+
details: error.details
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
function parseRetryAfterHeader(headerValue) {
|
|
567
|
+
if (!headerValue) return void 0;
|
|
568
|
+
const trimmed = headerValue.trim();
|
|
569
|
+
if (/^\d+$/.test(trimmed)) {
|
|
570
|
+
return Number.parseInt(trimmed, 10) * 1e3;
|
|
571
|
+
}
|
|
572
|
+
if (/[A-Za-z]/.test(trimmed)) {
|
|
573
|
+
const parsed = Date.parse(trimmed);
|
|
574
|
+
if (!Number.isNaN(parsed)) {
|
|
575
|
+
return Math.max(0, parsed - Date.now());
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return void 0;
|
|
579
|
+
}
|
|
580
|
+
function buildUrl(baseUrl, path, query) {
|
|
581
|
+
const base = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
582
|
+
const relative = path.startsWith("/") ? path.slice(1) : path;
|
|
583
|
+
const url = new URL(relative, base);
|
|
584
|
+
if (query) {
|
|
585
|
+
for (const [key, value] of Object.entries(query)) {
|
|
586
|
+
if (value === void 0) continue;
|
|
587
|
+
if (Array.isArray(value)) {
|
|
588
|
+
for (const item of value) url.searchParams.append(key, item);
|
|
589
|
+
continue;
|
|
590
|
+
}
|
|
591
|
+
url.searchParams.append(key, String(value));
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return url.toString();
|
|
595
|
+
}
|
|
596
|
+
function serializeBody(opts) {
|
|
597
|
+
if (opts.body === void 0) return void 0;
|
|
598
|
+
if (opts.method === "GET") return void 0;
|
|
599
|
+
return JSON.stringify(opts.body);
|
|
600
|
+
}
|
|
601
|
+
function composeSignals(signals) {
|
|
602
|
+
const present = signals.filter((s) => s !== void 0);
|
|
603
|
+
if (present.length === 0) return new AbortController().signal;
|
|
604
|
+
if (present.length === 1) return present[0];
|
|
605
|
+
const any = AbortSignal.any;
|
|
606
|
+
if (typeof any === "function") return any(present);
|
|
607
|
+
const ctrl = new AbortController();
|
|
608
|
+
for (const s of present) {
|
|
609
|
+
if (s.aborted) {
|
|
610
|
+
ctrl.abort(s.reason);
|
|
611
|
+
break;
|
|
612
|
+
}
|
|
613
|
+
s.addEventListener("abort", () => ctrl.abort(s.reason), { once: true });
|
|
614
|
+
}
|
|
615
|
+
return ctrl.signal;
|
|
616
|
+
}
|
|
617
|
+
function pickHeader(headers, name) {
|
|
618
|
+
const value = headers[name] ?? headers[name.toLowerCase()];
|
|
619
|
+
if (Array.isArray(value)) return value[0];
|
|
620
|
+
return value;
|
|
621
|
+
}
|
|
622
|
+
function redactAndTruncate(body) {
|
|
623
|
+
const input = body.length > REDACT_INPUT_CAP ? body.slice(0, REDACT_INPUT_CAP) : body;
|
|
624
|
+
const redacted = redactTokenLikeFields(input);
|
|
625
|
+
return redacted.length > REDACT_BODY_LIMIT ? `${redacted.slice(0, REDACT_BODY_LIMIT)}\u2026` : redacted;
|
|
626
|
+
}
|
|
627
|
+
function isAbortError(err) {
|
|
628
|
+
if (!(err instanceof Error)) return false;
|
|
629
|
+
return err.name === "AbortError" || /aborted/i.test(err.message);
|
|
630
|
+
}
|
|
631
|
+
function toAbortApiError(signal, cause) {
|
|
632
|
+
const reasonMessage = signal.reason instanceof Error ? signal.reason.message : "Request was aborted.";
|
|
633
|
+
return new ApiError("TIMEOUT", reasonMessage, { retryable: true }, cause);
|
|
634
|
+
}
|
|
635
|
+
function errorMessage(err) {
|
|
636
|
+
if (err instanceof Error) return err.message;
|
|
637
|
+
return String(err);
|
|
638
|
+
}
|
|
639
|
+
async function applyWorkflowWithRetry(params) {
|
|
640
|
+
try {
|
|
641
|
+
return await patch(params);
|
|
642
|
+
} catch (err) {
|
|
643
|
+
if (!(err instanceof ApiError)) throw err;
|
|
644
|
+
const isRevConflict = err.code === "CANVAS_REV_MISMATCH" || err.meta.status === 409 && err.meta.details != null;
|
|
645
|
+
if (!isRevConflict || !params.autoRev) throw err;
|
|
646
|
+
const snapshot = await params.client.request({
|
|
647
|
+
method: "GET",
|
|
648
|
+
path: `/workflows/${encodeURIComponent(params.id)}`,
|
|
649
|
+
responseSchema: params.schemas.detail,
|
|
650
|
+
signal: params.signal
|
|
651
|
+
});
|
|
652
|
+
const retryResult = await patch({
|
|
653
|
+
...params,
|
|
654
|
+
canvasRev: snapshot.canvas_rev,
|
|
655
|
+
// Fresh key — the body changed (different canvas_rev), so reusing the
|
|
656
|
+
// caller's key would trigger IDEMPOTENCY_KEY_CONFLICT.
|
|
657
|
+
idempotencyKey: void 0
|
|
658
|
+
});
|
|
659
|
+
return { ...retryResult, retriedWithRev: snapshot.canvas_rev };
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
async function patch(params) {
|
|
663
|
+
const data = await params.client.request({
|
|
664
|
+
method: "PATCH",
|
|
665
|
+
path: `/workflows/${encodeURIComponent(params.id)}`,
|
|
666
|
+
body: {
|
|
667
|
+
canvas_rev: params.canvasRev,
|
|
668
|
+
operations: params.operations
|
|
669
|
+
},
|
|
670
|
+
idempotencyKey: params.idempotencyKey,
|
|
671
|
+
responseSchema: params.schemas.mutate,
|
|
672
|
+
signal: params.signal
|
|
673
|
+
});
|
|
674
|
+
return {
|
|
675
|
+
canvasRev: data.canvas_rev,
|
|
676
|
+
applied: data.applied,
|
|
677
|
+
workflow: data.workflow
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// src/errors/codes.ts
|
|
682
|
+
var LOGIN_HINT = "Run `clickraft login` to authenticate.";
|
|
683
|
+
var RELOGIN_HINT = "Run `clickraft login` to refresh.";
|
|
684
|
+
var EXIT_CODE_MAP = {
|
|
685
|
+
// --- 37 Phase 8 Q7 codes ----------------------------------------------------
|
|
686
|
+
AUTH_TOKEN_MISSING: {
|
|
687
|
+
exitCode: 3,
|
|
688
|
+
category: "auth",
|
|
689
|
+
retryable: false,
|
|
690
|
+
hint: "Set `CLICKRAFT_TOKEN` or run `clickraft login`."
|
|
691
|
+
},
|
|
692
|
+
AUTH_TOKEN_INVALID: {
|
|
693
|
+
exitCode: 3,
|
|
694
|
+
category: "auth",
|
|
695
|
+
retryable: false,
|
|
696
|
+
hint: "The token is malformed or unknown. Run `clickraft login`."
|
|
697
|
+
},
|
|
698
|
+
AUTH_TOKEN_EXPIRED: {
|
|
699
|
+
exitCode: 3,
|
|
700
|
+
category: "auth",
|
|
701
|
+
retryable: false,
|
|
702
|
+
hint: RELOGIN_HINT
|
|
703
|
+
},
|
|
704
|
+
AUTH_TOKEN_REVOKED: {
|
|
705
|
+
exitCode: 3,
|
|
706
|
+
category: "auth",
|
|
707
|
+
retryable: false,
|
|
708
|
+
hint: "Token was revoked. Run `clickraft login` to mint a new one."
|
|
709
|
+
},
|
|
710
|
+
AUTH_TOKEN_SCOPE_INSUFFICIENT: {
|
|
711
|
+
exitCode: 4,
|
|
712
|
+
category: "permission",
|
|
713
|
+
retryable: false,
|
|
714
|
+
hint: "This token does not have the required scope. Re-run `clickraft login` selecting the needed scopes."
|
|
715
|
+
},
|
|
716
|
+
AUTH_ORG_MISMATCH: {
|
|
717
|
+
exitCode: 4,
|
|
718
|
+
category: "permission",
|
|
719
|
+
retryable: false,
|
|
720
|
+
hint: "This token's org no longer exists or you have been removed."
|
|
721
|
+
},
|
|
722
|
+
AUTH_DEVICE_CODE_PENDING: {
|
|
723
|
+
exitCode: 0,
|
|
724
|
+
category: "success",
|
|
725
|
+
retryable: false,
|
|
726
|
+
hint: "Continue polling."
|
|
727
|
+
},
|
|
728
|
+
AUTH_DEVICE_CODE_EXPIRED: {
|
|
729
|
+
exitCode: 3,
|
|
730
|
+
category: "auth",
|
|
731
|
+
retryable: false,
|
|
732
|
+
hint: "Device code expired. Re-run `clickraft login`."
|
|
733
|
+
},
|
|
734
|
+
AUTH_DEVICE_CODE_SLOW_DOWN: {
|
|
735
|
+
exitCode: 0,
|
|
736
|
+
category: "success",
|
|
737
|
+
retryable: false,
|
|
738
|
+
hint: "Increase polling interval (Retry-After)."
|
|
739
|
+
},
|
|
740
|
+
IDEMPOTENCY_KEY_MISSING: {
|
|
741
|
+
exitCode: 2,
|
|
742
|
+
category: "usage",
|
|
743
|
+
retryable: false,
|
|
744
|
+
hint: "Add `X-Idempotency-Key` header (or `--idempotency-key`)."
|
|
745
|
+
},
|
|
746
|
+
IDEMPOTENCY_KEY_CONFLICT: {
|
|
747
|
+
exitCode: 7,
|
|
748
|
+
category: "conflict",
|
|
749
|
+
retryable: false,
|
|
750
|
+
hint: "Same key was used with a different request body. Use a new key."
|
|
751
|
+
},
|
|
752
|
+
PLAN_FEATURE_LOCKED: {
|
|
753
|
+
exitCode: 4,
|
|
754
|
+
category: "permission",
|
|
755
|
+
retryable: false,
|
|
756
|
+
hint: "API access is not enabled on your plan. Visit billing settings."
|
|
757
|
+
},
|
|
758
|
+
PLAN_QUOTA_EXCEEDED: {
|
|
759
|
+
exitCode: 5,
|
|
760
|
+
category: "quota",
|
|
761
|
+
retryable: false,
|
|
762
|
+
hint: "Daily API quota exhausted. Wait, or upgrade your plan."
|
|
763
|
+
},
|
|
764
|
+
INSUFFICIENT_CREDITS: {
|
|
765
|
+
exitCode: 5,
|
|
766
|
+
category: "quota",
|
|
767
|
+
retryable: false,
|
|
768
|
+
hint: "Top up credits to retry."
|
|
769
|
+
},
|
|
770
|
+
CREDIT_DEDUCTION_FAILED: {
|
|
771
|
+
exitCode: 1,
|
|
772
|
+
category: "runtime",
|
|
773
|
+
retryable: true,
|
|
774
|
+
hint: "Could not charge credits. Retry; no charge was made."
|
|
775
|
+
},
|
|
776
|
+
RATE_LIMITED: {
|
|
777
|
+
exitCode: 5,
|
|
778
|
+
category: "quota",
|
|
779
|
+
retryable: true,
|
|
780
|
+
hint: "Slow down. Honor Retry-After."
|
|
781
|
+
},
|
|
782
|
+
INPUT_INVALID_FORMAT: {
|
|
783
|
+
exitCode: 2,
|
|
784
|
+
category: "usage",
|
|
785
|
+
retryable: false,
|
|
786
|
+
hint: "Check input format and try again."
|
|
787
|
+
},
|
|
788
|
+
INPUT_TOO_LONG: {
|
|
789
|
+
exitCode: 2,
|
|
790
|
+
category: "usage",
|
|
791
|
+
retryable: false,
|
|
792
|
+
hint: "Field exceeded max length."
|
|
793
|
+
},
|
|
794
|
+
WORKFLOW_VALIDATION_FAILED: {
|
|
795
|
+
exitCode: 2,
|
|
796
|
+
category: "usage",
|
|
797
|
+
retryable: false,
|
|
798
|
+
hint: "Run `workflow validate` for the full list of errors."
|
|
799
|
+
},
|
|
800
|
+
WORKFLOW_NODE_TYPE_UNKNOWN: {
|
|
801
|
+
exitCode: 2,
|
|
802
|
+
category: "usage",
|
|
803
|
+
retryable: false,
|
|
804
|
+
hint: "Run `clickraft nodes list` for the current set of node types."
|
|
805
|
+
},
|
|
806
|
+
CANVAS_REV_MISMATCH: {
|
|
807
|
+
exitCode: 7,
|
|
808
|
+
category: "conflict",
|
|
809
|
+
retryable: false,
|
|
810
|
+
hint: "The workflow was updated elsewhere. Re-fetch and retry with the current canvas_rev."
|
|
811
|
+
},
|
|
812
|
+
CANVAS_REV_REQUIRED: {
|
|
813
|
+
exitCode: 2,
|
|
814
|
+
category: "usage",
|
|
815
|
+
retryable: false,
|
|
816
|
+
hint: "Include --canvas-rev or use --auto-rev."
|
|
817
|
+
},
|
|
818
|
+
WORKER_UNREACHABLE: {
|
|
819
|
+
exitCode: 6,
|
|
820
|
+
category: "network",
|
|
821
|
+
retryable: true,
|
|
822
|
+
hint: "Collaboration service temporarily unavailable. Retry."
|
|
823
|
+
},
|
|
824
|
+
IDEMPOTENCY_IN_PROGRESS: {
|
|
825
|
+
exitCode: 7,
|
|
826
|
+
category: "conflict",
|
|
827
|
+
retryable: false,
|
|
828
|
+
hint: "A request with this idempotency key is currently in-flight. Retry in a moment."
|
|
829
|
+
},
|
|
830
|
+
TOKEN_NAME_INVALID: {
|
|
831
|
+
exitCode: 2,
|
|
832
|
+
category: "usage",
|
|
833
|
+
retryable: false,
|
|
834
|
+
hint: "Agent token name is malformed."
|
|
835
|
+
},
|
|
836
|
+
WORKFLOW_NOT_FOUND: {
|
|
837
|
+
exitCode: 2,
|
|
838
|
+
category: "usage",
|
|
839
|
+
retryable: false,
|
|
840
|
+
hint: "No workflow with that ID in your org."
|
|
841
|
+
},
|
|
842
|
+
NOT_FOUND: {
|
|
843
|
+
exitCode: 2,
|
|
844
|
+
category: "usage",
|
|
845
|
+
retryable: false,
|
|
846
|
+
hint: "Resource not found."
|
|
847
|
+
},
|
|
848
|
+
NODE_TYPE_NOT_FOUND: {
|
|
849
|
+
exitCode: 2,
|
|
850
|
+
category: "usage",
|
|
851
|
+
retryable: false,
|
|
852
|
+
hint: "Run `clickraft nodes list` for the current set of node types."
|
|
853
|
+
},
|
|
854
|
+
FEATURE_DISABLED: {
|
|
855
|
+
exitCode: 2,
|
|
856
|
+
category: "usage",
|
|
857
|
+
retryable: false,
|
|
858
|
+
hint: "This endpoint is not enabled. Contact support if you believe this is in error."
|
|
859
|
+
},
|
|
860
|
+
FORBIDDEN: {
|
|
861
|
+
exitCode: 4,
|
|
862
|
+
category: "permission",
|
|
863
|
+
retryable: false,
|
|
864
|
+
hint: "Your role does not permit this action."
|
|
865
|
+
},
|
|
866
|
+
MODEL_NOT_FOUND: {
|
|
867
|
+
exitCode: 2,
|
|
868
|
+
category: "usage",
|
|
869
|
+
retryable: false,
|
|
870
|
+
hint: "Unknown `modelSlug`. Run `brand-model list` for the current set."
|
|
871
|
+
},
|
|
872
|
+
PROVIDER_UNAVAILABLE: {
|
|
873
|
+
exitCode: 6,
|
|
874
|
+
category: "network",
|
|
875
|
+
retryable: true,
|
|
876
|
+
hint: "Provider is temporarily unavailable. Retry shortly."
|
|
877
|
+
},
|
|
878
|
+
STORAGE_UPLOAD_FAILED: {
|
|
879
|
+
exitCode: 1,
|
|
880
|
+
category: "runtime",
|
|
881
|
+
retryable: true,
|
|
882
|
+
hint: "Upload failed mid-stream. Retry."
|
|
883
|
+
},
|
|
884
|
+
STORAGE_QUOTA_EXCEEDED: {
|
|
885
|
+
exitCode: 5,
|
|
886
|
+
category: "quota",
|
|
887
|
+
retryable: false,
|
|
888
|
+
hint: "Org storage quota exceeded. Free space or upgrade."
|
|
889
|
+
},
|
|
890
|
+
GEN_CONTENT_REFUSAL: {
|
|
891
|
+
exitCode: 1,
|
|
892
|
+
category: "runtime",
|
|
893
|
+
retryable: false,
|
|
894
|
+
hint: "The model declined the request via safety policy."
|
|
895
|
+
},
|
|
896
|
+
GEN_OUTPUT_SCHEMA_MISMATCH: {
|
|
897
|
+
exitCode: 1,
|
|
898
|
+
category: "runtime",
|
|
899
|
+
retryable: true,
|
|
900
|
+
hint: "Provider returned malformed output. Full refund issued. Retry."
|
|
901
|
+
},
|
|
902
|
+
GEN_EMPTY_OUTPUT: {
|
|
903
|
+
exitCode: 1,
|
|
904
|
+
category: "runtime",
|
|
905
|
+
retryable: true,
|
|
906
|
+
hint: "Provider returned no usable output. Full refund issued. Retry."
|
|
907
|
+
},
|
|
908
|
+
GEN_ITEM_COUNT_MISMATCH: {
|
|
909
|
+
exitCode: 0,
|
|
910
|
+
category: "success",
|
|
911
|
+
retryable: false,
|
|
912
|
+
hint: "Partial output kept; no refund."
|
|
913
|
+
},
|
|
914
|
+
AGENT_FINGERPRINT_REJECTED: {
|
|
915
|
+
exitCode: 4,
|
|
916
|
+
category: "permission",
|
|
917
|
+
retryable: false,
|
|
918
|
+
hint: "This client identification is not permitted. Contact support."
|
|
919
|
+
},
|
|
920
|
+
INTERNAL_ERROR: {
|
|
921
|
+
exitCode: 1,
|
|
922
|
+
category: "runtime",
|
|
923
|
+
retryable: true,
|
|
924
|
+
hint: "Server error. Retry; if it persists, contact support with the request ID."
|
|
925
|
+
},
|
|
926
|
+
CREDENTIALS_LOCKED: {
|
|
927
|
+
exitCode: 1,
|
|
928
|
+
category: "runtime",
|
|
929
|
+
retryable: true,
|
|
930
|
+
hint: "Another CLI invocation holds the credentials lock. Retry shortly."
|
|
931
|
+
},
|
|
932
|
+
CREDENTIALS_PERMISSIONS_LOOSE: {
|
|
933
|
+
exitCode: 0,
|
|
934
|
+
category: "success",
|
|
935
|
+
retryable: false,
|
|
936
|
+
hint: "Credentials file mode is wider than 0600; continuing."
|
|
937
|
+
},
|
|
938
|
+
// --- 5 additional codes introduced by 1A.1 ---------------------------------
|
|
939
|
+
AUTH_ACCESS_DENIED: {
|
|
940
|
+
exitCode: 3,
|
|
941
|
+
category: "auth",
|
|
942
|
+
retryable: false,
|
|
943
|
+
hint: "You denied the OAuth grant. Re-run `clickraft login` to retry."
|
|
944
|
+
},
|
|
945
|
+
AUTH_FLOW_CANCELLED: {
|
|
946
|
+
exitCode: 3,
|
|
947
|
+
category: "auth",
|
|
948
|
+
retryable: false,
|
|
949
|
+
hint: "Authentication was cancelled. Re-run `clickraft login`."
|
|
950
|
+
},
|
|
951
|
+
CREDENTIALS_CORRUPT: {
|
|
952
|
+
exitCode: 1,
|
|
953
|
+
category: "runtime",
|
|
954
|
+
retryable: false,
|
|
955
|
+
hint: "Credentials file is unreadable. Remove it and run `clickraft login`."
|
|
956
|
+
},
|
|
957
|
+
CREDENTIALS_NOT_FOUND: {
|
|
958
|
+
exitCode: 3,
|
|
959
|
+
category: "auth",
|
|
960
|
+
retryable: false,
|
|
961
|
+
hint: LOGIN_HINT
|
|
962
|
+
},
|
|
963
|
+
CREDENTIALS_PROFILE_NOT_FOUND: {
|
|
964
|
+
exitCode: 2,
|
|
965
|
+
category: "usage",
|
|
966
|
+
retryable: false,
|
|
967
|
+
hint: "No such profile. Use `clickraft auth profiles` to list available profiles."
|
|
968
|
+
},
|
|
969
|
+
// --- 8 brand model resolution codes -----------------------------------------
|
|
970
|
+
BRAND_MODEL_NOT_FOUND: {
|
|
971
|
+
exitCode: 1,
|
|
972
|
+
category: "runtime",
|
|
973
|
+
retryable: false,
|
|
974
|
+
hint: "No brand model with that ID. Run `clickraft brand-model list`."
|
|
975
|
+
},
|
|
976
|
+
BRAND_MODEL_NOT_A_MODEL: {
|
|
977
|
+
exitCode: 1,
|
|
978
|
+
category: "runtime",
|
|
979
|
+
retryable: false,
|
|
980
|
+
hint: "The specified asset is not a brand model."
|
|
981
|
+
},
|
|
982
|
+
BRAND_MODEL_NOT_ACTIVE: {
|
|
983
|
+
exitCode: 1,
|
|
984
|
+
category: "runtime",
|
|
985
|
+
retryable: false,
|
|
986
|
+
hint: "Brand model is not active. Check its status in the dashboard."
|
|
987
|
+
},
|
|
988
|
+
BRAND_MODEL_NO_REFERENCES: {
|
|
989
|
+
exitCode: 1,
|
|
990
|
+
category: "runtime",
|
|
991
|
+
retryable: false,
|
|
992
|
+
hint: "Brand model has no reference images. Upload references first."
|
|
993
|
+
},
|
|
994
|
+
BRAND_MODEL_POSE_NOT_FOUND: {
|
|
995
|
+
exitCode: 1,
|
|
996
|
+
category: "runtime",
|
|
997
|
+
retryable: false,
|
|
998
|
+
hint: "Requested pose not found. Valid: front, 3/4-right, right, left, 3/4-left, back, approved."
|
|
999
|
+
},
|
|
1000
|
+
BRAND_MODEL_DUPLICATE_ID: {
|
|
1001
|
+
exitCode: 2,
|
|
1002
|
+
category: "usage",
|
|
1003
|
+
retryable: false,
|
|
1004
|
+
hint: "Same brand model ID appears more than once."
|
|
1005
|
+
},
|
|
1006
|
+
MODEL_DOES_NOT_SUPPORT_REFERENCES: {
|
|
1007
|
+
exitCode: 2,
|
|
1008
|
+
category: "usage",
|
|
1009
|
+
retryable: false,
|
|
1010
|
+
hint: "This model does not support brand model references."
|
|
1011
|
+
},
|
|
1012
|
+
REFERENCE_LIMIT_EXCEEDED: {
|
|
1013
|
+
exitCode: 2,
|
|
1014
|
+
category: "usage",
|
|
1015
|
+
retryable: false,
|
|
1016
|
+
hint: "Too many reference images. Check model limits."
|
|
1017
|
+
},
|
|
1018
|
+
// --- 8 product reference resolver codes -------------------------------------
|
|
1019
|
+
PRODUCT_NOT_FOUND: {
|
|
1020
|
+
exitCode: 1,
|
|
1021
|
+
category: "runtime",
|
|
1022
|
+
retryable: false,
|
|
1023
|
+
hint: "No product with that ID in your org. Run `clickraft product list`."
|
|
1024
|
+
},
|
|
1025
|
+
PRODUCT_DUPLICATE_REFERENCE: {
|
|
1026
|
+
exitCode: 2,
|
|
1027
|
+
category: "usage",
|
|
1028
|
+
retryable: false,
|
|
1029
|
+
hint: "Same (productId, imageId) tuple appears twice in the request."
|
|
1030
|
+
},
|
|
1031
|
+
PRODUCT_IMAGE_NOT_FOUND: {
|
|
1032
|
+
exitCode: 1,
|
|
1033
|
+
category: "runtime",
|
|
1034
|
+
retryable: false,
|
|
1035
|
+
hint: "imageId does not exist in the product images."
|
|
1036
|
+
},
|
|
1037
|
+
PRODUCT_IMAGE_MISMATCH: {
|
|
1038
|
+
exitCode: 2,
|
|
1039
|
+
category: "usage",
|
|
1040
|
+
retryable: false,
|
|
1041
|
+
hint: "imageId belongs to a different product."
|
|
1042
|
+
},
|
|
1043
|
+
PRODUCT_SYNC_ERROR: {
|
|
1044
|
+
exitCode: 1,
|
|
1045
|
+
category: "runtime",
|
|
1046
|
+
retryable: false,
|
|
1047
|
+
hint: "Product sync status is 'error'. Re-sync or use a different product."
|
|
1048
|
+
},
|
|
1049
|
+
PRODUCT_DELETED_UPSTREAM: {
|
|
1050
|
+
exitCode: 1,
|
|
1051
|
+
category: "runtime",
|
|
1052
|
+
retryable: false,
|
|
1053
|
+
hint: "Product was deleted upstream. Remove it or re-sync."
|
|
1054
|
+
},
|
|
1055
|
+
PRODUCT_NO_PRIMARY_IMAGE: {
|
|
1056
|
+
exitCode: 1,
|
|
1057
|
+
category: "runtime",
|
|
1058
|
+
retryable: false,
|
|
1059
|
+
hint: "Product has no primary image. Specify --product <id>:<imageId> explicitly."
|
|
1060
|
+
},
|
|
1061
|
+
PRODUCT_SOURCE_MANAGED: {
|
|
1062
|
+
exitCode: 4,
|
|
1063
|
+
category: "permission",
|
|
1064
|
+
retryable: false,
|
|
1065
|
+
hint: "Product is managed by an external integration."
|
|
1066
|
+
},
|
|
1067
|
+
// --- 4 CLI-synthetic codes --------------------------------------------------
|
|
1068
|
+
NETWORK_ERROR: {
|
|
1069
|
+
exitCode: 6,
|
|
1070
|
+
category: "network",
|
|
1071
|
+
retryable: true,
|
|
1072
|
+
hint: "Network connection failed. Check connectivity and retry."
|
|
1073
|
+
},
|
|
1074
|
+
TIMEOUT: {
|
|
1075
|
+
exitCode: 6,
|
|
1076
|
+
category: "network",
|
|
1077
|
+
retryable: true,
|
|
1078
|
+
hint: "Request timed out. Retry; consider increasing the timeout."
|
|
1079
|
+
},
|
|
1080
|
+
PARSE_ERROR: {
|
|
1081
|
+
exitCode: 1,
|
|
1082
|
+
category: "runtime",
|
|
1083
|
+
retryable: false,
|
|
1084
|
+
hint: "Server returned an unexpected response. Contact support with the request ID."
|
|
1085
|
+
},
|
|
1086
|
+
SCHEMA_MISMATCH: {
|
|
1087
|
+
exitCode: 1,
|
|
1088
|
+
category: "runtime",
|
|
1089
|
+
retryable: false,
|
|
1090
|
+
hint: "Server response did not match the expected schema. Update the CLI."
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
1093
|
+
function getExitCode(code) {
|
|
1094
|
+
return EXIT_CODE_MAP[code]?.exitCode ?? 1;
|
|
1055
1095
|
}
|
|
1056
|
-
function
|
|
1057
|
-
|
|
1058
|
-
return new ApiError("TIMEOUT", reasonMessage, { retryable: true }, cause);
|
|
1096
|
+
function toEnvelopeCode(code) {
|
|
1097
|
+
return code.startsWith("E_") ? code : `E_${code}`;
|
|
1059
1098
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1099
|
+
var SIGINT_EXIT_CODE = 130;
|
|
1100
|
+
|
|
1101
|
+
// src/api/client.ts
|
|
1102
|
+
var isRetryableServerCode = (code) => EXIT_CODE_MAP[code].retryable;
|
|
1103
|
+
var onWarning = (msg) => {
|
|
1104
|
+
console.warn(msg);
|
|
1105
|
+
};
|
|
1106
|
+
function createApiClient(opts) {
|
|
1107
|
+
return createClient({ ...opts, onWarning, isRetryableServerCode });
|
|
1063
1108
|
}
|
|
1064
1109
|
|
|
1065
1110
|
// src/auth/errors.ts
|
|
@@ -1488,7 +1533,7 @@ async function getBalance(options) {
|
|
|
1488
1533
|
if (!config.accessToken) {
|
|
1489
1534
|
throw new AuthError("AUTH_TOKEN_MISSING", `Not authenticated. Run \`clickraft login\` first.`);
|
|
1490
1535
|
}
|
|
1491
|
-
const factory = options.apiClientFactory ?? ((apiBaseUrl, accessToken) =>
|
|
1536
|
+
const factory = options.apiClientFactory ?? ((apiBaseUrl, accessToken) => createApiClient({
|
|
1492
1537
|
apiBaseUrl,
|
|
1493
1538
|
accessToken,
|
|
1494
1539
|
signal: options.signal,
|
|
@@ -1514,7 +1559,7 @@ async function listBrandModels(options) {
|
|
|
1514
1559
|
if (!config.accessToken) {
|
|
1515
1560
|
throw new AuthError("AUTH_TOKEN_MISSING", `Not authenticated. Run \`clickraft login\` first.`);
|
|
1516
1561
|
}
|
|
1517
|
-
const factory = options.apiClientFactory ?? ((apiBaseUrl, accessToken) =>
|
|
1562
|
+
const factory = options.apiClientFactory ?? ((apiBaseUrl, accessToken) => createApiClient({
|
|
1518
1563
|
apiBaseUrl,
|
|
1519
1564
|
accessToken,
|
|
1520
1565
|
signal: options.signal,
|
|
@@ -1679,7 +1724,7 @@ async function buildClient(opts) {
|
|
|
1679
1724
|
flagApiBaseUrl: opts.apiBaseUrl,
|
|
1680
1725
|
flagToken: opts.token
|
|
1681
1726
|
});
|
|
1682
|
-
return
|
|
1727
|
+
return createApiClient({
|
|
1683
1728
|
apiBaseUrl: cfg.apiBaseUrl,
|
|
1684
1729
|
accessToken: cfg.accessToken,
|
|
1685
1730
|
signal: opts.signal,
|
|
@@ -1846,7 +1891,7 @@ async function buildGenerateClient(opts) {
|
|
|
1846
1891
|
flagApiBaseUrl: opts.apiBaseUrl,
|
|
1847
1892
|
flagToken: opts.token
|
|
1848
1893
|
});
|
|
1849
|
-
return
|
|
1894
|
+
return createApiClient({
|
|
1850
1895
|
apiBaseUrl: cfg.apiBaseUrl,
|
|
1851
1896
|
accessToken: cfg.accessToken,
|
|
1852
1897
|
signal: opts.signal,
|
|
@@ -2618,7 +2663,7 @@ async function logout(options) {
|
|
|
2618
2663
|
let serverRevoked = false;
|
|
2619
2664
|
let revokeFailureMessage;
|
|
2620
2665
|
if (options.revoke) {
|
|
2621
|
-
const factory = options.apiClientFactory ?? ((apiBaseUrl, accessToken) =>
|
|
2666
|
+
const factory = options.apiClientFactory ?? ((apiBaseUrl, accessToken) => createApiClient({
|
|
2622
2667
|
apiBaseUrl,
|
|
2623
2668
|
accessToken,
|
|
2624
2669
|
signal: options.signal,
|
|
@@ -2703,7 +2748,7 @@ async function buildModelClient(opts) {
|
|
|
2703
2748
|
flagApiBaseUrl: opts.apiBaseUrl,
|
|
2704
2749
|
flagToken: opts.token
|
|
2705
2750
|
});
|
|
2706
|
-
return
|
|
2751
|
+
return createApiClient({
|
|
2707
2752
|
apiBaseUrl: cfg.apiBaseUrl,
|
|
2708
2753
|
accessToken: cfg.accessToken,
|
|
2709
2754
|
signal: opts.signal,
|
|
@@ -2756,7 +2801,7 @@ async function buildNodesClient(opts) {
|
|
|
2756
2801
|
flagApiBaseUrl: opts.apiBaseUrl,
|
|
2757
2802
|
flagToken: opts.token
|
|
2758
2803
|
});
|
|
2759
|
-
return
|
|
2804
|
+
return createApiClient({
|
|
2760
2805
|
apiBaseUrl: cfg.apiBaseUrl,
|
|
2761
2806
|
accessToken: cfg.accessToken,
|
|
2762
2807
|
signal: opts.signal,
|
|
@@ -2800,7 +2845,7 @@ async function listProducts(options) {
|
|
|
2800
2845
|
if (!config.accessToken) {
|
|
2801
2846
|
throw new AuthError("AUTH_TOKEN_MISSING", `Not authenticated. Run \`clickraft login\` first.`);
|
|
2802
2847
|
}
|
|
2803
|
-
const factory = options.apiClientFactory ?? ((apiBaseUrl, accessToken) =>
|
|
2848
|
+
const factory = options.apiClientFactory ?? ((apiBaseUrl, accessToken) => createApiClient({
|
|
2804
2849
|
apiBaseUrl,
|
|
2805
2850
|
accessToken,
|
|
2806
2851
|
signal: options.signal,
|
|
@@ -2909,7 +2954,7 @@ async function buildTemplateClient(opts) {
|
|
|
2909
2954
|
flagApiBaseUrl: opts.apiBaseUrl,
|
|
2910
2955
|
flagToken: opts.token
|
|
2911
2956
|
});
|
|
2912
|
-
return
|
|
2957
|
+
return createApiClient({
|
|
2913
2958
|
apiBaseUrl: cfg.apiBaseUrl,
|
|
2914
2959
|
accessToken: cfg.accessToken,
|
|
2915
2960
|
signal: opts.signal,
|
|
@@ -3003,7 +3048,7 @@ async function listTokens(options) {
|
|
|
3003
3048
|
if (!config.accessToken) {
|
|
3004
3049
|
throw new AuthError("AUTH_TOKEN_MISSING", `Not authenticated. Run \`clickraft login\` first.`);
|
|
3005
3050
|
}
|
|
3006
|
-
const factory = options.apiClientFactory ?? ((apiBaseUrl, accessToken) =>
|
|
3051
|
+
const factory = options.apiClientFactory ?? ((apiBaseUrl, accessToken) => createApiClient({
|
|
3007
3052
|
apiBaseUrl,
|
|
3008
3053
|
accessToken,
|
|
3009
3054
|
signal: options.signal,
|
|
@@ -3075,7 +3120,7 @@ async function buildWorkflowClient(opts) {
|
|
|
3075
3120
|
flagApiBaseUrl: opts.apiBaseUrl,
|
|
3076
3121
|
flagToken: opts.token
|
|
3077
3122
|
});
|
|
3078
|
-
return
|
|
3123
|
+
return createApiClient({
|
|
3079
3124
|
apiBaseUrl: cfg.apiBaseUrl,
|
|
3080
3125
|
accessToken: cfg.accessToken,
|
|
3081
3126
|
signal: opts.signal,
|
|
@@ -3083,54 +3128,19 @@ async function buildWorkflowClient(opts) {
|
|
|
3083
3128
|
});
|
|
3084
3129
|
}
|
|
3085
3130
|
|
|
3086
|
-
// src/commands/workflow/get.ts
|
|
3087
|
-
async function getWorkflow(opts) {
|
|
3088
|
-
const client = opts.client ?? await buildWorkflowClient(opts);
|
|
3089
|
-
return client.request({
|
|
3090
|
-
method: "GET",
|
|
3091
|
-
path: `/workflows/${encodeURIComponent(opts.id)}`,
|
|
3092
|
-
responseSchema: WorkflowDetailSchema,
|
|
3093
|
-
signal: opts.signal
|
|
3094
|
-
});
|
|
3095
|
-
}
|
|
3096
|
-
|
|
3097
3131
|
// src/commands/workflow/apply.ts
|
|
3098
3132
|
async function applyWorkflow(opts) {
|
|
3099
3133
|
const client = opts.client ?? await buildWorkflowClient(opts);
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
if (!isRevConflict || !opts.autoRev) throw err;
|
|
3106
|
-
const snapshot = await getWorkflow({ id: opts.id, client, signal: opts.signal });
|
|
3107
|
-
const retryResult = await patchWorkflow(client, {
|
|
3108
|
-
...opts,
|
|
3109
|
-
canvasRev: snapshot.canvas_rev,
|
|
3110
|
-
// Fresh key — the body changed (different canvas_rev), so reusing
|
|
3111
|
-
// the caller's key would trigger IDEMPOTENCY_KEY_CONFLICT.
|
|
3112
|
-
idempotencyKey: void 0
|
|
3113
|
-
});
|
|
3114
|
-
return { ...retryResult, retriedWithRev: snapshot.canvas_rev };
|
|
3115
|
-
}
|
|
3116
|
-
}
|
|
3117
|
-
async function patchWorkflow(client, opts) {
|
|
3118
|
-
const data = await client.request({
|
|
3119
|
-
method: "PATCH",
|
|
3120
|
-
path: `/workflows/${encodeURIComponent(opts.id)}`,
|
|
3121
|
-
body: {
|
|
3122
|
-
canvas_rev: opts.canvasRev,
|
|
3123
|
-
operations: opts.operations
|
|
3124
|
-
},
|
|
3134
|
+
return applyWorkflowWithRetry({
|
|
3135
|
+
client,
|
|
3136
|
+
id: opts.id,
|
|
3137
|
+
canvasRev: opts.canvasRev,
|
|
3138
|
+
operations: opts.operations,
|
|
3125
3139
|
idempotencyKey: opts.idempotencyKey,
|
|
3126
|
-
|
|
3127
|
-
signal: opts.signal
|
|
3140
|
+
autoRev: opts.autoRev,
|
|
3141
|
+
signal: opts.signal,
|
|
3142
|
+
schemas: { mutate: WorkflowMutateResponseSchema, detail: WorkflowDetailSchema }
|
|
3128
3143
|
});
|
|
3129
|
-
return {
|
|
3130
|
-
canvasRev: data.canvas_rev,
|
|
3131
|
-
applied: data.applied,
|
|
3132
|
-
workflow: data.workflow
|
|
3133
|
-
};
|
|
3134
3144
|
}
|
|
3135
3145
|
|
|
3136
3146
|
// src/commands/workflow/create.ts
|
|
@@ -3161,6 +3171,17 @@ async function createWorkflow(opts) {
|
|
|
3161
3171
|
});
|
|
3162
3172
|
}
|
|
3163
3173
|
|
|
3174
|
+
// src/commands/workflow/get.ts
|
|
3175
|
+
async function getWorkflow(opts) {
|
|
3176
|
+
const client = opts.client ?? await buildWorkflowClient(opts);
|
|
3177
|
+
return client.request({
|
|
3178
|
+
method: "GET",
|
|
3179
|
+
path: `/workflows/${encodeURIComponent(opts.id)}`,
|
|
3180
|
+
responseSchema: WorkflowDetailSchema,
|
|
3181
|
+
signal: opts.signal
|
|
3182
|
+
});
|
|
3183
|
+
}
|
|
3184
|
+
|
|
3164
3185
|
// src/commands/workflow/list.ts
|
|
3165
3186
|
async function listWorkflows(opts) {
|
|
3166
3187
|
const client = opts.client ?? await buildWorkflowClient(opts);
|