deepline 0.1.299 → 0.1.301
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundling-sources/sdk/src/http.ts +48 -30
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-store-adapter.ts +103 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-receipt-writer.ts +78 -5
- package/dist/cli/index.js +50 -39
- package/dist/cli/index.mjs +50 -39
- package/dist/index.js +27 -16
- package/dist/index.mjs +27 -16
- package/package.json +1 -1
|
@@ -54,8 +54,8 @@ interface RequestOptions {
|
|
|
54
54
|
* Treat HTTP 403 as a regular API error instead of a generic {@link AuthError}.
|
|
55
55
|
* Use for endpoints that return 403 with a meaningful server error message
|
|
56
56
|
* (e.g. feature-flagged-off billing subscription checkout) so the server
|
|
57
|
-
* message is preserved and surfaced loudly. HTTP 401
|
|
58
|
-
*
|
|
57
|
+
* message is preserved and surfaced loudly. A provider-originated HTTP 401
|
|
58
|
+
* remains a regular API error when its response envelope says so.
|
|
59
59
|
*/
|
|
60
60
|
forbiddenAsApiError?: boolean;
|
|
61
61
|
/**
|
|
@@ -311,7 +311,7 @@ export class HttpClient {
|
|
|
311
311
|
* @param path - API path (e.g. `"/api/v2/tools"`)
|
|
312
312
|
* @param options - HTTP method, body, headers, and timeout
|
|
313
313
|
* @returns Parsed JSON response body
|
|
314
|
-
* @throws {@link AuthError} on HTTP 401/403 (immediate, no retry)
|
|
314
|
+
* @throws {@link AuthError} on Deepline-auth HTTP 401/403 (immediate, no retry)
|
|
315
315
|
* @throws {@link RateLimitError} on HTTP 429 after all retries exhausted
|
|
316
316
|
* @throws {@link DeeplineError} on other API errors or connection failures
|
|
317
317
|
*/
|
|
@@ -374,13 +374,6 @@ export class HttpClient {
|
|
|
374
374
|
|
|
375
375
|
clearTimeout(timeoutId);
|
|
376
376
|
|
|
377
|
-
if (
|
|
378
|
-
response.status === 401 ||
|
|
379
|
-
(response.status === 403 && !options?.forbiddenAsApiError)
|
|
380
|
-
) {
|
|
381
|
-
throw new AuthError();
|
|
382
|
-
}
|
|
383
|
-
|
|
384
377
|
if (response.status === 429) {
|
|
385
378
|
const retryAfter = parseRetryAfter(response);
|
|
386
379
|
lastError = new RateLimitError(retryAfter);
|
|
@@ -392,11 +385,14 @@ export class HttpClient {
|
|
|
392
385
|
}
|
|
393
386
|
|
|
394
387
|
const body = await response.text();
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
388
|
+
const parsed = parseResponseBody(body);
|
|
389
|
+
|
|
390
|
+
if (
|
|
391
|
+
(response.status === 401 &&
|
|
392
|
+
!isProviderOriginatedHttpError(parsed)) ||
|
|
393
|
+
(response.status === 403 && !options?.forbiddenAsApiError)
|
|
394
|
+
) {
|
|
395
|
+
throw new AuthError();
|
|
400
396
|
}
|
|
401
397
|
|
|
402
398
|
if (!response.ok) {
|
|
@@ -451,16 +447,7 @@ export class HttpClient {
|
|
|
451
447
|
'string'
|
|
452
448
|
? (parsed as Record<string, string>).message
|
|
453
449
|
: `HTTP ${response.status}`;
|
|
454
|
-
const apiErrorCode =
|
|
455
|
-
errorValue &&
|
|
456
|
-
typeof errorValue === 'object' &&
|
|
457
|
-
typeof (errorValue as Record<string, unknown>).code === 'string'
|
|
458
|
-
? (errorValue as Record<string, string>).code
|
|
459
|
-
: typeof parsed === 'object' &&
|
|
460
|
-
parsed &&
|
|
461
|
-
typeof (parsed as Record<string, unknown>).code === 'string'
|
|
462
|
-
? (parsed as Record<string, string>).code
|
|
463
|
-
: 'API_ERROR';
|
|
450
|
+
const apiErrorCode = apiErrorCodeFromResponse(parsed);
|
|
464
451
|
lastError = new DeeplineError(msg, response.status, apiErrorCode, {
|
|
465
452
|
response: parsed,
|
|
466
453
|
});
|
|
@@ -547,11 +534,16 @@ export class HttpClient {
|
|
|
547
534
|
signal: options?.signal,
|
|
548
535
|
});
|
|
549
536
|
|
|
550
|
-
if (response.status === 401 || response.status === 403) {
|
|
551
|
-
throw new AuthError();
|
|
552
|
-
}
|
|
553
537
|
if (!response.ok) {
|
|
554
538
|
const body = await response.text();
|
|
539
|
+
const parsed = parseResponseBody(body);
|
|
540
|
+
if (
|
|
541
|
+
(response.status === 401 &&
|
|
542
|
+
!isProviderOriginatedHttpError(parsed)) ||
|
|
543
|
+
response.status === 403
|
|
544
|
+
) {
|
|
545
|
+
throw new AuthError();
|
|
546
|
+
}
|
|
555
547
|
const htmlError = detectHtmlErrorBody(
|
|
556
548
|
body,
|
|
557
549
|
response.headers.get('content-type'),
|
|
@@ -570,11 +562,10 @@ export class HttpClient {
|
|
|
570
562
|
},
|
|
571
563
|
);
|
|
572
564
|
}
|
|
573
|
-
const parsed = parseResponseBody(body);
|
|
574
565
|
throw new DeeplineError(
|
|
575
566
|
apiErrorMessage(parsed, response.status),
|
|
576
567
|
response.status,
|
|
577
|
-
|
|
568
|
+
apiErrorCodeFromResponse(parsed),
|
|
578
569
|
{ response: parsed },
|
|
579
570
|
);
|
|
580
571
|
}
|
|
@@ -679,6 +670,33 @@ function parseResponseBody(body: string): unknown {
|
|
|
679
670
|
}
|
|
680
671
|
}
|
|
681
672
|
|
|
673
|
+
/**
|
|
674
|
+
* A provider can reject its own credential with HTTP 401 while the caller's
|
|
675
|
+
* Deepline API key remains valid. The API envelope is authoritative in that
|
|
676
|
+
* case, so preserve it as a regular DeeplineError rather than prompting the
|
|
677
|
+
* caller to replace DEEPLINE_API_KEY.
|
|
678
|
+
*/
|
|
679
|
+
function isProviderOriginatedHttpError(parsed: unknown): boolean {
|
|
680
|
+
const response = asRecord(parsed);
|
|
681
|
+
const error = asRecord(response?.error);
|
|
682
|
+
const failureOrigin = error?.failure_origin ?? response?.failure_origin;
|
|
683
|
+
const code = error?.code ?? response?.code;
|
|
684
|
+
return failureOrigin === 'provider' || code === 'UPSTREAM_BLOCKED';
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function apiErrorCodeFromResponse(parsed: unknown): string {
|
|
688
|
+
const response = asRecord(parsed);
|
|
689
|
+
const error = asRecord(response?.error);
|
|
690
|
+
const code = error?.code ?? response?.code;
|
|
691
|
+
return typeof code === 'string' ? code : 'API_ERROR';
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function asRecord(value: unknown): Record<string, unknown> | null {
|
|
695
|
+
return typeof value === 'object' && value !== null
|
|
696
|
+
? (value as Record<string, unknown>)
|
|
697
|
+
: null;
|
|
698
|
+
}
|
|
699
|
+
|
|
682
700
|
function isRetryableApiErrorResponse(status: number): boolean {
|
|
683
701
|
return status === 408 || status === 425 || (status >= 500 && status < 600);
|
|
684
702
|
}
|
|
@@ -155,7 +155,7 @@ export const SDK_RELEASE = {
|
|
|
155
155
|
// 0.1.253 makes play-page browser opening opt-in and retires --no-open.
|
|
156
156
|
// 0.1.254 removes the internal operations tree from the published SDK CLI.
|
|
157
157
|
// Operators use the checkout-local deepline-admin binary instead.
|
|
158
|
-
version: '0.1.
|
|
158
|
+
version: '0.1.301',
|
|
159
159
|
contracts: {
|
|
160
160
|
api: {
|
|
161
161
|
name: 'sdk-http-api',
|
|
@@ -124,6 +124,7 @@ export class RuntimeReceiptStoreAdapter {
|
|
|
124
124
|
maxBatchBytes?: number;
|
|
125
125
|
maxFlushMs?: number;
|
|
126
126
|
maxBufferedBytes?: number;
|
|
127
|
+
onRetryTelemetry?: (line: string) => void;
|
|
127
128
|
}) {
|
|
128
129
|
const context: WorkerRuntimeApiContext = {
|
|
129
130
|
...options.context,
|
|
@@ -137,6 +138,14 @@ export class RuntimeReceiptStoreAdapter {
|
|
|
137
138
|
maxFlushMs: options.maxFlushMs,
|
|
138
139
|
maxBufferedBytes: options.maxBufferedBytes,
|
|
139
140
|
classifyRetryableError: classifyReceiptRetry,
|
|
141
|
+
onRetryEvent: (event) =>
|
|
142
|
+
emitReceiptWriterRetryTelemetry({
|
|
143
|
+
event,
|
|
144
|
+
playName: options.playName,
|
|
145
|
+
runId: options.runId,
|
|
146
|
+
gatewayHost: runtimeGatewayHost(context.baseUrl),
|
|
147
|
+
onTelemetry: options.onRetryTelemetry,
|
|
148
|
+
}),
|
|
140
149
|
send: async (commands, { signal }) =>
|
|
141
150
|
await sendReceiptCommands({
|
|
142
151
|
context: { ...context, signal },
|
|
@@ -260,6 +269,100 @@ export class RuntimeReceiptStoreAdapter {
|
|
|
260
269
|
}
|
|
261
270
|
}
|
|
262
271
|
|
|
272
|
+
const RECEIPT_WRITER_RETRY_TELEMETRY_TAG =
|
|
273
|
+
'[perf][worker.receipt_writer.retry]';
|
|
274
|
+
|
|
275
|
+
function isPowerOfTwo(value: number): boolean {
|
|
276
|
+
return value > 0 && (value & (value - 1)) === 0;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function runtimeGatewayHost(baseUrl: string): string | null {
|
|
280
|
+
try {
|
|
281
|
+
return new URL(baseUrl).host || null;
|
|
282
|
+
} catch {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function receiptCommandTelemetryAction(command: ReceiptCommand): string {
|
|
288
|
+
return command.kind === 'settle'
|
|
289
|
+
? `${command.kind}_${command.outcome}`
|
|
290
|
+
: command.kind;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function receiptRetryErrorTelemetry(error: unknown): Record<string, unknown> {
|
|
294
|
+
if (isAppRuntimeApiCapacityError(error)) {
|
|
295
|
+
return {
|
|
296
|
+
failureKind: 'capacity',
|
|
297
|
+
errorName: error.name,
|
|
298
|
+
status: error.status,
|
|
299
|
+
code: error.code,
|
|
300
|
+
requestId: error.requestId,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
if (error instanceof AppRuntimeApiTransportError) {
|
|
304
|
+
return {
|
|
305
|
+
failureKind: 'transport',
|
|
306
|
+
errorName: error.name,
|
|
307
|
+
transportAttempts: error.attempts,
|
|
308
|
+
transportAction: error.action,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
if (error instanceof AppRuntimeApiResponseError) {
|
|
312
|
+
return {
|
|
313
|
+
failureKind: 'http',
|
|
314
|
+
errorName: error.name,
|
|
315
|
+
status: error.status,
|
|
316
|
+
code: error.code,
|
|
317
|
+
requestId: error.requestId,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
failureKind: 'unknown',
|
|
322
|
+
errorName: error instanceof Error ? error.name : typeof error,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function emitReceiptWriterRetryTelemetry(input: {
|
|
327
|
+
event: import('./runtime-receipt-writer').RuntimeReceiptWriterRetryEvent<ReceiptCommand>;
|
|
328
|
+
playName: string;
|
|
329
|
+
runId: string;
|
|
330
|
+
gatewayHost: string | null;
|
|
331
|
+
onTelemetry?: (line: string) => void;
|
|
332
|
+
}): void {
|
|
333
|
+
const { event } = input;
|
|
334
|
+
if (event.phase === 'retry' && !isPowerOfTwo(event.attempt)) return;
|
|
335
|
+
const payload = {
|
|
336
|
+
phase: event.phase,
|
|
337
|
+
playName: input.playName,
|
|
338
|
+
runId: input.runId,
|
|
339
|
+
gatewayHost: input.gatewayHost,
|
|
340
|
+
action: receiptCommandTelemetryAction(event.firstInput),
|
|
341
|
+
batchId: event.batchId,
|
|
342
|
+
batchSize: event.batchSize,
|
|
343
|
+
attempt: event.attempt,
|
|
344
|
+
elapsedMs: event.elapsedMs,
|
|
345
|
+
retryAfterMs: event.retryAfterMs,
|
|
346
|
+
queued: event.queued,
|
|
347
|
+
blocked: event.blocked,
|
|
348
|
+
bufferedBytes: event.bufferedBytes,
|
|
349
|
+
...(event.error === undefined
|
|
350
|
+
? { failureKind: null }
|
|
351
|
+
: receiptRetryErrorTelemetry(event.error)),
|
|
352
|
+
};
|
|
353
|
+
try {
|
|
354
|
+
const serialized = JSON.stringify(payload);
|
|
355
|
+
input.onTelemetry?.(`${RECEIPT_WRITER_RETRY_TELEMETRY_TAG} ${serialized}`);
|
|
356
|
+
if (event.phase === 'retry') {
|
|
357
|
+
console.warn(RECEIPT_WRITER_RETRY_TELEMETRY_TAG, serialized);
|
|
358
|
+
} else {
|
|
359
|
+
console.info(RECEIPT_WRITER_RETRY_TELEMETRY_TAG, serialized);
|
|
360
|
+
}
|
|
361
|
+
} catch {
|
|
362
|
+
// Receipt telemetry must never affect durable delivery.
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
263
366
|
function receiptCommandBatchKey(command: ReceiptCommand): string {
|
|
264
367
|
switch (command.kind) {
|
|
265
368
|
case 'get':
|
|
@@ -22,6 +22,20 @@ export type RuntimeReceiptWriterStats = {
|
|
|
22
22
|
bufferedBytes: number;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
+
export type RuntimeReceiptWriterRetryEvent<Input> = {
|
|
26
|
+
phase: 'retry' | 'recovered';
|
|
27
|
+
attempt: number;
|
|
28
|
+
batchId: number;
|
|
29
|
+
batchSize: number;
|
|
30
|
+
firstInput: Input;
|
|
31
|
+
elapsedMs: number;
|
|
32
|
+
retryAfterMs: number;
|
|
33
|
+
queued: number;
|
|
34
|
+
blocked: number;
|
|
35
|
+
bufferedBytes: number;
|
|
36
|
+
error?: unknown;
|
|
37
|
+
};
|
|
38
|
+
|
|
25
39
|
export class RuntimeReceiptWriterBufferLimitError extends Error {
|
|
26
40
|
constructor(
|
|
27
41
|
readonly inputBytes: number,
|
|
@@ -76,11 +90,15 @@ export class RuntimeReceiptWriter<Input, Output> {
|
|
|
76
90
|
readonly #maxBatchBytes: number;
|
|
77
91
|
readonly #maxBufferedBytes: number;
|
|
78
92
|
readonly #maxFlushMs: number;
|
|
93
|
+
readonly #onRetryEvent:
|
|
94
|
+
| ((event: RuntimeReceiptWriterRetryEvent<Input>) => void)
|
|
95
|
+
| null;
|
|
79
96
|
readonly #queued: Array<PendingWrite<Input, Output>> = [];
|
|
80
97
|
readonly #blocked: Array<PendingWrite<Input, Output>> = [];
|
|
81
98
|
readonly #closeController = new AbortController();
|
|
82
99
|
|
|
83
100
|
#bufferedBytes = 0;
|
|
101
|
+
#nextBatchId = 0;
|
|
84
102
|
#activeBatch: Array<PendingWrite<Input, Output>> | null = null;
|
|
85
103
|
#pump: Promise<void> | null = null;
|
|
86
104
|
#flushTimer: ReturnType<typeof setTimeout> | null = null;
|
|
@@ -103,6 +121,7 @@ export class RuntimeReceiptWriter<Input, Output> {
|
|
|
103
121
|
maxBatchBytes?: number;
|
|
104
122
|
maxBufferedBytes?: number;
|
|
105
123
|
maxFlushMs?: number;
|
|
124
|
+
onRetryEvent?: (event: RuntimeReceiptWriterRetryEvent<Input>) => void;
|
|
106
125
|
}) {
|
|
107
126
|
this.#batchKey = options.batchKey;
|
|
108
127
|
this.#send = options.send;
|
|
@@ -119,6 +138,7 @@ export class RuntimeReceiptWriter<Input, Output> {
|
|
|
119
138
|
this.#maxBufferedBytes,
|
|
120
139
|
);
|
|
121
140
|
this.#maxFlushMs = Math.max(0, Math.floor(options.maxFlushMs ?? 5));
|
|
141
|
+
this.#onRetryEvent = options.onRetryEvent ?? null;
|
|
122
142
|
}
|
|
123
143
|
|
|
124
144
|
write(input: Input, options: { signal?: AbortSignal } = {}): Promise<Output> {
|
|
@@ -243,9 +263,10 @@ export class RuntimeReceiptWriter<Input, Output> {
|
|
|
243
263
|
while (this.#queued.length > 0) {
|
|
244
264
|
const batch = this.#takeBatch();
|
|
245
265
|
if (batch.length === 0) continue;
|
|
266
|
+
const batchId = ++this.#nextBatchId;
|
|
246
267
|
this.#activeBatch = batch;
|
|
247
268
|
try {
|
|
248
|
-
const outputs = await this.#sendWithRetry(batch);
|
|
269
|
+
const outputs = await this.#sendWithRetry(batch, batchId);
|
|
249
270
|
if (outputs.length !== batch.length) {
|
|
250
271
|
throw new RuntimeReceiptWriterResultCountError(
|
|
251
272
|
batch.length,
|
|
@@ -305,9 +326,13 @@ export class RuntimeReceiptWriter<Input, Output> {
|
|
|
305
326
|
|
|
306
327
|
async #sendWithRetry(
|
|
307
328
|
batch: readonly PendingWrite<Input, Output>[],
|
|
329
|
+
batchId: number,
|
|
308
330
|
): Promise<readonly Output[]> {
|
|
309
331
|
const inputs = batch.map((pending) => pending.input);
|
|
332
|
+
const startedAt = Date.now();
|
|
333
|
+
let attempt = 0;
|
|
310
334
|
while (true) {
|
|
335
|
+
attempt += 1;
|
|
311
336
|
if (this.#closeController.signal.aborted) {
|
|
312
337
|
throw this.#closeReason;
|
|
313
338
|
}
|
|
@@ -315,20 +340,68 @@ export class RuntimeReceiptWriter<Input, Output> {
|
|
|
315
340
|
throw batch[0]?.aborted;
|
|
316
341
|
}
|
|
317
342
|
try {
|
|
318
|
-
|
|
343
|
+
const outputs = await this.#send(inputs, {
|
|
319
344
|
signal: this.#closeController.signal,
|
|
320
345
|
});
|
|
346
|
+
if (attempt > 1) {
|
|
347
|
+
this.#emitRetryEvent({
|
|
348
|
+
phase: 'recovered',
|
|
349
|
+
attempt,
|
|
350
|
+
batchId,
|
|
351
|
+
batch,
|
|
352
|
+
startedAt,
|
|
353
|
+
retryAfterMs: 0,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
return outputs;
|
|
321
357
|
} catch (error) {
|
|
322
358
|
const retry = this.#classifyRetryableError(error);
|
|
323
359
|
if (!retry) throw error;
|
|
324
|
-
|
|
325
|
-
|
|
360
|
+
const retryAfterMs = jitteredRetryDelayMs(retry.retryAfterMs);
|
|
361
|
+
this.#emitRetryEvent({
|
|
362
|
+
phase: 'retry',
|
|
363
|
+
attempt,
|
|
364
|
+
batchId,
|
|
326
365
|
batch,
|
|
327
|
-
|
|
366
|
+
startedAt,
|
|
367
|
+
retryAfterMs,
|
|
368
|
+
error,
|
|
369
|
+
});
|
|
370
|
+
await this.#waitForRetry(retryAfterMs, batch);
|
|
328
371
|
}
|
|
329
372
|
}
|
|
330
373
|
}
|
|
331
374
|
|
|
375
|
+
#emitRetryEvent(input: {
|
|
376
|
+
phase: 'retry' | 'recovered';
|
|
377
|
+
attempt: number;
|
|
378
|
+
batchId: number;
|
|
379
|
+
batch: readonly PendingWrite<Input, Output>[];
|
|
380
|
+
startedAt: number;
|
|
381
|
+
retryAfterMs: number;
|
|
382
|
+
error?: unknown;
|
|
383
|
+
}): void {
|
|
384
|
+
const firstInput = input.batch[0]?.input;
|
|
385
|
+
if (!this.#onRetryEvent || firstInput === undefined) return;
|
|
386
|
+
try {
|
|
387
|
+
this.#onRetryEvent({
|
|
388
|
+
phase: input.phase,
|
|
389
|
+
attempt: input.attempt,
|
|
390
|
+
batchId: input.batchId,
|
|
391
|
+
batchSize: input.batch.length,
|
|
392
|
+
firstInput,
|
|
393
|
+
elapsedMs: Date.now() - input.startedAt,
|
|
394
|
+
retryAfterMs: input.retryAfterMs,
|
|
395
|
+
queued: this.#queued.length,
|
|
396
|
+
blocked: this.#blocked.length,
|
|
397
|
+
bufferedBytes: this.#bufferedBytes,
|
|
398
|
+
...(input.error === undefined ? {} : { error: input.error }),
|
|
399
|
+
});
|
|
400
|
+
} catch {
|
|
401
|
+
// Receipt telemetry must never affect durable delivery.
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
332
405
|
async #waitForRetry(
|
|
333
406
|
delayMs: number,
|
|
334
407
|
batch: readonly PendingWrite<Input, Output>[],
|
package/dist/cli/index.js
CHANGED
|
@@ -718,7 +718,7 @@ var SDK_RELEASE = {
|
|
|
718
718
|
// 0.1.253 makes play-page browser opening opt-in and retires --no-open.
|
|
719
719
|
// 0.1.254 removes the internal operations tree from the published SDK CLI.
|
|
720
720
|
// Operators use the checkout-local deepline-admin binary instead.
|
|
721
|
-
version: "0.1.
|
|
721
|
+
version: "0.1.301",
|
|
722
722
|
contracts: {
|
|
723
723
|
api: {
|
|
724
724
|
name: "sdk-http-api",
|
|
@@ -1063,7 +1063,7 @@ var HttpClient = class {
|
|
|
1063
1063
|
* @param path - API path (e.g. `"/api/v2/tools"`)
|
|
1064
1064
|
* @param options - HTTP method, body, headers, and timeout
|
|
1065
1065
|
* @returns Parsed JSON response body
|
|
1066
|
-
* @throws {@link AuthError} on HTTP 401/403 (immediate, no retry)
|
|
1066
|
+
* @throws {@link AuthError} on Deepline-auth HTTP 401/403 (immediate, no retry)
|
|
1067
1067
|
* @throws {@link RateLimitError} on HTTP 429 after all retries exhausted
|
|
1068
1068
|
* @throws {@link DeeplineError} on other API errors or connection failures
|
|
1069
1069
|
*/
|
|
@@ -1102,9 +1102,6 @@ var HttpClient = class {
|
|
|
1102
1102
|
signal: controller.signal
|
|
1103
1103
|
});
|
|
1104
1104
|
clearTimeout(timeoutId);
|
|
1105
|
-
if (response.status === 401 || response.status === 403 && !options?.forbiddenAsApiError) {
|
|
1106
|
-
throw new AuthError();
|
|
1107
|
-
}
|
|
1108
1105
|
if (response.status === 429) {
|
|
1109
1106
|
const retryAfter = parseRetryAfter(response);
|
|
1110
1107
|
lastError = new RateLimitError(retryAfter);
|
|
@@ -1115,11 +1112,9 @@ var HttpClient = class {
|
|
|
1115
1112
|
throw lastError;
|
|
1116
1113
|
}
|
|
1117
1114
|
const body = await response.text();
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
} catch {
|
|
1122
|
-
parsed = body;
|
|
1115
|
+
const parsed = parseResponseBody(body);
|
|
1116
|
+
if (response.status === 401 && !isProviderOriginatedHttpError(parsed) || response.status === 403 && !options?.forbiddenAsApiError) {
|
|
1117
|
+
throw new AuthError();
|
|
1123
1118
|
}
|
|
1124
1119
|
if (!response.ok) {
|
|
1125
1120
|
const retryableApiError = options?.retryApiErrors === true && isRetryableApiErrorResponse(response.status);
|
|
@@ -1146,7 +1141,7 @@ var HttpClient = class {
|
|
|
1146
1141
|
}
|
|
1147
1142
|
const errorValue = typeof parsed === "object" && parsed && "error" in parsed ? parsed.error : void 0;
|
|
1148
1143
|
const msg = typeof errorValue === "string" ? errorValue : errorValue && typeof errorValue === "object" && "message" in errorValue && typeof errorValue.message === "string" ? errorValue.message : typeof parsed === "object" && parsed && "message" in parsed && typeof parsed.message === "string" ? parsed.message : `HTTP ${response.status}`;
|
|
1149
|
-
const apiErrorCode2 =
|
|
1144
|
+
const apiErrorCode2 = apiErrorCodeFromResponse(parsed);
|
|
1150
1145
|
lastError = new DeeplineError(msg, response.status, apiErrorCode2, {
|
|
1151
1146
|
response: parsed
|
|
1152
1147
|
});
|
|
@@ -1220,11 +1215,12 @@ var HttpClient = class {
|
|
|
1220
1215
|
body: options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
1221
1216
|
signal: options?.signal
|
|
1222
1217
|
});
|
|
1223
|
-
if (response.status === 401 || response.status === 403) {
|
|
1224
|
-
throw new AuthError();
|
|
1225
|
-
}
|
|
1226
1218
|
if (!response.ok) {
|
|
1227
1219
|
const body = await response.text();
|
|
1220
|
+
const parsed = parseResponseBody(body);
|
|
1221
|
+
if (response.status === 401 && !isProviderOriginatedHttpError(parsed) || response.status === 403) {
|
|
1222
|
+
throw new AuthError();
|
|
1223
|
+
}
|
|
1228
1224
|
const htmlError = detectHtmlErrorBody(
|
|
1229
1225
|
body,
|
|
1230
1226
|
response.headers.get("content-type")
|
|
@@ -1241,11 +1237,10 @@ var HttpClient = class {
|
|
|
1241
1237
|
}
|
|
1242
1238
|
);
|
|
1243
1239
|
}
|
|
1244
|
-
const parsed = parseResponseBody(body);
|
|
1245
1240
|
throw new DeeplineError(
|
|
1246
1241
|
apiErrorMessage(parsed, response.status),
|
|
1247
1242
|
response.status,
|
|
1248
|
-
|
|
1243
|
+
apiErrorCodeFromResponse(parsed),
|
|
1249
1244
|
{ response: parsed }
|
|
1250
1245
|
);
|
|
1251
1246
|
}
|
|
@@ -1320,6 +1315,22 @@ function parseResponseBody(body) {
|
|
|
1320
1315
|
return body;
|
|
1321
1316
|
}
|
|
1322
1317
|
}
|
|
1318
|
+
function isProviderOriginatedHttpError(parsed) {
|
|
1319
|
+
const response = asRecord(parsed);
|
|
1320
|
+
const error = asRecord(response?.error);
|
|
1321
|
+
const failureOrigin = error?.failure_origin ?? response?.failure_origin;
|
|
1322
|
+
const code = error?.code ?? response?.code;
|
|
1323
|
+
return failureOrigin === "provider" || code === "UPSTREAM_BLOCKED";
|
|
1324
|
+
}
|
|
1325
|
+
function apiErrorCodeFromResponse(parsed) {
|
|
1326
|
+
const response = asRecord(parsed);
|
|
1327
|
+
const error = asRecord(response?.error);
|
|
1328
|
+
const code = error?.code ?? response?.code;
|
|
1329
|
+
return typeof code === "string" ? code : "API_ERROR";
|
|
1330
|
+
}
|
|
1331
|
+
function asRecord(value) {
|
|
1332
|
+
return typeof value === "object" && value !== null ? value : null;
|
|
1333
|
+
}
|
|
1323
1334
|
function isRetryableApiErrorResponse(status) {
|
|
1324
1335
|
return status === 408 || status === 425 || status >= 500 && status < 600;
|
|
1325
1336
|
}
|
|
@@ -24965,7 +24976,7 @@ function monitorsErrorExitCode(error) {
|
|
|
24965
24976
|
}
|
|
24966
24977
|
function monitorsFailureNextCommand(error, exitCode) {
|
|
24967
24978
|
if (error instanceof DeeplineError) {
|
|
24968
|
-
const response =
|
|
24979
|
+
const response = asRecord2(asRecord2(error.details)?.response);
|
|
24969
24980
|
const serverNextAction = asString(response?.next_action);
|
|
24970
24981
|
if (serverNextAction) return serverNextAction;
|
|
24971
24982
|
}
|
|
@@ -24981,10 +24992,10 @@ function monitorsFailureNextCommand(error, exitCode) {
|
|
|
24981
24992
|
}
|
|
24982
24993
|
function readValidationIssues(error) {
|
|
24983
24994
|
if (!(error instanceof DeeplineError)) return [];
|
|
24984
|
-
const response =
|
|
24995
|
+
const response = asRecord2(asRecord2(error.details)?.response);
|
|
24985
24996
|
const issues = Array.isArray(response?.issues) ? response.issues : [];
|
|
24986
24997
|
return issues.flatMap((raw) => {
|
|
24987
|
-
const issue =
|
|
24998
|
+
const issue = asRecord2(raw);
|
|
24988
24999
|
if (!issue) return [];
|
|
24989
25000
|
return [
|
|
24990
25001
|
{
|
|
@@ -25077,7 +25088,7 @@ or from a file / stdin:
|
|
|
25077
25088
|
cat monitor.json | ${input2.command} --file -`
|
|
25078
25089
|
);
|
|
25079
25090
|
}
|
|
25080
|
-
function
|
|
25091
|
+
function asRecord2(value) {
|
|
25081
25092
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
25082
25093
|
}
|
|
25083
25094
|
function asString(value) {
|
|
@@ -25087,7 +25098,7 @@ function asFiniteNumber(value) {
|
|
|
25087
25098
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
25088
25099
|
}
|
|
25089
25100
|
function monitorPricingLine(rawPricing) {
|
|
25090
|
-
const pricing =
|
|
25101
|
+
const pricing = asRecord2(rawPricing);
|
|
25091
25102
|
if (!pricing) return void 0;
|
|
25092
25103
|
const display = asString(pricing.display);
|
|
25093
25104
|
if (display) return display;
|
|
@@ -25102,16 +25113,16 @@ function yesNo(value) {
|
|
|
25102
25113
|
return "unknown";
|
|
25103
25114
|
}
|
|
25104
25115
|
function readDeployOutputContract(payload) {
|
|
25105
|
-
const contract =
|
|
25116
|
+
const contract = asRecord2(payload.output_contract);
|
|
25106
25117
|
if (!contract) return { streams: [] };
|
|
25107
25118
|
const rawOutputs = Array.isArray(contract.outputs) ? contract.outputs : [];
|
|
25108
25119
|
const streams = rawOutputs.flatMap((raw) => {
|
|
25109
|
-
const output2 =
|
|
25120
|
+
const output2 = asRecord2(raw);
|
|
25110
25121
|
const stream = output2 ? asString(output2.stream) : void 0;
|
|
25111
25122
|
const table = output2 ? asString(output2.table) : void 0;
|
|
25112
25123
|
if (!output2 || !stream || !table) return [];
|
|
25113
25124
|
const columns = Array.isArray(output2.columns) ? output2.columns.flatMap((rawColumn) => {
|
|
25114
|
-
const column =
|
|
25125
|
+
const column = asRecord2(rawColumn);
|
|
25115
25126
|
const name = column ? asString(column.name) : void 0;
|
|
25116
25127
|
return name ? [name] : [];
|
|
25117
25128
|
}) : [];
|
|
@@ -25128,7 +25139,7 @@ function readDeployOutputContract(payload) {
|
|
|
25128
25139
|
return { tool: asString(contract.tool), streams };
|
|
25129
25140
|
}
|
|
25130
25141
|
function renderMonitorDeployCompletion(payload) {
|
|
25131
|
-
const monitor =
|
|
25142
|
+
const monitor = asRecord2(payload.monitor);
|
|
25132
25143
|
const key = monitor ? asString(monitor.key) : void 0;
|
|
25133
25144
|
const { tool, streams } = readDeployOutputContract(payload);
|
|
25134
25145
|
if (!key || streams.length === 0) return void 0;
|
|
@@ -25179,13 +25190,13 @@ function renderMonitorDeployPlan(payload) {
|
|
|
25179
25190
|
if (!valid) {
|
|
25180
25191
|
const issues = Array.isArray(payload.issues) ? payload.issues : [];
|
|
25181
25192
|
for (const raw of issues) {
|
|
25182
|
-
const issue =
|
|
25193
|
+
const issue = asRecord2(raw);
|
|
25183
25194
|
const path = issue ? asString(issue.path) : void 0;
|
|
25184
25195
|
const message = issue ? asString(issue.message) : void 0;
|
|
25185
25196
|
if (message) lines.push(` - ${path ? `${path}: ` : ""}${message}`);
|
|
25186
25197
|
}
|
|
25187
25198
|
}
|
|
25188
|
-
const estimate =
|
|
25199
|
+
const estimate = asRecord2(payload.deploy_cost_estimate);
|
|
25189
25200
|
const credits = estimate ? asFiniteNumber(estimate.credits) : void 0;
|
|
25190
25201
|
if (credits !== void 0) {
|
|
25191
25202
|
const note = estimate ? asString(estimate.note) : void 0;
|
|
@@ -25203,7 +25214,7 @@ function renderMonitorDeployPlan(payload) {
|
|
|
25203
25214
|
}
|
|
25204
25215
|
const candidates = Array.isArray(payload.reuse_candidates) ? payload.reuse_candidates : [];
|
|
25205
25216
|
const candidateLines = candidates.flatMap((raw) => {
|
|
25206
|
-
const candidate =
|
|
25217
|
+
const candidate = asRecord2(raw);
|
|
25207
25218
|
const key = candidate ? asString(candidate.key) : void 0;
|
|
25208
25219
|
if (!candidate || !key) return [];
|
|
25209
25220
|
const name = asString(candidate.name);
|
|
@@ -25229,7 +25240,7 @@ function renderMonitorDeployPlan(payload) {
|
|
|
25229
25240
|
`;
|
|
25230
25241
|
}
|
|
25231
25242
|
function renderMonitorDeletePlan(payload, fallbackKey) {
|
|
25232
|
-
const plan =
|
|
25243
|
+
const plan = asRecord2(payload.plan);
|
|
25233
25244
|
const key = (plan ? asString(plan.monitor_key) : void 0) ?? fallbackKey;
|
|
25234
25245
|
const lines = [
|
|
25235
25246
|
"DRY RUN \u2014 nothing was deleted.",
|
|
@@ -25243,7 +25254,7 @@ function renderMonitorDeletePlan(payload, fallbackKey) {
|
|
|
25243
25254
|
`;
|
|
25244
25255
|
}
|
|
25245
25256
|
function renderMonitorReactivatePlan(payload, key) {
|
|
25246
|
-
const estimate =
|
|
25257
|
+
const estimate = asRecord2(payload.cost_estimate);
|
|
25247
25258
|
const credits = estimate ? asFiniteNumber(estimate.credits) : void 0;
|
|
25248
25259
|
const lines = [
|
|
25249
25260
|
"DRY RUN \u2014 nothing was reactivated.",
|
|
@@ -25295,7 +25306,7 @@ function renderAvailableToolsText(payload) {
|
|
|
25295
25306
|
];
|
|
25296
25307
|
let currentProvider;
|
|
25297
25308
|
for (const raw of tools) {
|
|
25298
|
-
const entry =
|
|
25309
|
+
const entry = asRecord2(raw);
|
|
25299
25310
|
const id = entry ? asString(entry.tool) : void 0;
|
|
25300
25311
|
if (!entry || !id) continue;
|
|
25301
25312
|
const name = asString(entry.name) ?? asString(entry.display_name);
|
|
@@ -25365,7 +25376,7 @@ function renderDeployedListText(payload, requestedStatus) {
|
|
|
25365
25376
|
const header = monitors.length === 0 ? "No deployed monitors matched." : total !== void 0 && total > monitors.length ? `Deployed monitors (${monitors.length} of ${total}):` : `Deployed monitors (${monitors.length}):`;
|
|
25366
25377
|
const lines = [header];
|
|
25367
25378
|
for (const raw of monitors) {
|
|
25368
|
-
const entry =
|
|
25379
|
+
const entry = asRecord2(raw);
|
|
25369
25380
|
const key = entry ? asString(entry.key) ?? asString(entry.monitor_key) : void 0;
|
|
25370
25381
|
if (!entry || !key) continue;
|
|
25371
25382
|
const status = asString(entry.status);
|
|
@@ -25450,11 +25461,11 @@ function renderMonitorGet(payload) {
|
|
|
25450
25461
|
const tool = asString(payload.tool);
|
|
25451
25462
|
if (!key || !tool) return void 0;
|
|
25452
25463
|
const status = asString(payload.status);
|
|
25453
|
-
const definition =
|
|
25464
|
+
const definition = asRecord2(payload.definition);
|
|
25454
25465
|
const pricingLine = monitorPricingLine(payload.pricing);
|
|
25455
|
-
const billing =
|
|
25466
|
+
const billing = asRecord2(payload.billing);
|
|
25456
25467
|
const nextRenewalAt = billing ? asString(billing.next_renewal_at) : void 0;
|
|
25457
|
-
const dependents =
|
|
25468
|
+
const dependents = asRecord2(payload.dependents);
|
|
25458
25469
|
const plays = dependents && Array.isArray(dependents.plays) ? dependents.plays : [];
|
|
25459
25470
|
const lines = [
|
|
25460
25471
|
`Monitor: ${key}`,
|
|
@@ -25471,7 +25482,7 @@ function renderMonitorGet(payload) {
|
|
|
25471
25482
|
lines.push(" none");
|
|
25472
25483
|
} else {
|
|
25473
25484
|
for (const raw of plays) {
|
|
25474
|
-
const play =
|
|
25485
|
+
const play = asRecord2(raw);
|
|
25475
25486
|
const name = play ? asString(play.name) : void 0;
|
|
25476
25487
|
if (!play || !name) continue;
|
|
25477
25488
|
const listener = asString(play.listener_key);
|
|
@@ -31216,7 +31227,7 @@ function parseCapturedJson(stdout) {
|
|
|
31216
31227
|
return null;
|
|
31217
31228
|
}
|
|
31218
31229
|
}
|
|
31219
|
-
function
|
|
31230
|
+
function asRecord3(value) {
|
|
31220
31231
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
31221
31232
|
}
|
|
31222
31233
|
function safeRead(path) {
|
|
@@ -31918,8 +31929,8 @@ async function runSetupCommand(options) {
|
|
|
31918
31929
|
status: "complete",
|
|
31919
31930
|
phases
|
|
31920
31931
|
});
|
|
31921
|
-
const doctorChecks =
|
|
31922
|
-
const apiCheck =
|
|
31932
|
+
const doctorChecks = asRecord3(doctorPayload?.checks);
|
|
31933
|
+
const apiCheck = asRecord3(doctorChecks?.api);
|
|
31923
31934
|
printCommandEnvelope(
|
|
31924
31935
|
{
|
|
31925
31936
|
ok: true,
|
package/dist/cli/index.mjs
CHANGED
|
@@ -703,7 +703,7 @@ var SDK_RELEASE = {
|
|
|
703
703
|
// 0.1.253 makes play-page browser opening opt-in and retires --no-open.
|
|
704
704
|
// 0.1.254 removes the internal operations tree from the published SDK CLI.
|
|
705
705
|
// Operators use the checkout-local deepline-admin binary instead.
|
|
706
|
-
version: "0.1.
|
|
706
|
+
version: "0.1.301",
|
|
707
707
|
contracts: {
|
|
708
708
|
api: {
|
|
709
709
|
name: "sdk-http-api",
|
|
@@ -1048,7 +1048,7 @@ var HttpClient = class {
|
|
|
1048
1048
|
* @param path - API path (e.g. `"/api/v2/tools"`)
|
|
1049
1049
|
* @param options - HTTP method, body, headers, and timeout
|
|
1050
1050
|
* @returns Parsed JSON response body
|
|
1051
|
-
* @throws {@link AuthError} on HTTP 401/403 (immediate, no retry)
|
|
1051
|
+
* @throws {@link AuthError} on Deepline-auth HTTP 401/403 (immediate, no retry)
|
|
1052
1052
|
* @throws {@link RateLimitError} on HTTP 429 after all retries exhausted
|
|
1053
1053
|
* @throws {@link DeeplineError} on other API errors or connection failures
|
|
1054
1054
|
*/
|
|
@@ -1087,9 +1087,6 @@ var HttpClient = class {
|
|
|
1087
1087
|
signal: controller.signal
|
|
1088
1088
|
});
|
|
1089
1089
|
clearTimeout(timeoutId);
|
|
1090
|
-
if (response.status === 401 || response.status === 403 && !options?.forbiddenAsApiError) {
|
|
1091
|
-
throw new AuthError();
|
|
1092
|
-
}
|
|
1093
1090
|
if (response.status === 429) {
|
|
1094
1091
|
const retryAfter = parseRetryAfter(response);
|
|
1095
1092
|
lastError = new RateLimitError(retryAfter);
|
|
@@ -1100,11 +1097,9 @@ var HttpClient = class {
|
|
|
1100
1097
|
throw lastError;
|
|
1101
1098
|
}
|
|
1102
1099
|
const body = await response.text();
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
} catch {
|
|
1107
|
-
parsed = body;
|
|
1100
|
+
const parsed = parseResponseBody(body);
|
|
1101
|
+
if (response.status === 401 && !isProviderOriginatedHttpError(parsed) || response.status === 403 && !options?.forbiddenAsApiError) {
|
|
1102
|
+
throw new AuthError();
|
|
1108
1103
|
}
|
|
1109
1104
|
if (!response.ok) {
|
|
1110
1105
|
const retryableApiError = options?.retryApiErrors === true && isRetryableApiErrorResponse(response.status);
|
|
@@ -1131,7 +1126,7 @@ var HttpClient = class {
|
|
|
1131
1126
|
}
|
|
1132
1127
|
const errorValue = typeof parsed === "object" && parsed && "error" in parsed ? parsed.error : void 0;
|
|
1133
1128
|
const msg = typeof errorValue === "string" ? errorValue : errorValue && typeof errorValue === "object" && "message" in errorValue && typeof errorValue.message === "string" ? errorValue.message : typeof parsed === "object" && parsed && "message" in parsed && typeof parsed.message === "string" ? parsed.message : `HTTP ${response.status}`;
|
|
1134
|
-
const apiErrorCode2 =
|
|
1129
|
+
const apiErrorCode2 = apiErrorCodeFromResponse(parsed);
|
|
1135
1130
|
lastError = new DeeplineError(msg, response.status, apiErrorCode2, {
|
|
1136
1131
|
response: parsed
|
|
1137
1132
|
});
|
|
@@ -1205,11 +1200,12 @@ var HttpClient = class {
|
|
|
1205
1200
|
body: options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
1206
1201
|
signal: options?.signal
|
|
1207
1202
|
});
|
|
1208
|
-
if (response.status === 401 || response.status === 403) {
|
|
1209
|
-
throw new AuthError();
|
|
1210
|
-
}
|
|
1211
1203
|
if (!response.ok) {
|
|
1212
1204
|
const body = await response.text();
|
|
1205
|
+
const parsed = parseResponseBody(body);
|
|
1206
|
+
if (response.status === 401 && !isProviderOriginatedHttpError(parsed) || response.status === 403) {
|
|
1207
|
+
throw new AuthError();
|
|
1208
|
+
}
|
|
1213
1209
|
const htmlError = detectHtmlErrorBody(
|
|
1214
1210
|
body,
|
|
1215
1211
|
response.headers.get("content-type")
|
|
@@ -1226,11 +1222,10 @@ var HttpClient = class {
|
|
|
1226
1222
|
}
|
|
1227
1223
|
);
|
|
1228
1224
|
}
|
|
1229
|
-
const parsed = parseResponseBody(body);
|
|
1230
1225
|
throw new DeeplineError(
|
|
1231
1226
|
apiErrorMessage(parsed, response.status),
|
|
1232
1227
|
response.status,
|
|
1233
|
-
|
|
1228
|
+
apiErrorCodeFromResponse(parsed),
|
|
1234
1229
|
{ response: parsed }
|
|
1235
1230
|
);
|
|
1236
1231
|
}
|
|
@@ -1305,6 +1300,22 @@ function parseResponseBody(body) {
|
|
|
1305
1300
|
return body;
|
|
1306
1301
|
}
|
|
1307
1302
|
}
|
|
1303
|
+
function isProviderOriginatedHttpError(parsed) {
|
|
1304
|
+
const response = asRecord(parsed);
|
|
1305
|
+
const error = asRecord(response?.error);
|
|
1306
|
+
const failureOrigin = error?.failure_origin ?? response?.failure_origin;
|
|
1307
|
+
const code = error?.code ?? response?.code;
|
|
1308
|
+
return failureOrigin === "provider" || code === "UPSTREAM_BLOCKED";
|
|
1309
|
+
}
|
|
1310
|
+
function apiErrorCodeFromResponse(parsed) {
|
|
1311
|
+
const response = asRecord(parsed);
|
|
1312
|
+
const error = asRecord(response?.error);
|
|
1313
|
+
const code = error?.code ?? response?.code;
|
|
1314
|
+
return typeof code === "string" ? code : "API_ERROR";
|
|
1315
|
+
}
|
|
1316
|
+
function asRecord(value) {
|
|
1317
|
+
return typeof value === "object" && value !== null ? value : null;
|
|
1318
|
+
}
|
|
1308
1319
|
function isRetryableApiErrorResponse(status) {
|
|
1309
1320
|
return status === 408 || status === 425 || status >= 500 && status < 600;
|
|
1310
1321
|
}
|
|
@@ -25001,7 +25012,7 @@ function monitorsErrorExitCode(error) {
|
|
|
25001
25012
|
}
|
|
25002
25013
|
function monitorsFailureNextCommand(error, exitCode) {
|
|
25003
25014
|
if (error instanceof DeeplineError) {
|
|
25004
|
-
const response =
|
|
25015
|
+
const response = asRecord2(asRecord2(error.details)?.response);
|
|
25005
25016
|
const serverNextAction = asString(response?.next_action);
|
|
25006
25017
|
if (serverNextAction) return serverNextAction;
|
|
25007
25018
|
}
|
|
@@ -25017,10 +25028,10 @@ function monitorsFailureNextCommand(error, exitCode) {
|
|
|
25017
25028
|
}
|
|
25018
25029
|
function readValidationIssues(error) {
|
|
25019
25030
|
if (!(error instanceof DeeplineError)) return [];
|
|
25020
|
-
const response =
|
|
25031
|
+
const response = asRecord2(asRecord2(error.details)?.response);
|
|
25021
25032
|
const issues = Array.isArray(response?.issues) ? response.issues : [];
|
|
25022
25033
|
return issues.flatMap((raw) => {
|
|
25023
|
-
const issue =
|
|
25034
|
+
const issue = asRecord2(raw);
|
|
25024
25035
|
if (!issue) return [];
|
|
25025
25036
|
return [
|
|
25026
25037
|
{
|
|
@@ -25113,7 +25124,7 @@ or from a file / stdin:
|
|
|
25113
25124
|
cat monitor.json | ${input2.command} --file -`
|
|
25114
25125
|
);
|
|
25115
25126
|
}
|
|
25116
|
-
function
|
|
25127
|
+
function asRecord2(value) {
|
|
25117
25128
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
25118
25129
|
}
|
|
25119
25130
|
function asString(value) {
|
|
@@ -25123,7 +25134,7 @@ function asFiniteNumber(value) {
|
|
|
25123
25134
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
25124
25135
|
}
|
|
25125
25136
|
function monitorPricingLine(rawPricing) {
|
|
25126
|
-
const pricing =
|
|
25137
|
+
const pricing = asRecord2(rawPricing);
|
|
25127
25138
|
if (!pricing) return void 0;
|
|
25128
25139
|
const display = asString(pricing.display);
|
|
25129
25140
|
if (display) return display;
|
|
@@ -25138,16 +25149,16 @@ function yesNo(value) {
|
|
|
25138
25149
|
return "unknown";
|
|
25139
25150
|
}
|
|
25140
25151
|
function readDeployOutputContract(payload) {
|
|
25141
|
-
const contract =
|
|
25152
|
+
const contract = asRecord2(payload.output_contract);
|
|
25142
25153
|
if (!contract) return { streams: [] };
|
|
25143
25154
|
const rawOutputs = Array.isArray(contract.outputs) ? contract.outputs : [];
|
|
25144
25155
|
const streams = rawOutputs.flatMap((raw) => {
|
|
25145
|
-
const output2 =
|
|
25156
|
+
const output2 = asRecord2(raw);
|
|
25146
25157
|
const stream = output2 ? asString(output2.stream) : void 0;
|
|
25147
25158
|
const table = output2 ? asString(output2.table) : void 0;
|
|
25148
25159
|
if (!output2 || !stream || !table) return [];
|
|
25149
25160
|
const columns = Array.isArray(output2.columns) ? output2.columns.flatMap((rawColumn) => {
|
|
25150
|
-
const column =
|
|
25161
|
+
const column = asRecord2(rawColumn);
|
|
25151
25162
|
const name = column ? asString(column.name) : void 0;
|
|
25152
25163
|
return name ? [name] : [];
|
|
25153
25164
|
}) : [];
|
|
@@ -25164,7 +25175,7 @@ function readDeployOutputContract(payload) {
|
|
|
25164
25175
|
return { tool: asString(contract.tool), streams };
|
|
25165
25176
|
}
|
|
25166
25177
|
function renderMonitorDeployCompletion(payload) {
|
|
25167
|
-
const monitor =
|
|
25178
|
+
const monitor = asRecord2(payload.monitor);
|
|
25168
25179
|
const key = monitor ? asString(monitor.key) : void 0;
|
|
25169
25180
|
const { tool, streams } = readDeployOutputContract(payload);
|
|
25170
25181
|
if (!key || streams.length === 0) return void 0;
|
|
@@ -25215,13 +25226,13 @@ function renderMonitorDeployPlan(payload) {
|
|
|
25215
25226
|
if (!valid) {
|
|
25216
25227
|
const issues = Array.isArray(payload.issues) ? payload.issues : [];
|
|
25217
25228
|
for (const raw of issues) {
|
|
25218
|
-
const issue =
|
|
25229
|
+
const issue = asRecord2(raw);
|
|
25219
25230
|
const path = issue ? asString(issue.path) : void 0;
|
|
25220
25231
|
const message = issue ? asString(issue.message) : void 0;
|
|
25221
25232
|
if (message) lines.push(` - ${path ? `${path}: ` : ""}${message}`);
|
|
25222
25233
|
}
|
|
25223
25234
|
}
|
|
25224
|
-
const estimate =
|
|
25235
|
+
const estimate = asRecord2(payload.deploy_cost_estimate);
|
|
25225
25236
|
const credits = estimate ? asFiniteNumber(estimate.credits) : void 0;
|
|
25226
25237
|
if (credits !== void 0) {
|
|
25227
25238
|
const note = estimate ? asString(estimate.note) : void 0;
|
|
@@ -25239,7 +25250,7 @@ function renderMonitorDeployPlan(payload) {
|
|
|
25239
25250
|
}
|
|
25240
25251
|
const candidates = Array.isArray(payload.reuse_candidates) ? payload.reuse_candidates : [];
|
|
25241
25252
|
const candidateLines = candidates.flatMap((raw) => {
|
|
25242
|
-
const candidate =
|
|
25253
|
+
const candidate = asRecord2(raw);
|
|
25243
25254
|
const key = candidate ? asString(candidate.key) : void 0;
|
|
25244
25255
|
if (!candidate || !key) return [];
|
|
25245
25256
|
const name = asString(candidate.name);
|
|
@@ -25265,7 +25276,7 @@ function renderMonitorDeployPlan(payload) {
|
|
|
25265
25276
|
`;
|
|
25266
25277
|
}
|
|
25267
25278
|
function renderMonitorDeletePlan(payload, fallbackKey) {
|
|
25268
|
-
const plan =
|
|
25279
|
+
const plan = asRecord2(payload.plan);
|
|
25269
25280
|
const key = (plan ? asString(plan.monitor_key) : void 0) ?? fallbackKey;
|
|
25270
25281
|
const lines = [
|
|
25271
25282
|
"DRY RUN \u2014 nothing was deleted.",
|
|
@@ -25279,7 +25290,7 @@ function renderMonitorDeletePlan(payload, fallbackKey) {
|
|
|
25279
25290
|
`;
|
|
25280
25291
|
}
|
|
25281
25292
|
function renderMonitorReactivatePlan(payload, key) {
|
|
25282
|
-
const estimate =
|
|
25293
|
+
const estimate = asRecord2(payload.cost_estimate);
|
|
25283
25294
|
const credits = estimate ? asFiniteNumber(estimate.credits) : void 0;
|
|
25284
25295
|
const lines = [
|
|
25285
25296
|
"DRY RUN \u2014 nothing was reactivated.",
|
|
@@ -25331,7 +25342,7 @@ function renderAvailableToolsText(payload) {
|
|
|
25331
25342
|
];
|
|
25332
25343
|
let currentProvider;
|
|
25333
25344
|
for (const raw of tools) {
|
|
25334
|
-
const entry =
|
|
25345
|
+
const entry = asRecord2(raw);
|
|
25335
25346
|
const id = entry ? asString(entry.tool) : void 0;
|
|
25336
25347
|
if (!entry || !id) continue;
|
|
25337
25348
|
const name = asString(entry.name) ?? asString(entry.display_name);
|
|
@@ -25401,7 +25412,7 @@ function renderDeployedListText(payload, requestedStatus) {
|
|
|
25401
25412
|
const header = monitors.length === 0 ? "No deployed monitors matched." : total !== void 0 && total > monitors.length ? `Deployed monitors (${monitors.length} of ${total}):` : `Deployed monitors (${monitors.length}):`;
|
|
25402
25413
|
const lines = [header];
|
|
25403
25414
|
for (const raw of monitors) {
|
|
25404
|
-
const entry =
|
|
25415
|
+
const entry = asRecord2(raw);
|
|
25405
25416
|
const key = entry ? asString(entry.key) ?? asString(entry.monitor_key) : void 0;
|
|
25406
25417
|
if (!entry || !key) continue;
|
|
25407
25418
|
const status = asString(entry.status);
|
|
@@ -25486,11 +25497,11 @@ function renderMonitorGet(payload) {
|
|
|
25486
25497
|
const tool = asString(payload.tool);
|
|
25487
25498
|
if (!key || !tool) return void 0;
|
|
25488
25499
|
const status = asString(payload.status);
|
|
25489
|
-
const definition =
|
|
25500
|
+
const definition = asRecord2(payload.definition);
|
|
25490
25501
|
const pricingLine = monitorPricingLine(payload.pricing);
|
|
25491
|
-
const billing =
|
|
25502
|
+
const billing = asRecord2(payload.billing);
|
|
25492
25503
|
const nextRenewalAt = billing ? asString(billing.next_renewal_at) : void 0;
|
|
25493
|
-
const dependents =
|
|
25504
|
+
const dependents = asRecord2(payload.dependents);
|
|
25494
25505
|
const plays = dependents && Array.isArray(dependents.plays) ? dependents.plays : [];
|
|
25495
25506
|
const lines = [
|
|
25496
25507
|
`Monitor: ${key}`,
|
|
@@ -25507,7 +25518,7 @@ function renderMonitorGet(payload) {
|
|
|
25507
25518
|
lines.push(" none");
|
|
25508
25519
|
} else {
|
|
25509
25520
|
for (const raw of plays) {
|
|
25510
|
-
const play =
|
|
25521
|
+
const play = asRecord2(raw);
|
|
25511
25522
|
const name = play ? asString(play.name) : void 0;
|
|
25512
25523
|
if (!play || !name) continue;
|
|
25513
25524
|
const listener = asString(play.listener_key);
|
|
@@ -31281,7 +31292,7 @@ function parseCapturedJson(stdout) {
|
|
|
31281
31292
|
return null;
|
|
31282
31293
|
}
|
|
31283
31294
|
}
|
|
31284
|
-
function
|
|
31295
|
+
function asRecord3(value) {
|
|
31285
31296
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
31286
31297
|
}
|
|
31287
31298
|
function safeRead(path) {
|
|
@@ -31983,8 +31994,8 @@ async function runSetupCommand(options) {
|
|
|
31983
31994
|
status: "complete",
|
|
31984
31995
|
phases
|
|
31985
31996
|
});
|
|
31986
|
-
const doctorChecks =
|
|
31987
|
-
const apiCheck =
|
|
31997
|
+
const doctorChecks = asRecord3(doctorPayload?.checks);
|
|
31998
|
+
const apiCheck = asRecord3(doctorChecks?.api);
|
|
31988
31999
|
printCommandEnvelope(
|
|
31989
32000
|
{
|
|
31990
32001
|
ok: true,
|
package/dist/index.js
CHANGED
|
@@ -438,7 +438,7 @@ var SDK_RELEASE = {
|
|
|
438
438
|
// 0.1.253 makes play-page browser opening opt-in and retires --no-open.
|
|
439
439
|
// 0.1.254 removes the internal operations tree from the published SDK CLI.
|
|
440
440
|
// Operators use the checkout-local deepline-admin binary instead.
|
|
441
|
-
version: "0.1.
|
|
441
|
+
version: "0.1.301",
|
|
442
442
|
contracts: {
|
|
443
443
|
api: {
|
|
444
444
|
name: "sdk-http-api",
|
|
@@ -783,7 +783,7 @@ var HttpClient = class {
|
|
|
783
783
|
* @param path - API path (e.g. `"/api/v2/tools"`)
|
|
784
784
|
* @param options - HTTP method, body, headers, and timeout
|
|
785
785
|
* @returns Parsed JSON response body
|
|
786
|
-
* @throws {@link AuthError} on HTTP 401/403 (immediate, no retry)
|
|
786
|
+
* @throws {@link AuthError} on Deepline-auth HTTP 401/403 (immediate, no retry)
|
|
787
787
|
* @throws {@link RateLimitError} on HTTP 429 after all retries exhausted
|
|
788
788
|
* @throws {@link DeeplineError} on other API errors or connection failures
|
|
789
789
|
*/
|
|
@@ -822,9 +822,6 @@ var HttpClient = class {
|
|
|
822
822
|
signal: controller.signal
|
|
823
823
|
});
|
|
824
824
|
clearTimeout(timeoutId);
|
|
825
|
-
if (response.status === 401 || response.status === 403 && !options?.forbiddenAsApiError) {
|
|
826
|
-
throw new AuthError();
|
|
827
|
-
}
|
|
828
825
|
if (response.status === 429) {
|
|
829
826
|
const retryAfter = parseRetryAfter(response);
|
|
830
827
|
lastError = new RateLimitError(retryAfter);
|
|
@@ -835,11 +832,9 @@ var HttpClient = class {
|
|
|
835
832
|
throw lastError;
|
|
836
833
|
}
|
|
837
834
|
const body = await response.text();
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
} catch {
|
|
842
|
-
parsed = body;
|
|
835
|
+
const parsed = parseResponseBody(body);
|
|
836
|
+
if (response.status === 401 && !isProviderOriginatedHttpError(parsed) || response.status === 403 && !options?.forbiddenAsApiError) {
|
|
837
|
+
throw new AuthError();
|
|
843
838
|
}
|
|
844
839
|
if (!response.ok) {
|
|
845
840
|
const retryableApiError = options?.retryApiErrors === true && isRetryableApiErrorResponse(response.status);
|
|
@@ -866,7 +861,7 @@ var HttpClient = class {
|
|
|
866
861
|
}
|
|
867
862
|
const errorValue = typeof parsed === "object" && parsed && "error" in parsed ? parsed.error : void 0;
|
|
868
863
|
const msg = typeof errorValue === "string" ? errorValue : errorValue && typeof errorValue === "object" && "message" in errorValue && typeof errorValue.message === "string" ? errorValue.message : typeof parsed === "object" && parsed && "message" in parsed && typeof parsed.message === "string" ? parsed.message : `HTTP ${response.status}`;
|
|
869
|
-
const apiErrorCode =
|
|
864
|
+
const apiErrorCode = apiErrorCodeFromResponse(parsed);
|
|
870
865
|
lastError = new DeeplineError(msg, response.status, apiErrorCode, {
|
|
871
866
|
response: parsed
|
|
872
867
|
});
|
|
@@ -940,11 +935,12 @@ var HttpClient = class {
|
|
|
940
935
|
body: options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
941
936
|
signal: options?.signal
|
|
942
937
|
});
|
|
943
|
-
if (response.status === 401 || response.status === 403) {
|
|
944
|
-
throw new AuthError();
|
|
945
|
-
}
|
|
946
938
|
if (!response.ok) {
|
|
947
939
|
const body = await response.text();
|
|
940
|
+
const parsed = parseResponseBody(body);
|
|
941
|
+
if (response.status === 401 && !isProviderOriginatedHttpError(parsed) || response.status === 403) {
|
|
942
|
+
throw new AuthError();
|
|
943
|
+
}
|
|
948
944
|
const htmlError = detectHtmlErrorBody(
|
|
949
945
|
body,
|
|
950
946
|
response.headers.get("content-type")
|
|
@@ -961,11 +957,10 @@ var HttpClient = class {
|
|
|
961
957
|
}
|
|
962
958
|
);
|
|
963
959
|
}
|
|
964
|
-
const parsed = parseResponseBody(body);
|
|
965
960
|
throw new DeeplineError(
|
|
966
961
|
apiErrorMessage(parsed, response.status),
|
|
967
962
|
response.status,
|
|
968
|
-
|
|
963
|
+
apiErrorCodeFromResponse(parsed),
|
|
969
964
|
{ response: parsed }
|
|
970
965
|
);
|
|
971
966
|
}
|
|
@@ -1040,6 +1035,22 @@ function parseResponseBody(body) {
|
|
|
1040
1035
|
return body;
|
|
1041
1036
|
}
|
|
1042
1037
|
}
|
|
1038
|
+
function isProviderOriginatedHttpError(parsed) {
|
|
1039
|
+
const response = asRecord(parsed);
|
|
1040
|
+
const error = asRecord(response?.error);
|
|
1041
|
+
const failureOrigin = error?.failure_origin ?? response?.failure_origin;
|
|
1042
|
+
const code = error?.code ?? response?.code;
|
|
1043
|
+
return failureOrigin === "provider" || code === "UPSTREAM_BLOCKED";
|
|
1044
|
+
}
|
|
1045
|
+
function apiErrorCodeFromResponse(parsed) {
|
|
1046
|
+
const response = asRecord(parsed);
|
|
1047
|
+
const error = asRecord(response?.error);
|
|
1048
|
+
const code = error?.code ?? response?.code;
|
|
1049
|
+
return typeof code === "string" ? code : "API_ERROR";
|
|
1050
|
+
}
|
|
1051
|
+
function asRecord(value) {
|
|
1052
|
+
return typeof value === "object" && value !== null ? value : null;
|
|
1053
|
+
}
|
|
1043
1054
|
function isRetryableApiErrorResponse(status) {
|
|
1044
1055
|
return status === 408 || status === 425 || status >= 500 && status < 600;
|
|
1045
1056
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -367,7 +367,7 @@ var SDK_RELEASE = {
|
|
|
367
367
|
// 0.1.253 makes play-page browser opening opt-in and retires --no-open.
|
|
368
368
|
// 0.1.254 removes the internal operations tree from the published SDK CLI.
|
|
369
369
|
// Operators use the checkout-local deepline-admin binary instead.
|
|
370
|
-
version: "0.1.
|
|
370
|
+
version: "0.1.301",
|
|
371
371
|
contracts: {
|
|
372
372
|
api: {
|
|
373
373
|
name: "sdk-http-api",
|
|
@@ -712,7 +712,7 @@ var HttpClient = class {
|
|
|
712
712
|
* @param path - API path (e.g. `"/api/v2/tools"`)
|
|
713
713
|
* @param options - HTTP method, body, headers, and timeout
|
|
714
714
|
* @returns Parsed JSON response body
|
|
715
|
-
* @throws {@link AuthError} on HTTP 401/403 (immediate, no retry)
|
|
715
|
+
* @throws {@link AuthError} on Deepline-auth HTTP 401/403 (immediate, no retry)
|
|
716
716
|
* @throws {@link RateLimitError} on HTTP 429 after all retries exhausted
|
|
717
717
|
* @throws {@link DeeplineError} on other API errors or connection failures
|
|
718
718
|
*/
|
|
@@ -751,9 +751,6 @@ var HttpClient = class {
|
|
|
751
751
|
signal: controller.signal
|
|
752
752
|
});
|
|
753
753
|
clearTimeout(timeoutId);
|
|
754
|
-
if (response.status === 401 || response.status === 403 && !options?.forbiddenAsApiError) {
|
|
755
|
-
throw new AuthError();
|
|
756
|
-
}
|
|
757
754
|
if (response.status === 429) {
|
|
758
755
|
const retryAfter = parseRetryAfter(response);
|
|
759
756
|
lastError = new RateLimitError(retryAfter);
|
|
@@ -764,11 +761,9 @@ var HttpClient = class {
|
|
|
764
761
|
throw lastError;
|
|
765
762
|
}
|
|
766
763
|
const body = await response.text();
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
} catch {
|
|
771
|
-
parsed = body;
|
|
764
|
+
const parsed = parseResponseBody(body);
|
|
765
|
+
if (response.status === 401 && !isProviderOriginatedHttpError(parsed) || response.status === 403 && !options?.forbiddenAsApiError) {
|
|
766
|
+
throw new AuthError();
|
|
772
767
|
}
|
|
773
768
|
if (!response.ok) {
|
|
774
769
|
const retryableApiError = options?.retryApiErrors === true && isRetryableApiErrorResponse(response.status);
|
|
@@ -795,7 +790,7 @@ var HttpClient = class {
|
|
|
795
790
|
}
|
|
796
791
|
const errorValue = typeof parsed === "object" && parsed && "error" in parsed ? parsed.error : void 0;
|
|
797
792
|
const msg = typeof errorValue === "string" ? errorValue : errorValue && typeof errorValue === "object" && "message" in errorValue && typeof errorValue.message === "string" ? errorValue.message : typeof parsed === "object" && parsed && "message" in parsed && typeof parsed.message === "string" ? parsed.message : `HTTP ${response.status}`;
|
|
798
|
-
const apiErrorCode =
|
|
793
|
+
const apiErrorCode = apiErrorCodeFromResponse(parsed);
|
|
799
794
|
lastError = new DeeplineError(msg, response.status, apiErrorCode, {
|
|
800
795
|
response: parsed
|
|
801
796
|
});
|
|
@@ -869,11 +864,12 @@ var HttpClient = class {
|
|
|
869
864
|
body: options?.body !== void 0 ? JSON.stringify(options.body) : void 0,
|
|
870
865
|
signal: options?.signal
|
|
871
866
|
});
|
|
872
|
-
if (response.status === 401 || response.status === 403) {
|
|
873
|
-
throw new AuthError();
|
|
874
|
-
}
|
|
875
867
|
if (!response.ok) {
|
|
876
868
|
const body = await response.text();
|
|
869
|
+
const parsed = parseResponseBody(body);
|
|
870
|
+
if (response.status === 401 && !isProviderOriginatedHttpError(parsed) || response.status === 403) {
|
|
871
|
+
throw new AuthError();
|
|
872
|
+
}
|
|
877
873
|
const htmlError = detectHtmlErrorBody(
|
|
878
874
|
body,
|
|
879
875
|
response.headers.get("content-type")
|
|
@@ -890,11 +886,10 @@ var HttpClient = class {
|
|
|
890
886
|
}
|
|
891
887
|
);
|
|
892
888
|
}
|
|
893
|
-
const parsed = parseResponseBody(body);
|
|
894
889
|
throw new DeeplineError(
|
|
895
890
|
apiErrorMessage(parsed, response.status),
|
|
896
891
|
response.status,
|
|
897
|
-
|
|
892
|
+
apiErrorCodeFromResponse(parsed),
|
|
898
893
|
{ response: parsed }
|
|
899
894
|
);
|
|
900
895
|
}
|
|
@@ -969,6 +964,22 @@ function parseResponseBody(body) {
|
|
|
969
964
|
return body;
|
|
970
965
|
}
|
|
971
966
|
}
|
|
967
|
+
function isProviderOriginatedHttpError(parsed) {
|
|
968
|
+
const response = asRecord(parsed);
|
|
969
|
+
const error = asRecord(response?.error);
|
|
970
|
+
const failureOrigin = error?.failure_origin ?? response?.failure_origin;
|
|
971
|
+
const code = error?.code ?? response?.code;
|
|
972
|
+
return failureOrigin === "provider" || code === "UPSTREAM_BLOCKED";
|
|
973
|
+
}
|
|
974
|
+
function apiErrorCodeFromResponse(parsed) {
|
|
975
|
+
const response = asRecord(parsed);
|
|
976
|
+
const error = asRecord(response?.error);
|
|
977
|
+
const code = error?.code ?? response?.code;
|
|
978
|
+
return typeof code === "string" ? code : "API_ERROR";
|
|
979
|
+
}
|
|
980
|
+
function asRecord(value) {
|
|
981
|
+
return typeof value === "object" && value !== null ? value : null;
|
|
982
|
+
}
|
|
972
983
|
function isRetryableApiErrorResponse(status) {
|
|
973
984
|
return status === 408 || status === 425 || status >= 500 && status < 600;
|
|
974
985
|
}
|