@datalayer/agent-runtimes 1.5.9 → 1.5.11

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.
@@ -106,9 +106,9 @@ export declare function uiProblems(app: Pick<AppSpec, 'ui' | 'enabled'>): string
106
106
  * What is wrong with the protocols an application says it speaks
107
107
  * (AGENT_PROTOCOLS PS-02), as agentspecs says it: an id the catalogue does not
108
108
  * know or said twice, a planned protocol while it is offered, and a `pap`,
109
- * `pact` or `a2a` block configuring a protocol it does not list.
109
+ * `pact`, `a2a` or `signed_fetch` block configuring a protocol it does not list.
110
110
  */
111
- export declare function protocolsProblems(app: Pick<AppSpec, 'protocols' | 'enabled' | 'pap' | 'pact' | 'a2a' | 'commerce'>): string[];
111
+ export declare function protocolsProblems(app: Pick<AppSpec, 'protocols' | 'enabled' | 'pap' | 'pact' | 'a2a' | 'commerce' | 'signed_fetch'>): string[];
112
112
  /**
113
113
  * An application as the document its file holds.
114
114
  *
@@ -205,6 +205,8 @@ const KNOWN_KEYS = [
205
205
  'pap',
206
206
  'pact',
207
207
  'commerce',
208
+ 'signed_fetch',
209
+ 'pair',
208
210
  'a2a',
209
211
  'rules',
210
212
  'permissions',
@@ -443,6 +445,35 @@ function dumpCommerce(commerce) {
443
445
  }
444
446
  : {}).data)).data;
445
447
  }
448
+ /** Signed fetch (AGENT_PROTOCOLS EX-11): the exact sites it reads pages of, each request signed. */
449
+ function parseSignedFetch(data) {
450
+ return {
451
+ sites: records(data.sites).map(site => ({
452
+ id: text(site.id),
453
+ site: text(site.site),
454
+ })),
455
+ };
456
+ }
457
+ /** Signed fetch as its document says it, nothing at its default. */
458
+ function dumpSignedFetch(signedFetch) {
459
+ if (!signedFetch)
460
+ return {};
461
+ return new Writer().list('sites', signedFetch.sites.map(site => new Writer()
462
+ .text('id', site.id, '\u0000')
463
+ .text('site', site.site, '\u0000').data)).data;
464
+ }
465
+ /** One of a pair of examples (AGENT_PROTOCOLS PR-13): its id and its side, or a problem. */
466
+ function parsePair(data) {
467
+ const id = text(data.id);
468
+ const role = text(data.role);
469
+ if (!/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(id)) {
470
+ return `A pair's id is lower-case letters, digits and hyphens: ${JSON.stringify(id)}.`;
471
+ }
472
+ if (role !== 'business' && role !== 'personal_agent') {
473
+ return `One of a pair is its business or its personal_agent, not ${JSON.stringify(role)}.`;
474
+ }
475
+ return { id, role };
476
+ }
446
477
  /** PACT as its document says it, nothing at its default. */
