deepline 0.3.31 → 0.3.33
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/compat.ts +1 -0
- package/dist/bundling-sources/sdk/src/release.ts +18 -8
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +17 -1
- package/dist/bundling-sources/shared_libs/play-runtime/vercel-protection.ts +44 -3
- package/dist/cli/index.js +4338 -3910
- package/dist/cli/index.mjs +4361 -3926
- package/dist/index.js +7 -3
- package/dist/index.mjs +7 -3
- package/package.json +2 -1
|
@@ -202,6 +202,7 @@ export async function checkSdkCompatibility(
|
|
|
202
202
|
'X-Deepline-SDK-Version': SDK_VERSION,
|
|
203
203
|
'X-Deepline-API-Major': String(SDK_API_MAJOR),
|
|
204
204
|
'X-Deepline-API-Contract': SDK_API_CONTRACT,
|
|
205
|
+
'X-Deepline-CLI-Update-Preferences': '1',
|
|
205
206
|
},
|
|
206
207
|
signal: controller.signal,
|
|
207
208
|
});
|
|
@@ -97,9 +97,11 @@ export type SdkSupportPolicy = {
|
|
|
97
97
|
* Deliberately not a support floor. `commandMinimumSupported` says an older
|
|
98
98
|
* client is BROKEN for a command and forces an update; this says only that
|
|
99
99
|
* an older client never had the command at all. That difference matters in
|
|
100
|
-
* both directions: introducing a command must not
|
|
101
|
-
* support floor may not exceed the version being released,
|
|
102
|
-
* introduction version is a fact about an already-published
|
|
100
|
+
* both directions: introducing a command must not independently require an
|
|
101
|
+
* update, and a support floor may not exceed the version being released,
|
|
102
|
+
* while an introduction version is a fact about an already-published
|
|
103
|
+
* release. The general default-on freshness policy may still update an older
|
|
104
|
+
* installed CLI.
|
|
103
105
|
*
|
|
104
106
|
* It exists because agent skills are served by the backend and re-synced on
|
|
105
107
|
* nearly every CLI invocation, while the CLI stays pinned. A release that
|
|
@@ -124,8 +126,8 @@ export type SdkSupportPolicy = {
|
|
|
124
126
|
directToolsCompatibilityVersions?: readonly string[];
|
|
125
127
|
/**
|
|
126
128
|
* Diagnostic freshness threshold reported by `/api/v2/sdk/compat`.
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
+
* Ownership-aware installed CLIs auto-update when older; this value remains
|
|
130
|
+
* metadata for clients and operational diagnostics.
|
|
129
131
|
*/
|
|
130
132
|
autoUpdatePatchLag?: number;
|
|
131
133
|
};
|
|
@@ -142,6 +144,10 @@ export type SdkRelease = {
|
|
|
142
144
|
* in its existing update message so older installed CLIs can print it too.
|
|
143
145
|
*/
|
|
144
146
|
updateSummary?: string;
|
|
147
|
+
/** Capabilities stamped into the published package's `deepline` metadata. */
|
|
148
|
+
packageCapabilities: {
|
|
149
|
+
updatePreferences: 1;
|
|
150
|
+
};
|
|
145
151
|
/** Named compatibility policies. This is the only authored contract policy. */
|
|
146
152
|
contracts: DeeplineContractPolicy;
|
|
147
153
|
/** Public support policy reported by `/api/v2/sdk/compat`. */
|
|
@@ -192,9 +198,13 @@ export const SDK_RELEASE = {
|
|
|
192
198
|
// 0.3.0 introduces raw-v2: complete scrubbed provider responses are
|
|
193
199
|
// available at toolResponse.rawV2 while toolResponse.raw and all declared
|
|
194
200
|
// getters keep their established compatibility behavior.
|
|
195
|
-
|
|
201
|
+
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
202
|
+
version: '0.3.33',
|
|
196
203
|
updateSummary:
|
|
197
|
-
'
|
|
204
|
+
'Automatic CLI updates are now enabled by default. To opt out, run `deepline settings autoupdate off`; use `deepline settings autoupdate on` to re-enable updates or `deepline settings autoupdate pin <version>` to hold an exact release. This release also adds raw-v2 tool responses at toolResponse.rawV2 while preserving existing toolResponse.raw and declared getters.',
|
|
205
|
+
packageCapabilities: {
|
|
206
|
+
updatePreferences: 1,
|
|
207
|
+
},
|
|
198
208
|
contracts: {
|
|
199
209
|
api: {
|
|
200
210
|
name: 'sdk-http-api',
|
|
@@ -221,7 +231,7 @@ export const SDK_RELEASE = {
|
|
|
221
231
|
},
|
|
222
232
|
supportPolicy: {
|
|
223
233
|
minimumSupported: '0.1.53',
|
|
224
|
-
deprecatedBelow: '0.1
|
|
234
|
+
deprecatedBelow: '0.3.1',
|
|
225
235
|
commandIntroducedIn: [
|
|
226
236
|
{
|
|
227
237
|
command: 'notifications',
|
|
@@ -49,7 +49,10 @@ import {
|
|
|
49
49
|
PLAY_RUNTIME_TEST_FAULT_HEADER,
|
|
50
50
|
recognizedRuntimeTestFaultCount,
|
|
51
51
|
} from '@shared_libs/play-runtime/test-runtime-seams';
|
|
52
|
-
import {
|
|
52
|
+
import {
|
|
53
|
+
isVercelSecurityCheckpointResponse,
|
|
54
|
+
vercelProtectionBypassHeaders,
|
|
55
|
+
} from '@shared_libs/play-runtime/vercel-protection';
|
|
53
56
|
import type { RuntimeReceiptAction } from '@shared_libs/play-runtime/runtime-actions';
|
|
54
57
|
import { RUNTIME_CAPACITY_POLICY } from '@shared_libs/play-runtime/runtime-capacity-policy';
|
|
55
58
|
import { RUNTIME_RELIABILITY_POLICY } from '@shared_libs/play-runtime/runtime-reliability-policy';
|
|
@@ -478,10 +481,20 @@ function isRetryableAppRuntimeResponse(input: {
|
|
|
478
481
|
action: RuntimeApiRequest['action'];
|
|
479
482
|
status: number;
|
|
480
483
|
body: string;
|
|
484
|
+
contentType: string | null;
|
|
481
485
|
}): boolean {
|
|
482
486
|
if (!isRetryableAppRuntimeAction(input.action)) {
|
|
483
487
|
return false;
|
|
484
488
|
}
|
|
489
|
+
// A WAF checkpoint is not a statement from the app about the idempotent
|
|
490
|
+
// projection. Do not make every 403 retryable: only this exact Vercel HTML
|
|
491
|
+
// response on a replay-safe action is an intermediary failure.
|
|
492
|
+
if (
|
|
493
|
+
input.action === 'append_run_events' &&
|
|
494
|
+
isVercelSecurityCheckpointResponse(input)
|
|
495
|
+
) {
|
|
496
|
+
return true;
|
|
497
|
+
}
|
|
485
498
|
// The app runtime may explicitly classify an otherwise-client-error status
|
|
486
499
|
// as transient. Keep the structured delivery contract authoritative rather
|
|
487
500
|
// than reducing every 4xx to permanent at the worker boundary.
|
|
@@ -1273,6 +1286,7 @@ async function postAppRuntimeApi<TResponse>(
|
|
|
1273
1286
|
action: body.action,
|
|
1274
1287
|
status: response.status,
|
|
1275
1288
|
body: responseText,
|
|
1289
|
+
contentType: response.headers.get('content-type'),
|
|
1276
1290
|
})
|
|
1277
1291
|
) {
|
|
1278
1292
|
const retryDelayMs = Math.max(
|
|
@@ -1299,6 +1313,7 @@ async function postAppRuntimeApi<TResponse>(
|
|
|
1299
1313
|
action: body.action,
|
|
1300
1314
|
status: response.status,
|
|
1301
1315
|
body: responseText,
|
|
1316
|
+
contentType: response.headers.get('content-type'),
|
|
1302
1317
|
})
|
|
1303
1318
|
? 'retryable_http'
|
|
1304
1319
|
: 'http_error',
|
|
@@ -1328,6 +1343,7 @@ async function postAppRuntimeApi<TResponse>(
|
|
|
1328
1343
|
action: body.action,
|
|
1329
1344
|
status: response.status,
|
|
1330
1345
|
body: responseText,
|
|
1346
|
+
contentType: response.headers.get('content-type'),
|
|
1331
1347
|
}),
|
|
1332
1348
|
detail: summarizeAppRuntimeErrorBody(responseText),
|
|
1333
1349
|
boundaryLabel,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
type FetchLike = typeof fetch;
|
|
2
2
|
|
|
3
3
|
const vercelProtectionCookieCache = new Map<string, Promise<string | null>>();
|
|
4
|
+
const VERCEL_PROTECTION_COOKIE_PREFLIGHT_TIMEOUT_MS = 5_000;
|
|
4
5
|
|
|
5
6
|
async function awaitWithSignal<T>(
|
|
6
7
|
promise: Promise<T>,
|
|
@@ -45,6 +46,25 @@ export function vercelProtectionBypassHeader(
|
|
|
45
46
|
return normalized ? { 'x-vercel-protection-bypass': normalized } : {};
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
/**
|
|
50
|
+
* A Vercel Firewall checkpoint is a transport/intermediary response, not an
|
|
51
|
+
* application authorization decision. Keep this deliberately narrow: callers
|
|
52
|
+
* still decide which replay-safe internal action may retry it.
|
|
53
|
+
*/
|
|
54
|
+
export function isVercelSecurityCheckpointResponse(input: {
|
|
55
|
+
status: number;
|
|
56
|
+
contentType: string | null | undefined;
|
|
57
|
+
body: string;
|
|
58
|
+
}): boolean {
|
|
59
|
+
if (input.status !== 403) return false;
|
|
60
|
+
if (!/^text\/html(?:\s*;|$)/i.test(input.contentType?.trim() ?? '')) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
return /<title\b[^>]*>\s*Vercel Security Checkpoint\s*<\/title>/i.test(
|
|
64
|
+
input.body,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
48
68
|
function setCookieHeaders(headers: Headers): string[] {
|
|
49
69
|
const getter = (headers as Headers & { getSetCookie?: () => string[] })
|
|
50
70
|
.getSetCookie;
|
|
@@ -75,14 +95,27 @@ export async function resolveVercelProtectionBypassCookie(input: {
|
|
|
75
95
|
|
|
76
96
|
const cacheKey = `${baseUrl}\n${token}`;
|
|
77
97
|
const cached = vercelProtectionCookieCache.get(cacheKey);
|
|
78
|
-
if (cached)
|
|
98
|
+
if (cached) {
|
|
99
|
+
try {
|
|
100
|
+
return await awaitWithSignal(cached, input.signal);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
if (input.signal?.aborted) throw input.signal.reason ?? error;
|
|
103
|
+
// A concurrent caller must retain the same header-only fallback as the
|
|
104
|
+
// caller that created the shared preflight promise.
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
79
108
|
|
|
80
109
|
const promise = (async () => {
|
|
81
110
|
const url = new URL(`${baseUrl}/api/v2/health`);
|
|
82
111
|
addVercelProtectionBypassSearchParams(url, token);
|
|
112
|
+
// Keep the cache independent from an individual outbox lease. A caller
|
|
113
|
+
// may stop waiting, but the shared bootstrap is always bounded and either
|
|
114
|
+
// supplies a reusable cookie or evicts itself for a later retry.
|
|
83
115
|
const response = await (input.fetchImpl ?? fetch)(url.toString(), {
|
|
84
116
|
headers: { 'x-vercel-protection-bypass': token },
|
|
85
|
-
|
|
117
|
+
signal: AbortSignal.timeout(VERCEL_PROTECTION_COOKIE_PREFLIGHT_TIMEOUT_MS),
|
|
118
|
+
});
|
|
86
119
|
return response ? cookieHeaderFromSetCookie(response.headers) : null;
|
|
87
120
|
})();
|
|
88
121
|
vercelProtectionCookieCache.set(cacheKey, promise);
|
|
@@ -91,7 +124,15 @@ export async function resolveVercelProtectionBypassCookie(input: {
|
|
|
91
124
|
vercelProtectionCookieCache.delete(cacheKey);
|
|
92
125
|
}
|
|
93
126
|
});
|
|
94
|
-
|
|
127
|
+
try {
|
|
128
|
+
return await awaitWithSignal(promise, input.signal);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
if (input.signal?.aborted) throw input.signal.reason ?? error;
|
|
131
|
+
// The header-only bypass remains useful when cookie bootstrap is briefly
|
|
132
|
+
// unavailable; do not turn a bounded preflight failure into a permanent
|
|
133
|
+
// delivery failure. The rejection handler above evicts this cache entry.
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
95
136
|
}
|
|
96
137
|
|
|
97
138
|
export async function vercelProtectionBypassHeaders(input: {
|