deepline 0.3.222 → 0.3.224
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/context.ts +26 -2
- package/dist/bundling-sources/shared_libs/play-runtime/ctx-types.ts +6 -1
- package/dist/bundling-sources/shared_libs/play-runtime/run-ledger-event-transport.ts +24 -0
- package/dist/bundling-sources/shared_libs/play-runtime/scheduler-backends/postgres.ts +16 -15
- package/dist/bundling-sources/shared_libs/product-notifications/slack-delivery.ts +19 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.mjs +1 -1
- 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.224',
|
|
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: {
|
|
@@ -1721,6 +1721,14 @@ function toolAttemptOutcomeForResult(
|
|
|
1721
1721
|
return record.status === 'no_result' ? 'no_result' : 'completed';
|
|
1722
1722
|
}
|
|
1723
1723
|
|
|
1724
|
+
function isControlledPlaySuspension(error: unknown): boolean {
|
|
1725
|
+
return (
|
|
1726
|
+
isPlayExecutionSuspendedError(error) ||
|
|
1727
|
+
isPlayRowExecutionSuspendedError(error) ||
|
|
1728
|
+
error instanceof PlayRowProviderBackpressureError
|
|
1729
|
+
);
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1724
1732
|
/**
|
|
1725
1733
|
* Explicit execution location for a queued row-scoped tool call (ADR 0018).
|
|
1726
1734
|
* `toolIdOverride` lets a native batch report the batch operation it actually
|
|
@@ -3614,6 +3622,9 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
3614
3622
|
});
|
|
3615
3623
|
};
|
|
3616
3624
|
const fail = (error: unknown): void => {
|
|
3625
|
+
// Durable waits unwind the authored node so the scheduler can resume it.
|
|
3626
|
+
// Keep its started observation; the resumed invocation will settle it.
|
|
3627
|
+
if (isControlledPlaySuspension(error)) return;
|
|
3617
3628
|
const errorPreview = buildPlayDocflowNodeErrorPreview(
|
|
3618
3629
|
error,
|
|
3619
3630
|
this.secretRedactor,
|
|
@@ -13476,7 +13487,13 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13476
13487
|
}
|
|
13477
13488
|
return this.materializeToolExecutionResult(wrapped);
|
|
13478
13489
|
} catch (error) {
|
|
13479
|
-
|
|
13490
|
+
if (isControlledPlaySuspension(error)) {
|
|
13491
|
+
// A durable pause is not a terminal tool failure. Leave the start
|
|
13492
|
+
// event open so the resumed attempt can settle the same node.
|
|
13493
|
+
directSettled = true;
|
|
13494
|
+
} else {
|
|
13495
|
+
settleDirectCall('failed', error);
|
|
13496
|
+
}
|
|
13480
13497
|
throw error;
|
|
13481
13498
|
} finally {
|
|
13482
13499
|
settleDirectCall('failed');
|
|
@@ -13541,8 +13558,14 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13541
13558
|
};
|
|
13542
13559
|
const settleProducerAttempt = (
|
|
13543
13560
|
outcome: PlayCellProducerAttempt['outcome'],
|
|
13561
|
+
waiting = false,
|
|
13544
13562
|
) => {
|
|
13545
13563
|
producerAttempt.outcome = outcome;
|
|
13564
|
+
if (waiting) {
|
|
13565
|
+
producerAttempt.waiting = true;
|
|
13566
|
+
} else {
|
|
13567
|
+
delete producerAttempt.waiting;
|
|
13568
|
+
}
|
|
13546
13569
|
producerAttempt.durationMs = Date.now() - producerAttemptStartedAt;
|
|
13547
13570
|
};
|
|
13548
13571
|
// Everything the resolver did since its previous result landed — user
|
|
@@ -13666,7 +13689,8 @@ export class PlayContextImpl implements ScalarPlayAuthoringRuntimeContext {
|
|
|
13666
13689
|
observeLogicalCallWall();
|
|
13667
13690
|
observeResultDelivery();
|
|
13668
13691
|
this.openRowResolverGap(resolverGap);
|
|
13669
|
-
|
|
13692
|
+
const waiting = isControlledPlaySuspension(error);
|
|
13693
|
+
settleProducerAttempt(waiting ? 'running' : 'failed', waiting);
|
|
13670
13694
|
throw error;
|
|
13671
13695
|
},
|
|
13672
13696
|
);
|
|
@@ -398,7 +398,10 @@ export type RuntimeDatasetRunOptions<TItem = Record<string, unknown>> = Pick<
|
|
|
398
398
|
'description' | 'key' | 'onRowError' | 'mode' | 'undrawnColumns'
|
|
399
399
|
>;
|
|
400
400
|
|
|
401
|
-
|
|
401
|
+
/** Runtime-only receipt recovery bit; never part of the customer authoring API. */
|
|
402
|
+
export type ToolCallOptions = PlayToolCallOptions & {
|
|
403
|
+
forceFailedRefresh?: boolean;
|
|
404
|
+
};
|
|
402
405
|
export type ToolExecutionRequest = PlayToolExecutionRequest;
|
|
403
406
|
|
|
404
407
|
export type PlayCallOptions = Omit<
|
|
@@ -1491,6 +1494,8 @@ export interface PlayCellProducerAttempt {
|
|
|
1491
1494
|
/** Attempt start, epoch ms. */
|
|
1492
1495
|
at: number;
|
|
1493
1496
|
outcome: 'running' | 'completed' | 'no_result' | 'failed' | 'cached';
|
|
1497
|
+
/** True when the in-flight call yielded to a durable runtime wait. */
|
|
1498
|
+
waiting?: boolean;
|
|
1494
1499
|
durationMs?: number | null;
|
|
1495
1500
|
}
|
|
1496
1501
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PlayRunLedgerIngressEvent } from './run-ledger';
|
|
2
|
+
import type { RunLedgerProjectionChunk } from './run-ledger-projection-contract';
|
|
2
3
|
|
|
3
4
|
const APP_RUNTIME_LOG_EVENT_MAX_JSON_BYTES = 128 * 1024;
|
|
4
5
|
const APP_RUNTIME_EVENT_BATCH_MAX_JSON_BYTES = 512 * 1024;
|
|
@@ -108,3 +109,26 @@ export function partitionRunEventsForAppRuntime(
|
|
|
108
109
|
if (current.length > 0) batches.push(current);
|
|
109
110
|
return batches;
|
|
110
111
|
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Select a projection chunk from the raw canonical prefix. Callers must apply
|
|
115
|
+
* route sanitization only after this boundary is selected, because sanitizing
|
|
116
|
+
* event payloads can change their encoded sizes and shift transport chunks.
|
|
117
|
+
*/
|
|
118
|
+
export function selectRunEventsForAppRuntimeProjectionChunk(
|
|
119
|
+
events: readonly PlayRunLedgerIngressEvent[],
|
|
120
|
+
chunk: RunLedgerProjectionChunk,
|
|
121
|
+
): PlayRunLedgerIngressEvent[] | undefined {
|
|
122
|
+
if (
|
|
123
|
+
!Number.isSafeInteger(chunk.index) ||
|
|
124
|
+
!Number.isSafeInteger(chunk.count) ||
|
|
125
|
+
chunk.index < 0 ||
|
|
126
|
+
chunk.count < 2 ||
|
|
127
|
+
chunk.index >= chunk.count
|
|
128
|
+
) {
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const batches = partitionRunEventsForAppRuntime(events);
|
|
133
|
+
return batches.length === chunk.count ? batches[chunk.index] : undefined;
|
|
134
|
+
}
|
|
@@ -101,7 +101,7 @@ import {
|
|
|
101
101
|
buildRunLedgerStartIdempotencyKey,
|
|
102
102
|
buildRunLedgerTerminalIdempotencyKey,
|
|
103
103
|
} from '../run-ledger-projection-contract';
|
|
104
|
-
import {
|
|
104
|
+
import { selectRunEventsForAppRuntimeProjectionChunk } from '../run-ledger-event-transport';
|
|
105
105
|
import {
|
|
106
106
|
assertTerminalRunResultWithinLimit,
|
|
107
107
|
isLedgerTerminalResultRef,
|
|
@@ -16030,7 +16030,7 @@ export async function assertPostgresRunLedgerDelivery(
|
|
|
16030
16030
|
leaseOwner?: string;
|
|
16031
16031
|
projectedEvents?: readonly AcceptedPlayRunLedgerEvent[];
|
|
16032
16032
|
projectionChunk?: RunLedgerProjectionChunk;
|
|
16033
|
-
|
|
16033
|
+
sanitizeExpectedProjectionEvents?: (
|
|
16034
16034
|
events: readonly PlayRunLedgerIngressEvent[],
|
|
16035
16035
|
) => readonly AcceptedPlayRunLedgerEvent[];
|
|
16036
16036
|
},
|
|
@@ -16163,24 +16163,25 @@ export async function assertPostgresRunLedgerDelivery(
|
|
|
16163
16163
|
}),
|
|
16164
16164
|
);
|
|
16165
16165
|
}
|
|
16166
|
-
const comparablePrefixEvents: readonly (
|
|
16167
|
-
| AcceptedPlayRunLedgerEvent
|
|
16168
|
-
| PlayRunLedgerIngressEvent
|
|
16169
|
-
)[] = input.normalizeProjectedEvents
|
|
16170
|
-
? input.normalizeProjectedEvents(prefixEvents)
|
|
16171
|
-
: prefixEvents;
|
|
16172
16166
|
let expectedProjectionEvents:
|
|
16173
16167
|
| readonly (AcceptedPlayRunLedgerEvent | PlayRunLedgerIngressEvent)[]
|
|
16174
|
-
| undefined
|
|
16168
|
+
| undefined;
|
|
16175
16169
|
if (input.projectionChunk) {
|
|
16176
16170
|
const chunk = input.projectionChunk;
|
|
16177
|
-
|
|
16178
|
-
|
|
16179
|
-
|
|
16180
|
-
|
|
16181
|
-
: [];
|
|
16171
|
+
// The sender partitions ingress events before the route sanitizes each chunk.
|
|
16172
|
+
const expectedTransportChunk = isRunLedgerProjectionChunk(chunk)
|
|
16173
|
+
? selectRunEventsForAppRuntimeProjectionChunk(prefixEvents, chunk)
|
|
16174
|
+
: undefined;
|
|
16182
16175
|
expectedProjectionEvents =
|
|
16183
|
-
|
|
16176
|
+
expectedTransportChunk === undefined
|
|
16177
|
+
? undefined
|
|
16178
|
+
: input.sanitizeExpectedProjectionEvents
|
|
16179
|
+
? input.sanitizeExpectedProjectionEvents(expectedTransportChunk)
|
|
16180
|
+
: expectedTransportChunk;
|
|
16181
|
+
} else {
|
|
16182
|
+
expectedProjectionEvents = input.sanitizeExpectedProjectionEvents
|
|
16183
|
+
? input.sanitizeExpectedProjectionEvents(prefixEvents)
|
|
16184
|
+
: prefixEvents;
|
|
16184
16185
|
}
|
|
16185
16186
|
if (
|
|
16186
16187
|
expectedProjectionEvents !== undefined &&
|
|
@@ -25,7 +25,8 @@ export type SlackChannelKey =
|
|
|
25
25
|
| 'python_cli_session'
|
|
26
26
|
| 'monitor_activity'
|
|
27
27
|
| 'monitor_error_activity'
|
|
28
|
-
| 'plain_thread_activity'
|
|
28
|
+
| 'plain_thread_activity'
|
|
29
|
+
| 'inbound_leads';
|
|
29
30
|
|
|
30
31
|
export type SlackPayloadEnricher = (
|
|
31
32
|
payload: SlackPayload,
|
|
@@ -57,6 +58,10 @@ const CHANNEL_WEBHOOK_ENV: Partial<Record<SlackChannelKey, string>> = {
|
|
|
57
58
|
monitor_activity: 'SLACK_MONITOR_ACTIVITY_WEBHOOK_URL',
|
|
58
59
|
monitor_error_activity: 'SLACK_MONITOR_ERROR_ACTIVITY_WEBHOOK_URL',
|
|
59
60
|
plain_thread_activity: 'SLACK_PLAIN_THREAD_ACTIVITY_WEBHOOK_URL',
|
|
61
|
+
// Inbound demo requests are delivered by the shared reporting bot (see
|
|
62
|
+
// resolveBotChannel). This webhook is only a fallback for a deployment that
|
|
63
|
+
// has no bot token.
|
|
64
|
+
inbound_leads: 'SLACK_INBOUND_LEADS_WEBHOOK_URL',
|
|
60
65
|
};
|
|
61
66
|
|
|
62
67
|
const CHANNEL_WEBHOOK_FALLBACK_ENVS: Partial<
|
|
@@ -70,6 +75,9 @@ const SLACK_DEEPLINE_REPORTING_BOT_TOKEN_ENV =
|
|
|
70
75
|
const SLACK_PROVIDER_ALERTS_CHANNEL_ID_ENV = 'SLACK_PROVIDER_ALERTS_CHANNEL_ID';
|
|
71
76
|
const SLACK_ERROR_ACTIVITY_CHANNEL_ID_ENV = 'SLACK_ERROR_ACTIVITY_CHANNEL_ID';
|
|
72
77
|
const SLACK_USER_SIGNUP_CHANNEL_ID_ENV = 'SLACK_USER_SIGNUP_CHANNEL_ID';
|
|
78
|
+
const SLACK_INBOUND_LEADS_CHANNEL_ID_ENV = 'SLACK_INBOUND_LEADS_CHANNEL_ID';
|
|
79
|
+
// #alerts-gtm. Inbound demo requests go where GTM already watches for leads.
|
|
80
|
+
const DEFAULT_INBOUND_LEADS_CHANNEL_ID = 'C07KT4HB24U';
|
|
73
81
|
const SLACK_MONITOR_ACTIVITY_CHANNEL_ID_ENV =
|
|
74
82
|
'SLACK_MONITOR_ACTIVITY_CHANNEL_ID';
|
|
75
83
|
const SLACK_MONITOR_ERROR_ACTIVITY_CHANNEL_ID_ENV =
|
|
@@ -117,6 +125,7 @@ const CHANNEL_ALERT_LEVEL: Record<
|
|
|
117
125
|
monitor_activity: 'info',
|
|
118
126
|
monitor_error_activity: 'error',
|
|
119
127
|
plain_thread_activity: 'info',
|
|
128
|
+
inbound_leads: 'info',
|
|
120
129
|
};
|
|
121
130
|
|
|
122
131
|
function normalizeText(value: unknown): string {
|
|
@@ -135,6 +144,8 @@ function getSlackChannelEnvironmentRequirement(
|
|
|
135
144
|
case 'user_signup':
|
|
136
145
|
case 'sdk_cli_activity':
|
|
137
146
|
case 'sdk_cli_errors':
|
|
147
|
+
// Preview and local deploys must never page GTM with test submissions.
|
|
148
|
+
case 'inbound_leads':
|
|
138
149
|
return 'prod';
|
|
139
150
|
case 'session_activity':
|
|
140
151
|
case 'error_activity':
|
|
@@ -242,6 +253,13 @@ function resolveBotChannel(
|
|
|
242
253
|
process.env[SLACK_MONITOR_ERROR_ACTIVITY_CHANNEL_ID_ENV]?.trim() ||
|
|
243
254
|
DEFAULT_MONITOR_ERROR_ACTIVITY_CHANNEL_ID,
|
|
244
255
|
};
|
|
256
|
+
case 'inbound_leads':
|
|
257
|
+
return {
|
|
258
|
+
token,
|
|
259
|
+
channelId:
|
|
260
|
+
process.env[SLACK_INBOUND_LEADS_CHANNEL_ID_ENV]?.trim() ||
|
|
261
|
+
DEFAULT_INBOUND_LEADS_CHANNEL_ID,
|
|
262
|
+
};
|
|
245
263
|
case 'sdk_cli_activity':
|
|
246
264
|
return { token, channelId: SDK_CLI_ACTIVITY_SLACK_CHANNEL_ID };
|
|
247
265
|
case 'sdk_cli_errors':
|
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.224",
|
|
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
|
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.224",
|
|
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
|
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.224",
|
|
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.224",
|
|
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.224";
|
|
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.224";
|
|
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.224",
|
|
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.224",
|
|
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
|