@frankzhang2026/opencode-android-orchestrator 1.0.1 → 1.0.2
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/CHANGELOG.md +9 -1
- package/README.md +8 -6
- package/dist/installer/opencode-config.d.ts +3 -3
- package/dist/installer/opencode-config.js +1 -1
- package/docs/MIGRATION.md +7 -7
- package/docs/QUEUE.md +5 -1
- package/docs/SECURITY.md +9 -1
- package/docs/TROUBLESHOOTING.md +8 -7
- package/package.json +1 -1
- package/templates/scripts/automation/lib.sh +34 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
## 1.0.
|
|
4
|
+
## 1.0.2 - 2026-09-15
|
|
5
|
+
|
|
6
|
+
- Accept queue-owned OpenCode shell commands that remain descendants of the
|
|
7
|
+
active Worker even when the tool runtime creates a separate process group.
|
|
8
|
+
- Keep run-id, queue-key and Worker ancestry checks so unrelated processes
|
|
9
|
+
cannot claim a queued task by copying its public run identifier.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## 1.0.1 - 2026-09-15
|
|
5
13
|
|
|
6
14
|
- Keep planning snapshots small in large repositories by returning only the
|
|
7
15
|
stable branch and commit metadata from the initial snapshot call.
|
package/README.md
CHANGED
|
@@ -30,7 +30,9 @@ fixed-directory tasks may use `autoCommit`. Optional isolated worktrees keep
|
|
|
30
30
|
human acceptance while allowing independent tasks to proceed. Every queued
|
|
31
31
|
execution requires build, fresh full unit tests and independent Review. All
|
|
32
32
|
completion and recovery paths remain local and never push.
|
|
33
|
-
Version `1.0.
|
|
33
|
+
Version `1.0.2` also allows queue-owned OpenCode shell commands to use a
|
|
34
|
+
separate process group while preserving Worker ancestry checks. Version `1.0.1`
|
|
35
|
+
bounds Planner snapshot output for large repositories. The
|
|
34
36
|
initial snapshot returns only the target branch and fixed commit; path discovery
|
|
35
37
|
and file content use bounded cursor pages tied to that commit.
|
|
36
38
|
|
|
@@ -69,9 +71,9 @@ project builds retain their configured cache behavior.
|
|
|
69
71
|
## Quick start
|
|
70
72
|
|
|
71
73
|
```sh
|
|
72
|
-
npx @frankzhang2026/opencode-android-orchestrator@1.0.
|
|
74
|
+
npx @frankzhang2026/opencode-android-orchestrator@1.0.2 init .
|
|
73
75
|
$EDITOR automation/automation-commit-prefix
|
|
74
|
-
npx @frankzhang2026/opencode-android-orchestrator@1.0.
|
|
76
|
+
npx @frankzhang2026/opencode-android-orchestrator@1.0.2 doctor .
|
|
75
77
|
opencode --agent scheduled-planner .
|
|
76
78
|
```
|
|
77
79
|
|
|
@@ -81,7 +83,7 @@ a task contract without selecting a primary module. To intentionally restrict
|
|
|
81
83
|
generated contracts to one module, opt into primary-module scope:
|
|
82
84
|
|
|
83
85
|
```sh
|
|
84
|
-
npx @frankzhang2026/opencode-android-orchestrator@1.0.
|
|
86
|
+
npx @frankzhang2026/opencode-android-orchestrator@1.0.2 init . \
|
|
85
87
|
--module-scope primary \
|
|
86
88
|
--primary-module :mobile
|
|
87
89
|
```
|
|
@@ -109,7 +111,7 @@ For an existing manifest-managed installation whose generated module/task
|
|
|
109
111
|
lists are incomplete, refresh all derived Gradle data in one upgrade:
|
|
110
112
|
|
|
111
113
|
```sh
|
|
112
|
-
npx @frankzhang2026/opencode-android-orchestrator@1.0.
|
|
114
|
+
npx @frankzhang2026/opencode-android-orchestrator@1.0.2 upgrade . \
|
|
113
115
|
--refresh-gradle-discovery
|
|
114
116
|
```
|
|
115
117
|
|
|
@@ -435,7 +437,7 @@ preparation alone as resource installation;
|
|
|
435
437
|
## Init
|
|
436
438
|
|
|
437
439
|
```sh
|
|
438
|
-
npx @frankzhang2026/opencode-android-orchestrator@1.0.
|
|
440
|
+
npx @frankzhang2026/opencode-android-orchestrator@1.0.2 init .
|
|
439
441
|
$EDITOR automation/automation-commit-prefix
|
|
440
442
|
opencode --agent scheduled-planner .
|
|
441
443
|
```
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export declare const ORCHESTRATOR_PACKAGE_NAME = "@frankzhang2026/opencode-android-orchestrator";
|
|
2
|
-
export declare const ORCHESTRATOR_PACKAGE_VERSION = "1.0.
|
|
3
|
-
export declare const ORCHESTRATOR_PLUGIN_REFERENCE = "@frankzhang2026/opencode-android-orchestrator@1.0.
|
|
2
|
+
export declare const ORCHESTRATOR_PACKAGE_VERSION = "1.0.2";
|
|
3
|
+
export declare const ORCHESTRATOR_PLUGIN_REFERENCE = "@frankzhang2026/opencode-android-orchestrator@1.0.2";
|
|
4
4
|
export declare const OPENCODE_CONFIG_SCHEMA_URL = "https://opencode.ai/config.json";
|
|
5
|
-
export declare const REQUIRED_PLUGIN_REFERENCES: readonly ["@frankzhang2026/opencode-android-orchestrator@1.0.
|
|
5
|
+
export declare const REQUIRED_PLUGIN_REFERENCES: readonly ["@frankzhang2026/opencode-android-orchestrator@1.0.2"];
|
|
6
6
|
export type OpenCodeConfigMergeErrorCode = "AMBIGUOUS_CONFIG" | "CONFIG_NOT_FILE" | "CONFIG_READ_FAILED" | "CONFIG_SYMLINK" | "DUPLICATE_PLUGIN" | "DUPLICATE_PROPERTY" | "INVALID_JSONC" | "INVALID_PLUGIN_ENTRY" | "PLUGIN_NOT_ARRAY" | "PLUGIN_VERSION_CONFLICT" | "ROOT_NOT_OBJECT" | "TARGET_NOT_DIRECTORY";
|
|
7
7
|
export declare class OpenCodeConfigMergeError extends Error {
|
|
8
8
|
readonly code: OpenCodeConfigMergeErrorCode;
|
|
@@ -2,7 +2,7 @@ import { lstatSync, readFileSync, statSync } from "node:fs";
|
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
import { applyEdits, findNodeAtLocation, getNodeValue, modify, parseTree, printParseErrorCode, } from "jsonc-parser";
|
|
4
4
|
export const ORCHESTRATOR_PACKAGE_NAME = "@frankzhang2026/opencode-android-orchestrator";
|
|
5
|
-
export const ORCHESTRATOR_PACKAGE_VERSION = "1.0.
|
|
5
|
+
export const ORCHESTRATOR_PACKAGE_VERSION = "1.0.2";
|
|
6
6
|
export const ORCHESTRATOR_PLUGIN_REFERENCE = `${ORCHESTRATOR_PACKAGE_NAME}@${ORCHESTRATOR_PACKAGE_VERSION}`;
|
|
7
7
|
export const OPENCODE_CONFIG_SCHEMA_URL = "https://opencode.ai/config.json";
|
|
8
8
|
export const REQUIRED_PLUGIN_REFERENCES = [
|
package/docs/MIGRATION.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Migration guide
|
|
2
2
|
|
|
3
3
|
This guide covers migration to
|
|
4
|
-
`@frankzhang2026/opencode-android-orchestrator@1.0.
|
|
4
|
+
`@frankzhang2026/opencode-android-orchestrator@1.0.2`. Pin the exact version and
|
|
5
5
|
prove the migration in a disposable clone before changing a long-lived
|
|
6
6
|
repository.
|
|
7
7
|
|
|
@@ -9,10 +9,10 @@ repository.
|
|
|
9
9
|
|
|
10
10
|
| Current state | Correct command after release | Important distinction |
|
|
11
11
|
| --- | --- | --- |
|
|
12
|
-
| No orchestrator files or manifest | `npx @frankzhang2026/opencode-android-orchestrator@1.0.
|
|
12
|
+
| No orchestrator files or manifest | `npx @frankzhang2026/opencode-android-orchestrator@1.0.2 init .` | Normal new installation; all runtime-detected Android modules and registered debug verification tasks are discovered automatically. |
|
|
13
13
|
| Published `0.1.0` scaffold only | Remove any project-local `@0.1.0` plugin reference after review, then run `init`. | `0.1.0` did not create a usable managed installation and cannot be upgraded. |
|
|
14
14
|
| `0.2.0` through `0.10.0` manifest-managed installation with intact managed/backup content | Run the `1.0.0` `upgrade`; add `--refresh-gradle-discovery` when generated module/task lists are incomplete. | Refresh replaces all derived module metadata, source paths, protected build files, and task allowlists from one Gradle runtime snapshot. Module scope, operator policies, user-owned AGENTS content, and an existing commit-prefix sidecar remain preserved. |
|
|
15
|
-
| Healthy `1.0.0` installation | Stop the queue service, finish or abort retained workspaces, then run the fixed `1.0.
|
|
15
|
+
| Healthy `1.0.0` or `1.0.1` installation | Stop the queue service, finish or abort retained workspaces, then run the fixed `1.0.2` `upgrade`. | Pending inbox contracts remain durable. Version 1.0.2 keeps bounded snapshots and fixes task claiming when OpenCode gives tool commands a separate process group. |
|
|
16
16
|
| Manually copied V3 files, no `.automation-plugin/manifest.json` | Finish active tasks, preserve historical evidence separately, then run `init`. | Exact files can be reused; differing managed files fail as conflicts. |
|
|
17
17
|
| Healthy older manifest-managed installation | Run `doctor`, then the fixed target version's `upgrade`. | `upgrade` requires a valid installed manifest and intact original backups. |
|
|
18
18
|
| Healthy current-version manifest | Run `doctor`; repeated `init` or same-version `upgrade` is verification-only and byte-idempotent. | Do not reinstall or delete the manifest. |
|
|
@@ -58,7 +58,7 @@ installation of `0.1.0` alone does not require project-file cleanup.
|
|
|
58
58
|
After release, initialize with the fixed version:
|
|
59
59
|
|
|
60
60
|
```sh
|
|
61
|
-
npx @frankzhang2026/opencode-android-orchestrator@1.0.
|
|
61
|
+
npx @frankzhang2026/opencode-android-orchestrator@1.0.2 init .
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
New installations default to all-module scope, so multiple application modules
|
|
@@ -106,7 +106,7 @@ Use the lifecycle command selected by the active manifest:
|
|
|
106
106
|
|
|
107
107
|
```sh
|
|
108
108
|
npx --yes --registry=https://registry.npmjs.org/ \
|
|
109
|
-
@frankzhang2026/opencode-android-orchestrator@1.0.
|
|
109
|
+
@frankzhang2026/opencode-android-orchestrator@1.0.2 upgrade . --json
|
|
110
110
|
```
|
|
111
111
|
|
|
112
112
|
The command-level Registry option is useful when a company-wide npm Registry
|
|
@@ -136,7 +136,7 @@ computed includes dynamically or a company convention plugin applied
|
|
|
136
136
|
|
|
137
137
|
```sh
|
|
138
138
|
npx --yes --registry=https://registry.npmjs.org/ \
|
|
139
|
-
@frankzhang2026/opencode-android-orchestrator@1.0.
|
|
139
|
+
@frankzhang2026/opencode-android-orchestrator@1.0.2 upgrade . \
|
|
140
140
|
--refresh-gradle-discovery --json
|
|
141
141
|
```
|
|
142
142
|
|
|
@@ -220,7 +220,7 @@ use the queue rather than the old direct Shell commands. See [Queue operation](Q
|
|
|
220
220
|
Run all checks from the detected Git root:
|
|
221
221
|
|
|
222
222
|
```sh
|
|
223
|
-
npx @frankzhang2026/opencode-android-orchestrator@1.0.
|
|
223
|
+
npx @frankzhang2026/opencode-android-orchestrator@1.0.2 doctor .
|
|
224
224
|
opencode debug config
|
|
225
225
|
opencode debug skill
|
|
226
226
|
opencode debug agent scheduled-planner
|
package/docs/QUEUE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Queue and background execution
|
|
2
2
|
|
|
3
|
-
Version `1.0.
|
|
3
|
+
Version `1.0.2` stores proposals and approved contracts under
|
|
4
4
|
`<git-common-dir>/automation-runtime/inbox/queue.json`. A contract is runnable
|
|
5
5
|
only after its full plan, version, digest, target branch and commit policy are
|
|
6
6
|
approved and durably recorded. Planning reads a fixed `planningHead`, so another
|
|
@@ -42,6 +42,10 @@ dependency. Fixed workspaces remain occupied during failures and human waiting.
|
|
|
42
42
|
The worker resolves the Android SDK from `ANDROID_HOME`, `ANDROID_SDK_ROOT`,
|
|
43
43
|
then the source repository's `local.properties` and passes the resolved location
|
|
44
44
|
to its shell and Gradle processes. Isolated worktrees do not copy that local file.
|
|
45
|
+
Every mutating task script binds the workspace queue key and run ID to the
|
|
46
|
+
active Worker. The script may share the Worker's process group or be a proven
|
|
47
|
+
descendant in a separate OpenCode tool process group; an unrelated process is
|
|
48
|
+
rejected even if it copies the visible run ID.
|
|
45
49
|
|
|
46
50
|
## Service and scheduling
|
|
47
51
|
|
package/docs/SECURITY.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Security model
|
|
2
2
|
|
|
3
3
|
This document describes the security properties of
|
|
4
|
-
`@frankzhang2026/opencode-android-orchestrator@1.0.
|
|
4
|
+
`@frankzhang2026/opencode-android-orchestrator@1.0.2`. The lifecycle foundation
|
|
5
5
|
completed the real OpenCode `1.14.22` and `1.15.13` release matrix in `0.2.0`;
|
|
6
6
|
`1.0.0` retains that compatibility boundary.
|
|
7
7
|
|
|
@@ -95,6 +95,14 @@ value is an integer from `120000` through `7200000` milliseconds, defaults to
|
|
|
95
95
|
`1800000`, never shortens a larger caller timeout, and does not rewrite the
|
|
96
96
|
command or authorize a state change.
|
|
97
97
|
|
|
98
|
+
Queued task scripts validate the sealed workspace queue key and run ID against
|
|
99
|
+
the active Worker. OpenCode tool shells are accepted only when they either
|
|
100
|
+
share that Worker's process group or remain provable descendants of its PID;
|
|
101
|
+
this supports PTY/sandbox process-group isolation without accepting an
|
|
102
|
+
unrelated process that merely copies the run ID. Recovery continues to retain
|
|
103
|
+
the execution slot while the recorded Worker or its original process group is
|
|
104
|
+
alive.
|
|
105
|
+
|
|
98
106
|
The five namespaced bundled workflow skills are loaded from a fixed directory
|
|
99
107
|
inside the installed npm package. The compatible `config` hook only appends
|
|
100
108
|
that directory to `skills.paths`, and plugin startup verifies that every
|
package/docs/TROUBLESHOOTING.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Troubleshooting
|
|
2
2
|
|
|
3
3
|
Use this guide for
|
|
4
|
-
`@frankzhang2026/opencode-android-orchestrator@1.0.
|
|
4
|
+
`@frankzhang2026/opencode-android-orchestrator@1.0.2`.
|
|
5
5
|
|
|
6
6
|
## Start with read-only evidence
|
|
7
7
|
|
|
@@ -11,7 +11,7 @@ From the repository root, capture:
|
|
|
11
11
|
git status --short --branch
|
|
12
12
|
git rev-parse HEAD
|
|
13
13
|
opencode --version
|
|
14
|
-
npx @frankzhang2026/opencode-android-orchestrator@1.0.
|
|
14
|
+
npx @frankzhang2026/opencode-android-orchestrator@1.0.2 doctor . --json
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
If installation never completed, doctor will correctly report a missing or
|
|
@@ -39,9 +39,9 @@ command-scoped override:
|
|
|
39
39
|
|
|
40
40
|
```sh
|
|
41
41
|
npm --registry=https://registry.npmjs.org/ view \
|
|
42
|
-
@frankzhang2026/opencode-android-orchestrator@1.0.
|
|
42
|
+
@frankzhang2026/opencode-android-orchestrator@1.0.2 version
|
|
43
43
|
npx --yes --registry=https://registry.npmjs.org/ \
|
|
44
|
-
@frankzhang2026/opencode-android-orchestrator@1.0.
|
|
44
|
+
@frankzhang2026/opencode-android-orchestrator@1.0.2 upgrade . --json
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
This leaves the company's saved npm configuration unchanged. Use the option
|
|
@@ -71,7 +71,8 @@ Git-backed Superpowers plugin at runtime.
|
|
|
71
71
|
| Invalid `--long-command-timeout-ms` | The value is not an integer from `120000` through `7200000`. | Use the `1800000` ms default or pass an intentional bounded value to `init`/`upgrade`; do not edit the generated config directly. |
|
|
72
72
|
| Android SDK failure | No valid explicit SDK, `ANDROID_HOME`, `ANDROID_SDK_ROOT`, or `local.properties` `sdk.dir` was found. | Configure one real SDK root containing `platforms/` and `build-tools/`. Do not publish `local.properties`. |
|
|
73
73
|
| Missing `git`, `jq`, `rg`, `shasum`, or Java | Required deterministic command is unavailable on `PATH`. | Install or restore the missing command, record its version, and rerun the read-only checks. |
|
|
74
|
-
| `Bundled Orchestrator skill is unavailable` | The installed `1.0.
|
|
74
|
+
| `Bundled Orchestrator skill is unavailable` | The installed `1.0.2` package is incomplete, damaged, or loaded from an unsupported partial copy. | Reinstall the exact package, inspect its `resources/third-party/superpowers-v6.2.0/skills/` entries, restart OpenCode, and rerun `opencode debug skill`. Do not add an external Superpowers plugin as a fallback. |
|
|
75
|
+
| `current process does not own this task queue execution` immediately after Coder start on 1.0.1 | OpenCode created the tool shell in a separate process group, so 1.0.1 rejected a legitimate Worker descendant. | Upgrade to 1.0.2, restart OpenCode, then use the approved resume or abort workflow for the retained task. Do not edit the queue or lease files. |
|
|
75
76
|
| The exact Superpowers v6.2.0 plugin remains after upgrade | That entry existed in the verified pre-install OpenCode file and is therefore user-owned. | Leave it in place or remove it as a separate reviewed configuration change. Upgrade only removes the old Orchestrator-managed entry. |
|
|
76
77
|
|
|
77
78
|
Version `0.6.0` always passes `--no-configuration-cache` to its temporary
|
|
@@ -87,7 +88,7 @@ silence of `./gradlew tasks --all --console=plain | rg ...` in a large build.
|
|
|
87
88
|
For an existing installation, run:
|
|
88
89
|
|
|
89
90
|
```sh
|
|
90
|
-
npx @frankzhang2026/opencode-android-orchestrator@1.0.
|
|
91
|
+
npx @frankzhang2026/opencode-android-orchestrator@1.0.2 upgrade . \
|
|
91
92
|
--refresh-gradle-discovery
|
|
92
93
|
```
|
|
93
94
|
|
|
@@ -96,7 +97,7 @@ least `1800000` milliseconds. A higher timeout already supplied by the caller
|
|
|
96
97
|
is preserved; unrelated Bash commands are unchanged. To configure one hour,
|
|
97
98
|
run `upgrade . --long-command-timeout-ms 3600000` on a healthy installation.
|
|
98
99
|
If a command still reports `120000 ms`, confirm that the project manifest and
|
|
99
|
-
OpenCode plugin reference are both `1.0.
|
|
100
|
+
OpenCode plugin reference are both `1.0.2`, restart the OpenCode session so the
|
|
100
101
|
plugin reloads, and rerun doctor before attempting recovery.
|
|
101
102
|
|
|
102
103
|
After installation, inspect OpenCode discovery separately:
|
package/package.json
CHANGED
|
@@ -219,9 +219,28 @@ automation_workspace_path() {
|
|
|
219
219
|
printf '%s/%s.json\n' "$AUTOMATION_WORKSPACES_DIR" "$task_id"
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
+
automation_process_descends_from() {
|
|
223
|
+
local current_pid="$1"
|
|
224
|
+
local ancestor_pid="$2"
|
|
225
|
+
local parent_pid=""
|
|
226
|
+
local depth=0
|
|
227
|
+
|
|
228
|
+
[[ "$current_pid" =~ ^[0-9]+$ && "$ancestor_pid" =~ ^[0-9]+$ ]] || return 1
|
|
229
|
+
while (( depth < 128 )); do
|
|
230
|
+
[[ "$current_pid" == "$ancestor_pid" ]] && return 0
|
|
231
|
+
parent_pid="$(ps -o ppid= -p "$current_pid" 2>/dev/null | tr -d '[:space:]')"
|
|
232
|
+
[[ "$parent_pid" =~ ^[0-9]+$ ]] || return 1
|
|
233
|
+
(( parent_pid > 1 )) || return 1
|
|
234
|
+
[[ "$parent_pid" != "$current_pid" ]] || return 1
|
|
235
|
+
current_pid="$parent_pid"
|
|
236
|
+
depth=$((depth + 1))
|
|
237
|
+
done
|
|
238
|
+
return 1
|
|
239
|
+
}
|
|
240
|
+
|
|
222
241
|
automation_require_queue_execution() {
|
|
223
242
|
local task_id="$1"
|
|
224
|
-
local workspace_file queue_key group
|
|
243
|
+
local workspace_file queue_key group worker_pid
|
|
225
244
|
workspace_file="$(automation_workspace_path "$task_id")"
|
|
226
245
|
[[ -f "$workspace_file" ]] || return 0
|
|
227
246
|
queue_key="$(jq -r '.queueKey // empty' "$workspace_file")"
|
|
@@ -230,13 +249,21 @@ automation_require_queue_execution() {
|
|
|
230
249
|
automation_die "queued tasks must use the repository queue for execution, integration and recovery"
|
|
231
250
|
return 1
|
|
232
251
|
}
|
|
252
|
+
if ! worker_pid="$(jq -er --arg key "$queue_key" --arg run "$AUTOMATION_QUEUE_RUN_ID" \
|
|
253
|
+
'select(.active.key == $key and .active.id == $run) | .active.worker.pid' \
|
|
254
|
+
"$AUTOMATION_RUNTIME_ROOT/inbox/queue.json")"; then
|
|
255
|
+
automation_die "current process does not own this task queue execution"
|
|
256
|
+
return 1
|
|
257
|
+
fi
|
|
258
|
+
[[ "$worker_pid" =~ ^[0-9]+$ ]] || {
|
|
259
|
+
automation_die "current process does not own this task queue execution"
|
|
260
|
+
return 1
|
|
261
|
+
}
|
|
233
262
|
group="$(ps -o pgid= -p "$$" | tr -d ' ')"
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
return 1
|
|
239
|
-
}
|
|
263
|
+
if [[ "$group" != "$worker_pid" ]] && ! automation_process_descends_from "$$" "$worker_pid"; then
|
|
264
|
+
automation_die "current process does not own this task queue execution"
|
|
265
|
+
return 1
|
|
266
|
+
fi
|
|
240
267
|
}
|
|
241
268
|
|
|
242
269
|
automation_workspace_strategy() {
|