447
478
  function dumpPact(pact) {
448
479
  if (!pact)
@@ -1047,6 +1078,18 @@ export function parseAppspec(document) {
1047
1078
  if (isData(data.commerce)) {
1048
1079
  app.commerce = parseCommerce(data.commerce);
1049
1080
  }
1081
+ if (isData(data.signed_fetch)) {
1082
+ app.signed_fetch = parseSignedFetch(data.signed_fetch);
1083
+ }
1084
+ if (isData(data.pair)) {
1085
+ const pair = parsePair(data.pair);
1086
+ if (typeof pair === 'string') {
1087
+ problems.push(pair);
1088
+ }
1089
+ else {
1090
+ app.pair = pair;
1091
+ }
1092
+ }
1050
1093
  if (isData(data.a2a)) {
1051
1094
  app.a2a = parseA2A(data.a2a);
1052
1095
  }
@@ -1081,7 +1124,7 @@ export function uiProblems(app) {
1081
1124
  * What is wrong with the protocols an application says it speaks
1082
1125
  * (AGENT_PROTOCOLS PS-02), as agentspecs says it: an id the catalogue does not
1083
1126
  * know or said twice, a planned protocol while it is offered, and a `pap`,
1084
- * `pact` or `a2a` block configuring a protocol it does not list.
1127
+ * `pact`, `a2a` or `signed_fetch` block configuring a protocol it does not list.
1085
1128
  */
1086
1129
  export function protocolsProblems(app) {
1087
1130
  const problems = [];
@@ -1105,6 +1148,7 @@ export function protocolsProblems(app) {
1105
1148
  pact: Boolean(app.pact &&
1106
1149
  (app.pact.connections.length > 0 || app.pact.business?.enabled)),
1107
1150
  a2a: Boolean(app.a2a && app.a2a.extensions.length > 0),
1151
+ signed_fetch: Boolean(app.signed_fetch && app.signed_fetch.sites.length > 0),
1108
1152
  };
1109
1153
  for (const spec of Object.values(PROTOCOL_CATALOGUE)) {
1110
1154
  const block = spec.appspecBlock;
@@ -1386,6 +1430,8 @@ export function dumpAppspec(app) {
1386
1430
  .part('pap', dumpPap(app.pap))
1387
1431
  .part('pact', dumpPact(app.pact))
1388
1432
  .part('commerce', dumpCommerce(app.commerce))
1433
+ .part('signed_fetch', dumpSignedFetch(app.signed_fetch))
1434
+ .part('pair', app.pair ? { id: app.pair.id, role: app.pair.role } : {})
1389
1435
  .part('a2a', dumpA2A(app.a2a))
1390
1436
  .list('rules', app.rules.map(rule => ({
1391
1437
  action: rule.action,
@@ -0,0 +1,16 @@
1
+ /**
2
+ * What no file the embed ships may hold (PAP_DATALAYER B-02): the PAP SDK's
3
+ * emitted-bundle test (`src/__tests__/canaries.test.mjs`, `EMBEDDED_SECRETS`)
4
+ * extended to the embed, a browser artifact anybody's page loads. Its build
5
+ * fails on a file holding one (`vite.embed.config.ts`), naming the kind and
6
+ * the file, never what matched.
7
+ *
8
+ * Found 2026-10-13: the embed then served at datalayer.ai held the builder's
9
+ * Datalayer API key, because `import.meta.env` read whole (`env?.VITE_…`) is
10
+ * every `VITE_` variable of the environment it was built in.
11
+ *
12
+ * @module apps/embed/embeddedSecrets
13
+ */
14
+ export declare const EMBEDDED_SECRETS: ReadonlyArray<readonly [string, RegExp]>;
15
+ /** The kinds of secret a file's text holds, by name: empty when none. */
16
+ export declare function secretsIn(text: string): string[];
@@ -0,0 +1,34 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * What no file the embed ships may hold (PAP_DATALAYER B-02): the PAP SDK's
7
+ * emitted-bundle test (`src/__tests__/canaries.test.mjs`, `EMBEDDED_SECRETS`)
8
+ * extended to the embed, a browser artifact anybody's page loads. Its build
9
+ * fails on a file holding one (`vite.embed.config.ts`), naming the kind and
10
+ * the file, never what matched.
11
+ *
12
+ * Found 2026-10-13: the embed then served at datalayer.ai held the builder's
13
+ * Datalayer API key, because `import.meta.env` read whole (`env?.VITE_…`) is
14
+ * every `VITE_` variable of the environment it was built in.
15
+ *
16
+ * @module apps/embed/embeddedSecrets
17
+ */
18
+ export const EMBEDDED_SECRETS = [
19
+ // Key material after the header: a library may name the header to refuse it.
20
+ [
21
+ 'a PEM private key',
22
+ /-----BEGIN [A-Z ]*PRIVATE KEY-----(?:\\n|\s)*[A-Za-z0-9+/=]{40,}/,
23
+ ],
24
+ ['a private JWK member', /["']d["']\s*:\s*["'][A-Za-z0-9_-]{32,}["']/],
25
+ [
26
+ 'a compact JWT',
27
+ /eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/,
28
+ ],
29
+ ['a Bearer or DPoP token', /(?:Bearer|DPoP) [A-Za-z0-9_-]{20,}/],
30
+ ];
31
+ /** The kinds of secret a file's text holds, by name: empty when none. */
32
+ export function secretsIn(text) {
33
+ return EMBEDDED_SECRETS.filter(([, pattern]) => pattern.test(text)).map(([kind]) => kind);
34
+ }
@@ -1,14 +1,38 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
2
+ import { useEffect, useState } from 'react';
3
3
  import { Button, Flash, Heading, Text } from '@primer/react';
4
4
  import { CopyIcon, LinkExternalIcon } from '@primer/octicons-react';
5
5
  import { Box } from '@datalayer/primer-addons';
6
6
  import QRCode from 'react-qr-code';
7
- import { PAP_SIGN_IN_WORDS, readPapSignIn } from './signIn';
7
+ import { PAP_SIGN_IN_CHANNEL, PAP_SIGN_IN_WORDS, readPapSignIn, signInReturnOf, } from './signIn';
8
8
  export function PapSignInCard({ context, send, }) {
9
9
  const [copied, setCopied] = useState(false);
10
10
  const [sent, setSent] = useState(false);
11
+ const [returned, setReturned] = useState(null);
11
12
  const read = context.status === 'complete' ? readPapSignIn(context.result) : null;
13
+ // A Direct Sign-In: Datalayer's `/pap/signed-in`, in the tab the company sent the person
14
+ // back to, says how it came back (PAP_DATALAYER P-04); the conversation goes on here.
15
+ const company = read && !('refused' in read) && read.mode === 'direct'
16
+ ? (read.company ?? '')
17
+ : '';
18
+ useEffect(() => {
19
+ if (!company || typeof BroadcastChannel === 'undefined') {
20
+ return;
21
+ }
22
+ const channel = new BroadcastChannel(PAP_SIGN_IN_CHANNEL);
23
+ channel.onmessage = (event) => {
24
+ const outcome = signInReturnOf(event.data, company);
25
+ if (outcome) {
26
+ setReturned(outcome);
27
+ }
28
+ };
29
+ return () => channel.close();
30
+ }, [company]);
31
+ useEffect(() => {
32
+ if (returned === 'signed_in' && send && !sent) {
33
+ setSent(send(PAP_SIGN_IN_WORDS.checkMessage(company)));
34
+ }
35
+ }, [returned, send, sent, company]);
12
36
  if (!read) {
13
37
  // Running, or another answer: the chat's own row says it.
14
38
  return _jsx(_Fragment, { children: context.defaultUI });
@@ -25,9 +49,11 @@ export function PapSignInCard({ context, send, }) {
25
49
  setCopied(false);
26
50
  }
27
51
  };
52
+ // Who the person signs in at: the company itself for a Direct Sign-In (P-04), the page's host otherwise.
53
+ const named = read.company ?? read.host;
28
54
  const title = read.mode === 'mediated'
29
55
  ? PAP_SIGN_IN_WORDS.mediatedTitle(read.host)
30
- : PAP_SIGN_IN_WORDS.title(read.host);
56
+ : PAP_SIGN_IN_WORDS.title(named);
31
57
  return (_jsxs(Box, { as: "section", "aria-label": title, my: 2, p: 3, border: "1px solid", borderColor: "border.default", borderRadius: 2, bg: "canvas.subtle", maxWidth: 480, children: [_jsx(Heading, { as: "h4", sx: { fontSize: 2, mb: 1 }, children: title }), _jsx(Text, { as: "p", sx: { mt: 0, mb: 2, color: 'fg.muted', fontSize: 1 }, children: read.mode === 'device'
32
58
  ? PAP_SIGN_IN_WORDS.device
33
59
  : read.mode === 'mediated'
@@ -37,6 +63,6 @@ export function PapSignInCard({ context, send, }) {
37
63
  fontSize: 5,
38
64
  fontWeight: 'bold',
39
65
  letterSpacing: '0.15em',
40
- }, children: read.code }), _jsx(Button, { size: "small", leadingVisual: CopyIcon, onClick: () => void copy(), children: copied ? PAP_SIGN_IN_WORDS.copied : PAP_SIGN_IN_WORDS.copy })] }), _jsx(Box, { bg: "#ffffff", p: 2, borderRadius: 2, lineHeight: 0, children: _jsx(QRCode, { value: read.open, size: 112, title: PAP_SIGN_IN_WORDS.qr(read.host) }) })] })) : null, _jsxs(Box, { display: "flex", gap: 2, flexWrap: "wrap", alignItems: "center", children: [_jsx(Button, { as: "a", href: read.open, target: "_blank", rel: "noopener noreferrer", variant: "primary", trailingVisual: LinkExternalIcon, children: PAP_SIGN_IN_WORDS.open(read.host) }), send ? (_jsx(Button, { disabled: sent, onClick: () => setSent(send(PAP_SIGN_IN_WORDS.checkMessage(read.host))), children: PAP_SIGN_IN_WORDS.checkAgain })) : null] }), read.expiresAt ? (_jsx(Text, { as: "p", sx: { mt: 2, mb: 0, fontSize: 0, color: 'fg.muted' }, children: PAP_SIGN_IN_WORDS.until(read.expiresAt) })) : null, send ? null : (_jsx(Text, { as: "p", sx: { mt: 1, mb: 0, fontSize: 1 }, children: PAP_SIGN_IN_WORDS.tellMe }))] }));
66
+ }, children: read.code }), _jsx(Button, { size: "small", leadingVisual: CopyIcon, onClick: () => void copy(), children: copied ? PAP_SIGN_IN_WORDS.copied : PAP_SIGN_IN_WORDS.copy })] }), _jsx(Box, { bg: "#ffffff", p: 2, borderRadius: 2, lineHeight: 0, children: _jsx(QRCode, { value: read.open, size: 112, title: PAP_SIGN_IN_WORDS.qr(read.host) }) })] })) : null, _jsxs(Box, { display: "flex", gap: 2, flexWrap: "wrap", alignItems: "center", children: [_jsx(Button, { as: "a", href: read.open, target: "_blank", rel: "noopener noreferrer", variant: "primary", trailingVisual: LinkExternalIcon, children: PAP_SIGN_IN_WORDS.open(named) }), send ? (_jsx(Button, { disabled: sent, onClick: () => setSent(send(PAP_SIGN_IN_WORDS.checkMessage(named))), children: PAP_SIGN_IN_WORDS.checkAgain })) : null] }), returned ? (_jsx(Text, { as: "p", role: "status", sx: { mt: 2, mb: 0, fontSize: 1 }, children: PAP_SIGN_IN_WORDS.returned[returned](named) })) : null, read.expiresAt ? (_jsx(Text, { as: "p", sx: { mt: 2, mb: 0, fontSize: 0, color: 'fg.muted' }, children: PAP_SIGN_IN_WORDS.until(read.expiresAt) })) : null, send ? null : (_jsx(Text, { as: "p", sx: { mt: 1, mb: 0, fontSize: 1 }, children: PAP_SIGN_IN_WORDS.tellMe }))] }));
41
67
  }
