deepline 0.3.202 → 0.3.204
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/release.ts +1 -1
- package/dist/bundling-sources/shared_libs/play-runtime/deployment/fly-registry.ts +86 -5
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres-pool.ts +238 -204
- package/dist/bundling-sources/shared_libs/product-notifications/catalog.ts +1 -1
- package/dist/cli/index.js +24 -21
- package/dist/cli/index.mjs +24 -21
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/release.d.mts +1 -1
- package/dist/release.d.ts +1 -1
- package/dist/release.js +1 -1
- package/dist/release.mjs +1 -1
- package/package.json +1 -1
|
@@ -200,7 +200,7 @@ export const SDK_RELEASE = {
|
|
|
200
200
|
// getters keep their established compatibility behavior.
|
|
201
201
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
202
202
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
203
|
-
version: '0.3.
|
|
203
|
+
version: '0.3.204',
|
|
204
204
|
updateSummary:
|
|
205
205
|
'Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.',
|
|
206
206
|
packageCapabilities: {
|
|
@@ -1,16 +1,73 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
const FLY_REGISTRY_COMMAND_TIMEOUT_MS = 30_000;
|
|
4
|
+
const RUNTIME_IMAGE_MANIFEST_TIMEOUT_MS = 10_000;
|
|
5
|
+
|
|
6
|
+
type FlyRegistryCommandResult = Readonly<{
|
|
7
|
+
error?: Error & { code?: string };
|
|
8
|
+
status: number | null;
|
|
9
|
+
stdout?: string | null;
|
|
10
|
+
stderr?: string | null;
|
|
11
|
+
}>;
|
|
12
|
+
|
|
13
|
+
type FlyRegistryCommandSpawn = (
|
|
14
|
+
command: string,
|
|
15
|
+
args: readonly string[],
|
|
16
|
+
options: Readonly<{
|
|
17
|
+
encoding: 'utf8';
|
|
18
|
+
killSignal: 'SIGKILL';
|
|
19
|
+
stdio: 'inherit' | 'pipe';
|
|
20
|
+
timeout: number;
|
|
21
|
+
}>,
|
|
22
|
+
) => FlyRegistryCommandResult;
|
|
23
|
+
|
|
24
|
+
function run(
|
|
25
|
+
command: string,
|
|
26
|
+
args: string[],
|
|
27
|
+
quiet = false,
|
|
28
|
+
timeoutMs = FLY_REGISTRY_COMMAND_TIMEOUT_MS,
|
|
29
|
+
spawnOverride?: FlyRegistryCommandSpawn,
|
|
30
|
+
): boolean {
|
|
31
|
+
const spawnCommand: FlyRegistryCommandSpawn =
|
|
32
|
+
spawnOverride ??
|
|
33
|
+
((executable, commandArgs, spawnOptions) =>
|
|
34
|
+
spawnSync(executable, [...commandArgs], spawnOptions));
|
|
35
|
+
const result = spawnCommand(command, args, {
|
|
5
36
|
encoding: 'utf8',
|
|
37
|
+
killSignal: 'SIGKILL',
|
|
6
38
|
stdio: quiet ? 'pipe' : 'inherit',
|
|
39
|
+
timeout: timeoutMs,
|
|
7
40
|
});
|
|
41
|
+
if (result.error) {
|
|
42
|
+
const error = result.error as NodeJS.ErrnoException;
|
|
43
|
+
if (error.code === 'ETIMEDOUT') {
|
|
44
|
+
throw Object.assign(
|
|
45
|
+
new Error(
|
|
46
|
+
`[fly-registry] ${command} ${args.join(' ')} timed out after ${timeoutMs}ms.`,
|
|
47
|
+
),
|
|
48
|
+
{ code: error.code },
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
throw result.error;
|
|
52
|
+
}
|
|
8
53
|
return result.status === 0;
|
|
9
54
|
}
|
|
10
55
|
|
|
11
56
|
/** Shared immutable-tag presence probe for image builds and rollback planning. */
|
|
12
|
-
export function runtimeImageManifestExists(
|
|
13
|
-
|
|
57
|
+
export function runtimeImageManifestExists(
|
|
58
|
+
image: string,
|
|
59
|
+
options: {
|
|
60
|
+
timeoutMs?: number;
|
|
61
|
+
spawnCommand?: FlyRegistryCommandSpawn;
|
|
62
|
+
} = {},
|
|
63
|
+
): boolean {
|
|
64
|
+
return run(
|
|
65
|
+
'docker',
|
|
66
|
+
['manifest', 'inspect', image],
|
|
67
|
+
true,
|
|
68
|
+
options.timeoutMs ?? RUNTIME_IMAGE_MANIFEST_TIMEOUT_MS,
|
|
69
|
+
options.spawnCommand,
|
|
70
|
+
);
|
|
14
71
|
}
|
|
15
72
|
|
|
16
73
|
export const FLY_REGISTRY_AUTH_ATTEMPTS = 3;
|
|
@@ -25,13 +82,37 @@ const FLY_REGISTRY_AUTH_RETRY_DELAY_MS = 1_500;
|
|
|
25
82
|
*/
|
|
26
83
|
export function authenticateFlyRegistry(
|
|
27
84
|
sleep: (ms: number) => void = sleepSync,
|
|
85
|
+
spawnOverride?: FlyRegistryCommandSpawn,
|
|
28
86
|
): void {
|
|
87
|
+
let lastTimeoutError: Error | undefined;
|
|
29
88
|
for (let attempt = 1; attempt <= FLY_REGISTRY_AUTH_ATTEMPTS; attempt += 1) {
|
|
30
|
-
|
|
89
|
+
try {
|
|
90
|
+
if (
|
|
91
|
+
run(
|
|
92
|
+
'flyctl',
|
|
93
|
+
['auth', 'docker'],
|
|
94
|
+
false,
|
|
95
|
+
FLY_REGISTRY_COMMAND_TIMEOUT_MS,
|
|
96
|
+
spawnOverride,
|
|
97
|
+
)
|
|
98
|
+
) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
lastTimeoutError = undefined;
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if (
|
|
104
|
+
!(error instanceof Error) ||
|
|
105
|
+
(error as NodeJS.ErrnoException).code !== 'ETIMEDOUT'
|
|
106
|
+
) {
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
lastTimeoutError = error;
|
|
110
|
+
}
|
|
31
111
|
if (attempt < FLY_REGISTRY_AUTH_ATTEMPTS) {
|
|
32
112
|
sleep(FLY_REGISTRY_AUTH_RETRY_DELAY_MS * attempt);
|
|
33
113
|
}
|
|
34
114
|
}
|
|
115
|
+
if (lastTimeoutError) throw lastTimeoutError;
|
|
35
116
|
throw new Error(
|
|
36
117
|
`Fly registry authentication failed after ${FLY_REGISTRY_AUTH_ATTEMPTS} attempts.`,
|
|
37
118
|
);
|
|
@@ -307,7 +307,7 @@ type BreakerState = {
|
|
|
307
307
|
consecutiveFailures: number;
|
|
308
308
|
openedAt: number | null;
|
|
309
309
|
lastFailureMessage: string;
|
|
310
|
-
|
|
310
|
+
halfOpenProbe: symbol | null;
|
|
311
311
|
};
|
|
312
312
|
|
|
313
313
|
export type RuntimeSchedulerPoolPriority = 'ordinary' | 'critical';
|
|
@@ -645,6 +645,9 @@ function getPoolEntry(options: RuntimeSchedulerPoolOptions): PoolEntry {
|
|
|
645
645
|
maxAcquireQueueDepth,
|
|
646
646
|
maxAcquireQueueWaitMs,
|
|
647
647
|
);
|
|
648
|
+
// A pool created by the old breaker used a boolean claim. The new token
|
|
649
|
+
// does not inherit a stranded half-open claim across a hot reload.
|
|
650
|
+
existing.breaker.halfOpenProbe ??= null;
|
|
648
651
|
return existing;
|
|
649
652
|
}
|
|
650
653
|
|
|
@@ -671,7 +674,7 @@ function getPoolEntry(options: RuntimeSchedulerPoolOptions): PoolEntry {
|
|
|
671
674
|
consecutiveFailures: 0,
|
|
672
675
|
openedAt: null,
|
|
673
676
|
lastFailureMessage: '',
|
|
674
|
-
|
|
677
|
+
halfOpenProbe: null,
|
|
675
678
|
},
|
|
676
679
|
stats: {
|
|
677
680
|
acquires: 0,
|
|
@@ -698,31 +701,39 @@ function getPoolEntry(options: RuntimeSchedulerPoolOptions): PoolEntry {
|
|
|
698
701
|
return entry;
|
|
699
702
|
}
|
|
700
703
|
|
|
701
|
-
function breakerGate(entry: PoolEntry):
|
|
704
|
+
function breakerGate(entry: PoolEntry): symbol | null {
|
|
702
705
|
const { breaker } = entry;
|
|
703
|
-
if (breaker.openedAt === null) return;
|
|
706
|
+
if (breaker.openedAt === null) return null;
|
|
704
707
|
const elapsed = Date.now() - breaker.openedAt;
|
|
705
708
|
if (elapsed < RUNTIME_SCHEDULER_POOL_DEFAULTS.breaker.openMs) {
|
|
706
709
|
entry.stats.breakerRejections += 1;
|
|
707
710
|
throw new RuntimeSchedulerCircuitOpenError(breaker.lastFailureMessage);
|
|
708
711
|
}
|
|
709
712
|
// Half-open: let exactly one caller through as the trial.
|
|
710
|
-
if (breaker.
|
|
713
|
+
if (breaker.halfOpenProbe !== null) {
|
|
711
714
|
entry.stats.breakerRejections += 1;
|
|
712
715
|
throw new RuntimeSchedulerCircuitOpenError(breaker.lastFailureMessage);
|
|
713
716
|
}
|
|
714
|
-
|
|
717
|
+
const probe = Symbol('runtime-scheduler-half-open-probe');
|
|
718
|
+
breaker.halfOpenProbe = probe;
|
|
719
|
+
return probe;
|
|
715
720
|
}
|
|
716
721
|
|
|
717
722
|
function recordBreakerSuccess(entry: PoolEntry): void {
|
|
718
723
|
entry.breaker.consecutiveFailures = 0;
|
|
719
724
|
entry.breaker.openedAt = null;
|
|
720
|
-
entry.breaker.
|
|
725
|
+
entry.breaker.halfOpenProbe = null;
|
|
721
726
|
}
|
|
722
727
|
|
|
723
|
-
function recordBreakerFailure(
|
|
728
|
+
function recordBreakerFailure(
|
|
729
|
+
entry: PoolEntry,
|
|
730
|
+
error: unknown,
|
|
731
|
+
probe: symbol | null,
|
|
732
|
+
): void {
|
|
724
733
|
const { breaker } = entry;
|
|
725
|
-
breaker.
|
|
734
|
+
if (probe !== null && breaker.halfOpenProbe === probe) {
|
|
735
|
+
breaker.halfOpenProbe = null;
|
|
736
|
+
}
|
|
726
737
|
breaker.consecutiveFailures += 1;
|
|
727
738
|
breaker.lastFailureMessage =
|
|
728
739
|
error instanceof Error ? error.message : String(error);
|
|
@@ -798,190 +809,46 @@ export async function withRuntimeSchedulerClient<T>(
|
|
|
798
809
|
throwIfSchedulerAborted(signal);
|
|
799
810
|
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
800
811
|
throwIfSchedulerAborted(signal);
|
|
801
|
-
breakerGate(entry);
|
|
802
|
-
|
|
803
|
-
entry.stats.saturationRejections += 1;
|
|
804
|
-
throw new RuntimeSchedulerSaturatedError(entry.pool.waitingCount);
|
|
805
|
-
}
|
|
806
|
-
const acquireStart = performance.now();
|
|
807
|
-
entry.stats.peakTotalClients = Math.max(
|
|
808
|
-
entry.stats.peakTotalClients,
|
|
809
|
-
entry.pool.totalCount,
|
|
810
|
-
);
|
|
811
|
-
entry.stats.peakWaitingClients = Math.max(
|
|
812
|
-
entry.stats.peakWaitingClients,
|
|
813
|
-
entry.pool.waitingCount,
|
|
814
|
-
);
|
|
815
|
-
let poolClient;
|
|
812
|
+
const halfOpenProbe = breakerGate(entry);
|
|
813
|
+
let capacityObserved = false;
|
|
816
814
|
try {
|
|
817
|
-
|
|
818
|
-
} catch (error) {
|
|
819
|
-
if (isRuntimeSchedulerLocalAcquireTimeout(error)) {
|
|
815
|
+
if (entry.pool.waitingCount >= entry.maxAcquireQueueDepth) {
|
|
820
816
|
entry.stats.saturationRejections += 1;
|
|
821
|
-
console.warn('[runtime-scheduler-pool.local_acquire_saturated]', {
|
|
822
|
-
maxClients: entry.maxClients,
|
|
823
|
-
totalClients: entry.pool.totalCount,
|
|
824
|
-
idleClients: entry.pool.idleCount,
|
|
825
|
-
waitingClients: entry.pool.waitingCount,
|
|
826
|
-
});
|
|
827
817
|
throw new RuntimeSchedulerSaturatedError(entry.pool.waitingCount);
|
|
828
818
|
}
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
);
|
|
834
|
-
}
|
|
835
|
-
recordBreakerFailure(entry, error);
|
|
836
|
-
entry.stats.connectionErrors += 1;
|
|
837
|
-
lastError = error;
|
|
838
|
-
if (attempt + 1 < maxAttempts) {
|
|
839
|
-
entry.stats.retries += 1;
|
|
840
|
-
await sleep(jitteredBackoffMs(attempt));
|
|
841
|
-
throwIfSchedulerAborted(signal);
|
|
842
|
-
continue;
|
|
843
|
-
}
|
|
844
|
-
throw error;
|
|
845
|
-
}
|
|
846
|
-
entry.stats.acquires += 1;
|
|
847
|
-
const acquireWaitMs = performance.now() - acquireStart;
|
|
848
|
-
entry.stats.peakTotalClients = Math.max(
|
|
849
|
-
entry.stats.peakTotalClients,
|
|
850
|
-
entry.pool.totalCount,
|
|
851
|
-
);
|
|
852
|
-
pushSample(entry.stats.acquireWait, acquireWaitMs);
|
|
853
|
-
|
|
854
|
-
// A checked-out pg client emits 'error' (not via the pool) when its TCP
|
|
855
|
-
// connection dies while idle — e.g. a worker holding a client across a
|
|
856
|
-
// long play execution. With no listener that is an uncaught exception
|
|
857
|
-
// that kills the process (observed: worker crash mid-150k-row run).
|
|
858
|
-
// Swallow it here; the next query on the dead client fails normally and
|
|
859
|
-
// flows through the connection-error retry/poison path below.
|
|
860
|
-
let checkedOutError: unknown = null;
|
|
861
|
-
const onCheckedOutError = (error: Error) => {
|
|
862
|
-
checkedOutError = error;
|
|
863
|
-
entry.stats.connectionErrors += 1;
|
|
864
|
-
console.warn('[runtime-scheduler-pool.checked_out_client_error]', {
|
|
865
|
-
error: error.message,
|
|
866
|
-
});
|
|
867
|
-
};
|
|
868
|
-
poolClient.on('error', onCheckedOutError);
|
|
869
|
-
let clientReleased = false;
|
|
870
|
-
const releasePoolClient = (error?: Error) => {
|
|
871
|
-
if (clientReleased) return;
|
|
872
|
-
clientReleased = true;
|
|
873
|
-
poolClient.release(error);
|
|
874
|
-
};
|
|
875
|
-
const onAbort = () => releasePoolClient(schedulerAbortReason(signal!));
|
|
876
|
-
signal?.addEventListener('abort', onAbort, { once: true });
|
|
877
|
-
|
|
878
|
-
let queryCount = 0;
|
|
879
|
-
let queryMs = 0;
|
|
880
|
-
try {
|
|
881
|
-
throwIfSchedulerAborted(signal);
|
|
882
|
-
const result = await fn({
|
|
883
|
-
query: async (sql, params) => {
|
|
884
|
-
throwIfSchedulerAborted(signal);
|
|
885
|
-
const queryStartedAt = performance.now();
|
|
886
|
-
queryCount += 1;
|
|
887
|
-
try {
|
|
888
|
-
try {
|
|
889
|
-
const queryResult = await poolClient.query(sql, params);
|
|
890
|
-
throwIfSchedulerAborted(signal);
|
|
891
|
-
return { rows: queryResult.rows };
|
|
892
|
-
} catch (error) {
|
|
893
|
-
// node-postgres' query timeout and an AbortSignal reject the
|
|
894
|
-
// promise, but the server may still be processing the SQL.
|
|
895
|
-
// Never return that socket to the pool: doing so lets the
|
|
896
|
-
// next retry overlap the first statement on the same backend.
|
|
897
|
-
if (isRuntimeSchedulerQueryCancellationError(error)) {
|
|
898
|
-
releasePoolClient(
|
|
899
|
-
error instanceof Error ? error : new Error(String(error)),
|
|
900
|
-
);
|
|
901
|
-
}
|
|
902
|
-
throw error;
|
|
903
|
-
}
|
|
904
|
-
} finally {
|
|
905
|
-
queryMs += performance.now() - queryStartedAt;
|
|
906
|
-
}
|
|
907
|
-
},
|
|
908
|
-
});
|
|
909
|
-
throwIfSchedulerAborted(signal);
|
|
910
|
-
recordBreakerSuccess(entry);
|
|
911
|
-
signal?.removeEventListener('abort', onAbort);
|
|
912
|
-
poolClient.removeListener('error', onCheckedOutError);
|
|
913
|
-
// The connection died mid-fn but fn happened to finish without another
|
|
914
|
-
// query — destroy the dead connection rather than pooling it.
|
|
915
|
-
releasePoolClient(
|
|
916
|
-
checkedOutError instanceof Error ? checkedOutError : undefined,
|
|
819
|
+
const acquireStart = performance.now();
|
|
820
|
+
entry.stats.peakTotalClients = Math.max(
|
|
821
|
+
entry.stats.peakTotalClients,
|
|
822
|
+
entry.pool.totalCount,
|
|
917
823
|
);
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
attempt: attempt + 1,
|
|
945
|
-
priority: trace.priority ?? 'ordinary',
|
|
946
|
-
admissionWaitMs,
|
|
947
|
-
acquireWaitMs,
|
|
948
|
-
queryCount,
|
|
949
|
-
queryMs,
|
|
950
|
-
totalMs: performance.now() - operationStartedAt,
|
|
951
|
-
errorName: error instanceof Error ? error.name : 'UnknownError',
|
|
952
|
-
}),
|
|
953
|
-
);
|
|
954
|
-
}
|
|
955
|
-
signal?.removeEventListener('abort', onAbort);
|
|
956
|
-
poolClient.removeListener('error', onCheckedOutError);
|
|
957
|
-
if (signal?.aborted) {
|
|
958
|
-
const cancellation = schedulerAbortReason(signal);
|
|
959
|
-
releasePoolClient(cancellation);
|
|
960
|
-
throw cancellation;
|
|
961
|
-
}
|
|
962
|
-
if (isPgBouncerCapacityError(error)) {
|
|
963
|
-
recordCapacityRejection(entry, error);
|
|
964
|
-
releasePoolClient();
|
|
965
|
-
throw new RuntimeSchedulerPgBouncerCapacityError(
|
|
966
|
-
error instanceof Error ? error.message : String(error),
|
|
967
|
-
);
|
|
968
|
-
}
|
|
969
|
-
if (isRuntimeSchedulerQueryCancellationError(error)) {
|
|
970
|
-
entry.stats.queryErrors += 1;
|
|
971
|
-
recordBreakerSuccess(entry);
|
|
972
|
-
releasePoolClient(
|
|
973
|
-
error instanceof Error ? error : new Error(String(error)),
|
|
974
|
-
);
|
|
975
|
-
throw error;
|
|
976
|
-
}
|
|
977
|
-
if (isConnectionCategoryError(error)) {
|
|
978
|
-
recordBreakerFailure(entry, error);
|
|
824
|
+
entry.stats.peakWaitingClients = Math.max(
|
|
825
|
+
entry.stats.peakWaitingClients,
|
|
826
|
+
entry.pool.waitingCount,
|
|
827
|
+
);
|
|
828
|
+
let poolClient;
|
|
829
|
+
try {
|
|
830
|
+
poolClient = await entry.pool.connect();
|
|
831
|
+
} catch (error) {
|
|
832
|
+
if (isRuntimeSchedulerLocalAcquireTimeout(error)) {
|
|
833
|
+
entry.stats.saturationRejections += 1;
|
|
834
|
+
console.warn('[runtime-scheduler-pool.local_acquire_saturated]', {
|
|
835
|
+
maxClients: entry.maxClients,
|
|
836
|
+
totalClients: entry.pool.totalCount,
|
|
837
|
+
idleClients: entry.pool.idleCount,
|
|
838
|
+
waitingClients: entry.pool.waitingCount,
|
|
839
|
+
});
|
|
840
|
+
throw new RuntimeSchedulerSaturatedError(entry.pool.waitingCount);
|
|
841
|
+
}
|
|
842
|
+
if (isPgBouncerCapacityError(error)) {
|
|
843
|
+
recordCapacityRejection(entry, error);
|
|
844
|
+
capacityObserved = true;
|
|
845
|
+
throw new RuntimeSchedulerPgBouncerCapacityError(
|
|
846
|
+
error instanceof Error ? error.message : String(error),
|
|
847
|
+
);
|
|
848
|
+
}
|
|
849
|
+
recordBreakerFailure(entry, error, halfOpenProbe);
|
|
979
850
|
entry.stats.connectionErrors += 1;
|
|
980
851
|
lastError = error;
|
|
981
|
-
// Destroy the broken connection instead of returning it to the pool.
|
|
982
|
-
releasePoolClient(
|
|
983
|
-
error instanceof Error ? error : new Error(String(error)),
|
|
984
|
-
);
|
|
985
852
|
if (attempt + 1 < maxAttempts) {
|
|
986
853
|
entry.stats.retries += 1;
|
|
987
854
|
await sleep(jitteredBackoffMs(attempt));
|
|
@@ -990,30 +857,197 @@ export async function withRuntimeSchedulerClient<T>(
|
|
|
990
857
|
}
|
|
991
858
|
throw error;
|
|
992
859
|
}
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
entry.
|
|
860
|
+
entry.stats.acquires += 1;
|
|
861
|
+
const acquireWaitMs = performance.now() - acquireStart;
|
|
862
|
+
entry.stats.peakTotalClients = Math.max(
|
|
863
|
+
entry.stats.peakTotalClients,
|
|
864
|
+
entry.pool.totalCount,
|
|
865
|
+
);
|
|
866
|
+
pushSample(entry.stats.acquireWait, acquireWaitMs);
|
|
867
|
+
|
|
868
|
+
// A checked-out pg client emits 'error' (not via the pool) when its TCP
|
|
869
|
+
// connection dies while idle — e.g. a worker holding a client across a
|
|
870
|
+
// long play execution. With no listener that is an uncaught exception
|
|
871
|
+
// that kills the process (observed: worker crash mid-150k-row run).
|
|
872
|
+
// Swallow it here; the next query on the dead client fails normally and
|
|
873
|
+
// flows through the connection-error retry/poison path below.
|
|
874
|
+
let checkedOutError: unknown = null;
|
|
875
|
+
const onCheckedOutError = (error: Error) => {
|
|
876
|
+
checkedOutError = error;
|
|
877
|
+
entry.stats.connectionErrors += 1;
|
|
878
|
+
console.warn('[runtime-scheduler-pool.checked_out_client_error]', {
|
|
879
|
+
error: error.message,
|
|
880
|
+
});
|
|
881
|
+
};
|
|
882
|
+
poolClient.on('error', onCheckedOutError);
|
|
883
|
+
let clientReleased = false;
|
|
884
|
+
const releasePoolClient = (error?: Error) => {
|
|
885
|
+
if (clientReleased) return;
|
|
886
|
+
clientReleased = true;
|
|
887
|
+
poolClient.release(error);
|
|
888
|
+
};
|
|
889
|
+
const onAbort = () => releasePoolClient(schedulerAbortReason(signal!));
|
|
890
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
891
|
+
|
|
892
|
+
let queryCount = 0;
|
|
893
|
+
let queryMs = 0;
|
|
894
|
+
try {
|
|
895
|
+
throwIfSchedulerAborted(signal);
|
|
896
|
+
const result = await fn({
|
|
897
|
+
query: async (sql, params) => {
|
|
898
|
+
throwIfSchedulerAborted(signal);
|
|
899
|
+
const queryStartedAt = performance.now();
|
|
900
|
+
queryCount += 1;
|
|
901
|
+
try {
|
|
902
|
+
try {
|
|
903
|
+
const queryResult = await poolClient.query(sql, params);
|
|
904
|
+
throwIfSchedulerAborted(signal);
|
|
905
|
+
return { rows: queryResult.rows };
|
|
906
|
+
} catch (error) {
|
|
907
|
+
// node-postgres' query timeout and an AbortSignal reject the
|
|
908
|
+
// promise, but the server may still be processing the SQL.
|
|
909
|
+
// Never return that socket to the pool: doing so lets the
|
|
910
|
+
// next retry overlap the first statement on the same backend.
|
|
911
|
+
if (isRuntimeSchedulerQueryCancellationError(error)) {
|
|
912
|
+
releasePoolClient(
|
|
913
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
914
|
+
);
|
|
915
|
+
}
|
|
916
|
+
throw error;
|
|
917
|
+
}
|
|
918
|
+
} finally {
|
|
919
|
+
queryMs += performance.now() - queryStartedAt;
|
|
920
|
+
}
|
|
921
|
+
},
|
|
922
|
+
});
|
|
923
|
+
throwIfSchedulerAborted(signal);
|
|
998
924
|
recordBreakerSuccess(entry);
|
|
999
|
-
|
|
925
|
+
signal?.removeEventListener('abort', onAbort);
|
|
926
|
+
poolClient.removeListener('error', onCheckedOutError);
|
|
927
|
+
// The connection died mid-fn but fn happened to finish without another
|
|
928
|
+
// query — destroy the dead connection rather than pooling it.
|
|
1000
929
|
releasePoolClient(
|
|
1001
|
-
|
|
930
|
+
checkedOutError instanceof Error ? checkedOutError : undefined,
|
|
1002
931
|
);
|
|
1003
|
-
if (
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
932
|
+
if (trace) {
|
|
933
|
+
console.info(
|
|
934
|
+
'[runtime-scheduler-pool.operation] ' +
|
|
935
|
+
JSON.stringify({
|
|
936
|
+
runId: trace.runId,
|
|
937
|
+
phase: trace.phase,
|
|
938
|
+
outcome: 'success',
|
|
939
|
+
attempt: attempt + 1,
|
|
940
|
+
priority: trace.priority ?? 'ordinary',
|
|
941
|
+
admissionWaitMs,
|
|
942
|
+
acquireWaitMs,
|
|
943
|
+
queryCount,
|
|
944
|
+
queryMs,
|
|
945
|
+
totalMs: performance.now() - operationStartedAt,
|
|
946
|
+
}),
|
|
947
|
+
);
|
|
1008
948
|
}
|
|
949
|
+
return result;
|
|
950
|
+
} catch (error) {
|
|
951
|
+
if (trace) {
|
|
952
|
+
console.info(
|
|
953
|
+
'[runtime-scheduler-pool.operation] ' +
|
|
954
|
+
JSON.stringify({
|
|
955
|
+
runId: trace.runId,
|
|
956
|
+
phase: trace.phase,
|
|
957
|
+
outcome: 'failed',
|
|
958
|
+
attempt: attempt + 1,
|
|
959
|
+
priority: trace.priority ?? 'ordinary',
|
|
960
|
+
admissionWaitMs,
|
|
961
|
+
acquireWaitMs,
|
|
962
|
+
queryCount,
|
|
963
|
+
queryMs,
|
|
964
|
+
totalMs: performance.now() - operationStartedAt,
|
|
965
|
+
errorName:
|
|
966
|
+
error instanceof Error ? error.name : 'UnknownError',
|
|
967
|
+
}),
|
|
968
|
+
);
|
|
969
|
+
}
|
|
970
|
+
signal?.removeEventListener('abort', onAbort);
|
|
971
|
+
poolClient.removeListener('error', onCheckedOutError);
|
|
972
|
+
if (signal?.aborted) {
|
|
973
|
+
const cancellation = schedulerAbortReason(signal);
|
|
974
|
+
releasePoolClient(cancellation);
|
|
975
|
+
throw cancellation;
|
|
976
|
+
}
|
|
977
|
+
if (isPgBouncerCapacityError(error)) {
|
|
978
|
+
recordCapacityRejection(entry, error);
|
|
979
|
+
capacityObserved = true;
|
|
980
|
+
releasePoolClient();
|
|
981
|
+
throw new RuntimeSchedulerPgBouncerCapacityError(
|
|
982
|
+
error instanceof Error ? error.message : String(error),
|
|
983
|
+
);
|
|
984
|
+
}
|
|
985
|
+
if (isRuntimeSchedulerQueryCancellationError(error)) {
|
|
986
|
+
entry.stats.queryErrors += 1;
|
|
987
|
+
recordBreakerSuccess(entry);
|
|
988
|
+
releasePoolClient(
|
|
989
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
990
|
+
);
|
|
991
|
+
throw error;
|
|
992
|
+
}
|
|
993
|
+
if (isConnectionCategoryError(error)) {
|
|
994
|
+
recordBreakerFailure(entry, error, halfOpenProbe);
|
|
995
|
+
entry.stats.connectionErrors += 1;
|
|
996
|
+
lastError = error;
|
|
997
|
+
// Destroy the broken connection instead of returning it to the pool.
|
|
998
|
+
releasePoolClient(
|
|
999
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
1000
|
+
);
|
|
1001
|
+
if (attempt + 1 < maxAttempts) {
|
|
1002
|
+
entry.stats.retries += 1;
|
|
1003
|
+
await sleep(jitteredBackoffMs(attempt));
|
|
1004
|
+
throwIfSchedulerAborted(signal);
|
|
1005
|
+
continue;
|
|
1006
|
+
}
|
|
1007
|
+
throw error;
|
|
1008
|
+
}
|
|
1009
|
+
if (isRuntimeSchedulerRetryableTransactionError(error)) {
|
|
1010
|
+
// PostgreSQL has aborted the transaction, so never return this
|
|
1011
|
+
// client to the pool. Re-run the idempotent scheduler operation on a
|
|
1012
|
+
// new session without opening the connection-failure breaker.
|
|
1013
|
+
entry.stats.queryErrors += 1;
|
|
1014
|
+
recordBreakerSuccess(entry);
|
|
1015
|
+
lastError = error;
|
|
1016
|
+
releasePoolClient(
|
|
1017
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
1018
|
+
);
|
|
1019
|
+
if (attempt + 1 < maxAttempts) {
|
|
1020
|
+
entry.stats.retries += 1;
|
|
1021
|
+
await sleep(jitteredBackoffMs(attempt));
|
|
1022
|
+
throwIfSchedulerAborted(signal);
|
|
1023
|
+
continue;
|
|
1024
|
+
}
|
|
1025
|
+
throw error;
|
|
1026
|
+
}
|
|
1027
|
+
// Query-level failure on a healthy connection: count it, return the
|
|
1028
|
+
// connection to the pool, surface the error without retry.
|
|
1029
|
+
entry.stats.queryErrors += 1;
|
|
1030
|
+
recordBreakerSuccess(entry);
|
|
1031
|
+
releasePoolClient();
|
|
1009
1032
|
throw error;
|
|
1010
1033
|
}
|
|
1011
|
-
|
|
1012
|
-
//
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1034
|
+
} finally {
|
|
1035
|
+
// A canceled, locally saturated, or capacity-rejected trial did not
|
|
1036
|
+
// call either breaker recorder. Never let it own the probe forever.
|
|
1037
|
+
// Only the trial that claimed this token may release it: another
|
|
1038
|
+
// operation can close and reopen the breaker while this one awaits.
|
|
1039
|
+
if (
|
|
1040
|
+
halfOpenProbe !== null &&
|
|
1041
|
+
entry.breaker.halfOpenProbe === halfOpenProbe
|
|
1042
|
+
) {
|
|
1043
|
+
entry.breaker.halfOpenProbe = null;
|
|
1044
|
+
if (capacityObserved && entry.breaker.openedAt !== null) {
|
|
1045
|
+
// Capacity is not a connection failure. Keep the existing open
|
|
1046
|
+
// state and try once more after the ordinary cooldown instead of
|
|
1047
|
+
// probing the still-full database on every request.
|
|
1048
|
+
entry.breaker.openedAt = Date.now();
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1017
1051
|
}
|
|
1018
1052
|
}
|
|
1019
1053
|
throw lastError instanceof Error
|
|
@@ -239,7 +239,7 @@ function defaultThreshold(
|
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
function formatCredits(value: number): string {
|
|
242
|
-
return value.toLocaleString('en-US', { maximumFractionDigits:
|
|
242
|
+
return value.toLocaleString('en-US', { maximumFractionDigits: 3 });
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
export const DEEPLINE_DOCS_URL = 'https://deepline.com/docs';
|
package/dist/cli/index.js
CHANGED
|
@@ -3051,7 +3051,7 @@ var SDK_RELEASE = {
|
|
|
3051
3051
|
// getters keep their established compatibility behavior.
|
|
3052
3052
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
3053
3053
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
3054
|
-
version: "0.3.
|
|
3054
|
+
version: "0.3.204",
|
|
3055
3055
|
updateSummary: "Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.",
|
|
3056
3056
|
packageCapabilities: {
|
|
3057
3057
|
updatePreferences: 1
|
|
@@ -29298,7 +29298,12 @@ async function handleRunStop(args) {
|
|
|
29298
29298
|
}
|
|
29299
29299
|
const client2 = new DeeplineClient();
|
|
29300
29300
|
const result = await client2.runs.stop(runId, { reason });
|
|
29301
|
-
if (result.cancelling === true) {
|
|
29301
|
+
if (result.cancelling === true && result.hitlCancellationPending !== true) {
|
|
29302
|
+
const lines2 = [
|
|
29303
|
+
`Cancellation queued for ${result.runId || runId}; sandbox cleanup is pending`,
|
|
29304
|
+
...result.hitlCancelledCount > 0 ? [`cancelled HITL waits: ${result.hitlCancelledCount}`] : [],
|
|
29305
|
+
`inspect: deepline runs get ${runId} --full --json`
|
|
29306
|
+
];
|
|
29302
29307
|
printCommandEnvelope(
|
|
29303
29308
|
{
|
|
29304
29309
|
...result,
|
|
@@ -29306,14 +29311,7 @@ async function handleRunStop(args) {
|
|
|
29306
29311
|
sections: [
|
|
29307
29312
|
{
|
|
29308
29313
|
title: "run stop",
|
|
29309
|
-
lines:
|
|
29310
|
-
`Cancellation queued for ${result.runId || runId}; sandbox cleanup is pending`,
|
|
29311
|
-
...result.hitlCancelledCount > 0 ? [`cancelled HITL waits: ${result.hitlCancelledCount}`] : [],
|
|
29312
|
-
...result.hitlCancellationPending === true ? [
|
|
29313
|
-
"warning: HITL cancellation or same-card Slack refresh is still pending"
|
|
29314
|
-
] : [],
|
|
29315
|
-
`inspect: deepline runs get ${runId} --full --json`
|
|
29316
|
-
]
|
|
29314
|
+
lines: lines2
|
|
29317
29315
|
}
|
|
29318
29316
|
]
|
|
29319
29317
|
}
|
|
@@ -50804,8 +50802,8 @@ function readPendingToolExecution(path) {
|
|
|
50804
50802
|
}
|
|
50805
50803
|
return saved.idempotencyKey;
|
|
50806
50804
|
}
|
|
50807
|
-
async function completePendingToolExecution(path) {
|
|
50808
|
-
if (!path || !(0, import_node_fs26.existsSync)(path)) return;
|
|
50805
|
+
async function completePendingToolExecution(path, idempotencyKey) {
|
|
50806
|
+
if (!path || !idempotencyKey || !(0, import_node_fs26.existsSync)(path)) return;
|
|
50809
50807
|
await new Promise((resolve22, reject) => {
|
|
50810
50808
|
const onError = (error) => {
|
|
50811
50809
|
process.stdout.off("error", onError);
|
|
@@ -50821,7 +50819,12 @@ async function completePendingToolExecution(path) {
|
|
|
50821
50819
|
}
|
|
50822
50820
|
});
|
|
50823
50821
|
});
|
|
50824
|
-
|
|
50822
|
+
try {
|
|
50823
|
+
if (readPendingToolExecution(path) !== idempotencyKey) return;
|
|
50824
|
+
(0, import_node_fs26.unlinkSync)(path);
|
|
50825
|
+
} catch (error) {
|
|
50826
|
+
if (error.code !== "ENOENT") throw error;
|
|
50827
|
+
}
|
|
50825
50828
|
}
|
|
50826
50829
|
function safeFileStem(value) {
|
|
50827
50830
|
return value.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "tool";
|
|
@@ -51169,7 +51172,7 @@ async function executeTool(args) {
|
|
|
51169
51172
|
const executeExitCode = toolExecuteExitCode(baseEnvelope);
|
|
51170
51173
|
if (!parsed.outPath && (parsed.outputFormat === "json" || parsed.outputFormat === "auto" && shouldEmitJson())) {
|
|
51171
51174
|
printCommandEnvelope(baseEnvelope, { json: true });
|
|
51172
|
-
await completePendingToolExecution(pendingRecoveryPath);
|
|
51175
|
+
await completePendingToolExecution(pendingRecoveryPath, idempotencyKey);
|
|
51173
51176
|
return executeExitCode;
|
|
51174
51177
|
}
|
|
51175
51178
|
if (parsed.outputFormat === "json_file") {
|
|
@@ -51187,7 +51190,7 @@ async function executeTool(args) {
|
|
|
51187
51190
|
},
|
|
51188
51191
|
{ json: true }
|
|
51189
51192
|
);
|
|
51190
|
-
await completePendingToolExecution(pendingRecoveryPath);
|
|
51193
|
+
await completePendingToolExecution(pendingRecoveryPath, idempotencyKey);
|
|
51191
51194
|
return executeExitCode;
|
|
51192
51195
|
}
|
|
51193
51196
|
if (!listConversion) {
|
|
@@ -51216,11 +51219,11 @@ async function executeTool(args) {
|
|
|
51216
51219
|
},
|
|
51217
51220
|
{ json: parsed.outputFormat === "csv_file" || shouldEmitJson() }
|
|
51218
51221
|
);
|
|
51219
|
-
await completePendingToolExecution(pendingRecoveryPath);
|
|
51222
|
+
await completePendingToolExecution(pendingRecoveryPath, idempotencyKey);
|
|
51220
51223
|
return executeExitCode;
|
|
51221
51224
|
}
|
|
51222
51225
|
printCommandEnvelope(baseEnvelope, { json: false });
|
|
51223
|
-
await completePendingToolExecution(pendingRecoveryPath);
|
|
51226
|
+
await completePendingToolExecution(pendingRecoveryPath, idempotencyKey);
|
|
51224
51227
|
return executeExitCode;
|
|
51225
51228
|
}
|
|
51226
51229
|
const rowOutput = projectRowOutput(listConversion);
|
|
@@ -51243,7 +51246,7 @@ async function executeTool(args) {
|
|
|
51243
51246
|
});
|
|
51244
51247
|
if (parsed.outputFormat === "csv_file") {
|
|
51245
51248
|
printCommandEnvelope(materializedEnvelope, { json: true });
|
|
51246
|
-
await completePendingToolExecution(pendingRecoveryPath);
|
|
51249
|
+
await completePendingToolExecution(pendingRecoveryPath, idempotencyKey);
|
|
51247
51250
|
return executeExitCode;
|
|
51248
51251
|
}
|
|
51249
51252
|
if (parsed.outPath) {
|
|
@@ -51252,7 +51255,7 @@ async function executeTool(args) {
|
|
|
51252
51255
|
text: `Wrote ${csv.rowCount} row(s) to ${csv.path}
|
|
51253
51256
|
`
|
|
51254
51257
|
});
|
|
51255
|
-
await completePendingToolExecution(pendingRecoveryPath);
|
|
51258
|
+
await completePendingToolExecution(pendingRecoveryPath, idempotencyKey);
|
|
51256
51259
|
return executeExitCode;
|
|
51257
51260
|
}
|
|
51258
51261
|
if (parsed.noPreview) {
|
|
@@ -51267,11 +51270,11 @@ async function executeTool(args) {
|
|
|
51267
51270
|
{ json: shouldEmitJson(), text: `${csv.path}
|
|
51268
51271
|
` }
|
|
51269
51272
|
);
|
|
51270
|
-
await completePendingToolExecution(pendingRecoveryPath);
|
|
51273
|
+
await completePendingToolExecution(pendingRecoveryPath, idempotencyKey);
|
|
51271
51274
|
return executeExitCode;
|
|
51272
51275
|
}
|
|
51273
51276
|
printCommandEnvelope(materializedEnvelope, { json: false });
|
|
51274
|
-
await completePendingToolExecution(pendingRecoveryPath);
|
|
51277
|
+
await completePendingToolExecution(pendingRecoveryPath, idempotencyKey);
|
|
51275
51278
|
return executeExitCode;
|
|
51276
51279
|
}
|
|
51277
51280
|
|
package/dist/cli/index.mjs
CHANGED
|
@@ -3046,7 +3046,7 @@ var SDK_RELEASE = {
|
|
|
3046
3046
|
// getters keep their established compatibility behavior.
|
|
3047
3047
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
3048
3048
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
3049
|
-
version: "0.3.
|
|
3049
|
+
version: "0.3.204",
|
|
3050
3050
|
updateSummary: "Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.",
|
|
3051
3051
|
packageCapabilities: {
|
|
3052
3052
|
updatePreferences: 1
|
|
@@ -29371,7 +29371,12 @@ async function handleRunStop(args) {
|
|
|
29371
29371
|
}
|
|
29372
29372
|
const client2 = new DeeplineClient();
|
|
29373
29373
|
const result = await client2.runs.stop(runId, { reason });
|
|
29374
|
-
if (result.cancelling === true) {
|
|
29374
|
+
if (result.cancelling === true && result.hitlCancellationPending !== true) {
|
|
29375
|
+
const lines2 = [
|
|
29376
|
+
`Cancellation queued for ${result.runId || runId}; sandbox cleanup is pending`,
|
|
29377
|
+
...result.hitlCancelledCount > 0 ? [`cancelled HITL waits: ${result.hitlCancelledCount}`] : [],
|
|
29378
|
+
`inspect: deepline runs get ${runId} --full --json`
|
|
29379
|
+
];
|
|
29375
29380
|
printCommandEnvelope(
|
|
29376
29381
|
{
|
|
29377
29382
|
...result,
|
|
@@ -29379,14 +29384,7 @@ async function handleRunStop(args) {
|
|
|
29379
29384
|
sections: [
|
|
29380
29385
|
{
|
|
29381
29386
|
title: "run stop",
|
|
29382
|
-
lines:
|
|
29383
|
-
`Cancellation queued for ${result.runId || runId}; sandbox cleanup is pending`,
|
|
29384
|
-
...result.hitlCancelledCount > 0 ? [`cancelled HITL waits: ${result.hitlCancelledCount}`] : [],
|
|
29385
|
-
...result.hitlCancellationPending === true ? [
|
|
29386
|
-
"warning: HITL cancellation or same-card Slack refresh is still pending"
|
|
29387
|
-
] : [],
|
|
29388
|
-
`inspect: deepline runs get ${runId} --full --json`
|
|
29389
|
-
]
|
|
29387
|
+
lines: lines2
|
|
29390
29388
|
}
|
|
29391
29389
|
]
|
|
29392
29390
|
}
|
|
@@ -50952,8 +50950,8 @@ function readPendingToolExecution(path) {
|
|
|
50952
50950
|
}
|
|
50953
50951
|
return saved.idempotencyKey;
|
|
50954
50952
|
}
|
|
50955
|
-
async function completePendingToolExecution(path) {
|
|
50956
|
-
if (!path || !existsSync19(path)) return;
|
|
50953
|
+
async function completePendingToolExecution(path, idempotencyKey) {
|
|
50954
|
+
if (!path || !idempotencyKey || !existsSync19(path)) return;
|
|
50957
50955
|
await new Promise((resolve22, reject) => {
|
|
50958
50956
|
const onError = (error) => {
|
|
50959
50957
|
process.stdout.off("error", onError);
|
|
@@ -50969,7 +50967,12 @@ async function completePendingToolExecution(path) {
|
|
|
50969
50967
|
}
|
|
50970
50968
|
});
|
|
50971
50969
|
});
|
|
50972
|
-
|
|
50970
|
+
try {
|
|
50971
|
+
if (readPendingToolExecution(path) !== idempotencyKey) return;
|
|
50972
|
+
unlinkSync4(path);
|
|
50973
|
+
} catch (error) {
|
|
50974
|
+
if (error.code !== "ENOENT") throw error;
|
|
50975
|
+
}
|
|
50973
50976
|
}
|
|
50974
50977
|
function safeFileStem(value) {
|
|
50975
50978
|
return value.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "tool";
|
|
@@ -51317,7 +51320,7 @@ async function executeTool(args) {
|
|
|
51317
51320
|
const executeExitCode = toolExecuteExitCode(baseEnvelope);
|
|
51318
51321
|
if (!parsed.outPath && (parsed.outputFormat === "json" || parsed.outputFormat === "auto" && shouldEmitJson())) {
|
|
51319
51322
|
printCommandEnvelope(baseEnvelope, { json: true });
|
|
51320
|
-
await completePendingToolExecution(pendingRecoveryPath);
|
|
51323
|
+
await completePendingToolExecution(pendingRecoveryPath, idempotencyKey);
|
|
51321
51324
|
return executeExitCode;
|
|
51322
51325
|
}
|
|
51323
51326
|
if (parsed.outputFormat === "json_file") {
|
|
@@ -51335,7 +51338,7 @@ async function executeTool(args) {
|
|
|
51335
51338
|
},
|
|
51336
51339
|
{ json: true }
|
|
51337
51340
|
);
|
|
51338
|
-
await completePendingToolExecution(pendingRecoveryPath);
|
|
51341
|
+
await completePendingToolExecution(pendingRecoveryPath, idempotencyKey);
|
|
51339
51342
|
return executeExitCode;
|
|
51340
51343
|
}
|
|
51341
51344
|
if (!listConversion) {
|
|
@@ -51364,11 +51367,11 @@ async function executeTool(args) {
|
|
|
51364
51367
|
},
|
|
51365
51368
|
{ json: parsed.outputFormat === "csv_file" || shouldEmitJson() }
|
|
51366
51369
|
);
|
|
51367
|
-
await completePendingToolExecution(pendingRecoveryPath);
|
|
51370
|
+
await completePendingToolExecution(pendingRecoveryPath, idempotencyKey);
|
|
51368
51371
|
return executeExitCode;
|
|
51369
51372
|
}
|
|
51370
51373
|
printCommandEnvelope(baseEnvelope, { json: false });
|
|
51371
|
-
await completePendingToolExecution(pendingRecoveryPath);
|
|
51374
|
+
await completePendingToolExecution(pendingRecoveryPath, idempotencyKey);
|
|
51372
51375
|
return executeExitCode;
|
|
51373
51376
|
}
|
|
51374
51377
|
const rowOutput = projectRowOutput(listConversion);
|
|
@@ -51391,7 +51394,7 @@ async function executeTool(args) {
|
|
|
51391
51394
|
});
|
|
51392
51395
|
if (parsed.outputFormat === "csv_file") {
|
|
51393
51396
|
printCommandEnvelope(materializedEnvelope, { json: true });
|
|
51394
|
-
await completePendingToolExecution(pendingRecoveryPath);
|
|
51397
|
+
await completePendingToolExecution(pendingRecoveryPath, idempotencyKey);
|
|
51395
51398
|
return executeExitCode;
|
|
51396
51399
|
}
|
|
51397
51400
|
if (parsed.outPath) {
|
|
@@ -51400,7 +51403,7 @@ async function executeTool(args) {
|
|
|
51400
51403
|
text: `Wrote ${csv.rowCount} row(s) to ${csv.path}
|
|
51401
51404
|
`
|
|
51402
51405
|
});
|
|
51403
|
-
await completePendingToolExecution(pendingRecoveryPath);
|
|
51406
|
+
await completePendingToolExecution(pendingRecoveryPath, idempotencyKey);
|
|
51404
51407
|
return executeExitCode;
|
|
51405
51408
|
}
|
|
51406
51409
|
if (parsed.noPreview) {
|
|
@@ -51415,11 +51418,11 @@ async function executeTool(args) {
|
|
|
51415
51418
|
{ json: shouldEmitJson(), text: `${csv.path}
|
|
51416
51419
|
` }
|
|
51417
51420
|
);
|
|
51418
|
-
await completePendingToolExecution(pendingRecoveryPath);
|
|
51421
|
+
await completePendingToolExecution(pendingRecoveryPath, idempotencyKey);
|
|
51419
51422
|
return executeExitCode;
|
|
51420
51423
|
}
|
|
51421
51424
|
printCommandEnvelope(materializedEnvelope, { json: false });
|
|
51422
|
-
await completePendingToolExecution(pendingRecoveryPath);
|
|
51425
|
+
await completePendingToolExecution(pendingRecoveryPath, idempotencyKey);
|
|
51423
51426
|
return executeExitCode;
|
|
51424
51427
|
}
|
|
51425
51428
|
|
package/dist/index.js
CHANGED
|
@@ -864,7 +864,7 @@ var SDK_RELEASE = {
|
|
|
864
864
|
// getters keep their established compatibility behavior.
|
|
865
865
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
866
866
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
867
|
-
version: "0.3.
|
|
867
|
+
version: "0.3.204",
|
|
868
868
|
updateSummary: "Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.",
|
|
869
869
|
packageCapabilities: {
|
|
870
870
|
updatePreferences: 1
|
package/dist/index.mjs
CHANGED
|
@@ -768,7 +768,7 @@ var SDK_RELEASE = {
|
|
|
768
768
|
// getters keep their established compatibility behavior.
|
|
769
769
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
770
770
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
771
|
-
version: "0.3.
|
|
771
|
+
version: "0.3.204",
|
|
772
772
|
updateSummary: "Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.",
|
|
773
773
|
packageCapabilities: {
|
|
774
774
|
updatePreferences: 1
|
package/dist/release.d.mts
CHANGED
|
@@ -149,7 +149,7 @@ type SdkRelease = {
|
|
|
149
149
|
supportPolicy: SdkSupportPolicy;
|
|
150
150
|
};
|
|
151
151
|
declare const SDK_RELEASE: {
|
|
152
|
-
readonly version: "0.3.
|
|
152
|
+
readonly version: "0.3.204";
|
|
153
153
|
readonly updateSummary: "Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.";
|
|
154
154
|
readonly packageCapabilities: {
|
|
155
155
|
readonly updatePreferences: 1;
|
package/dist/release.d.ts
CHANGED
|
@@ -149,7 +149,7 @@ type SdkRelease = {
|
|
|
149
149
|
supportPolicy: SdkSupportPolicy;
|
|
150
150
|
};
|
|
151
151
|
declare const SDK_RELEASE: {
|
|
152
|
-
readonly version: "0.3.
|
|
152
|
+
readonly version: "0.3.204";
|
|
153
153
|
readonly updateSummary: "Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.";
|
|
154
154
|
readonly packageCapabilities: {
|
|
155
155
|
readonly updatePreferences: 1;
|
package/dist/release.js
CHANGED
|
@@ -74,7 +74,7 @@ var SDK_RELEASE = {
|
|
|
74
74
|
// getters keep their established compatibility behavior.
|
|
75
75
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
76
76
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
77
|
-
version: "0.3.
|
|
77
|
+
version: "0.3.204",
|
|
78
78
|
updateSummary: "Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.",
|
|
79
79
|
packageCapabilities: {
|
|
80
80
|
updatePreferences: 1
|
package/dist/release.mjs
CHANGED
|
@@ -48,7 +48,7 @@ var SDK_RELEASE = {
|
|
|
48
48
|
// getters keep their established compatibility behavior.
|
|
49
49
|
// 0.3.1 deprecated the legacy `deepline enrich` command in favor of Plays.
|
|
50
50
|
// 0.3.90 is the first deliberately versioned SDK release for API v3.
|
|
51
|
-
version: "0.3.
|
|
51
|
+
version: "0.3.204",
|
|
52
52
|
updateSummary: "Play grep now returns concise results with the matched fields and terms. Automatic CLI updates are enabled by default; use `deepline settings autoupdate off` to opt out, `deepline settings autoupdate on` to re-enable updates, or `deepline settings autoupdate pin <version>` to hold an exact release.",
|
|
53
53
|
packageCapabilities: {
|
|
54
54
|
updatePreferences: 1
|