deepline 0.1.119 → 0.1.121
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/README.md +4 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/README.md +21 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/batching.ts +185 -0
- package/dist/bundling-sources/apps/play-runner-workers/src/runtime/tool-batch.ts +107 -0
- package/dist/{repo → bundling-sources}/sdk/src/client.ts +116 -12
- package/dist/bundling-sources/sdk/src/compat.ts +191 -0
- package/dist/bundling-sources/sdk/src/gtm.ts +146 -0
- package/dist/bundling-sources/sdk/src/helpers.ts +12 -0
- package/dist/{repo → bundling-sources}/sdk/src/index.ts +2 -1
- package/dist/{repo → bundling-sources}/sdk/src/play.ts +3 -1
- package/dist/{repo → bundling-sources}/sdk/src/plays/bundle-play-file.ts +17 -5
- package/dist/{repo → bundling-sources}/sdk/src/release.ts +2 -2
- package/dist/{repo → bundling-sources}/sdk/src/runs/observe-transport.ts +2 -3
- package/dist/bundling-sources/shared_libs/play-data-plane/index.ts +3 -0
- package/dist/bundling-sources/shared_libs/play-runtime/app-runtime-api.ts +838 -0
- package/dist/bundling-sources/shared_libs/play-runtime/context.ts +5510 -0
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-contract.ts +261 -0
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +828 -0
- package/dist/bundling-sources/shared_libs/play-runtime/dataset-id.ts +10 -0
- package/dist/bundling-sources/shared_libs/play-runtime/daytona-runtime-config.ts +50 -0
- package/dist/bundling-sources/shared_libs/play-runtime/durability-store.ts +20 -0
- package/dist/bundling-sources/shared_libs/play-runtime/event-wait-tools.ts +9 -0
- package/dist/bundling-sources/shared_libs/play-runtime/governor/in-memory-rate-state-backend.ts +171 -0
- package/dist/bundling-sources/shared_libs/play-runtime/hatchet-cold-execution-diagnosis.ts +321 -0
- package/dist/bundling-sources/shared_libs/play-runtime/hatchet-cold-execution-target.ts +158 -0
- package/dist/bundling-sources/shared_libs/play-runtime/internal-step-ids.ts +34 -0
- package/dist/bundling-sources/shared_libs/play-runtime/ledger-safe-payload.ts +34 -0
- package/dist/bundling-sources/shared_libs/play-runtime/live-state-contract.ts +50 -0
- package/dist/bundling-sources/shared_libs/play-runtime/map-execution-frame.ts +119 -0
- package/dist/{repo → bundling-sources}/shared_libs/play-runtime/map-row-identity.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/play-latency-trace.ts +636 -0
- package/dist/bundling-sources/shared_libs/play-runtime/postgres-json.ts +9 -0
- package/dist/bundling-sources/shared_libs/play-runtime/progress-emitter.ts +197 -0
- package/dist/bundling-sources/shared_libs/play-runtime/projection.ts +262 -0
- package/dist/bundling-sources/shared_libs/play-runtime/protocol.ts +143 -0
- package/dist/bundling-sources/shared_libs/play-runtime/public-play-contract.ts +42 -0
- package/dist/bundling-sources/shared_libs/play-runtime/receipt-status.ts +40 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-actions.ts +178 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-api.ts +4015 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-constraints.ts +2 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver-neon-serverless.ts +238 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver-pg.ts +53 -0
- package/dist/bundling-sources/shared_libs/play-runtime/runtime-pg-driver.ts +149 -0
- package/dist/bundling-sources/shared_libs/play-runtime/suspension.ts +68 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-batch-executor.ts +149 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tool-result-types.ts +159 -0
- package/dist/bundling-sources/shared_libs/play-runtime/tracing.ts +33 -0
- package/dist/bundling-sources/shared_libs/play-runtime/waterfall-replay.ts +79 -0
- package/dist/bundling-sources/shared_libs/play-runtime/worker-api-types.ts +139 -0
- package/dist/bundling-sources/shared_libs/plays/artifact-transport.ts +14 -0
- package/dist/bundling-sources/shared_libs/plays/artifact-types.ts +49 -0
- package/dist/bundling-sources/shared_libs/plays/compiler-manifest.ts +41 -0
- package/dist/bundling-sources/shared_libs/plays/dataset-summary.ts +163 -0
- package/dist/bundling-sources/shared_libs/plays/definition.ts +267 -0
- package/dist/bundling-sources/shared_libs/plays/file-refs.ts +11 -0
- package/dist/bundling-sources/shared_libs/plays/input-contract.ts +146 -0
- package/dist/bundling-sources/shared_libs/plays/resolve-static-pipeline.ts +190 -0
- package/dist/bundling-sources/shared_libs/plays/runtime-validation.ts +417 -0
- package/dist/bundling-sources/shared_libs/plays/tool-codegen.ts +142 -0
- package/dist/bundling-sources/shared_libs/security/safe-outbound-fetch.ts +274 -0
- package/dist/bundling-sources/shared_libs/temporal/preview-config.ts +150 -0
- package/dist/cli/index.js +811 -2207
- package/dist/cli/index.mjs +847 -2258
- package/dist/compiler-manifest-BjoRENv9.d.mts +227 -0
- package/dist/compiler-manifest-BjoRENv9.d.ts +227 -0
- package/dist/index.d.mts +8 -231
- package/dist/index.d.ts +8 -231
- package/dist/index.js +101 -15
- package/dist/index.mjs +101 -15
- package/dist/plays/bundle-play-file.d.mts +120 -0
- package/dist/plays/bundle-play-file.d.ts +120 -0
- package/dist/plays/bundle-play-file.mjs +1830 -0
- package/package.json +4 -9
- /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/child-play-await.ts +0 -0
- /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/child-play-submit.ts +0 -0
- /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/coordinator-entry.ts +0 -0
- /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/dedup-do.ts +0 -0
- /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/entry.ts +0 -0
- /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/runtime/csv-rows.ts +0 -0
- /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/runtime/dataset-handles.ts +0 -0
- /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/runtime/harness-receipt-store.ts +0 -0
- /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/runtime/live-progress.ts +0 -0
- /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/runtime/map-chunk-plan.ts +0 -0
- /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/runtime/receipts.ts +0 -0
- /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/runtime/row-isolation.ts +0 -0
- /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/runtime/tool-http-errors.ts +0 -0
- /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/workflow-instance-create.ts +0 -0
- /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/workflow-retry-state.ts +0 -0
- /package/dist/{repo → bundling-sources}/apps/play-runner-workers/src/workflow-retry.ts +0 -0
- /package/dist/{repo → bundling-sources}/sdk/src/agent-runtime.ts +0 -0
- /package/dist/{repo → bundling-sources}/sdk/src/config.ts +0 -0
- /package/dist/{repo → bundling-sources}/sdk/src/errors.ts +0 -0
- /package/dist/{repo → bundling-sources}/sdk/src/http.ts +0 -0
- /package/dist/{repo → bundling-sources}/sdk/src/plays/harness-stub.ts +0 -0
- /package/dist/{repo → bundling-sources}/sdk/src/plays/local-file-discovery.ts +0 -0
- /package/dist/{repo → bundling-sources}/sdk/src/stream-reconnect.ts +0 -0
- /package/dist/{repo → bundling-sources}/sdk/src/tool-output.ts +0 -0
- /package/dist/{repo → bundling-sources}/sdk/src/types.ts +0 -0
- /package/dist/{repo → bundling-sources}/sdk/src/version.ts +0 -0
- /package/dist/{repo → bundling-sources}/sdk/src/worker-play-entry.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-data-plane/cell-policy.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-data-plane/column-names.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-data-plane/sheet-contract.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/backend.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/batch-runtime.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/batching-types.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/cell-staleness.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/coordinator-headers.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/csv-rename.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/db-session-crypto.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/db-session-plan.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/db-session.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/dedup-backend.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/default-batch-strategies.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/email-status.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/execution-plan.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/extractor-targets.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/fullenrich-batching.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/governor/coordinator-rate-state-backend.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/governor/governor.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/governor/policy.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/governor/rate-state-backend.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/live-events.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/play-runtime-batching-registry.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/profiles.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/providers.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/run-failure.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/run-ledger.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/run-snapshot-stream.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/scheduler-backend.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/secret-capability.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/secret-redaction.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/step-lifecycle-tracker.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/step-program-dataset-builder.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/submit-limits.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/tool-result.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/play-runtime/work-receipts.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/plays/bootstrap-routes.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/plays/bundling/index.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/plays/bundling/limits.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/plays/contracts.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/plays/dataset.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/plays/row-identity.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/plays/secret-guardrails.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/plays/static-pipeline.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/security/outbound-url-policy.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/security/safe-fetch.ts +0 -0
- /package/dist/{repo → bundling-sources}/shared_libs/temporal/constants.ts +0 -0
package/README.md
CHANGED
|
@@ -48,6 +48,10 @@ Only two cases require `npm run sdk:build`:
|
|
|
48
48
|
cd sdk && bun run build # or: npm run sdk:build from root
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
The published npm package is intentionally thin: it ships the SDK, CLI, and the
|
|
52
|
+
minimal local play bundler required for `plays run --file`, `plays publish`, and
|
|
53
|
+
`enrich`, without shipping Convex or broad app/provider source snapshots.
|
|
54
|
+
|
|
51
55
|
## Env files
|
|
52
56
|
|
|
53
57
|
| File | Set by | Contains |
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Per-play Worker Runtime
|
|
2
|
+
|
|
3
|
+
These modules are bundled into every `esm_workers` play artifact. Keep them:
|
|
4
|
+
|
|
5
|
+
- dependency-free, except for type-only imports
|
|
6
|
+
- small enough to understand in one sitting
|
|
7
|
+
- specific to the Cloudflare Worker execution path
|
|
8
|
+
|
|
9
|
+
Do not import broad `shared_libs/play-runtime/*` modules here. Shared runtime
|
|
10
|
+
modules are optimized for the Node/Temporal runner and often bring along code
|
|
11
|
+
that is correct but expensive in a per-graphHash Worker isolate.
|
|
12
|
+
|
|
13
|
+
If a helper needs zod, Neon, runtime API transport, validation registries, or
|
|
14
|
+
other heavy leaves, put it behind the `env.HARNESS` service binding in
|
|
15
|
+
`apps/play-harness-worker` and call it through `sdk/src/plays/harness-stub.ts`.
|
|
16
|
+
|
|
17
|
+
The intended split is:
|
|
18
|
+
|
|
19
|
+
- `entry.ts`: request/workflow orchestration and ctx construction
|
|
20
|
+
- `runtime/*`: tiny per-play primitives that must execute in the isolate
|
|
21
|
+
- `apps/play-harness-worker`: heavy shared leaves kept warm behind RPC
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tiny batching helpers for the per-play Worker runtime.
|
|
3
|
+
*
|
|
4
|
+
* This mirrors the small subset of shared_libs/play-runtime batching that the
|
|
5
|
+
* Workers harness needs without importing the broader shared runtime graph.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { AnyBatchOperationStrategy } from '../../../../shared_libs/play-runtime/batching-types';
|
|
9
|
+
|
|
10
|
+
export type ChunkExecutionResult<TRequest, TResult> = {
|
|
11
|
+
request: TRequest;
|
|
12
|
+
result: TResult | null;
|
|
13
|
+
/**
|
|
14
|
+
* Present when this request's execution rejected. The request failed but
|
|
15
|
+
* its siblings in the chunk kept their results — one provider hiccup must
|
|
16
|
+
* stay a per-request failure, not a chunk-level abort that discards
|
|
17
|
+
* completed sibling work.
|
|
18
|
+
*/
|
|
19
|
+
error?: unknown;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export async function executeChunkedRequests<TRequest, TResult>(input: {
|
|
23
|
+
requests: TRequest[];
|
|
24
|
+
batchSize: number;
|
|
25
|
+
execute: (request: TRequest) => Promise<TResult>;
|
|
26
|
+
onChunkComplete?: (
|
|
27
|
+
results: Array<ChunkExecutionResult<TRequest, TResult>>,
|
|
28
|
+
) => void | Promise<void>;
|
|
29
|
+
}): Promise<Array<ChunkExecutionResult<TRequest, TResult>>> {
|
|
30
|
+
const results: Array<ChunkExecutionResult<TRequest, TResult>> = [];
|
|
31
|
+
for (let start = 0; start < input.requests.length; start += input.batchSize) {
|
|
32
|
+
const chunk = input.requests.slice(start, start + input.batchSize);
|
|
33
|
+
const settled = await Promise.allSettled(
|
|
34
|
+
chunk.map((request) => input.execute(request)),
|
|
35
|
+
);
|
|
36
|
+
for (let index = 0; index < chunk.length; index += 1) {
|
|
37
|
+
const request = chunk[index]!;
|
|
38
|
+
const outcome = settled[index]!;
|
|
39
|
+
if (outcome.status === 'rejected') {
|
|
40
|
+
results.push({ request, result: null, error: outcome.reason });
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
results.push({ request, result: outcome.value });
|
|
44
|
+
}
|
|
45
|
+
await input.onChunkComplete?.(results.slice(results.length - chunk.length));
|
|
46
|
+
}
|
|
47
|
+
return results;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function compileRequestsWithStrategy<TRequest>(input: {
|
|
51
|
+
requests: TRequest[];
|
|
52
|
+
strategy: AnyBatchOperationStrategy;
|
|
53
|
+
getPayload: (request: TRequest) => Record<string, unknown>;
|
|
54
|
+
}): Array<{
|
|
55
|
+
batchOperation: string;
|
|
56
|
+
memberRequests: TRequest[];
|
|
57
|
+
batchPayload: Record<string, unknown>;
|
|
58
|
+
splitResults: (value: unknown) => Array<unknown | null>;
|
|
59
|
+
}> {
|
|
60
|
+
const compiledBatches: Array<{
|
|
61
|
+
batchOperation: string;
|
|
62
|
+
memberRequests: TRequest[];
|
|
63
|
+
batchPayload: Record<string, unknown>;
|
|
64
|
+
splitResults: (value: unknown) => Array<unknown | null>;
|
|
65
|
+
}> = [];
|
|
66
|
+
const bucketedRequests = new Map<string, TRequest[]>();
|
|
67
|
+
for (const request of input.requests) {
|
|
68
|
+
const payload = input.getPayload(request);
|
|
69
|
+
const bucketKey = String(input.strategy.toBucketKey(payload));
|
|
70
|
+
const bucket = bucketedRequests.get(bucketKey);
|
|
71
|
+
if (bucket) bucket.push(request);
|
|
72
|
+
else bucketedRequests.set(bucketKey, [request]);
|
|
73
|
+
}
|
|
74
|
+
for (const bucketRequests of bucketedRequests.values()) {
|
|
75
|
+
let currentBatch: TRequest[] = [];
|
|
76
|
+
const flushBatch = () => {
|
|
77
|
+
if (currentBatch.length === 0) return;
|
|
78
|
+
const memberRequests = [...currentBatch];
|
|
79
|
+
const compiled = input.strategy.compile(
|
|
80
|
+
memberRequests.map((request) => input.getPayload(request)),
|
|
81
|
+
);
|
|
82
|
+
compiledBatches.push({
|
|
83
|
+
batchOperation: compiled.batchOperation,
|
|
84
|
+
memberRequests,
|
|
85
|
+
batchPayload: compiled.batchPayload,
|
|
86
|
+
splitResults: (value: unknown) => {
|
|
87
|
+
const splitResults = input.strategy.splitResult(value, compiled);
|
|
88
|
+
return memberRequests.map(
|
|
89
|
+
(_, index) => splitResults[index]?.result ?? null,
|
|
90
|
+
);
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
currentBatch = [];
|
|
94
|
+
};
|
|
95
|
+
for (const request of bucketRequests) {
|
|
96
|
+
const payload = input.getPayload(request);
|
|
97
|
+
const canAppend =
|
|
98
|
+
currentBatch.length > 0 &&
|
|
99
|
+
currentBatch.length < input.strategy.maxBatchSize &&
|
|
100
|
+
currentBatch.every((existing) =>
|
|
101
|
+
input.strategy.canBatchWith(input.getPayload(existing), payload),
|
|
102
|
+
);
|
|
103
|
+
if (!canAppend && currentBatch.length > 0) flushBatch();
|
|
104
|
+
currentBatch.push(request);
|
|
105
|
+
}
|
|
106
|
+
flushBatch();
|
|
107
|
+
}
|
|
108
|
+
return compiledBatches;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function getDefaultPlayRuntimeBatchStrategy(
|
|
112
|
+
operation: string | null | undefined,
|
|
113
|
+
): AnyBatchOperationStrategy | null {
|
|
114
|
+
if (operation !== 'test_rate_limit') return null;
|
|
115
|
+
return {
|
|
116
|
+
sourceOperation: 'test_rate_limit',
|
|
117
|
+
batchOperation: 'test_batch_rate_limit',
|
|
118
|
+
kind: 'identifier_batch',
|
|
119
|
+
maxBatchSize: 200,
|
|
120
|
+
canBatchWith(left, right) {
|
|
121
|
+
return String(left.stage || '') === String(right.stage || '');
|
|
122
|
+
},
|
|
123
|
+
toBucketKey(payload) {
|
|
124
|
+
return `test_batch_rate_limit:${String(payload.stage || '')}`;
|
|
125
|
+
},
|
|
126
|
+
toItemKey(payload) {
|
|
127
|
+
return String(payload.lead_id || payload.row_number || payload.key || '');
|
|
128
|
+
},
|
|
129
|
+
compile(payloads) {
|
|
130
|
+
const stage = String(payloads[0]?.stage || '');
|
|
131
|
+
const simulatedDelayMs =
|
|
132
|
+
typeof payloads[0]?.simulated_delay_ms === 'number'
|
|
133
|
+
? payloads[0].simulated_delay_ms
|
|
134
|
+
: undefined;
|
|
135
|
+
const items = payloads.map((payload, index) => ({
|
|
136
|
+
itemKey: String(
|
|
137
|
+
payload.lead_id || payload.row_number || `row_${index}`,
|
|
138
|
+
),
|
|
139
|
+
payload,
|
|
140
|
+
}));
|
|
141
|
+
return {
|
|
142
|
+
batchOperation: 'test_batch_rate_limit',
|
|
143
|
+
batchPayload: {
|
|
144
|
+
key: 'batch',
|
|
145
|
+
...(stage ? { stage } : {}),
|
|
146
|
+
...(simulatedDelayMs !== undefined
|
|
147
|
+
? { simulated_delay_ms: simulatedDelayMs }
|
|
148
|
+
: {}),
|
|
149
|
+
items,
|
|
150
|
+
},
|
|
151
|
+
items,
|
|
152
|
+
};
|
|
153
|
+
},
|
|
154
|
+
splitResult(fullResult, compiled) {
|
|
155
|
+
const container =
|
|
156
|
+
fullResult != null &&
|
|
157
|
+
typeof fullResult === 'object' &&
|
|
158
|
+
!Array.isArray(fullResult)
|
|
159
|
+
? (fullResult as Record<string, unknown>)
|
|
160
|
+
: {};
|
|
161
|
+
const nestedData =
|
|
162
|
+
container.data != null &&
|
|
163
|
+
typeof container.data === 'object' &&
|
|
164
|
+
!Array.isArray(container.data)
|
|
165
|
+
? (container.data as Record<string, unknown>)
|
|
166
|
+
: {};
|
|
167
|
+
const resultItems = Array.isArray(container.items)
|
|
168
|
+
? (container.items as Array<{ itemKey?: string; result?: unknown }>)
|
|
169
|
+
: Array.isArray(nestedData.items)
|
|
170
|
+
? (nestedData.items as Array<{ itemKey?: string; result?: unknown }>)
|
|
171
|
+
: [];
|
|
172
|
+
return compiled.items.map((item, index) => ({
|
|
173
|
+
itemKey: item.itemKey,
|
|
174
|
+
result:
|
|
175
|
+
index < resultItems.length
|
|
176
|
+
? (resultItems[index]?.result ?? null)
|
|
177
|
+
: null,
|
|
178
|
+
rawResult:
|
|
179
|
+
index < resultItems.length
|
|
180
|
+
? (resultItems[index]?.result ?? null)
|
|
181
|
+
: null,
|
|
182
|
+
}));
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-play Worker tool batch executor.
|
|
3
|
+
*
|
|
4
|
+
* This is intentionally local to the Worker bundle. Pulling the shared
|
|
5
|
+
* executor module back in would widen the bundle graph for a tiny helper.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type ToolBatchRequest = {
|
|
9
|
+
runId: string;
|
|
10
|
+
orgId: string;
|
|
11
|
+
toolId: string;
|
|
12
|
+
operation: string;
|
|
13
|
+
provider: string;
|
|
14
|
+
items: Array<{
|
|
15
|
+
itemKey: string;
|
|
16
|
+
payload: Record<string, unknown>;
|
|
17
|
+
inputHash?: string | null;
|
|
18
|
+
}>;
|
|
19
|
+
waterfallId?: string | null;
|
|
20
|
+
stageId?: string | null;
|
|
21
|
+
fieldName?: string | null;
|
|
22
|
+
mapName?: string | null;
|
|
23
|
+
chunkIndex?: number | null;
|
|
24
|
+
userProvidedRateLimitKey?: string | null;
|
|
25
|
+
providerBatchSize: number;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type ToolBatchItemResult = {
|
|
29
|
+
itemKey: string;
|
|
30
|
+
result: unknown;
|
|
31
|
+
cached?: boolean;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export function createToolBatchExecutor(input: {
|
|
35
|
+
executeProviderBatch(batch: {
|
|
36
|
+
request: ToolBatchRequest;
|
|
37
|
+
batchIndex: number;
|
|
38
|
+
idempotencyKeys: string[];
|
|
39
|
+
rateLimitKey: string;
|
|
40
|
+
items: ToolBatchRequest['items'];
|
|
41
|
+
}): Promise<ToolBatchItemResult[]>;
|
|
42
|
+
}) {
|
|
43
|
+
return {
|
|
44
|
+
async executeToolBatch(request: ToolBatchRequest) {
|
|
45
|
+
const providerBatchSize = Math.max(
|
|
46
|
+
1,
|
|
47
|
+
Math.floor(request.providerBatchSize),
|
|
48
|
+
);
|
|
49
|
+
const batches: Array<ToolBatchRequest['items']> = [];
|
|
50
|
+
for (
|
|
51
|
+
let index = 0;
|
|
52
|
+
index < request.items.length;
|
|
53
|
+
index += providerBatchSize
|
|
54
|
+
) {
|
|
55
|
+
batches.push(request.items.slice(index, index + providerBatchSize));
|
|
56
|
+
}
|
|
57
|
+
const results: ToolBatchItemResult[] = [];
|
|
58
|
+
for (let batchIndex = 0; batchIndex < batches.length; batchIndex += 1) {
|
|
59
|
+
const items = batches[batchIndex]!;
|
|
60
|
+
results.push(
|
|
61
|
+
...(await input.executeProviderBatch({
|
|
62
|
+
request,
|
|
63
|
+
batchIndex,
|
|
64
|
+
items,
|
|
65
|
+
rateLimitKey: [
|
|
66
|
+
request.orgId,
|
|
67
|
+
request.provider,
|
|
68
|
+
request.operation,
|
|
69
|
+
request.userProvidedRateLimitKey ?? '',
|
|
70
|
+
].join(':'),
|
|
71
|
+
idempotencyKeys: items.map((item) =>
|
|
72
|
+
[
|
|
73
|
+
request.runId,
|
|
74
|
+
request.mapName ?? '',
|
|
75
|
+
request.chunkIndex ?? '',
|
|
76
|
+
item.itemKey,
|
|
77
|
+
request.fieldName ?? '',
|
|
78
|
+
request.waterfallId ?? '',
|
|
79
|
+
request.stageId ?? '',
|
|
80
|
+
item.inputHash ?? stableStringify(item.payload),
|
|
81
|
+
].join(':'),
|
|
82
|
+
),
|
|
83
|
+
})),
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
runId: request.runId,
|
|
88
|
+
toolId: request.toolId,
|
|
89
|
+
operation: request.operation,
|
|
90
|
+
provider: request.provider,
|
|
91
|
+
batchCount: batches.length,
|
|
92
|
+
itemCount: request.items.length,
|
|
93
|
+
results,
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function stableStringify(value: unknown): string {
|
|
100
|
+
if (value === null || typeof value !== 'object') return JSON.stringify(value);
|
|
101
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`;
|
|
102
|
+
const record = value as Record<string, unknown>;
|
|
103
|
+
return `{${Object.keys(record)
|
|
104
|
+
.sort()
|
|
105
|
+
.map((key) => `${JSON.stringify(key)}:${stableStringify(record[key])}`)
|
|
106
|
+
.join(',')}}`;
|
|
107
|
+
}
|
|
@@ -2051,23 +2051,126 @@ export class DeeplineClient {
|
|
|
2051
2051
|
}
|
|
2052
2052
|
|
|
2053
2053
|
/**
|
|
2054
|
-
* Observe one run's live events
|
|
2055
|
-
*
|
|
2056
|
-
*
|
|
2057
|
-
*
|
|
2058
|
-
*
|
|
2059
|
-
* unreachable Convex) — callers fall back to the SSE stream with a notice.
|
|
2054
|
+
* Observe one run's live events. Uses the Convex Run Snapshot subscription
|
|
2055
|
+
* transport first (ADR-0008), then falls back to the canonical SSE stream
|
|
2056
|
+
* when the subscription transport or its optional client modules are not
|
|
2057
|
+
* available. Pass `fallback: 'none'` to receive
|
|
2058
|
+
* {@link RunObserveTransportUnavailableError} instead.
|
|
2060
2059
|
*/
|
|
2061
|
-
observeRunEvents(
|
|
2060
|
+
async *observeRunEvents(
|
|
2061
|
+
runId: string,
|
|
2062
|
+
options?: {
|
|
2063
|
+
signal?: AbortSignal;
|
|
2064
|
+
onNotice?: (message: string) => void;
|
|
2065
|
+
fallback?: 'sse' | 'none';
|
|
2066
|
+
},
|
|
2067
|
+
): AsyncGenerator<PlayLiveEvent> {
|
|
2068
|
+
let yieldedObserveEvent = false;
|
|
2069
|
+
try {
|
|
2070
|
+
for await (const event of observeRunEvents({
|
|
2071
|
+
http: this.http,
|
|
2072
|
+
runId,
|
|
2073
|
+
signal: options?.signal,
|
|
2074
|
+
onNotice: options?.onNotice,
|
|
2075
|
+
}) as AsyncGenerator<PlayLiveEvent>) {
|
|
2076
|
+
yieldedObserveEvent = true;
|
|
2077
|
+
yield event;
|
|
2078
|
+
}
|
|
2079
|
+
} catch (error) {
|
|
2080
|
+
if (
|
|
2081
|
+
!(error instanceof RunObserveTransportUnavailableError) ||
|
|
2082
|
+
yieldedObserveEvent ||
|
|
2083
|
+
options?.fallback === 'none'
|
|
2084
|
+
) {
|
|
2085
|
+
throw error;
|
|
2086
|
+
}
|
|
2087
|
+
options?.onNotice?.(
|
|
2088
|
+
`[observe] live subscription unavailable (${error.reason}); falling back to SSE tail (support window, ADR-0008)`,
|
|
2089
|
+
);
|
|
2090
|
+
yield* this.streamPlayRunEventsUntilTerminal(runId, options);
|
|
2091
|
+
}
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
private async *streamPlayRunEventsUntilTerminal(
|
|
2062
2095
|
runId: string,
|
|
2063
2096
|
options?: { signal?: AbortSignal; onNotice?: (message: string) => void },
|
|
2064
2097
|
): AsyncGenerator<PlayLiveEvent> {
|
|
2065
|
-
|
|
2066
|
-
http: this.http,
|
|
2098
|
+
const state: PlayLiveStatusState = {
|
|
2067
2099
|
runId,
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2100
|
+
status: 'running',
|
|
2101
|
+
logs: [],
|
|
2102
|
+
lastLogSeq: 0,
|
|
2103
|
+
latest: null,
|
|
2104
|
+
};
|
|
2105
|
+
let lastEventId: string | undefined;
|
|
2106
|
+
let reconnectAttempt = 0;
|
|
2107
|
+
|
|
2108
|
+
for (;;) {
|
|
2109
|
+
if (options?.signal?.aborted) {
|
|
2110
|
+
return;
|
|
2111
|
+
}
|
|
2112
|
+
const connectedAt = Date.now();
|
|
2113
|
+
let sawEvent = false;
|
|
2114
|
+
let endedReason = 'stream window ended before a terminal event';
|
|
2115
|
+
try {
|
|
2116
|
+
for await (const event of this.streamPlayRunEvents(runId, {
|
|
2117
|
+
mode: 'cli',
|
|
2118
|
+
signal: options?.signal,
|
|
2119
|
+
...(lastEventId ? { lastEventId } : {}),
|
|
2120
|
+
})) {
|
|
2121
|
+
sawEvent = true;
|
|
2122
|
+
if (event.cursor?.trim()) {
|
|
2123
|
+
lastEventId = event.cursor;
|
|
2124
|
+
}
|
|
2125
|
+
yield event;
|
|
2126
|
+
const status = updatePlayLiveStatusState(state, event);
|
|
2127
|
+
if (status && TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
2128
|
+
return;
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
} catch (error) {
|
|
2132
|
+
if (options?.signal?.aborted) {
|
|
2133
|
+
return;
|
|
2134
|
+
}
|
|
2135
|
+
if (!isTransientPlayStreamError(error)) {
|
|
2136
|
+
throw error;
|
|
2137
|
+
}
|
|
2138
|
+
endedReason = error instanceof Error ? error.message : String(error);
|
|
2139
|
+
}
|
|
2140
|
+
|
|
2141
|
+
let refreshed: PlayStatus | null = null;
|
|
2142
|
+
try {
|
|
2143
|
+
refreshed = await this.getRunStatus(runId);
|
|
2144
|
+
} catch (error) {
|
|
2145
|
+
if (!isTransientPlayStreamError(error)) {
|
|
2146
|
+
throw error;
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
if (refreshed && TERMINAL_PLAY_STATUSES.has(refreshed.status)) {
|
|
2150
|
+
yield {
|
|
2151
|
+
cursor: String(Date.now()),
|
|
2152
|
+
streamId: `sse-fallback:${runId}`,
|
|
2153
|
+
scope: 'play',
|
|
2154
|
+
type: 'play.run.status',
|
|
2155
|
+
at: new Date().toISOString(),
|
|
2156
|
+
payload: refreshed as unknown as Record<string, unknown>,
|
|
2157
|
+
};
|
|
2158
|
+
return;
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
if (
|
|
2162
|
+
sawEvent ||
|
|
2163
|
+
Date.now() - connectedAt >= STREAM_HEALTHY_CONNECTION_MS
|
|
2164
|
+
) {
|
|
2165
|
+
reconnectAttempt = 0;
|
|
2166
|
+
}
|
|
2167
|
+
const delayMs = streamReconnectDelayMs(reconnectAttempt);
|
|
2168
|
+
reconnectAttempt += 1;
|
|
2169
|
+
options?.onNotice?.(
|
|
2170
|
+
`[observe] SSE tail window ended before terminal status (${endedReason}); reconnecting to run ${runId}`,
|
|
2171
|
+
);
|
|
2172
|
+
await sleep(delayMs);
|
|
2173
|
+
}
|
|
2071
2174
|
}
|
|
2072
2175
|
|
|
2073
2176
|
/**
|
|
@@ -2088,6 +2191,7 @@ export class DeeplineClient {
|
|
|
2088
2191
|
for await (const event of this.observeRunEvents(runId, {
|
|
2089
2192
|
signal: options?.signal,
|
|
2090
2193
|
onNotice: options?.onNotice,
|
|
2194
|
+
fallback: 'none',
|
|
2091
2195
|
})) {
|
|
2092
2196
|
const status = updatePlayLiveStatusState(state, event);
|
|
2093
2197
|
if (!status || !TERMINAL_PLAY_STATUSES.has(status.status)) {
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { SDK_API_CONTRACT, SDK_VERSION } from './version.js';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
export type SdkCompatibilityStatus =
|
|
7
|
+
| 'current'
|
|
8
|
+
| 'update_available'
|
|
9
|
+
| 'deprecated'
|
|
10
|
+
| 'unsupported';
|
|
11
|
+
|
|
12
|
+
export type SdkCompatibilityResponse = {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
status: SdkCompatibilityStatus;
|
|
15
|
+
current: string | null;
|
|
16
|
+
latest: string;
|
|
17
|
+
minimum_supported: string;
|
|
18
|
+
deprecated_below: string;
|
|
19
|
+
api_contract: string;
|
|
20
|
+
update_available: boolean;
|
|
21
|
+
update_required: boolean;
|
|
22
|
+
message: string;
|
|
23
|
+
update_command: string;
|
|
24
|
+
command?: string | null;
|
|
25
|
+
auto_update?: {
|
|
26
|
+
should_auto_update: boolean;
|
|
27
|
+
required: boolean;
|
|
28
|
+
reason: 'required' | 'patch_lag' | 'rollback_forced' | null;
|
|
29
|
+
patch_lag: number | null;
|
|
30
|
+
patch_lag_threshold: number;
|
|
31
|
+
update_command: string;
|
|
32
|
+
};
|
|
33
|
+
cli_family?: {
|
|
34
|
+
action: 'force_python';
|
|
35
|
+
reason: 'rollback_forced';
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const CHECK_TIMEOUT_MS = 2_000;
|
|
40
|
+
const COMPAT_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
41
|
+
|
|
42
|
+
type CompatCacheFile = {
|
|
43
|
+
entries?: Record<
|
|
44
|
+
string,
|
|
45
|
+
{
|
|
46
|
+
savedAt: number;
|
|
47
|
+
response: SdkCompatibilityResponse;
|
|
48
|
+
}
|
|
49
|
+
>;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
function shouldSkipCompatibilityCheck(): boolean {
|
|
53
|
+
const value =
|
|
54
|
+
process.env.DEEPLINE_SKIP_SDK_COMPAT_CHECK?.trim().toLowerCase();
|
|
55
|
+
return value === '1' || value === 'true' || value === 'yes';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function compatibilityCachePath(): string {
|
|
59
|
+
return join(homedir(), '.cache', 'deepline', 'sdk-compat-cache.json');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function compatibilityCacheKey(
|
|
63
|
+
baseUrl: string,
|
|
64
|
+
command: string | null | undefined,
|
|
65
|
+
): string {
|
|
66
|
+
return JSON.stringify({
|
|
67
|
+
baseUrl: baseUrl.replace(/\/$/, ''),
|
|
68
|
+
version: SDK_VERSION,
|
|
69
|
+
apiContract: SDK_API_CONTRACT,
|
|
70
|
+
command: command?.trim() || null,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function readCachedCompatibility(
|
|
75
|
+
baseUrl: string,
|
|
76
|
+
command: string | null | undefined,
|
|
77
|
+
): SdkCompatibilityResponse | null {
|
|
78
|
+
try {
|
|
79
|
+
const path = compatibilityCachePath();
|
|
80
|
+
if (!existsSync(path)) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8')) as CompatCacheFile;
|
|
84
|
+
const entry = parsed.entries?.[compatibilityCacheKey(baseUrl, command)];
|
|
85
|
+
if (!entry || Date.now() - entry.savedAt > COMPAT_CACHE_TTL_MS) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
return entry.response;
|
|
89
|
+
} catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function writeCachedCompatibility(
|
|
95
|
+
baseUrl: string,
|
|
96
|
+
command: string | null | undefined,
|
|
97
|
+
response: SdkCompatibilityResponse,
|
|
98
|
+
): void {
|
|
99
|
+
try {
|
|
100
|
+
const path = compatibilityCachePath();
|
|
101
|
+
const existing = existsSync(path)
|
|
102
|
+
? (JSON.parse(readFileSync(path, 'utf8')) as CompatCacheFile)
|
|
103
|
+
: {};
|
|
104
|
+
const entries = existing.entries ?? {};
|
|
105
|
+
entries[compatibilityCacheKey(baseUrl, command)] = {
|
|
106
|
+
savedAt: Date.now(),
|
|
107
|
+
response,
|
|
108
|
+
};
|
|
109
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
110
|
+
writeFileSync(path, `${JSON.stringify({ entries }, null, 2)}\n`);
|
|
111
|
+
} catch {
|
|
112
|
+
// Compatibility checks are advisory unless the server explicitly responds
|
|
113
|
+
// with a blocking policy. Cache failures must not block normal CLI usage.
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export async function checkSdkCompatibility(
|
|
118
|
+
baseUrl: string,
|
|
119
|
+
options: { command?: string | null } = {},
|
|
120
|
+
): Promise<{
|
|
121
|
+
response: SdkCompatibilityResponse | null;
|
|
122
|
+
error: Error | null;
|
|
123
|
+
}> {
|
|
124
|
+
if (shouldSkipCompatibilityCheck()) {
|
|
125
|
+
return { response: null, error: null };
|
|
126
|
+
}
|
|
127
|
+
const controller = new AbortController();
|
|
128
|
+
const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS);
|
|
129
|
+
try {
|
|
130
|
+
const url = new URL('/api/v2/sdk/compat', baseUrl);
|
|
131
|
+
url.searchParams.set('version', SDK_VERSION);
|
|
132
|
+
if (options.command?.trim()) {
|
|
133
|
+
url.searchParams.set('command', options.command.trim());
|
|
134
|
+
}
|
|
135
|
+
const response = await fetch(url, {
|
|
136
|
+
method: 'GET',
|
|
137
|
+
headers: {
|
|
138
|
+
'User-Agent': `deepline-ts-sdk/${SDK_VERSION}`,
|
|
139
|
+
'X-Deepline-SDK-Version': SDK_VERSION,
|
|
140
|
+
'X-Deepline-API-Contract': SDK_API_CONTRACT,
|
|
141
|
+
},
|
|
142
|
+
signal: controller.signal,
|
|
143
|
+
});
|
|
144
|
+
const data = (await response
|
|
145
|
+
.json()
|
|
146
|
+
.catch(() => null)) as SdkCompatibilityResponse | null;
|
|
147
|
+
if (data) {
|
|
148
|
+
writeCachedCompatibility(baseUrl, options.command, data);
|
|
149
|
+
}
|
|
150
|
+
return { response: data, error: null };
|
|
151
|
+
} catch (error) {
|
|
152
|
+
const cached = readCachedCompatibility(baseUrl, options.command);
|
|
153
|
+
if (cached?.ok === false || cached?.status === 'unsupported') {
|
|
154
|
+
return { response: cached, error: null };
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
response: null,
|
|
158
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
159
|
+
};
|
|
160
|
+
} finally {
|
|
161
|
+
clearTimeout(timeout);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function enforceSdkCompatibilityResponse(
|
|
166
|
+
response: SdkCompatibilityResponse | null,
|
|
167
|
+
): void {
|
|
168
|
+
if (!response) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (response.update_required) {
|
|
172
|
+
throw new Error(response.message);
|
|
173
|
+
}
|
|
174
|
+
if (
|
|
175
|
+
response.status === 'deprecated' ||
|
|
176
|
+
response.status === 'update_available'
|
|
177
|
+
) {
|
|
178
|
+
process.stderr.write(`${response.message}\n`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export async function enforceSdkCompatibility(
|
|
183
|
+
baseUrl: string,
|
|
184
|
+
options: { command?: string | null } = {},
|
|
185
|
+
): Promise<void> {
|
|
186
|
+
const { response, error } = await checkSdkCompatibility(baseUrl, options);
|
|
187
|
+
if (error || !response) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
enforceSdkCompatibilityResponse(response);
|
|
191
|
+
}
|