@verentis/sdk 0.2.6 → 1.0.1

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/index.cjs CHANGED
@@ -261,6 +261,7 @@ var ContextModule = class {
261
261
  };
262
262
 
263
263
  // src/modules/executions.ts
264
+ var SCRIPT_ARGUMENTS_SCHEMA = "verentis.io/script-arguments/v1";
264
265
  var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
265
266
  "Completed",
266
267
  "Failed",
@@ -291,7 +292,8 @@ var ExecutionsModule = class {
291
292
  tool: options?.tool,
292
293
  mode: options?.mode,
293
294
  arguments: options?.arguments ?? [],
294
- parameters: serializeParameters(options?.parameters)
295
+ parameters: serializeParameters(options?.parameters),
296
+ ...options?.invocationKey ? { invocationKey: options.invocationKey } : {}
295
297
  }),
296
298
  headers: { "Content-Type": "application/json" }
297
299
  });
@@ -316,10 +318,30 @@ var ExecutionsModule = class {
316
318
  headers: { "Content-Type": "application/json" }
317
319
  });
318
320
  }
321
+ /**
322
+ * Read the arguments a script declares (its exported `<script>.arguments.json`), e.g. to build a form
323
+ * before {@link run}. Returns `null` for scripts that declare none. Reading never runs the script.
324
+ */
325
+ async getArguments(filePath, options) {
326
+ const path = `${filePath}.arguments.json`.replace(/\/+/g, "/").replace(/^\//, "");
327
+ const response = await this.http.requestRaw("GET", `/files/raw/${path}`, {
328
+ params: { branch: options?.branch ?? "main" },
329
+ headers: { ...this.workspaceId ? { "X-Workspace-Id": this.workspaceId } : {}, Accept: "application/json" }
330
+ });
331
+ if (response.status === 404) return null;
332
+ if (!response.ok) {
333
+ throw new Error(`Failed to read script arguments: ${response.status} ${response.statusText}`);
334
+ }
335
+ const descriptor = await response.json();
336
+ if (descriptor?.schema !== SCRIPT_ARGUMENTS_SCHEMA || !Array.isArray(descriptor.arguments)) {
337
+ throw new Error(`Unsupported script argument descriptor for ${filePath}`);
338
+ }
339
+ return descriptor;
340
+ }
319
341
  /** Get an execution by id. */
320
- async get(id, options) {
342
+ async get(id2, options) {
321
343
  const workspaceId = this.requireWorkspaceId(options?.workspaceId);
322
- return this.http.request("GET", `/executions/${encodeURIComponent(id)}`, {
344
+ return this.http.request("GET", `/executions/${encodeURIComponent(id2)}`, {
323
345
  params: { workspaceId }
324
346
  });
325
347
  }
@@ -344,17 +366,17 @@ var ExecutionsModule = class {
344
366
  });
345
367
  }
346
368
  /** Get the captured stdout/stderr of an execution. */
347
- async logs(id, options) {
369
+ async logs(id2, options) {
348
370
  const workspaceId = this.requireWorkspaceId(options?.workspaceId);
349
- return this.http.request("GET", `/executions/${encodeURIComponent(id)}/logs`, {
371
+ return this.http.request("GET", `/executions/${encodeURIComponent(id2)}/logs`, {
350
372
  params: { workspaceId }
351
373
  });
352
374
  }
353
375
  /** Cancel a pending or running execution. */
354
- async cancel(id, options) {
376
+ async cancel(id2, options) {
355
377
  const workspaceId = this.requireWorkspaceId(options?.workspaceId);
356
- return this.http.request("POST", `/executions/${encodeURIComponent(id)}/cancel`, {
357
- body: JSON.stringify({ id, workspaceId }),
378
+ return this.http.request("POST", `/executions/${encodeURIComponent(id2)}/cancel`, {
379
+ body: JSON.stringify({ id: id2, workspaceId }),
358
380
  headers: { "Content-Type": "application/json" }
359
381
  });
360
382
  }
@@ -362,16 +384,16 @@ var ExecutionsModule = class {
362
384
  * Poll an execution until it reaches a terminal status. Returns immediately if it is already
363
385
  * terminal. Throws if `timeoutMs` elapses or the supplied signal aborts.
364
386
  */
365
- async wait(id, options) {
387
+ async wait(id2, options) {
366
388
  const interval = options?.intervalMs ?? 1e3;
367
389
  const deadline = options?.timeoutMs ? Date.now() + options.timeoutMs : void 0;
368
390
  for (; ; ) {
369
- const execution = await this.get(id, { workspaceId: options?.workspaceId });
391
+ const execution = await this.get(id2, { workspaceId: options?.workspaceId });
370
392
  if (isTerminalStatus(execution.status)) {
371
393
  return execution;
372
394
  }
373
395
  if (deadline !== void 0 && Date.now() >= deadline) {
374
- throw new Error(`Timed out waiting for execution '${id}' to complete`);
396
+ throw new Error(`Timed out waiting for execution '${id2}' to complete`);
375
397
  }
376
398
  await delay(interval, options?.signal);
377
399
  }
@@ -388,11 +410,11 @@ var ExecutionsModule = class {
388
410
  return this.wait(execution.id, options);
389
411
  }
390
412
  requireWorkspaceId(override) {
391
- const id = override ?? this.workspaceId;
392
- if (!id) {
413
+ const id2 = override ?? this.workspaceId;
414
+ if (!id2) {
393
415
  throw new Error("workspaceId is required: provide it in options or when creating the client");
394
416
  }
395
- return id;
417
+ return id2;
396
418
  }
397
419
  };
398
420
  function serializeParameters(parameters) {
@@ -433,8 +455,8 @@ var EnginesModule = class {
433
455
  }
434
456
  });
435
457
  }
436
- async get(id, options) {
437
- return this.http.request("GET", `/engines/${encodeURIComponent(id)}`, {
458
+ async get(id2, options) {
459
+ return this.http.request("GET", `/engines/${encodeURIComponent(id2)}`, {
438
460
  params: { workspaceId: this.requireWorkspaceId(options?.workspaceId) }
439
461
  });
440
462
  }
@@ -655,11 +677,11 @@ var FilesModule = class {
655
677
  });
656
678
  }
657
679
  workspaceHeaders() {
658
- const headers = {};
680
+ const headers2 = {};
659
681
  if (this.workspaceId) {
660
- headers["X-Workspace-Id"] = this.workspaceId;
682
+ headers2["X-Workspace-Id"] = this.workspaceId;
661
683
  }
662
- return headers;
684
+ return headers2;
663
685
  }
664
686
  };
665
687
  function requireNonBlank(value, name) {
@@ -751,14 +773,14 @@ var InstallationsModule = class {
751
773
  }
752
774
  });
753
775
  }
754
- async update(id, options) {
776
+ async update(id2, options) {
755
777
  const workspaceId = this.requireWorkspaceId(options?.workspaceId);
756
778
  return this.http.request(
757
779
  "PUT",
758
- `/installations/${encodeURIComponent(id)}`,
780
+ `/installations/${encodeURIComponent(id2)}`,
759
781
  {
760
782
  body: JSON.stringify({
761
- id,
783
+ id: id2,
762
784
  workspaceId,
763
785
  packageVersion: options?.packageVersion,
764
786
  installMode: options?.installMode
@@ -767,8 +789,8 @@ var InstallationsModule = class {
767
789
  }
768
790
  );
769
791
  }
770
- async uninstall(id, options) {
771
- await this.http.request("DELETE", `/installations/${encodeURIComponent(id)}`, {
792
+ async uninstall(id2, options) {
793
+ await this.http.request("DELETE", `/installations/${encodeURIComponent(id2)}`, {
772
794
  params: { workspaceId: this.requireWorkspaceId(options?.workspaceId) }
773
795
  });
774
796
  }
@@ -794,10 +816,10 @@ var ResourcesModule = class {
794
816
  params: { workspaceId: this.requireWorkspaceId(options?.workspaceId) }
795
817
  });
796
818
  }
797
- async get(id, options) {
819
+ async get(id2, options) {
798
820
  return this.http.request(
799
821
  "GET",
800
- `/resources/${encodeURIComponent(id)}`,
822
+ `/resources/${encodeURIComponent(id2)}`,
801
823
  { params: { workspaceId: this.requireWorkspaceId(options?.workspaceId) } }
802
824
  );
803
825
  }
@@ -849,7 +871,7 @@ var SettingsModule = class {
849
871
  }
850
872
  });
851
873
  }
852
- /** Upsert a setting value by its full key. Requires the `fabric.setting.update` scope. */
874
+ /** Upsert a setting value by its full key. Requires the `setting.write` scope. */
853
875
  async set(key, value, options) {
854
876
  await this.http.request("PUT", `/settings/${encodeURIComponent(key)}`, {
855
877
  params: {
@@ -860,7 +882,7 @@ var SettingsModule = class {
860
882
  headers: { "Content-Type": "application/json" }
861
883
  });
862
884
  }
863
- /** Delete a stored value (reverting to the definition default). Requires `fabric.setting.delete`. */
885
+ /** Delete a stored value (reverting to the definition default). Requires `setting.delete`. */
864
886
  async reset(key, options) {
865
887
  await this.http.request("DELETE", `/settings/${encodeURIComponent(key)}`, {
866
888
  params: {
@@ -907,104 +929,78 @@ var SettingsModule = class {
907
929
  }
908
930
  };
909
931
 
910
- // src/modules/email.ts
911
- var EmailModule = class {
932
+ // src/types.ts
933
+ var ApiError = class extends Error {
934
+ constructor(status, detail, title, code, retryAfterMs) {
935
+ super(title ?? `API error ${status}: ${detail}`);
936
+ this.status = status;
937
+ this.detail = detail;
938
+ this.title = title;
939
+ this.code = code;
940
+ this.retryAfterMs = retryAfterMs;
941
+ this.name = "ApiError";
942
+ }
943
+ status;
944
+ detail;
945
+ title;
946
+ code;
947
+ retryAfterMs;
948
+ };
949
+
950
+ // src/modules/notifications.ts
951
+ var NotificationsModule = class {
912
952
  constructor(http, workspaceId) {
913
953
  this.http = http;
914
954
  this.workspaceId = workspaceId;
915
955
  }
916
956
  http;
917
957
  workspaceId;
918
- async preview(installationId, options) {
919
- return this.http.request("GET", `/installations/${encodeURIComponent(installationId)}/email-consent-preview`, {
920
- params: { workspaceId: this.workspace(options) }
921
- });
922
- }
923
- async consents(options) {
924
- return this.http.request("GET", "/email/consents", { params: { workspaceId: this.workspace(options) } });
925
- }
926
- /** Approve exactly the reviewed signed candidate; tracking stays off unless explicitly opted in. */
927
- async consent(preview, approval, options) {
928
- if (approval !== true) throw new Error("Explicit email consent is required");
929
- if (!options?.senderGrants?.length) throw new Error("Explicit purpose/sender grants are required");
930
- return this.http.request("PUT", `/email/consents/${encodeURIComponent(preview.installationId)}`, {
931
- params: { workspaceId: preview.workspaceId },
958
+ async send(options) {
959
+ const request = {
932
960
  body: JSON.stringify({
933
- candidateDigest: preview.candidateDigest,
934
- enabled: true,
935
- allowOpenTracking: options?.allowOpenTracking ?? false,
936
- allowClickTracking: options?.allowClickTracking ?? false,
937
- senderGrants: options.senderGrants.map((grant) => ({ purpose: grant.purpose, senderId: grant.senderId }))
938
- }),
939
- headers: { "Content-Type": "application/json" }
940
- });
941
- }
942
- async revoke(consent, options) {
943
- return this.http.request("PUT", `/email/consents/${encodeURIComponent(consent.installationId)}`, {
944
- params: { workspaceId: this.workspace(options) },
945
- body: JSON.stringify({ candidateDigest: consent.candidateDigest, enabled: false, allowOpenTracking: false, allowClickTracking: false }),
946
- headers: { "Content-Type": "application/json" }
947
- });
948
- }
949
- async senders(options) {
950
- return this.http.request("GET", "/email/senders", { params: { workspaceId: this.workspace(options) } });
951
- }
952
- async invoicePdf(recordId, installationId, options) {
953
- this.mainBranch(options);
954
- return this.http.request("POST", "/email/artifacts/invoice", {
955
- body: JSON.stringify({ workspaceId: this.workspace(options), recordId, installationId }),
956
- headers: { "Content-Type": "application/json" }
957
- });
958
- }
959
- /** Authenticated immutable bytes; no public URL. The caller owns any local object URL it creates. */
960
- async artifactContent(artifactId, options) {
961
- this.mainBranch(options);
962
- return this.http.request("GET", `/email/artifacts/${encodeURIComponent(artifactId)}/content`, {
963
- params: { workspaceId: this.workspace(options), branch: "main" }
964
- });
965
- }
966
- /** A stable key binds this exact request. After a lost response inspect status; never automatically resend. */
967
- async dispatch(options) {
968
- this.mainBranch(options);
969
- return this.http.request("POST", "/email/dispatches", {
970
- body: JSON.stringify({
971
- workspaceId: this.workspace(options),
961
+ workspaceId: this.requireWorkspaceId(options.workspaceId),
972
962
  installationId: options.installationId,
973
- recordId: options.recordId,
974
- purpose: options.purpose,
975
- templateId: options.templateId,
976
- senderId: options.senderId,
977
- recipients: options.recipients,
978
- artifactIds: options.artifactIds,
963
+ typeKey: options.typeKey,
979
964
  idempotencyKey: options.idempotencyKey,
980
- replyTo: options.replyTo
965
+ userIds: options.userIds,
966
+ audienceScope: options.audienceScope,
967
+ emailAddresses: options.emailAddresses,
968
+ parameters: options.parameters
981
969
  }),
982
970
  headers: { "Content-Type": "application/json" }
983
- });
984
- }
985
- async outcome(id, options) {
986
- this.mainBranch(options);
987
- return this.http.request("GET", `/email/dispatches/${encodeURIComponent(id)}`, {
988
- params: { workspaceId: this.workspace(options) }
989
- });
990
- }
991
- async history(options) {
992
- this.mainBranch(options);
993
- return this.http.request("GET", "/email/dispatches", {
994
- params: { workspaceId: this.workspace(options), recordId: options?.recordId, installationId: options?.installationId }
995
- });
996
- }
997
- async status(id, options) {
998
- return this.outcome(id, options);
971
+ };
972
+ const deadline = Date.now() + 1e4;
973
+ for (; ; ) {
974
+ try {
975
+ return await this.http.request("POST", "/notifications/send", request);
976
+ } catch (error) {
977
+ if (!(error instanceof ApiError) || error.status !== 409 || error.code !== "execution_not_ready") {
978
+ throw error;
979
+ }
980
+ const delay2 = Math.max(250, error.retryAfterMs ?? 1e3);
981
+ if (Date.now() + delay2 > deadline) throw error;
982
+ await new Promise((resolve) => setTimeout(resolve, delay2));
983
+ }
984
+ }
999
985
  }
1000
- workspace(options) {
1001
- const value = options?.workspaceId ?? this.workspaceId;
1002
- if (!value) throw new Error("A workspace is required for transactional email");
1003
- return value;
986
+ async request(id2, options) {
987
+ return this.http.request(
988
+ "GET",
989
+ `/notifications/requests/${encodeURIComponent(id2)}`,
990
+ {
991
+ params: {
992
+ workspaceId: this.requireWorkspaceId(options?.workspaceId),
993
+ installationId: options?.installationId
994
+ }
995
+ }
996
+ );
1004
997
  }
1005
- mainBranch(options) {
1006
- if (options?.branch !== void 0 && options.branch !== "main")
1007
- throw new Error("Transactional email currently supports only the main business branch");
998
+ requireWorkspaceId(override) {
999
+ const workspaceId = override ?? this.workspaceId;
1000
+ if (!workspaceId) {
1001
+ throw new Error("workspaceId is required: provide it in options or when creating the client");
1002
+ }
1003
+ return workspaceId;
1008
1004
  }
1009
1005
  };
1010
1006
 
@@ -1034,8 +1030,8 @@ var BusinessModule = class _BusinessModule {
1034
1030
  body: JSON.stringify(command)
1035
1031
  });
1036
1032
  }
1037
- async read(id) {
1038
- return this.http.request("GET", `/business/records/${encodeURIComponent(id)}`, { headers: this.headers() });
1033
+ async read(id2) {
1034
+ return this.http.request("GET", `/business/records/${encodeURIComponent(id2)}`, { headers: this.headers() });
1039
1035
  }
1040
1036
  async list(kind) {
1041
1037
  return this.http.request("GET", "/business/records", {
@@ -1077,6 +1073,201 @@ var BusinessModule = class _BusinessModule {
1077
1073
  }
1078
1074
  };
1079
1075
 
1076
+ // src/modules/chat.ts
1077
+ var JSON_HEADERS = { "Content-Type": "application/json" };
1078
+ var ChatModule = class {
1079
+ constructor(http, bridge = null, executions) {
1080
+ this.http = http;
1081
+ this.bridge = bridge;
1082
+ this.executions = executions;
1083
+ }
1084
+ http;
1085
+ bridge;
1086
+ executions;
1087
+ // ── Conversations ─────────────────────────────────────────────────
1088
+ listConversations(options) {
1089
+ return this.http.request("GET", "/chat/conversations", { params: page(options) });
1090
+ }
1091
+ createConversation(options) {
1092
+ const { idempotencyKey, ...body } = options;
1093
+ return this.http.request("POST", "/chat/conversations", { body: JSON.stringify(body), headers: headers(idempotencyKey) });
1094
+ }
1095
+ /** Opens (or returns the existing) direct conversation with one participant. */
1096
+ openDirect(participant) {
1097
+ return this.createConversation({ kind: "direct", participants: [participant] });
1098
+ }
1099
+ getConversation(conversationId) {
1100
+ return this.http.request("GET", `/chat/conversations/${id(conversationId)}`);
1101
+ }
1102
+ updateConversation(conversationId, changes) {
1103
+ return this.http.request("PATCH", `/chat/conversations/${id(conversationId)}`, { body: JSON.stringify(changes), headers: JSON_HEADERS });
1104
+ }
1105
+ join(conversationId) {
1106
+ return this.http.request("POST", `/chat/conversations/${id(conversationId)}/join`, { body: "{}", headers: JSON_HEADERS });
1107
+ }
1108
+ listPublicChannels(options) {
1109
+ return this.http.request("GET", "/chat/channels", { params: { ...page(options), query: options?.query } });
1110
+ }
1111
+ // ── Members ───────────────────────────────────────────────────────
1112
+ listMembers(conversationId) {
1113
+ return this.http.request("GET", `/chat/conversations/${id(conversationId)}/members`);
1114
+ }
1115
+ addMembers(conversationId, participants, role) {
1116
+ return this.http.request("POST", `/chat/conversations/${id(conversationId)}/members`, {
1117
+ body: JSON.stringify({ participants, role }),
1118
+ headers: JSON_HEADERS
1119
+ });
1120
+ }
1121
+ /** Removes a member; pass yourself to leave. */
1122
+ async removeMember(conversationId, participant) {
1123
+ await this.http.request("DELETE", `/chat/conversations/${id(conversationId)}/members/${participant.kind}/${id(participant.id)}`);
1124
+ }
1125
+ changeRole(conversationId, participant, role) {
1126
+ return this.http.request("PUT", `/chat/conversations/${id(conversationId)}/members/${participant.kind}/${id(participant.id)}/role`, {
1127
+ body: JSON.stringify({ role }),
1128
+ headers: JSON_HEADERS
1129
+ });
1130
+ }
1131
+ // ── Messages ──────────────────────────────────────────────────────
1132
+ listMessages(conversationId, options) {
1133
+ return this.http.request("GET", `/chat/conversations/${id(conversationId)}/messages`, {
1134
+ params: { before: options?.before, limit: options?.limit }
1135
+ });
1136
+ }
1137
+ getMessage(conversationId, messageId) {
1138
+ return this.http.request("GET", `/chat/conversations/${id(conversationId)}/messages/${id(messageId)}`);
1139
+ }
1140
+ send(conversationId, parts, options) {
1141
+ return this.http.request("POST", `/chat/conversations/${id(conversationId)}/messages`, {
1142
+ body: JSON.stringify({ parts, replyTo: options?.replyTo }),
1143
+ headers: headers(options?.idempotencyKey)
1144
+ });
1145
+ }
1146
+ /** Sends Markdown text (rendered without raw HTML by every reader). */
1147
+ sendText(conversationId, markdown, options) {
1148
+ return this.send(conversationId, [{ type: "text", data: { markdown } }], options);
1149
+ }
1150
+ /** Edits your own message; pass the revision you saw to reject a concurrent edit. */
1151
+ edit(conversationId, messageId, parts, options) {
1152
+ return this.http.request("PATCH", `/chat/conversations/${id(conversationId)}/messages/${id(messageId)}`, {
1153
+ body: JSON.stringify({ parts }),
1154
+ headers: { ...JSON_HEADERS, ...options?.revision ? { "If-Match": `"${options.revision}"` } : {} }
1155
+ });
1156
+ }
1157
+ delete(conversationId, messageId) {
1158
+ return this.http.request("DELETE", `/chat/conversations/${id(conversationId)}/messages/${id(messageId)}`);
1159
+ }
1160
+ /**
1161
+ * Durable changes after a position: `afterSeq` is a snapshot's `highWatermark` (or the last sequence you applied);
1162
+ * `after` is the `next` cursor of a previous page.
1163
+ */
1164
+ events(conversationId, options) {
1165
+ return this.http.request("GET", `/chat/conversations/${id(conversationId)}/events`, {
1166
+ params: { after: options?.after, afterSeq: options?.afterSeq, limit: options?.limit }
1167
+ });
1168
+ }
1169
+ // ── Read state and preferences ────────────────────────────────────
1170
+ /** Marks messages read through a sequence; never moves back and never past what the server has. */
1171
+ markRead(conversationId, readThroughSeq) {
1172
+ return this.http.request("PUT", `/chat/conversations/${id(conversationId)}/read-state`, {
1173
+ body: JSON.stringify({ readThroughSeq }),
1174
+ headers: JSON_HEADERS
1175
+ });
1176
+ }
1177
+ setPreferences(conversationId, preferences) {
1178
+ return this.http.request("PUT", `/chat/conversations/${id(conversationId)}/preferences`, {
1179
+ body: JSON.stringify(preferences),
1180
+ headers: JSON_HEADERS
1181
+ });
1182
+ }
1183
+ // ── People, bots and commands ─────────────────────────────────────
1184
+ /** People in your workspace (name and avatar only). */
1185
+ searchPeople(options) {
1186
+ return this.http.request("GET", "/chat/people", { params: { ...page(options), query: options?.query } });
1187
+ }
1188
+ listBots() {
1189
+ return this.http.request("GET", "/chat/bots");
1190
+ }
1191
+ /** Workspace managers (`chat.manage`) decide whether a bot may message people directly. */
1192
+ updateBotSettings(botId, settings) {
1193
+ return this.http.request("PUT", `/chat/bots/${id(botId)}/settings`, { body: JSON.stringify(settings), headers: JSON_HEADERS });
1194
+ }
1195
+ listCommands() {
1196
+ return this.http.request("GET", "/chat/commands");
1197
+ }
1198
+ /** Validates and records a command invocation, returning the Execution request to run as you. */
1199
+ invokeCommand(conversationId, command, args, options) {
1200
+ return this.http.request("POST", `/chat/conversations/${id(conversationId)}/commands`, {
1201
+ body: JSON.stringify({ command, arguments: args ?? {} }),
1202
+ headers: headers(options?.idempotencyKey)
1203
+ });
1204
+ }
1205
+ /**
1206
+ * Invokes an installed app's command and runs its script with your own authorisation. Retrying with the same
1207
+ * idempotency key reuses the invocation key, so Execution never runs it twice.
1208
+ */
1209
+ async runCommand(conversationId, command, args, options) {
1210
+ if (!this.executions) throw new Error("Running commands needs the executions module.");
1211
+ const invocation = await this.invokeCommand(conversationId, command, args, options);
1212
+ const execution = await this.executions.run(invocation.filePath, {
1213
+ workspaceId: invocation.workspaceId,
1214
+ branch: invocation.branch,
1215
+ parameters: JSON.parse(invocation.parameters),
1216
+ invocationKey: invocation.invocationKey
1217
+ });
1218
+ return { invocation, execution };
1219
+ }
1220
+ /**
1221
+ * Posts as the installed app's bot from a running script. The bot is derived from the script's execution; the
1222
+ * conversation must have added the bot (or, for `userId`, the workspace must allow it to message people).
1223
+ * Retries briefly while Execution's start event is still on its way.
1224
+ */
1225
+ async postAsBot(options) {
1226
+ const { idempotencyKey, ...body } = options;
1227
+ const request = { body: JSON.stringify(body), headers: headers(idempotencyKey ?? globalThis.crypto?.randomUUID?.()) };
1228
+ const deadline = Date.now() + 1e4;
1229
+ for (; ; ) {
1230
+ try {
1231
+ return await this.http.request("POST", "/chat/bot-messages", request);
1232
+ } catch (error) {
1233
+ if (!(error instanceof ApiError) || error.status !== 409 || error.code !== "execution_not_ready") throw error;
1234
+ const delay2 = Math.max(250, error.retryAfterMs ?? 1e3);
1235
+ if (Date.now() + delay2 > deadline) throw error;
1236
+ await new Promise((resolve) => setTimeout(resolve, delay2));
1237
+ }
1238
+ }
1239
+ }
1240
+ // ── Live hints ────────────────────────────────────────────────────
1241
+ /**
1242
+ * Subscribes to live change hints through the host (installed apps only). Returns an unsubscribe function.
1243
+ * Hints carry ids only; duplicates and reordering are normal, and hints are lost while disconnected.
1244
+ */
1245
+ subscribeChanges(handlers) {
1246
+ if (!this.bridge) throw new Error("Live chat hints are only available to apps running inside the workspace.");
1247
+ return this.bridge.subscribeChat(handlers);
1248
+ }
1249
+ };
1250
+ function id(value) {
1251
+ return encodeURIComponent(value);
1252
+ }
1253
+ function page(options) {
1254
+ return { cursor: options?.cursor, limit: options?.limit };
1255
+ }
1256
+ function headers(idempotencyKey) {
1257
+ return idempotencyKey ? { ...JSON_HEADERS, "Idempotency-Key": idempotencyKey } : JSON_HEADERS;
1258
+ }
1259
+
1260
+ // src/transport/app-commands.ts
1261
+ var APP_COMMAND_BATCH_SIZE = 200;
1262
+ var APP_COMMANDS_CAPABILITY = "commands";
1263
+ var APP_COMMAND_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:/#@-]{0,255}$/;
1264
+ function isAppCommand(value) {
1265
+ if (!value || typeof value !== "object") return false;
1266
+ const c = value;
1267
+ const optionalText = (key, max) => c[key] === void 0 || typeof c[key] === "string" && c[key].length <= max;
1268
+ return typeof c.id === "string" && APP_COMMAND_ID_PATTERN.test(c.id) && typeof c.label === "string" && c.label.trim().length > 0 && c.label.length <= 200 && optionalText("description", 500) && optionalText("group", 100) && optionalText("disabledReason", 200) && (c.icon === void 0 || typeof c.icon === "string" && /^lucide:[a-z0-9-]{1,64}$/.test(c.icon)) && (c.keywords === void 0 || Array.isArray(c.keywords) && c.keywords.length <= 16 && c.keywords.every((k) => typeof k === "string" && k.length <= 64)) && (c.variant === void 0 || ["default", "outline", "destructive"].includes(String(c.variant))) && ["enabled", "busy"].every((key) => c[key] === void 0 || typeof c[key] === "boolean");
1269
+ }
1270
+
1080
1271
  // src/transport/header-actions.ts
1081
1272
  function isHeaderAction(value) {
1082
1273
  if (!value || typeof value !== "object") return false;
@@ -1109,7 +1300,7 @@ function element(value) {
1109
1300
  return record(value) && identifier(value.id) && natural(value.version) && (value.versionNonce === void 0 || natural(value.versionNonce)) && (value.isDeleted === void 0 || typeof value.isDeleted === "boolean");
1110
1301
  }
1111
1302
  function files(value) {
1112
- return record(value) && Object.keys(value).length <= 1e4 && Object.entries(value).every(([id, file]) => record(file) && identifier(id) && file.id === id && typeof file.dataURL === "string" && file.dataURL.length <= 10 * 1024 * 1024 && typeof file.mimeType === "string" && file.mimeType.length > 0 && file.mimeType.length <= 128 && natural(file.created) && (file.lastRetrieved === void 0 || natural(file.lastRetrieved)));
1303
+ return record(value) && Object.keys(value).length <= 1e4 && Object.entries(value).every(([id2, file]) => record(file) && identifier(id2) && file.id === id2 && typeof file.dataURL === "string" && file.dataURL.length <= 10 * 1024 * 1024 && typeof file.mimeType === "string" && file.mimeType.length > 0 && file.mimeType.length <= 128 && natural(file.created) && (file.lastRetrieved === void 0 || natural(file.lastRetrieved)));
1113
1304
  }
1114
1305
  function isNativeSnapshot(value) {
1115
1306
  if (!record(value) || !natural(value.sequence) || !natural(value.savedSequence) || value.savedSequence > value.sequence || typeof value.revision !== "string" || !value.revision || value.revision.length > 1024 || typeof value.readOnly !== "boolean" || !record(value.scene) || !Array.isArray(value.scene.elements) || value.scene.elements.length > 1e4 || !value.scene.elements.every(element) || !files(value.scene.files) || !record(value.scene.appState) || !Array.isArray(value.participants) || value.participants.length > 20)
@@ -1217,6 +1408,14 @@ var Bridge = class {
1217
1408
  sessionId = null;
1218
1409
  actions = /* @__PURE__ */ new Map();
1219
1410
  requests = /* @__PURE__ */ new Set();
1411
+ commands = /* @__PURE__ */ new Map();
1412
+ commandUpserts = /* @__PURE__ */ new Set();
1413
+ commandRemovals = /* @__PURE__ */ new Set();
1414
+ commandReset = false;
1415
+ commandFlushScheduled = false;
1416
+ commandRequests = /* @__PURE__ */ new Set();
1417
+ _hostCapabilities = [];
1418
+ chatSubscribers = /* @__PURE__ */ new Set();
1220
1419
  hostMessageHandlers = /* @__PURE__ */ new Map();
1221
1420
  hostMessageRequests = /* @__PURE__ */ new Set();
1222
1421
  wopiRequests = /* @__PURE__ */ new Map();
@@ -1245,6 +1444,17 @@ var Bridge = class {
1245
1444
  get hostOrigin() {
1246
1445
  return this._hostOrigin;
1247
1446
  }
1447
+ /** Capabilities the host announced in `verentis:init`. */
1448
+ get hostCapabilities() {
1449
+ return this._hostCapabilities;
1450
+ }
1451
+ /**
1452
+ * Whether the host exports page commands to its command registry. Older hosts ignore registrations;
1453
+ * commands still run locally, they are just not discoverable in the workspace palette.
1454
+ */
1455
+ get supportsCommands() {
1456
+ return this._hostCapabilities.includes(APP_COMMANDS_CAPABILITY);
1457
+ }
1248
1458
  /** Send a message to the host */
1249
1459
  postToHost(message) {
1250
1460
  if (this._destroyed) throw new Error("Bridge has been destroyed.");
@@ -1383,6 +1593,113 @@ var Bridge = class {
1383
1593
  }
1384
1594
  };
1385
1595
  }
1596
+ /**
1597
+ * Export a page command to the workspace command registry while it is active. There is no count limit;
1598
+ * dispose the registration when the control unmounts. May be called before the init handshake.
1599
+ */
1600
+ registerCommand(descriptor, handler) {
1601
+ if (this._destroyed) throw new Error("Bridge has been destroyed.");
1602
+ if (!isAppCommand(descriptor)) throw new Error("Invalid command.");
1603
+ if (this.commands.has(descriptor.id)) throw new Error(`Command '${descriptor.id}' is already registered.`);
1604
+ const command = { descriptor: cloneCommand(descriptor), handler, busy: false };
1605
+ this.commands.set(descriptor.id, command);
1606
+ this.queueCommand(descriptor.id, "upsert");
1607
+ return {
1608
+ update: (next) => {
1609
+ if (this.commands.get(descriptor.id) !== command) throw new Error("Command has been disposed.");
1610
+ if (!isAppCommand(next) || next.id !== descriptor.id) throw new Error("Invalid command update.");
1611
+ if (sameCommand(command.descriptor, next)) return;
1612
+ command.descriptor = cloneCommand(next);
1613
+ this.queueCommand(descriptor.id, "upsert");
1614
+ },
1615
+ dispose: () => {
1616
+ if (this.commands.get(descriptor.id) !== command) return;
1617
+ this.commands.delete(descriptor.id);
1618
+ this.queueCommand(descriptor.id, "remove");
1619
+ }
1620
+ };
1621
+ }
1622
+ queueCommand(id2, change) {
1623
+ if (change === "upsert") {
1624
+ this.commandRemovals.delete(id2);
1625
+ this.commandUpserts.add(id2);
1626
+ } else {
1627
+ this.commandUpserts.delete(id2);
1628
+ this.commandRemovals.add(id2);
1629
+ }
1630
+ this.scheduleCommandFlush();
1631
+ }
1632
+ scheduleCommandFlush() {
1633
+ if (this.commandFlushScheduled) return;
1634
+ this.commandFlushScheduled = true;
1635
+ queueMicrotask(() => {
1636
+ this.commandFlushScheduled = false;
1637
+ this.flushCommands();
1638
+ });
1639
+ }
1640
+ commandSnapshot(id2) {
1641
+ const command = this.commands.get(id2);
1642
+ if (!command) return null;
1643
+ return command.busy ? { ...command.descriptor, busy: true } : command.descriptor;
1644
+ }
1645
+ flushCommands() {
1646
+ if (!this.sessionId || !this._hostOrigin || this._destroyed) return;
1647
+ const upsert = [...this.commandUpserts].map((id2) => this.commandSnapshot(id2)).filter((c) => !!c);
1648
+ const remove = this.commandReset ? [] : [...this.commandRemovals];
1649
+ let reset = this.commandReset;
1650
+ this.commandUpserts.clear();
1651
+ this.commandRemovals.clear();
1652
+ this.commandReset = false;
1653
+ if (!reset && !upsert.length && !remove.length) return;
1654
+ let offset = 0;
1655
+ let removeOffset = 0;
1656
+ do {
1657
+ const batchUpsert = upsert.slice(offset, offset + APP_COMMAND_BATCH_SIZE);
1658
+ offset += batchUpsert.length;
1659
+ const room = APP_COMMAND_BATCH_SIZE - batchUpsert.length;
1660
+ const batchRemove = remove.slice(removeOffset, removeOffset + room);
1661
+ removeOffset += batchRemove.length;
1662
+ this.postToHost({
1663
+ type: "verentis:commands:update",
1664
+ version: 1,
1665
+ sessionId: this.sessionId,
1666
+ ...reset ? { reset: true } : {},
1667
+ upsert: batchUpsert,
1668
+ remove: batchRemove
1669
+ });
1670
+ reset = false;
1671
+ } while (offset < upsert.length || removeOffset < remove.length);
1672
+ }
1673
+ async invokeCommand(message) {
1674
+ if (message.sessionId !== this.sessionId || typeof message.requestId !== "string" || message.requestId.length > 128 || !message.requestId || this.commandRequests.has(message.requestId) || typeof message.commandId !== "string") return;
1675
+ this.commandRequests.add(message.requestId);
1676
+ if (this.commandRequests.size > 1e3) this.commandRequests.delete(this.commandRequests.values().next().value);
1677
+ const command = this.commands.get(message.commandId);
1678
+ const reply = {
1679
+ type: "verentis:command:result",
1680
+ version: 1,
1681
+ sessionId: message.sessionId,
1682
+ requestId: message.requestId,
1683
+ commandId: message.commandId
1684
+ };
1685
+ if (!command || command.busy || command.descriptor.busy || command.descriptor.enabled === false) {
1686
+ this.postToHost({ ...reply, error: "Command is unavailable." });
1687
+ return;
1688
+ }
1689
+ command.busy = true;
1690
+ this.queueCommand(message.commandId, "upsert");
1691
+ try {
1692
+ await command.handler();
1693
+ if (!this._destroyed && this.sessionId === message.sessionId) this.postToHost(reply);
1694
+ } catch (error) {
1695
+ if (!this._destroyed && this.sessionId === message.sessionId) {
1696
+ this.postToHost({ ...reply, error: (error instanceof Error ? error.message : "Command failed.").slice(0, 500) || "Command failed." });
1697
+ }
1698
+ } finally {
1699
+ command.busy = false;
1700
+ if (this.commands.get(message.commandId) === command) this.queueCommand(message.commandId, "upsert");
1701
+ }
1702
+ }
1386
1703
  publishActions() {
1387
1704
  if (!this.sessionId || this._destroyed) return;
1388
1705
  this.postToHost({
@@ -1467,6 +1784,41 @@ var Bridge = class {
1467
1784
  this.postToHost({ ...reply, error: "The reply could not be sent to the workspace." });
1468
1785
  }
1469
1786
  }
1787
+ /**
1788
+ * Subscribes to the host's chat live hints (see `ChatModule.subscribeChanges`). The first subscriber asks the host
1789
+ * to relay; the last one to leave stops it. Messages from another session are ignored.
1790
+ */
1791
+ subscribeChat(handlers) {
1792
+ if (this._destroyed) throw new Error("Bridge has been destroyed.");
1793
+ this.chatSubscribers.add(handlers);
1794
+ const subscribe = () => {
1795
+ if (this.sessionId && this._hostOrigin && this.chatSubscribers.size > 0)
1796
+ this.postToHost({ type: "verentis:chat:subscribe", version: 1, sessionId: this.sessionId });
1797
+ };
1798
+ if (this.chatSubscribers.size === 1) {
1799
+ if (this._hostOrigin) subscribe();
1800
+ else void this._initPromise.then(subscribe, () => void 0);
1801
+ }
1802
+ return () => {
1803
+ if (!this.chatSubscribers.delete(handlers) || this.chatSubscribers.size > 0) return;
1804
+ if (!this._destroyed && this.sessionId && this._hostOrigin)
1805
+ this.postToHost({ type: "verentis:chat:unsubscribe", version: 1, sessionId: this.sessionId });
1806
+ };
1807
+ }
1808
+ receiveChat(data) {
1809
+ if (data.type !== "verentis:chat:changed" && data.type !== "verentis:chat:connection" && data.type !== "verentis:chat:subscribed" && data.type !== "verentis:chat:error") return false;
1810
+ if (!this._hostOrigin || data.sessionId !== this.sessionId) return true;
1811
+ for (const subscriber of [...this.chatSubscribers]) {
1812
+ try {
1813
+ if (data.type === "verentis:chat:changed") subscriber.onChange(data.hint);
1814
+ else if (data.type === "verentis:chat:error") subscriber.onError?.(String(data.reason).slice(0, 500));
1815
+ else subscriber.onConnection?.(data.status);
1816
+ } catch (error) {
1817
+ console.error("[Verentis SDK] Chat subscriber failed", error);
1818
+ }
1819
+ }
1820
+ return true;
1821
+ }
1470
1822
  /** Subscribe to a specific message type from the host */
1471
1823
  on(type, listener) {
1472
1824
  if (!this.listeners.has(type)) {
@@ -1485,8 +1837,13 @@ var Bridge = class {
1485
1837
  if (!this._hostOrigin) this._initReject(new Error("Bridge has been destroyed."));
1486
1838
  this.actions.clear();
1487
1839
  this.requests.clear();
1840
+ this.commands.clear();
1841
+ this.commandUpserts.clear();
1842
+ this.commandRemovals.clear();
1843
+ this.commandRequests.clear();
1488
1844
  this.hostMessageHandlers.clear();
1489
1845
  this.hostMessageRequests.clear();
1846
+ this.chatSubscribers.clear();
1490
1847
  this.listeners.clear();
1491
1848
  for (const pending of this.wopiRequests.values()) {
1492
1849
  clearTimeout(pending.timer);
@@ -1515,6 +1872,13 @@ var Bridge = class {
1515
1872
  this._initTimer = void 0;
1516
1873
  this._hostOrigin = event.origin;
1517
1874
  this.sessionId = typeof data.sessionId === "string" ? data.sessionId : null;
1875
+ this._hostCapabilities = Array.isArray(data.capabilities) ? data.capabilities.filter((c) => typeof c === "string").slice(0, 64) : [];
1876
+ if (this.sessionId && this.commands.size > 0) {
1877
+ this.commandReset = true;
1878
+ this.commandRemovals.clear();
1879
+ for (const id2 of this.commands.keys()) this.commandUpserts.add(id2);
1880
+ this.scheduleCommandFlush();
1881
+ }
1518
1882
  console.info("[Verentis SDK] Received verentis:init from host", {
1519
1883
  origin: event.origin,
1520
1884
  workspaceId: data.context.workspace.id,
@@ -1552,10 +1916,15 @@ var Bridge = class {
1552
1916
  void this.invokeAction(data);
1553
1917
  return;
1554
1918
  }
1919
+ if (data.type === "verentis:command:invoke") {
1920
+ void this.invokeCommand(data);
1921
+ return;
1922
+ }
1555
1923
  if (data.type === "verentis:host:message") {
1556
1924
  void this.receiveHostMessage(data);
1557
1925
  return;
1558
1926
  }
1927
+ if (this.receiveChat(data)) return;
1559
1928
  const typeListeners = this.listeners.get(data.type);
1560
1929
  if (typeListeners) {
1561
1930
  for (const listener of typeListeners) {
@@ -1564,22 +1933,18 @@ var Bridge = class {
1564
1933
  }
1565
1934
  }
1566
1935
  };
1567
-
1568
- // src/types.ts
1569
- var ApiError = class extends Error {
1570
- constructor(status, detail, title, code) {
1571
- super(title ?? `API error ${status}: ${detail}`);
1572
- this.status = status;
1573
- this.detail = detail;
1574
- this.title = title;
1575
- this.code = code;
1576
- this.name = "ApiError";
1577
- }
1578
- status;
1579
- detail;
1580
- title;
1581
- code;
1582
- };
1936
+ function cloneCommand(command) {
1937
+ return { ...command, ...command.keywords ? { keywords: [...command.keywords] } : {} };
1938
+ }
1939
+ function sameCommand(a, b) {
1940
+ const keys = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
1941
+ for (const key of keys) {
1942
+ if (key === "keywords") {
1943
+ if ((a.keywords ?? []).join("\0") !== (b.keywords ?? []).join("\0")) return false;
1944
+ } else if (a[key] !== b[key]) return false;
1945
+ }
1946
+ return true;
1947
+ }
1583
1948
 
1584
1949
  // src/transport/http.transport.ts
1585
1950
  var HttpTransport = class {
@@ -1594,17 +1959,17 @@ var HttpTransport = class {
1594
1959
  async request(method, path, options) {
1595
1960
  const url = this.buildUrl(path, options?.params);
1596
1961
  console.info("[Verentis SDK] Request started", { method, path, url });
1597
- const headers = {};
1962
+ const headers2 = {};
1598
1963
  const authHeader = await this.auth.getAuthHeader();
1599
1964
  if (authHeader) {
1600
- headers["Authorization"] = authHeader;
1965
+ headers2["Authorization"] = authHeader;
1601
1966
  }
1602
1967
  if (options?.headers) {
1603
- Object.assign(headers, options.headers);
1968
+ Object.assign(headers2, options.headers);
1604
1969
  }
1605
1970
  const response = await fetch(url, {
1606
1971
  method,
1607
- headers,
1972
+ headers: headers2,
1608
1973
  body: options?.body
1609
1974
  });
1610
1975
  await this.ensureSuccess(response);
@@ -1623,15 +1988,15 @@ var HttpTransport = class {
1623
1988
  async requestRaw(method, path, options) {
1624
1989
  const url = this.buildUrl(path, options?.params);
1625
1990
  console.info("[Verentis SDK] Raw request started", { method, path, url });
1626
- const headers = {};
1991
+ const headers2 = {};
1627
1992
  const authHeader = await this.auth.getAuthHeader();
1628
1993
  if (authHeader) {
1629
- headers["Authorization"] = authHeader;
1994
+ headers2["Authorization"] = authHeader;
1630
1995
  }
1631
1996
  if (options?.headers) {
1632
- Object.assign(headers, options.headers);
1997
+ Object.assign(headers2, options.headers);
1633
1998
  }
1634
- const response = await fetch(url, { method, headers, body: options?.body });
1999
+ const response = await fetch(url, { method, headers: headers2, body: options?.body });
1635
2000
  console.info("[Verentis SDK] Raw request completed", {
1636
2001
  method,
1637
2002
  path,
@@ -1659,7 +2024,15 @@ var HttpTransport = class {
1659
2024
  if (!(error instanceof SyntaxError)) throw error;
1660
2025
  }
1661
2026
  console.error("[Verentis SDK] Request failed", { status: response.status, code });
1662
- throw new ApiError(response.status, detail, title, code);
2027
+ const retryAfter = response.headers.get("Retry-After");
2028
+ const retryAfterMs = retryAfter === null ? void 0 : /^\d+$/.test(retryAfter) ? Number(retryAfter) * 1e3 : Date.parse(retryAfter) - Date.now();
2029
+ throw new ApiError(
2030
+ response.status,
2031
+ detail,
2032
+ title,
2033
+ code,
2034
+ retryAfterMs !== void 0 && Number.isFinite(retryAfterMs) ? Math.max(0, retryAfterMs) : void 0
2035
+ );
1663
2036
  }
1664
2037
  buildUrl(path, params) {
1665
2038
  const base = `${this.baseUrl}/${this.apiVersion}${path}`;
@@ -1690,8 +2063,9 @@ var VerentisClient = class {
1690
2063
  installations;
1691
2064
  resources;
1692
2065
  settings;
1693
- email;
2066
+ notifications;
1694
2067
  business;
2068
+ chat;
1695
2069
  context;
1696
2070
  bridge;
1697
2071
  collaboration;
@@ -1726,8 +2100,9 @@ var VerentisClient = class {
1726
2100
  this.installations = new InstallationsModule(this._http, options?.workspaceId);
1727
2101
  this.resources = new ResourcesModule(this._http, options?.workspaceId);
1728
2102
  this.settings = new SettingsModule(this._http, options?.workspaceId);
1729
- this.email = new EmailModule(this._http, options?.workspaceId);
2103
+ this.notifications = new NotificationsModule(this._http, options?.workspaceId);
1730
2104
  this.business = new BusinessModule(this._http);
2105
+ this.chat = new ChatModule(this._http, null, this.executions);
1731
2106
  this._ready = Promise.resolve();
1732
2107
  return;
1733
2108
  }
@@ -1776,8 +2151,9 @@ var VerentisClient = class {
1776
2151
  this.installations = new InstallationsModule(http, context.workspace.id);
1777
2152
  this.resources = new ResourcesModule(http, context.workspace.id);
1778
2153
  this.settings = new SettingsModule(http, context.workspace.id);
1779
- this.email = new EmailModule(http, context.workspace.id);
2154
+ this.notifications = new NotificationsModule(http, context.workspace.id);
1780
2155
  this.business = new BusinessModule(http);
2156
+ this.chat = new ChatModule(http, this.bridge, this.executions);
1781
2157
  }
1782
2158
  };
1783
2159
  function detectEnvironment() {
@@ -1873,13 +2249,16 @@ function createVerentisClient(options) {
1873
2249
  return new VerentisClient(options);
1874
2250
  }
1875
2251
 
2252
+ exports.APP_COMMANDS_CAPABILITY = APP_COMMANDS_CAPABILITY;
2253
+ exports.APP_COMMAND_BATCH_SIZE = APP_COMMAND_BATCH_SIZE;
2254
+ exports.APP_COMMAND_ID_PATTERN = APP_COMMAND_ID_PATTERN;
1876
2255
  exports.ApiError = ApiError;
1877
2256
  exports.ApiKeyAuth = ApiKeyAuth;
1878
2257
  exports.Bridge = Bridge;
1879
2258
  exports.BridgeAuth = BridgeAuth;
1880
2259
  exports.BusinessModule = BusinessModule;
2260
+ exports.ChatModule = ChatModule;
1881
2261
  exports.ContextModule = ContextModule;
1882
- exports.EmailModule = EmailModule;
1883
2262
  exports.EnginesModule = EnginesModule;
1884
2263
  exports.ExecutionsModule = ExecutionsModule;
1885
2264
  exports.FilesModule = FilesModule;
@@ -1888,13 +2267,16 @@ exports.HttpTransport = HttpTransport;
1888
2267
  exports.InstallationsModule = InstallationsModule;
1889
2268
  exports.NativeCollaborationClient = NativeCollaborationClient;
1890
2269
  exports.NativeCollaborationError = NativeCollaborationError;
2270
+ exports.NotificationsModule = NotificationsModule;
1891
2271
  exports.ResourcesModule = ResourcesModule;
2272
+ exports.SCRIPT_ARGUMENTS_SCHEMA = SCRIPT_ARGUMENTS_SCHEMA;
1892
2273
  exports.SettingsModule = SettingsModule;
1893
2274
  exports.TokenAuth = TokenAuth;
1894
2275
  exports.VerentisClient = VerentisClient;
1895
2276
  exports.createVerentisClient = createVerentisClient;
1896
2277
  exports.exactHttpsOrigin = exactHttpsOrigin;
1897
2278
  exports.findBestMatch = findBestMatch;
2279
+ exports.isAppCommand = isAppCommand;
1898
2280
  exports.isDurableSaveConfirmed = isDurableSaveConfirmed;
1899
2281
  exports.isHeaderAction = isHeaderAction;
1900
2282
  exports.isNativeSaveResult = isNativeSaveResult;