@foldspace_npm/harness 0.1.27 → 0.1.29
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/CLAUDE.md +301 -188
- package/README.md +54 -36
- package/bin/ask.mjs +9 -7
- package/bin/attach.mjs +80 -51
- package/bin/badge.mjs +28 -8
- package/bin/build.ts +19 -6
- package/bin/cli.mjs +23 -6
- package/bin/inject.mjs +3 -4
- package/bin/observe.mjs +22 -6
- package/bin/published.mjs +1 -1
- package/package.json +6 -2
- package/recipes/INDEX.md +13 -8
- package/recipes/README.md +30 -7
- package/recipes/account-overview/README.md +6 -6
- package/recipes/account-overview/agent/actions/show_account_overview.ts +11 -16
- package/recipes/account-overview/agent/views/brand.ts +23 -14
- package/recipes/account-overview/agent/views/overview.ts +13 -17
- package/recipes/bottom-bar/README.md +12 -9
- package/recipes/bottom-bar/agent/bottomBar.ts +14 -12
- package/recipes/create-a-record/README.md +97 -0
- package/recipes/create-a-record/agent/actions/create_project.ts +26 -0
- package/recipes/create-a-record/agent/api/projects.ts +62 -0
- package/recipes/create-a-record/agent/projects.ts +145 -0
- package/recipes/create-a-record/agent/views/approval.ts +181 -0
- package/recipes/create-a-record/agent/views/brand.ts +35 -0
- package/recipes/create-a-record/agent/views/fields.ts +252 -0
- package/recipes/create-a-record/agent/views/project-card.ts +253 -0
- package/recipes/create-a-record/agent/views/steps.ts +92 -0
- package/recipes/create-a-record/fixtures/owners.ok.json +9 -0
- package/recipes/create-a-record/fixtures/project.created.json +1 -0
- package/recipes/create-a-record/fixtures/project.rejected.json +1 -0
- package/recipes/create-a-record/recipe.json +13 -0
- package/recipes/create-a-record/variants/actions/create_project.steps.ts +160 -0
- package/recipes/find-by-name/README.md +3 -1
- package/recipes/find-by-name/agent/actions/find_project_id.ts +1 -1
- package/recipes/lookup-then-view/README.md +73 -0
- package/recipes/lookup-then-view/agent/accounts.ts +174 -0
- package/recipes/lookup-then-view/agent/actions/find_account.ts +108 -0
- package/recipes/lookup-then-view/agent/actions/show_account_overview.ts +42 -0
- package/recipes/lookup-then-view/agent/api/accounts.ts +68 -0
- package/recipes/lookup-then-view/agent/views/account.ts +183 -0
- package/recipes/lookup-then-view/agent/views/brand.ts +35 -0
- package/recipes/lookup-then-view/agent/views/format.ts +73 -0
- package/recipes/lookup-then-view/agent/views/pick-one.ts +240 -0
- package/recipes/lookup-then-view/fixtures/accounts.json +319 -0
- package/recipes/lookup-then-view/fixtures/overview.ok.json +122 -0
- package/recipes/lookup-then-view/fixtures/overview.unsigned.json +14 -0
- package/recipes/lookup-then-view/recipe.json +11 -0
- package/recipes/lookup-then-view/studio/find_account.json +10 -0
- package/recipes/lookup-then-view/studio/show_account_overview.json +15 -0
- package/recipes/opportunities-at-risk/README.md +5 -5
- package/recipes/opportunities-at-risk/agent/actions/show_opportunities_at_risk.ts +9 -7
- package/recipes/opportunities-at-risk/agent/opportunities.ts +11 -10
- package/recipes/opportunities-at-risk/agent/views/at-risk.ts +1 -1
- package/recipes/opportunities-at-risk/agent/views/brand.ts +26 -15
- package/recipes/pick-from-a-list/README.md +7 -6
- package/recipes/pick-from-a-list/agent/actions/choose_project.ts +27 -12
- package/recipes/pick-from-a-list/agent/views/brand.ts +30 -9
- package/recipes/pick-from-a-list/agent/views/picker.ts +3 -3
- package/recipes/prepare-for-a-meeting/README.md +4 -4
- package/recipes/prepare-for-a-meeting/agent/actions/prepare_for_meeting.ts +3 -3
- package/recipes/records-list/README.md +6 -6
- package/recipes/records-list/agent/accounts.ts +3 -1
- package/recipes/records-list/agent/actions/list_accounts.ts +7 -7
- package/recipes/records-list/agent/views/brand.ts +26 -14
- package/recipes/server-rendered-forms/README.md +35 -0
- package/recipes/server-rendered-forms/agent/actions/create_record_via_form.ts +62 -0
- package/recipes/server-rendered-forms/agent/api/pages.ts +57 -0
- package/recipes/server-rendered-forms/agent/forms.ts +192 -0
- package/recipes/server-rendered-forms/fixtures/form.errors.html +9 -0
- package/recipes/server-rendered-forms/fixtures/form.html +17 -0
- package/recipes/server-rendered-forms/fixtures/list.html +9 -0
- package/recipes/server-rendered-forms/fixtures/record.html +4 -0
- package/recipes/server-rendered-forms/recipe.json +10 -0
- package/recipes/several-hosts/README.md +10 -13
- package/recipes/several-hosts/agent/hosts.ts +19 -136
- package/recipes/several-hosts/fixtures/wrong-page.json +6 -0
- package/recipes/several-hosts/recipe.json +3 -3
- package/recipes/swap-the-login-method/README.md +16 -5
- package/recipes/swap-the-login-method/agent/utils.ts +52 -3
- package/recipes/swap-the-login-method/variants/utils.cookies.ts +59 -6
- package/recipes/update-meeting-notes/README.md +10 -4
- package/recipes/update-meeting-notes/agent/actions/update_meeting_notes.ts +16 -5
- package/recipes/upload-contacts/README.md +5 -5
- package/recipes/upload-contacts/agent/actions/upload_contacts.ts +7 -7
- package/recipes/upload-contacts/agent/views/brand.ts +24 -14
- package/recipes/upload-contacts/agent/views/uploader.ts +28 -17
- package/recipes/who-is-the-user/README.md +7 -3
- package/src/ask-core.mjs +3 -3
- package/src/attach-daemon.mjs +9 -2
- package/src/attach-helpers.mjs +105 -0
- package/src/attach-preflight.mjs +34 -11
- package/src/badge-core.mjs +1 -1
- package/src/cdp-client.mjs +14 -19
- package/src/cdp-ownership.mjs +41 -20
- package/src/cli-help.mjs +7 -5
- package/src/cli-registry.mjs +65 -46
- package/src/diagnostics.mjs +1 -1
- package/src/init.mjs +5 -3
- package/src/lint/index.mjs +10 -1
- package/src/lint/rules.mjs +371 -35
- package/src/observe-core.mjs +163 -34
- package/src/region.mjs +1 -1
- package/src/runtime/agent.ts +16 -12
- package/src/runtime/http.ts +25 -15
- package/src/runtime/match.ts +1 -1
- package/src/runtime/render.ts +4 -4
- package/src/transports/README.md +2 -2
- package/src/upgrade.mjs +33 -10
- package/templates/agent-starter/CLAUDE.md +8 -5
- package/templates/agent-starter/README.md +32 -33
- package/templates/agent-starter/agent/actions/_example.ts +4 -4
- package/templates/agent-starter/agent/actions/index.ts +2 -2
- package/templates/agent-starter/agent/constants.ts +4 -3
- package/templates/agent-starter/agent/utils.ts +15 -18
- package/templates/agent-starter/claude-settings.json +1 -1
- package/templates/agent-starter/docs/app-profile.md +19 -14
- package/templates/agent-starter/foldspace.dev.json +1 -1
- package/recipes/several-hosts/fixtures/nav-record.json +0 -5
|
@@ -24,63 +24,62 @@ npm run build
|
|
|
24
24
|
agent/
|
|
25
25
|
actions/ one file per action; register actions in index.ts
|
|
26
26
|
api/ one file per HTTP helper
|
|
27
|
+
views/ cards; brand.ts comes from `npx foldspace observe styles --write`
|
|
27
28
|
constants.ts product, agent, and domain identifiers
|
|
28
|
-
utils.ts
|
|
29
|
-
docs/
|
|
29
|
+
utils.ts configures the harness runtime; the only import surface for actions
|
|
30
|
+
docs/
|
|
31
|
+
app-profile.md what is known about this app, and how each fact was established
|
|
30
32
|
foldspace.dev.json
|
|
31
33
|
CLAUDE.md imports platform instructions from the installed harness
|
|
32
34
|
```
|
|
33
35
|
|
|
34
|
-
##
|
|
36
|
+
## What is known about this app
|
|
35
37
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
Good notes cover verified gotchas, failed approaches, design rationale, and
|
|
41
|
-
useful verification commands. Keep them concise and evidence-based. Before
|
|
42
|
-
similar work, read any relevant notes in `docs/`. Do not store secrets, cookies,
|
|
43
|
-
HAR files, browser storage, or other transient session data there.
|
|
38
|
+
`docs/app-profile.md` holds every fact established about this app, each with
|
|
39
|
+
how it was established, and every workaround. Never store secrets, cookies, HAR
|
|
40
|
+
files or browser storage there.
|
|
44
41
|
|
|
45
42
|
## Commands
|
|
46
43
|
|
|
47
44
|
```bash
|
|
48
|
-
npx foldspace help
|
|
49
|
-
npm run build
|
|
50
|
-
npm run dev
|
|
51
|
-
npm run inject
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
npx foldspace help # commands, risks, and next steps
|
|
46
|
+
npm run build # create dist/index.js
|
|
47
|
+
npm run dev # rebuild dist/index.js on changes
|
|
48
|
+
npm run inject # open the test window; the human signs in there
|
|
49
|
+
npx foldspace observe wait-login # returns when they are signed in
|
|
50
|
+
npx foldspace attach --daemon # load this build into the test window
|
|
54
51
|
```
|
|
55
52
|
|
|
56
|
-
Run `inject` before `attach`.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
`dist/index.js` directly through CDP. An empty local registry is valid,
|
|
61
|
-
so you can attach before implementing handlers. Coding agents should use
|
|
62
|
-
`attach --daemon` (or `npm run attach:daemon`) so the command returns after
|
|
53
|
+
Run `inject` before `attach`. `npx foldspace observe` reads the test window,
|
|
54
|
+
read-only, and works while `attach` runs. `attach` loads `dist/index.js` into
|
|
55
|
+
the page over CDP; an empty registry is valid, so attach before any handler
|
|
56
|
+
exists. Run it as `npx foldspace attach --daemon`: it returns after
|
|
63
57
|
registration instead of occupying the terminal.
|
|
64
58
|
|
|
65
|
-
|
|
59
|
+
The attach mode is read off the page:
|
|
66
60
|
|
|
67
61
|
- Default swap: the page already has the configured product and agent.
|
|
68
|
-
- `
|
|
69
|
-
- `
|
|
70
|
-
an SDK without the configured agent.
|
|
62
|
+
- `npx foldspace attach --bootstrap --daemon`: the page has no Foldspace SDK.
|
|
63
|
+
- `npx foldspace attach --replace --daemon`: the page has a different product
|
|
64
|
+
or agent, or an SDK without the configured agent.
|
|
71
65
|
|
|
72
66
|
Run `npx foldspace help attach` for full requirements and safety options. Coding
|
|
73
67
|
agents can read the versioned contract with `npx foldspace help --json`.
|
|
74
68
|
|
|
75
69
|
## Add an action
|
|
76
70
|
|
|
77
|
-
1.
|
|
78
|
-
|
|
79
|
-
|
|
71
|
+
1. Once the human has chosen the experience, observe what it needs in the
|
|
72
|
+
test window (`npx foldspace observe screen`, then `read`) and capture a
|
|
73
|
+
real 200 for the API the action will call.
|
|
74
|
+
2. Create the action as a draft with the Foldspace tools (`create_action`).
|
|
80
75
|
3. Copy `agent/actions/_example.ts` to `agent/actions/<action_key>.ts`.
|
|
81
76
|
4. Implement the handler from that observed request and response.
|
|
82
77
|
5. Import and register the handler in `agent/actions/index.ts`.
|
|
83
|
-
6. Type-check
|
|
84
|
-
|
|
78
|
+
6. Type-check and rebuild; if attach is running, `npx foldspace attach --stop`,
|
|
79
|
+
then `npx foldspace attach --daemon` with the flags it ran with. Ask the human
|
|
80
|
+
before publishing - the agent cannot call the action until it is published.
|
|
81
|
+
On a yes, publish it and prove it with
|
|
82
|
+
`npx foldspace ask "<question>" --retry 2`; on a not yet, it stays a draft:
|
|
83
|
+
say so in one line.
|
|
85
84
|
|
|
86
85
|
Action keys and parameter names must exactly match the action schema.
|
|
@@ -17,9 +17,9 @@ export const example_action = {
|
|
|
17
17
|
|
|
18
18
|
const result = await apiFetch<{ items?: Item[] }>("/__observe_me");
|
|
19
19
|
if (!result.ok) {
|
|
20
|
-
// Pass `reason` through so `render` can call renderFailure
|
|
21
|
-
// session, a 404 and a throttled call are three different
|
|
22
|
-
return {
|
|
20
|
+
// Pass `reason` through so `render` can call renderFailure - an expired
|
|
21
|
+
// session, a 404 and a throttled call are three different card states.
|
|
22
|
+
return { success: false, error: result.error, reason: result.reason };
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
const items = result.data.items ?? [];
|
|
@@ -30,6 +30,6 @@ export const example_action = {
|
|
|
30
30
|
|
|
31
31
|
// Zero matches is an answer, not an error. Return the query so the empty
|
|
32
32
|
// state can name what was searched for.
|
|
33
|
-
return {
|
|
33
|
+
return { success: true, query, matches };
|
|
34
34
|
},
|
|
35
35
|
};
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
//
|
|
3
3
|
// Registration here is the switch: the SDK transmits these to the server, and
|
|
4
4
|
// the server excludes any active action it does not receive. Comment an entry
|
|
5
|
-
// out and the
|
|
5
|
+
// out and the agent can no longer see it - no unpublishing required.
|
|
6
6
|
//
|
|
7
|
-
// LOAD_MODE "injected"
|
|
7
|
+
// LOAD_MODE "injected" puts the registry on window for attach to swap. "embedded"
|
|
8
8
|
// skips that assignment because the host page already loads Foldspace.
|
|
9
9
|
|
|
10
10
|
import { LOAD_MODE } from "../constants";
|
|
@@ -13,8 +13,9 @@ export const API_BASE = "";
|
|
|
13
13
|
* `{ kind: "localStorage", name: "access_token" }` or
|
|
14
14
|
* `{ kind: "cookie", name: "session" }`.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
* see
|
|
16
|
+
* Auth that is not Bearer (a custom header, or cookies with no token) needs
|
|
17
|
+
* its own `apiFetch` - see "Custom auth" in `agent/utils.ts`. Do not guess
|
|
18
|
+
* this value.
|
|
18
19
|
*/
|
|
19
20
|
export const AUTH_SOURCE:
|
|
20
21
|
| { kind: "localStorage" | "cookie"; name: string }
|
|
@@ -22,7 +23,7 @@ export const AUTH_SOURCE:
|
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* "injected" assigns `window.__FOLDSPACE_REMOTE_ACTIONS__` so attach can swap
|
|
25
|
-
* the bundle. "embedded" does not
|
|
26
|
+
* the bundle. "embedded" does not - the page already registers handlers
|
|
26
27
|
* through its own SDK snippet.
|
|
27
28
|
*/
|
|
28
29
|
export const LOAD_MODE: "injected" | "embedded" = "injected";
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
* ## What `export *` gives you
|
|
12
12
|
*
|
|
13
13
|
* HTTP (customer API, from the signed-in page):
|
|
14
|
-
* - `apiFetch`
|
|
15
|
-
*
|
|
14
|
+
* - `apiFetch` - JSON + `Authorization: Bearer`. For any other sign-in, see
|
|
15
|
+
* "Custom auth" below.
|
|
16
16
|
* - `apiFetchBinary` — same auth, raw bytes (file download).
|
|
17
17
|
* - `publicFetch` — no token; marketing / public origin only.
|
|
18
18
|
* - `getAuthToken` / `parseJwt` / `redact` — session read and safe logging.
|
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
* Foldspace SDK (thin wrappers; the SDK is the source of truth):
|
|
22
22
|
* - `getAgent` — overlay handle for `searchInList` and similar.
|
|
23
23
|
* - `rankBy` — substring first, then SDK fuzzy match. Not for domain ranking.
|
|
24
|
-
* - `armAllInstances`
|
|
25
|
-
*
|
|
26
|
-
*
|
|
24
|
+
* - `armAllInstances` - never in agent code: attach handles the page, and
|
|
25
|
+
* test mode is `npx foldspace attach --test-mode`, only for an agent that
|
|
26
|
+
* already has real users. Anything in `agent/` ships to production.
|
|
27
27
|
*
|
|
28
|
-
*
|
|
28
|
+
* Card states (unstyled; replace in `agent/views/` when brand matters):
|
|
29
29
|
* - `renderLoading` / `renderEmpty` / `renderError` / `renderFatal`
|
|
30
30
|
* - `renderFailure` — picks one of those from `ApiFailure.reason`, so an expired
|
|
31
31
|
* session, a 404 and a throttled call each draw the right state.
|
|
@@ -36,20 +36,17 @@
|
|
|
36
36
|
* Leave `API_BASE` and `AUTH_SOURCE` empty in `constants.ts` until you capture
|
|
37
37
|
* a real XHR. Empty config makes `apiFetch` return `{ ok: false }` instead of
|
|
38
38
|
* guessing a host. Errors are returned, never thrown; show `result.error` in
|
|
39
|
-
* the
|
|
39
|
+
* the card and log `result.detail` only.
|
|
40
40
|
*
|
|
41
|
-
* ##
|
|
41
|
+
* ## Custom auth (not Bearer)
|
|
42
42
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* export { getAgent, rankBy, renderEmpty, renderError, renderFatal, renderLoading };
|
|
51
|
-
* // Do not `export *` if that would re-export the default apiFetch.
|
|
52
|
-
* ```
|
|
43
|
+
* Read `node_modules/@foldspace_npm/harness/recipes/swap-the-login-method/README.md`
|
|
44
|
+
* and copy over this file the variant it names for what the app's requests
|
|
45
|
+
* carry: `agent/utils.ts` for a custom header, `variants/utils.cookies.ts` for
|
|
46
|
+
* cookies with no token. Each defines its own `apiFetch`, keeps the harness's
|
|
47
|
+
* failure `reason`s through `httpFailure` / `networkFailure`, and re-exports
|
|
48
|
+
* the other helpers by name - keep it that way: an `export *` would also bring
|
|
49
|
+
* in helpers such as `apiFetchBinary` that still use the default sign-in.
|
|
53
50
|
*/
|
|
54
51
|
|
|
55
52
|
import { configure } from "@foldspace_npm/harness/runtime";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$comment": "Written by foldspace init
|
|
2
|
+
"$comment": "Written by foldspace init: the commands a build runs, allowed up front so the coding agent is not stopped one denial at a time. init and upgrade are left out on purpose, so they ask, like anything not listed. foldspace deploy is Foldspace-internal and never runs in a build.",
|
|
3
3
|
"permissions": {
|
|
4
4
|
"allow": [
|
|
5
5
|
"Bash(npx foldspace help:*)",
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
Durable facts about `{{APP_DOMAIN}}` and its API, established by observing the
|
|
4
4
|
running app on **<date>** from a logged-in session.
|
|
5
5
|
|
|
6
|
-
**
|
|
7
|
-
|
|
8
|
-
quoting this file. Re-run the probe before trusting an entry
|
|
6
|
+
**Every entry says how it was established.** Where something is only
|
|
7
|
+
believed, mark it **assumed** in the same breath - do not promote an
|
|
8
|
+
assumption by quoting this file. Re-run the probe before trusting an entry
|
|
9
|
+
that matters.
|
|
9
10
|
|
|
10
11
|
Do not store secrets, cookies, HAR files, tokens, or session data here. Record
|
|
11
12
|
*where* the token lives and *what a redacted value looks like*, never the value.
|
|
@@ -13,9 +14,9 @@ Do not store secrets, cookies, HAR files, tokens, or session data here. Record
|
|
|
13
14
|
How to re-run any probe below:
|
|
14
15
|
|
|
15
16
|
```bash
|
|
16
|
-
npm run inject
|
|
17
|
-
|
|
18
|
-
#
|
|
17
|
+
npm run inject # only if the test window is closed; the human signs in there
|
|
18
|
+
npx foldspace observe wait-login
|
|
19
|
+
# observe reads the test window, and works while attach runs.
|
|
19
20
|
npx foldspace observe screen --click "<a label from observe menu>" --match "<words for the data>"
|
|
20
21
|
npx foldspace observe read "<the GET path screen returned>"
|
|
21
22
|
```
|
|
@@ -27,9 +28,9 @@ Start URL: `{{START_URL}}`
|
|
|
27
28
|
## Auth
|
|
28
29
|
|
|
29
30
|
- Where the token lives (cookie name, `localStorage` key, custom header), and
|
|
30
|
-
how you know
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
how you know: `npx foldspace observe auth` reports where each header value
|
|
32
|
+
comes from, never the value.
|
|
33
|
+
- Its lifetime, measured: a short one ends probing sessions halfway.
|
|
33
34
|
- What an expired or refused response looks like, verbatim. A CORS-less refusal
|
|
34
35
|
often surfaces as `TypeError: Failed to fetch` with no status — that is not
|
|
35
36
|
"host down".
|
|
@@ -43,8 +44,11 @@ Start URL: `{{START_URL}}`
|
|
|
43
44
|
|
|
44
45
|
## Account contents
|
|
45
46
|
|
|
46
|
-
Row counts
|
|
47
|
-
|
|
47
|
+
Row counts for the experience being built, checked when you observe it. Zero
|
|
48
|
+
rows makes a demo of an empty state: say so and offer the next experience. On
|
|
49
|
+
an account with nothing to read at all, say so and offer first a card that
|
|
50
|
+
creates a record when the end user approves
|
|
51
|
+
(`node_modules/@foldspace_npm/harness/recipes/create-a-record`).
|
|
48
52
|
|
|
49
53
|
| Resource | Count | How established |
|
|
50
54
|
|---|---|---|
|
|
@@ -93,9 +97,10 @@ Mark which you verified and which came from reading the app bundle.
|
|
|
93
97
|
|
|
94
98
|
## Brand (sampled, not guessed)
|
|
95
99
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
100
|
+
`npx foldspace observe styles --write` samples the app's accent, text, border,
|
|
101
|
+
font and radius and writes `agent/views/brand.ts`. Record here the page and
|
|
102
|
+
date it sampled, and anything it fell back on. Re-sample per product; never
|
|
103
|
+
copy values across.
|
|
99
104
|
|
|
100
105
|
## Endpoints seen in the bundle but NOT called
|
|
101
106
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$comment": "Generated by foldspace init. Use the bare product ID, not the EU
|
|
2
|
+
"$comment": "Generated by foldspace init. Use the bare product ID, not the SDK key (the one that starts with EU-, in every region).",
|
|
3
3
|
"defaultTarget": "app",
|
|
4
4
|
"region": {{REGION_JSON}},
|
|
5
5
|
"sdkUrl": {{SDK_URL_JSON}},
|