@agentshouse/kit 0.1.0-alpha.32 → 0.1.0-alpha.33
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/README.md +32 -1
- package/bin/connect-linux.sh +87 -30
- package/bin/kit.js +1 -0
- package/bootstrap.json +3 -3
- package/package.json +1 -1
- package/src/login.js +4 -3
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ The Skill set Kit installs is Matt Pocock's
|
|
|
12
12
|
On Linux `amd64` or `arm64`, run the published, versioned bootstrap once:
|
|
13
13
|
|
|
14
14
|
```sh
|
|
15
|
-
bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.
|
|
15
|
+
bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.33/connect-linux.sh)
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
The bootstrap first establishes Docker Engine from what it observes on the
|
|
@@ -145,6 +145,37 @@ matching directory beneath `/agents/house`, through the resident's lease when
|
|
|
145
145
|
the container runs, and refuses a directory outside the workspace before
|
|
146
146
|
anything reaches House.
|
|
147
147
|
|
|
148
|
+
## Connect a macOS computer
|
|
149
|
+
|
|
150
|
+
On a Mac with Apple silicon or an Intel chip, run the same
|
|
151
|
+
`bash <(curl -fsSL …/connect-linux.sh)` command as
|
|
152
|
+
[a Linux computer](#connect-a-linux-computer). It runs in macOS's own
|
|
153
|
+
`bash` and reads the chip from the hardware, so it selects `arm64` or `amd64`
|
|
154
|
+
correctly even from a terminal running under Rosetta.
|
|
155
|
+
|
|
156
|
+
The bootstrap first establishes Docker Desktop from what it observes. A Docker
|
|
157
|
+
Desktop that already answers is used as it is. Without
|
|
158
|
+
`/Applications/Docker.app` it follows Docker's
|
|
159
|
+
[command-line installation](https://docs.docker.com/desktop/setup/install/mac-install/):
|
|
160
|
+
it downloads `Docker.dmg` for the chip from `desktop.docker.com`, attaches it
|
|
161
|
+
through `sudo hdiutil`, runs its `install --user=<account>` through `sudo`, and
|
|
162
|
+
detaches it. `sudo` asks for the password as it ordinarily does, and the
|
|
163
|
+
installer does not accept Docker's Subscription Service Agreement for you. The
|
|
164
|
+
bootstrap then opens Docker Desktop, including one a restart left stopped, and
|
|
165
|
+
waits up to five minutes for its Linux containers to answer while you accept
|
|
166
|
+
its terms or finish what its window asks. If Docker Desktop is still not ready,
|
|
167
|
+
or the download or the installer fails, the bootstrap names the cause and changes
|
|
168
|
+
nothing further; running it again continues from that state. Docker Desktop
|
|
169
|
+
built for the other chip is refused. The bootstrap reaches `docker` in
|
|
170
|
+
Docker Desktop's app bundle when no terminal has added it to `PATH` yet.
|
|
171
|
+
|
|
172
|
+
Everything after that is the Linux journey above, with `open` as the host
|
|
173
|
+
browser opener. With an HTTPS House, login's container publishes its one
|
|
174
|
+
callback port on the Mac's `127.0.0.1`, so House Login returns from the Mac's
|
|
175
|
+
browser, and no Kit container uses the host network. A loopback House needs
|
|
176
|
+
Docker Desktop's host networking enabled. A `--workspace` root must be a
|
|
177
|
+
directory Docker Desktop shares, such as one under `/Users`.
|
|
178
|
+
|
|
148
179
|
## Building the package
|
|
149
180
|
|
|
150
181
|
The repository checkout is the development source. Build the package that can
|
package/bin/connect-linux.sh
CHANGED
|
@@ -15,7 +15,12 @@ FORWARD=()
|
|
|
15
15
|
UBUNTU_RELEASES=(jammy noble resolute)
|
|
16
16
|
DOCKER_PACKAGES=(docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin)
|
|
17
17
|
CONFLICTING_PACKAGES=(docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc)
|
|
18
|
-
|
|
18
|
+
DOCKER_APP=/Applications/Docker.app
|
|
19
|
+
DESKTOP_WAIT_SECONDS=300
|
|
20
|
+
RUNTIME='Docker Engine'
|
|
21
|
+
OPENER=xdg-open
|
|
22
|
+
NETWORK=(--network host)
|
|
23
|
+
PUBLISHED=()
|
|
19
24
|
|
|
20
25
|
refuse() {
|
|
21
26
|
printf 'kit_bootstrap_refused: %s\n' "$1" >&2
|
|
@@ -32,11 +37,11 @@ login_kit() {
|
|
|
32
37
|
"${DOCKER_RUN[@]:0:2}" -i "${DOCKER_RUN[@]:2}" login "$@"
|
|
33
38
|
return
|
|
34
39
|
fi
|
|
35
|
-
command -v
|
|
36
|
-
"${DOCKER_RUN[@]}" login "$@" --host-browser | while IFS= read -r line; do
|
|
40
|
+
command -v "$OPENER" >/dev/null || refuse 'no host browser opener; rerun with --manual'
|
|
41
|
+
"${DOCKER_RUN[@]:0:2}" ${PUBLISHED[@]+"${PUBLISHED[@]}"} "${DOCKER_RUN[@]:2}" login "$@" --host-browser | while IFS= read -r line; do
|
|
37
42
|
if [[ "$line" == 'Open '* ]]; then
|
|
38
43
|
printf 'Opening House Login in the host browser.\n'
|
|
39
|
-
|
|
44
|
+
"$OPENER" "${line#Open }" >/dev/null 2>&1 || refuse 'host browser did not open; rerun with --manual'
|
|
40
45
|
else
|
|
41
46
|
printf '%s\n' "$line"
|
|
42
47
|
fi
|
|
@@ -46,15 +51,7 @@ login_kit() {
|
|
|
46
51
|
start_resident() {
|
|
47
52
|
local network=()
|
|
48
53
|
[[ "$HOUSE" == https://* ]] || network=(--network host)
|
|
49
|
-
docker run -d --name "$NAME" --restart unless-stopped "${network[@]}" --user "$(id -u):$(id -g)" --mount "$KIT_HOME_MOUNT" --mount "$WORKSPACE_MOUNT" --label "agentshouse.house=$HOUSE" --env HOME=/kit-home --env HOUSE_KIT_HOME=/kit-home --env HOUSE_KIT_CONTAINER=1 "$IMAGE" resident >/dev/null
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
docker() {
|
|
53
|
-
if ((DOCKER_GROUP)); then
|
|
54
|
-
sudo -u "$(id -un)" docker "$@"
|
|
55
|
-
else
|
|
56
|
-
command docker "$@"
|
|
57
|
-
fi
|
|
54
|
+
docker run -d --name "$NAME" --restart unless-stopped ${network[@]+"${network[@]}"} --user "$(id -u):$(id -g)" --mount "$KIT_HOME_MOUNT" --mount "$WORKSPACE_MOUNT" --label "agentshouse.house=$HOUSE" --env HOME=/kit-home --env HOUSE_KIT_HOME=/kit-home --env HOUSE_KIT_CONTAINER=1 "$IMAGE" resident >/dev/null
|
|
58
55
|
}
|
|
59
56
|
|
|
60
57
|
elevated() {
|
|
@@ -82,6 +79,41 @@ unanswered() {
|
|
|
82
79
|
printf '; Docker Engine does not answer: %s' "$(docker_cause)"
|
|
83
80
|
}
|
|
84
81
|
|
|
82
|
+
install_docker_desktop() {
|
|
83
|
+
local mount
|
|
84
|
+
DOWNLOAD=$(mktemp -d) || refuse 'no temporary directory for the Docker Desktop download'
|
|
85
|
+
trap 'rm -rf "$DOWNLOAD"' EXIT
|
|
86
|
+
mount="$DOWNLOAD/volume"
|
|
87
|
+
printf 'Downloading Docker Desktop for %s from docker.com.\n' "$1"
|
|
88
|
+
curl -fsSL -o "$DOWNLOAD/Docker.dmg" "https://desktop.docker.com/mac/main/$ARCH/Docker.dmg" || refuse 'Docker Desktop could not be downloaded'
|
|
89
|
+
printf 'Installing Docker Desktop in /Applications; sudo may ask for your password.\n'
|
|
90
|
+
mkdir "$mount" || refuse 'the Docker Desktop disk image has no mount point'
|
|
91
|
+
elevated hdiutil attach -nobrowse -readonly -mountpoint "$mount" "$DOWNLOAD/Docker.dmg" >/dev/null || refuse 'the Docker Desktop disk image could not be opened'
|
|
92
|
+
if ! elevated "$mount/Docker.app/Contents/MacOS/install" "--user=$(id -un)"; then
|
|
93
|
+
elevated hdiutil detach "$mount" >/dev/null 2>&1 || true
|
|
94
|
+
refuse 'the Docker Desktop installer did not finish'
|
|
95
|
+
fi
|
|
96
|
+
elevated hdiutil detach "$mount" >/dev/null || refuse 'the Docker Desktop disk image could not be closed'
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
establish_docker_desktop() {
|
|
100
|
+
PATH="$PATH:$DOCKER_APP/Contents/Resources/bin"
|
|
101
|
+
docker_answers && return
|
|
102
|
+
if ((FORWARDING)); then
|
|
103
|
+
[[ -d "$DOCKER_APP" ]] || refuse 'House Kit is not installed; run this bootstrap without arguments first'
|
|
104
|
+
refuse "Docker Desktop does not answer: $(docker_cause); start Docker Desktop, then rerun this command"
|
|
105
|
+
fi
|
|
106
|
+
[[ -d "$DOCKER_APP" ]] || install_docker_desktop "$1"
|
|
107
|
+
printf 'Starting Docker Desktop; accept its terms or finish its setup if its window asks.\n'
|
|
108
|
+
open "$DOCKER_APP" || refuse 'Docker Desktop could not be opened'
|
|
109
|
+
local waited=0
|
|
110
|
+
until docker_answers; do
|
|
111
|
+
((waited < DESKTOP_WAIT_SECONDS)) || refuse "Docker Desktop is not ready: $(docker_cause); finish what its window asks, then rerun this bootstrap"
|
|
112
|
+
sleep 2
|
|
113
|
+
waited=$((waited + 2))
|
|
114
|
+
done
|
|
115
|
+
}
|
|
116
|
+
|
|
85
117
|
installed_packages() {
|
|
86
118
|
{ dpkg-query -W -f='${db:Status-Status} ${Package}\n' "$@" 2>/dev/null || true; } | awk '$1 == "installed" { print $2 }'
|
|
87
119
|
}
|
|
@@ -132,7 +164,7 @@ establish_docker_engine() {
|
|
|
132
164
|
printf 'Adding %s to the docker group; it applies from your next sign-in, so this run reaches Docker Engine through sudo.\n' "$(id -un)"
|
|
133
165
|
elevated usermod -aG docker "$(id -un)" || refuse "$(id -un) could not be added to the docker group"
|
|
134
166
|
fi
|
|
135
|
-
docker_member ||
|
|
167
|
+
docker_member || docker() { sudo -u "$(id -un)" docker "$@"; }
|
|
136
168
|
fi
|
|
137
169
|
docker_answers || refuse "Docker Engine does not answer: $(docker_cause)"
|
|
138
170
|
}
|
|
@@ -172,20 +204,39 @@ while (($#)); do
|
|
|
172
204
|
done
|
|
173
205
|
|
|
174
206
|
[[ "$IMAGE" =~ ^ghcr\.io/agentshouse/kit@sha256:[0-9a-f]{64}$ ]] || refuse 'this bootstrap has not been published with an immutable image digest'
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
207
|
+
case "$(uname -s)" in
|
|
208
|
+
Linux)
|
|
209
|
+
case "$(uname -m)" in
|
|
210
|
+
x86_64|amd64) ARCH=amd64 ;;
|
|
211
|
+
aarch64|arm64) ARCH=arm64 ;;
|
|
212
|
+
*) refuse "unsupported Linux architecture $(uname -m)" ;;
|
|
213
|
+
esac
|
|
214
|
+
;;
|
|
215
|
+
Darwin)
|
|
216
|
+
RUNTIME='Docker Desktop'
|
|
217
|
+
OPENER=open
|
|
218
|
+
if [[ "$(sysctl -n hw.optional.arm64 2>/dev/null)" == 1 ]]; then
|
|
219
|
+
ARCH=arm64
|
|
220
|
+
CHIP='Apple silicon'
|
|
221
|
+
else
|
|
222
|
+
ARCH=amd64
|
|
223
|
+
CHIP='an Intel chip'
|
|
224
|
+
fi
|
|
225
|
+
;;
|
|
226
|
+
*) refuse "unsupported host $(uname -s); this bootstrap is for Linux and macOS" ;;
|
|
180
227
|
esac
|
|
181
228
|
[[ "$WORKSPACE" == /* ]] || refuse 'workspace root must be an absolute path'
|
|
182
|
-
|
|
229
|
+
if [[ "$RUNTIME" == 'Docker Desktop' ]]; then
|
|
230
|
+
establish_docker_desktop "$CHIP"
|
|
231
|
+
else
|
|
232
|
+
establish_docker_engine
|
|
233
|
+
fi
|
|
183
234
|
case "$DOCKER_PLATFORM" in
|
|
184
235
|
linux/x86_64|linux/amd64) DOCKER_ARCH=amd64 ;;
|
|
185
236
|
linux/aarch64|linux/arm64) DOCKER_ARCH=arm64 ;;
|
|
186
|
-
*) refuse "unsupported
|
|
237
|
+
*) refuse "unsupported $RUNTIME platform $DOCKER_PLATFORM" ;;
|
|
187
238
|
esac
|
|
188
|
-
[[ "$DOCKER_ARCH" == "$ARCH" ]] || refuse "host linux/$ARCH does not match
|
|
239
|
+
[[ "$DOCKER_ARCH" == "$ARCH" ]] || refuse "host linux/$ARCH does not match $RUNTIME linux/$DOCKER_ARCH"
|
|
189
240
|
if ((FORWARDING)); then
|
|
190
241
|
docker container inspect "$NAME" >/dev/null 2>&1 || refuse 'House Kit is not installed; run this bootstrap without arguments first'
|
|
191
242
|
WORKSPACE=$(docker inspect --format '{{range .Mounts}}{{if eq .Destination "/agents/house"}}{{.Source}}{{end}}{{end}}' "$NAME")
|
|
@@ -197,7 +248,7 @@ for path in "$KIT_HOME" "$WORKSPACE"; do
|
|
|
197
248
|
[[ ! -L "$path" ]] || refuse "$path is a symbolic link"
|
|
198
249
|
mkdir -p -m 700 "$path" || refuse "$path could not be created"
|
|
199
250
|
[[ -d "$path" ]] || refuse "$path is not a directory"
|
|
200
|
-
[[
|
|
251
|
+
[[ -O "$path" ]] || refuse "$path is not owned by this User"
|
|
201
252
|
chmod 700 "$path" || refuse "$path could not be owner-protected"
|
|
202
253
|
done
|
|
203
254
|
KIT_HOME=$(cd "$KIT_HOME" && pwd -P)
|
|
@@ -219,13 +270,19 @@ if ((FORWARDING == 0)); then
|
|
|
219
270
|
if [[ ! -f "$KIT_HOME/kit.json" ]]; then
|
|
220
271
|
printf '{"availability":"intermittent","execution_authority":"allow","migrators":[],"instances":[],"clients":[],"host":null,"skills":%s}\n' "$SKILLS" > "$KIT_HOME/kit.json"
|
|
221
272
|
elif grep -Eq '"skills"[[:space:]]*:' "$KIT_HOME/kit.json"; then
|
|
222
|
-
sed -E -i "s/(\"skills\"[[:space:]]*:[[:space:]]*)(true|false)/\\1$SKILLS/" "$KIT_HOME/kit.json" || refuse 'kit.json could not be updated'
|
|
273
|
+
sed -E -i.old "s/(\"skills\"[[:space:]]*:[[:space:]]*)(true|false)/\\1$SKILLS/" "$KIT_HOME/kit.json" || refuse 'kit.json could not be updated'
|
|
223
274
|
else
|
|
224
|
-
sed -E -i "1s/^([[:space:]]*)\\{/\\1{\"skills\":$SKILLS,/" "$KIT_HOME/kit.json" || refuse 'kit.json could not be updated'
|
|
275
|
+
sed -E -i.old "1s/^([[:space:]]*)\\{/\\1{\"skills\":$SKILLS,/" "$KIT_HOME/kit.json" || refuse 'kit.json could not be updated'
|
|
225
276
|
fi
|
|
277
|
+
rm -f "$KIT_HOME/kit.json.old"
|
|
226
278
|
grep -Eq "\"skills\"[[:space:]]*:[[:space:]]*$SKILLS" "$KIT_HOME/kit.json" || refuse 'kit.json could not be updated'
|
|
227
279
|
fi
|
|
228
|
-
|
|
280
|
+
if [[ "$RUNTIME" == 'Docker Desktop' && "$HOUSE" == https://* ]]; then
|
|
281
|
+
NETWORK=()
|
|
282
|
+
LOGIN_PORT=$((49152 + RANDOM % 16384))
|
|
283
|
+
PUBLISHED=(--publish "127.0.0.1:$LOGIN_PORT:$LOGIN_PORT" --env "HOUSE_KIT_LOGIN_PORT=$LOGIN_PORT")
|
|
284
|
+
fi
|
|
285
|
+
DOCKER_RUN=(docker run --rm ${NETWORK[@]+"${NETWORK[@]}"} --user "$(id -u):$(id -g)" --mount "$KIT_HOME_MOUNT" --mount "$WORKSPACE_MOUNT" --env HOME=/kit-home --env HOUSE_KIT_HOME=/kit-home --env HOUSE_KIT_CONTAINER=1 --env "HOUSE_KIT_HOSTNAME=$(hostname)" "$IMAGE")
|
|
229
286
|
|
|
230
287
|
if docker container inspect "$NAME" >/dev/null 2>&1; then
|
|
231
288
|
INSTALLED_IMAGE=$(docker inspect --format '{{.Config.Image}}' "$NAME")
|
|
@@ -240,7 +297,7 @@ if docker container inspect "$NAME" >/dev/null 2>&1; then
|
|
|
240
297
|
[[ -f "$KIT_HOME/credential" && -f "$KIT_HOME/environment" && -f "$KIT_HOME/house" ]] || refuse 'the installed Kit has no enrolled authority'
|
|
241
298
|
if ((FORWARDING)); then
|
|
242
299
|
if [[ "$FORWARDED" == kit && "${FORWARD[0]:-}" == login ]]; then
|
|
243
|
-
login_kit "${FORWARD[@]:1}"
|
|
300
|
+
login_kit ${FORWARD[@]+"${FORWARD[@]:1}"}
|
|
244
301
|
exit 0
|
|
245
302
|
fi
|
|
246
303
|
PLACED=()
|
|
@@ -259,9 +316,9 @@ if docker container inspect "$NAME" >/dev/null 2>&1; then
|
|
|
259
316
|
stty -echo
|
|
260
317
|
fi
|
|
261
318
|
if [[ "$(docker inspect --format '{{.State.Running}}' "$NAME")" == true ]]; then
|
|
262
|
-
docker exec -i "${TERMINAL[@]}" "${PLACED[@]}" "$NAME" "$FORWARDED" "${FORWARD[@]}"
|
|
319
|
+
docker exec -i ${TERMINAL[@]+"${TERMINAL[@]}"} ${PLACED[@]+"${PLACED[@]}"} "$NAME" "$FORWARDED" ${FORWARD[@]+"${FORWARD[@]}"}
|
|
263
320
|
else
|
|
264
|
-
"${DOCKER_RUN[@]:0:2}" -i "${TERMINAL[@]}" "${PLACED[@]}" "${ENTRY[@]}" "${DOCKER_RUN[@]:2}" "${FORWARD[@]}"
|
|
321
|
+
"${DOCKER_RUN[@]:0:2}" -i ${TERMINAL[@]+"${TERMINAL[@]}"} ${PLACED[@]+"${PLACED[@]}"} ${ENTRY[@]+"${ENTRY[@]}"} "${DOCKER_RUN[@]:2}" ${FORWARD[@]+"${FORWARD[@]}"}
|
|
265
322
|
fi
|
|
266
323
|
exit
|
|
267
324
|
fi
|
|
@@ -294,7 +351,7 @@ else
|
|
|
294
351
|
LOGIN=()
|
|
295
352
|
if ((HOUSE_SELECTED)); then LOGIN+=(--house "$HOUSE"); fi
|
|
296
353
|
if ((MANUAL)); then LOGIN+=(--manual); fi
|
|
297
|
-
login_kit "${LOGIN[@]}"
|
|
354
|
+
login_kit ${LOGIN[@]+"${LOGIN[@]}"}
|
|
298
355
|
fi
|
|
299
356
|
|
|
300
357
|
"${DOCKER_RUN[@]}" apply
|
package/bin/kit.js
CHANGED
|
@@ -119,6 +119,7 @@ async function runLogin(args, directory) {
|
|
|
119
119
|
directory,
|
|
120
120
|
hostBrowser: values["--host-browser"] === true,
|
|
121
121
|
hostname: process.env.HOUSE_KIT_HOSTNAME,
|
|
122
|
+
loginPort: process.env.HOUSE_KIT_LOGIN_PORT,
|
|
122
123
|
...(values["--environment"] === undefined ? {} : { environment: values["--environment"] }),
|
|
123
124
|
});
|
|
124
125
|
}
|
package/bootstrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.33",
|
|
4
4
|
"hosts": {
|
|
5
5
|
"linux": {
|
|
6
|
-
"command": "bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.
|
|
6
|
+
"command": "bash <(curl -fsSL https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.33/connect-linux.sh)",
|
|
7
7
|
"asset": {
|
|
8
8
|
"name": "connect-linux.sh",
|
|
9
|
-
"url": "https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.
|
|
9
|
+
"url": "https://github.com/agentshouse/kit/releases/download/v0.1.0-alpha.33/connect-linux.sh"
|
|
10
10
|
},
|
|
11
11
|
"workspace": {
|
|
12
12
|
"argument": "--workspace",
|
package/package.json
CHANGED
package/src/login.js
CHANGED
|
@@ -216,11 +216,12 @@ export async function login(options, dependencies = {}) {
|
|
|
216
216
|
return { environment: exchanged.environment };
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
export async function loopback() {
|
|
219
|
+
export async function loopback(published) {
|
|
220
220
|
const server = createServer();
|
|
221
221
|
await new Promise((resolve, reject) => {
|
|
222
222
|
server.once("error", reject);
|
|
223
|
-
server.listen(0, "127.0.0.1", resolve);
|
|
223
|
+
if (published === undefined) server.listen(0, "127.0.0.1", resolve);
|
|
224
|
+
else server.listen(Number(published), "0.0.0.0", resolve);
|
|
224
225
|
});
|
|
225
226
|
const address = server.address();
|
|
226
227
|
const code = new Promise((resolve, reject) => {
|
|
@@ -274,7 +275,7 @@ export async function connectHouse(options, dependencies = {}) {
|
|
|
274
275
|
refuseDrained(options.directory);
|
|
275
276
|
await prepareLaunchBase(launchBase(dependencies));
|
|
276
277
|
if (options.presentation === "manual") return login(options, dependencies);
|
|
277
|
-
const callback = await loopback();
|
|
278
|
+
const callback = await loopback(options.loginPort);
|
|
278
279
|
try {
|
|
279
280
|
return await login(
|
|
280
281
|
{ ...options, presentation: "loopback", loopbackUri: callback.uri },
|