42
68
  export default PapSignInCard;
@@ -19,6 +19,12 @@ export type PapSignInWaiting = {
19
19
  open: string;
20
20
  /** The page's domain, read from its address: what the person can trust. */
21
21
  host: string;
22
+ /**
23
+ * The company's domain, when the gateway names it: for a bound `direct`
24
+ * sign-in the page opened is Datalayer's start link (PAP_DATALAYER P-04),
25
+ * which sends the person on to the company and takes them back only from it.
26
+ */
27
+ company?: string;
22
28
  /** The code to enter, for `device`. */
23
29
  code?: string;
24
30
  /** Where the company sent a code, for `mediated`, when it did. */
@@ -29,7 +35,7 @@ export type PapSignInWaiting = {
29
35
  /** What the card says. */
30
36
  export declare const PAP_SIGN_IN_WORDS: {
31
37
  readonly title: (host: string) => string;
32
- readonly direct: "Open the company’s page, sign in there, and come back here.";
38
+ readonly direct: "Open the company’s page through Datalayer, sign in there, and this conversation goes on once you are back.";
33
39
  readonly device: "Open the company’s page and enter this code there.";
34
40
  /** `mediated`: the page is Datalayer's, which names the company and asks what it asks. */
35
41
  readonly mediatedTitle: (host: string) => string;
@@ -45,7 +51,23 @@ export declare const PAP_SIGN_IN_WORDS: {
45
51
  readonly checkMessage: (host: string) => string;
46
52
  readonly refused: "This sign-in link cannot be opened safely, so it is not shown.";
47
53
  readonly qr: (host: string) => string;
54
+ /** Once the person's browser came back from the company (P-04), said under the card. */
55
+ readonly returned: {
56
+ readonly signed_in: (company: string) => string;
57
+ readonly denied: (company: string) => string;
58
+ readonly refused: (company: string) => string;
59
+ };
48
60
  };
61
+ /**
62
+ * Where Datalayer's `/pap/signed-in` page says a Direct Sign-In came back
63
+ * (PAP_DATALAYER P-04): the card waiting for that company hears it in the
64
+ * person's tab, so the conversation goes on there.
65
+ */
66
+ export declare const PAP_SIGN_IN_CHANNEL = "datalayer-pap-sign-in";
67
+ /** How a Direct Sign-In came back. */
68
+ export type PapSignInReturn = keyof typeof PAP_SIGN_IN_WORDS.returned;
69
+ /** What a message on the channel says of this company's sign-in, or `null`: another company's, or not one. */
70
+ export declare function signInReturnOf(message: unknown, company: string): PapSignInReturn | null;
49
71
  /**
50
72
  * `pap_sign_in` — or a business's `pact_sign_in` (PI-56) — as the gateway
51
73
  * names it with or without a prefix.
@@ -5,7 +5,7 @@
5
5
  /** What the card says. */
6
6
  export const PAP_SIGN_IN_WORDS = {
7
7
  title: (host) => `Sign in at ${host}`,
8
- direct: 'Open the company’s page, sign in there, and come back here.',
8
+ direct: 'Open the company’s page through Datalayer, sign in there, and this conversation goes on once you are back.',
9
9
  device: 'Open the company’s page and enter this code there.',
10
10
  /** `mediated`: the page is Datalayer's, which names the company and asks what it asks. */
11
11
  mediatedTitle: (host) => `Sign in through ${host}`,
@@ -21,7 +21,32 @@ export const PAP_SIGN_IN_WORDS = {
21
21
  checkMessage: (host) => `I have signed in at ${host}. Please check again.`,
22
22
  refused: 'This sign-in link cannot be opened safely, so it is not shown.',
23
23
  qr: (host) => `A code to scan to open ${host} on your phone`,
24
+ /** Once the person's browser came back from the company (P-04), said under the card. */
25
+ returned: {
26
+ signed_in: (company) => `Signed in at ${company}: checking again.`,
27
+ denied: (company) => `You did not sign in at ${company}: ask again when you want to.`,
28
+ refused: (company) => `That sign-in at ${company} was refused, and nothing was kept: start it again here.`,
29
+ },
24
30
  };
31
+ /**
32
+ * Where Datalayer's `/pap/signed-in` page says a Direct Sign-In came back
33
+ * (PAP_DATALAYER P-04): the card waiting for that company hears it in the
34
+ * person's tab, so the conversation goes on there.
35
+ */
36
+ export const PAP_SIGN_IN_CHANNEL = 'datalayer-pap-sign-in';
37
+ const DOMAIN = /^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/;
38
+ /** What a message on the channel says of this company's sign-in, or `null`: another company's, or not one. */
39
+ export function signInReturnOf(message, company) {
40
+ if (!message || typeof message !== 'object' || Array.isArray(message)) {
41
+ return null;
42
+ }
43
+ const said = message;
44
+ const outcome = said.outcome;
45
+ return said.company === company &&
46
+ (outcome === 'signed_in' || outcome === 'denied' || outcome === 'refused')
47
+ ? outcome
48
+ : null;
49
+ }
25
50
  /**
26
51
  * `pap_sign_in` — or a business's `pact_sign_in` (PI-56) — as the gateway
27
52
  * names it with or without a prefix.
@@ -92,10 +117,16 @@ export function readPapSignIn(result) {
92
117
  if (data.mode === 'device' && (typeof data.code !== 'string' || !data.code)) {
93
118
  return null;
94
119
  }
120
+ // A bound Direct Sign-In opens Datalayer's start link: the card names the company the gateway
121
+ // names (`PapSignIn.company`); without one, the page's own domain is the company's.
122
+ const company = typeof data.company === 'string' && DOMAIN.test(data.company)
123
+ ? data.company
124
+ : '';
95
125
  return {
96
126
  mode: data.mode,
97
127
  open: url.toString(),
98
128
  host: url.hostname,
129
+ ...(company ? { company } : {}),
99
130
  ...(typeof data.code === 'string' && data.code ? { code: data.code } : {}),
100
131
  ...(data.mode === 'mediated' &&
101
132
  typeof data.sent_to === 'string' &&
@@ -704,6 +704,12 @@ export const PROTOCOL_TOOL_CLASSES = {
704
704
  pap_disconnect: ['write'],
705
705
  },
706
706
  },
707
+ 'signed-fetch': {
708
+ connection: 'signed-fetch',
709
+ tools: {
710
+ signed_fetch: ['read'],
711
+ },
712
+ },
707
713
  ucp: {
708
714
  connection: 'ucp',
709
715
  tools: {
@@ -793,8 +799,10 @@ export const APP_BEHAVIOURS = {
793
799
  'earthdata.search_earth_datagranules': 'do_it',
794
800
  'earthdata.search_earth_datasets': 'do_it',
795
801
  },
802
+ 'errand-runner': {},
796
803
  'event-response': {},
797
804
  'exchange-desk': {},
805
+ 'guest-desk': {},
798
806
  'inbox-triage': {
799
807
  'google-workspace.append_table_rows': 'leave_to_me',
800
808
  'google-workspace.batch_modify_gmail_message_labels': 'do_it',
@@ -981,6 +989,7 @@ export const APP_BEHAVIOURS = {
981
989
  'shop-concierge': {},
982
990
  'shopping-desk': {},
983
991
  'sign-in-desk': {},
992
+ 'signed-reader': {},
984
993
  'supplier-comparison': {},
985
994
  'support-desk': {},
986
995
  'travel-desk': {},
@@ -1005,8 +1014,10 @@ export const APP_ESCALATIONS = {
1005
1014
  'data-quality': {},
1006
1015
  decide: {},
1007
1016
  'disaster-assessment': {},
1017
+ 'errand-runner': {},
1008
1018
  'event-response': {},
1009
1019
  'exchange-desk': {},
1020
+ 'guest-desk': {},
1010
1021
  'inbox-triage': {
1011
1022
  'google-workspace.batch_modify_gmail_message_labels': [
1012
1023
  {
@@ -1055,6 +1066,7 @@ export const APP_ESCALATIONS = {
1055
1066
  'shop-concierge': {},
1056
1067
  'shopping-desk': {},
1057
1068
  'sign-in-desk': {},
1069
+ 'signed-reader': {},
1058
1070
  'supplier-comparison': {},
1059
1071
  'support-desk': {},
1060
1072
  'travel-desk': {},
@@ -16,8 +16,10 @@ export declare const CUSTOMER_INTERVIEW_APP_0_0_1: AppSpec;
16
16
  export declare const DATA_QUALITY_APP_0_0_1: AppSpec;
17
17
  export declare const DECIDE_APP_0_0_1: AppSpec;
18
18
  export declare const DISASTER_ASSESSMENT_APP_0_0_1: AppSpec;
19
+ export declare const ERRAND_RUNNER_APP_0_0_1: AppSpec;
19
20
  export declare const EVENT_RESPONSE_APP_0_0_1: AppSpec;
20
21
  export declare const EXCHANGE_DESK_APP_0_0_1: AppSpec;
22
+ export declare const GUEST_DESK_APP_0_0_1: AppSpec;
21
23
  export declare const INBOX_TRIAGE_APP_0_0_1: AppSpec;
22
24
  export declare const LIVE_SHOP_CHAT_APP_0_0_1: AppSpec;
23
25
  export declare const MODEL_CHOICE_APP_0_0_1: AppSpec;
@@ -33,6 +35,7 @@ export declare const SHIP_OR_FIX_APP_0_0_1: AppSpec;
33
35
  export declare const SHOP_CONCIERGE_APP_0_0_1: AppSpec;
34
36
  export declare const SHOPPING_DESK_APP_0_0_1: AppSpec;
35
37
  export declare const SIGN_IN_DESK_APP_0_0_1: AppSpec;
38
+ export declare const SIGNED_READER_APP_0_0_1: AppSpec;
36
39
  export declare const SUPPLIER_COMPARISON_APP_0_0_1: AppSpec;
37
40
  export declare const SUPPORT_DESK_APP_0_0_1: AppSpec;
38
41
  export declare const TRAVEL_DESK_APP_0_0_1: AppSpec;