opensandbox-cli 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,184 @@
1
+ """Built-in OpenSandbox skill metadata and rendering helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib.resources
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class SkillSpec:
12
+ """A bundled skill shipped with the CLI."""
13
+
14
+ slug: str
15
+ package_file: str
16
+ title: str
17
+ summary: str
18
+ trigger_hint: str
19
+ marker_id: str
20
+
21
+
22
+ BUILTIN_SKILLS: dict[str, SkillSpec] = {
23
+ "sandbox-troubleshooting": SkillSpec(
24
+ slug="sandbox-troubleshooting",
25
+ package_file="opensandbox-sandbox-troubleshooting.md",
26
+ title="OpenSandbox Sandbox Troubleshooting",
27
+ summary=(
28
+ "Triage failed or unhealthy sandboxes with state, health, summary, "
29
+ "inspect, events, logs, and concrete remediation steps."
30
+ ),
31
+ trigger_hint=(
32
+ "Use when the user reports sandbox startup failures, crashes, OOM, "
33
+ "image pull problems, pending sandboxes, or unreachable services."
34
+ ),
35
+ marker_id="opensandbox-sandbox-troubleshooting",
36
+ ),
37
+ "sandbox-lifecycle": SkillSpec(
38
+ slug="sandbox-lifecycle",
39
+ package_file="opensandbox-sandbox-lifecycle.md",
40
+ title="OpenSandbox Sandbox Lifecycle",
41
+ summary=(
42
+ "Create, inspect, renew, pause, resume, and terminate sandboxes "
43
+ "with the right defaults and follow-up checks."
44
+ ),
45
+ trigger_hint=(
46
+ "Use when the user wants to create or manage a sandbox and needs "
47
+ "the exact OpenSandbox CLI/API flow."
48
+ ),
49
+ marker_id="opensandbox-sandbox-lifecycle",
50
+ ),
51
+ "command-execution": SkillSpec(
52
+ slug="command-execution",
53
+ package_file="opensandbox-command-execution.md",
54
+ title="OpenSandbox Command Execution",
55
+ summary=(
56
+ "Run foreground and background commands, inspect status/logs, and "
57
+ "use persistent sessions inside a sandbox."
58
+ ),
59
+ trigger_hint=(
60
+ "Use when the user wants to execute commands in a sandbox, collect "
61
+ "logs, interrupt work, or reuse a persistent shell session."
62
+ ),
63
+ marker_id="opensandbox-command-execution",
64
+ ),
65
+ "file-operations": SkillSpec(
66
+ slug="file-operations",
67
+ package_file="opensandbox-file-operations.md",
68
+ title="OpenSandbox File Operations",
69
+ summary=(
70
+ "Read, write, upload, download, search, replace, and manage files "
71
+ "inside a sandbox without hand-wavy shell advice."
72
+ ),
73
+ trigger_hint=(
74
+ "Use when the user needs file or directory manipulation inside an "
75
+ "OpenSandbox sandbox."
76
+ ),
77
+ marker_id="opensandbox-file-operations",
78
+ ),
79
+ "network-egress": SkillSpec(
80
+ slug="network-egress",
81
+ package_file="opensandbox-network-egress.md",
82
+ title="OpenSandbox Network Egress",
83
+ summary=(
84
+ "Inspect and patch sandbox runtime egress rules when the user "
85
+ "needs to allow or deny outbound domains."
86
+ ),
87
+ trigger_hint=(
88
+ "Use when the user needs to view or modify outbound network access "
89
+ "for a sandbox, or debug domain allow and deny behavior."
90
+ ),
91
+ marker_id="opensandbox-network-egress",
92
+ ),
93
+ }
94
+
95
+ DEFAULT_SKILL = "sandbox-troubleshooting"
96
+
97
+
98
+ def list_builtin_skills() -> list[SkillSpec]:
99
+ """Return bundled skills in stable declaration order."""
100
+ return list(BUILTIN_SKILLS.values())
101
+
102
+
103
+ def get_builtin_skill(slug: str) -> SkillSpec:
104
+ """Return a bundled skill definition."""
105
+ return BUILTIN_SKILLS[slug]
106
+
107
+
108
+ def get_builtin_skill_source(skill: SkillSpec) -> Path:
109
+ """Locate the bundled skill file shipped with the CLI package."""
110
+ pkg = importlib.resources.files("opensandbox_cli") / "skills" / skill.package_file
111
+ if hasattr(pkg, "__fspath__"):
112
+ return Path(str(pkg))
113
+ with importlib.resources.as_file(pkg) as resolved:
114
+ return Path(resolved)
115
+
116
+
117
+ def read_skill_markdown(skill: SkillSpec) -> str:
118
+ """Load bundled skill markdown."""
119
+ return get_builtin_skill_source(skill).read_text(encoding="utf-8")
120
+
121
+
122
+ def split_frontmatter(markdown: str) -> tuple[str | None, str]:
123
+ """Split markdown into YAML frontmatter and body."""
124
+ if not markdown.startswith("---\n"):
125
+ return None, markdown
126
+
127
+ closing = markdown.find("\n---\n", 4)
128
+ if closing == -1:
129
+ return None, markdown
130
+
131
+ frontmatter = markdown[4:closing]
132
+ body = markdown[closing + 5 :]
133
+ return frontmatter, body
134
+
135
+
136
+ def extract_section(body: str, heading: str) -> str | None:
137
+ """Extract a markdown section by exact heading text."""
138
+ lines = body.splitlines()
139
+ capture = False
140
+ capture_level = 0
141
+ captured: list[str] = []
142
+
143
+ for line in lines:
144
+ if line.startswith("## ") or line.startswith("### "):
145
+ if capture:
146
+ if capture_level == 2 and line.startswith("## "):
147
+ break
148
+ if capture_level == 3 and (line.startswith("## ") or line.startswith("### ")):
149
+ break
150
+ if line == f"## {heading}":
151
+ capture = True
152
+ capture_level = 2
153
+ continue
154
+ if line == f"### {heading}":
155
+ capture = True
156
+ capture_level = 3
157
+ continue
158
+ if capture:
159
+ captured.append(line)
160
+
161
+ if not captured:
162
+ return None
163
+
164
+ return "\n".join(captured).strip() or None
165
+
166
+
167
+ def render_skill_for_target(
168
+ skill: SkillSpec,
169
+ markdown: str,
170
+ *,
171
+ preserve_frontmatter: bool,
172
+ ) -> str:
173
+ """Render skill content for the target tool."""
174
+ frontmatter, body = split_frontmatter(markdown)
175
+ body = body.strip()
176
+
177
+ if preserve_frontmatter or not frontmatter:
178
+ return markdown.strip() + "\n"
179
+
180
+ return (
181
+ f"# {skill.title}\n\n"
182
+ f"{skill.summary}\n\n"
183
+ f"{body}\n"
184
+ )
@@ -0,0 +1,215 @@
1
+ ---
2
+ name: command-execution
3
+ description: Use OpenSandbox command execution commands to run foreground or background processes, inspect tracked execution status and logs, interrupt work, and manage persistent shell sessions inside a sandbox. Trigger when users want to execute commands in a sandbox and need exact OpenSandbox CLI flows.
4
+ ---
5
+
6
+ # OpenSandbox Command Execution
7
+
8
+ Run commands with `osb command`. Use foreground streaming by default, and add `--background` when you need tracked execution.
9
+
10
+ ## When To Use
11
+
12
+ - the user wants to run a one-off command in a sandbox
13
+ - the user needs a tracked background command with later status/log inspection
14
+ - the user wants to stop a running command
15
+ - the user wants a persistent shell session that keeps working directory or environment state across runs
16
+
17
+ ## Config Gate
18
+
19
+ Run this first:
20
+
21
+ ```bash
22
+ osb config show -o json
23
+ ```
24
+
25
+ If `domain` is missing, stop and set it before command execution:
26
+
27
+ ```bash
28
+ osb config set connection.domain <host:port> -o json
29
+ osb config show -o json
30
+ ```
31
+
32
+ If auth is required and `api_key` is missing, stop and set it before command execution:
33
+
34
+ ```bash
35
+ osb config set connection.api_key <api-key> -o json
36
+ osb config show -o json
37
+ ```
38
+
39
+ ## Separator Rule
40
+
41
+ Use `--` before the sandbox command payload. This is required when the payload contains flags like `-l`, `-c`, or `-m`.
42
+
43
+ ```bash
44
+ osb command run <sandbox-id> -o raw -- sh -lc 'echo ready'
45
+ osb command run <sandbox-id> -o raw -- python -m http.server
46
+ osb command session run <sandbox-id> <session-id> -o raw -- sh -lc 'pwd'
47
+ ```
48
+
49
+ ## Execution Modes
50
+
51
+ Treat these as three distinct execution paths:
52
+
53
+ - `osb command run` without `--background`
54
+ Use for foreground one-shot commands when the result should stream directly to the terminal
55
+ - `osb command run --background`
56
+ Use when the user needs an execution ID, later status checks, or log retrieval
57
+ - `osb command session ...`
58
+ Use when shell state must persist across commands, such as exported variables or a working directory
59
+
60
+ ## Golden Paths
61
+
62
+ Foreground one-shot command:
63
+
64
+ ```bash
65
+ osb command run <sandbox-id> -o raw -- python -c "print(1 + 1)"
66
+ ```
67
+
68
+ Tracked background command:
69
+
70
+ ```bash
71
+ osb command run <sandbox-id> --background -o json -- sh -c "sleep 10; echo done"
72
+ osb command status <sandbox-id> <execution-id> -o json
73
+ osb command logs <sandbox-id> <execution-id> -o json
74
+ ```
75
+
76
+ Persistent session:
77
+
78
+ ```bash
79
+ osb command session create <sandbox-id> --workdir /workspace -o json
80
+ osb command session run <sandbox-id> <session-id> -o raw -- pwd
81
+ osb command session run <sandbox-id> <session-id> -o raw -- export FOO=bar
82
+ osb command session run <sandbox-id> <session-id> -o raw -- sh -c 'echo $FOO'
83
+ osb command session delete <sandbox-id> <session-id> -o json
84
+ ```
85
+
86
+ ## Foreground Commands
87
+
88
+ For simple one-off execution, use:
89
+
90
+ ```bash
91
+ osb command run <sandbox-id> -o raw -- <command>
92
+ osb command run <sandbox-id> --workdir /workspace -o raw -- <command>
93
+ osb command run <sandbox-id> --timeout 30s -o raw -- <command>
94
+ ```
95
+
96
+ Use foreground mode when the user wants immediate output and does not need a tracked execution ID.
97
+
98
+ ## Background Commands
99
+
100
+ Use background mode when the user will need follow-up inspection:
101
+
102
+ ```bash
103
+ osb command run <sandbox-id> --background -o json -- <command>
104
+ osb command run <sandbox-id> --background --workdir /workspace -o json -- <command>
105
+ osb command run <sandbox-id> --background --timeout 5m -o json -- <command>
106
+ ```
107
+
108
+ Then inspect the tracked execution:
109
+
110
+ ```bash
111
+ osb command status <sandbox-id> <execution-id> -o json
112
+ osb command logs <sandbox-id> <execution-id> -o json
113
+ osb command logs <sandbox-id> <execution-id> --cursor 0 -o json
114
+ ```
115
+
116
+ Use `status` for lifecycle state and exit information. Use `logs` for tracked background output. Do not suggest `command logs` for foreground commands that already streamed to the terminal.
117
+
118
+ ## Persistent Sessions
119
+
120
+ Use sessions when commands must share shell state:
121
+
122
+ ```bash
123
+ osb command session create <sandbox-id> --workdir /workspace -o json
124
+ osb command session run <sandbox-id> <session-id> -o raw -- pwd
125
+ osb command session run <sandbox-id> <session-id> -o raw -- export FOO=bar
126
+ osb command session run <sandbox-id> <session-id> -o raw -- sh -c 'echo $FOO'
127
+ osb command session run <sandbox-id> <session-id> --workdir /var -o raw -- pwd
128
+ osb command session delete <sandbox-id> <session-id> -o json
129
+ ```
130
+
131
+ Rules:
132
+
133
+ - `session create --workdir` sets the initial working directory for the session
134
+ - `session run --workdir` overrides the working directory for that single run only
135
+ - exported variables and shell state persist across `session run` calls in the same session
136
+ - delete the session when the user is done; do not leave idle sessions around
137
+
138
+ ## Interrupting Work
139
+
140
+ Interrupt only tracked executions:
141
+
142
+ ```bash
143
+ osb command interrupt <sandbox-id> <execution-id> -o json
144
+ ```
145
+
146
+ Only suggest interruption when the user explicitly wants to stop work or the process is clearly stuck.
147
+
148
+ ## Failure Semantics
149
+
150
+ - foreground `osb command run` streams output directly and requires `-o raw`
151
+ - background `osb command run --background` returns tracked execution metadata and supports structured output
152
+ - `session run` also exits non-zero on execution error
153
+ - tracked background commands should be checked with `status` and `logs`
154
+ - if the command failure is caused by an unhealthy sandbox rather than the command itself, switch to `sandbox-troubleshooting`
155
+
156
+ ## Response Pattern
157
+
158
+ Structure the answer as:
159
+
160
+ 1. exact command to run
161
+ 2. which execution mode it uses
162
+ 3. the next inspection or cleanup command if the workflow continues
163
+
164
+ Keep command examples concrete and ready to paste.
165
+
166
+ ## Minimal Closed Loops
167
+
168
+ Foreground command with timeout:
169
+
170
+ ```bash
171
+ osb command run <sandbox-id> --timeout 30s -o raw -- python -c "print(1 + 1)"
172
+ ```
173
+
174
+ Tracked background execution:
175
+
176
+ ```bash
177
+ osb command run <sandbox-id> --background -o json -- sh -c "sleep 10; echo done"
178
+ osb command status <sandbox-id> <execution-id> -o json
179
+ osb command logs <sandbox-id> <execution-id> -o json
180
+ ```
181
+
182
+ Background execution with interrupt:
183
+
184
+ ```bash
185
+ osb command run <sandbox-id> --background -o json -- sh -c "sleep 300"
186
+ osb command interrupt <sandbox-id> <execution-id> -o json
187
+ osb command status <sandbox-id> <execution-id> -o json
188
+ ```
189
+
190
+ Persistent session with shared shell state:
191
+
192
+ ```bash
193
+ osb command session create <sandbox-id> --workdir /workspace -o json
194
+ osb command session run <sandbox-id> <session-id> -o raw -- export FOO=bar
195
+ osb command session run <sandbox-id> <session-id> -o raw -- sh -c 'echo $FOO'
196
+ osb command session delete <sandbox-id> <session-id> -o json
197
+ ```
198
+
199
+ Per-run working directory override inside a session:
200
+
201
+ ```bash
202
+ osb command session create <sandbox-id> --workdir /tmp -o json
203
+ osb command session run <sandbox-id> <session-id> -o raw -- pwd
204
+ osb command session run <sandbox-id> <session-id> --workdir /var -o raw -- pwd
205
+ osb command session delete <sandbox-id> <session-id> -o json
206
+ ```
207
+
208
+ ## Best Practices
209
+
210
+ - use `osb command run -o raw -- ...` for quick foreground commands
211
+ - use `--background` when the user will need execution tracking
212
+ - use sessions only when state persistence is actually needed
213
+ - use `--workdir` explicitly when directory context matters
214
+ - use `--timeout` when the command should not run indefinitely
215
+ - prefer `status` before guessing whether a background command is still running or already failed
@@ -0,0 +1,244 @@
1
+ ---
2
+ name: file-operations
3
+ description: Use OpenSandbox file commands to read, write, upload, download, search, replace, move, delete, and inspect files or directories inside a sandbox. Trigger when users want exact sandbox file manipulation commands instead of generic shell guidance.
4
+ ---
5
+
6
+ # OpenSandbox File Operations
7
+
8
+ Manipulate sandbox files with `osb file` commands. Choose the operation mode first, then use the matching verification step. Do not mix sandbox-internal edits with host-to-sandbox transfer commands casually.
9
+
10
+ ## When To Use
11
+
12
+ - the user wants to read or write a file inside a sandbox
13
+ - the user needs to upload a local file into a sandbox or download a sandbox file back to the host
14
+ - the user wants to search, replace, move, chmod, or inspect paths
15
+ - the user needs directory creation or cleanup
16
+
17
+ ## Config Gate
18
+
19
+ Run this first:
20
+
21
+ ```bash
22
+ osb config show -o json
23
+ ```
24
+
25
+ If `domain` is missing, stop and set it before file commands:
26
+
27
+ ```bash
28
+ osb config set connection.domain <host:port> -o json
29
+ osb config show -o json
30
+ ```
31
+
32
+ If auth is required and `api_key` is missing, stop and set it before file commands:
33
+
34
+ ```bash
35
+ osb config set connection.api_key <api-key> -o json
36
+ osb config show -o json
37
+ ```
38
+
39
+ ## Operation Modes
40
+
41
+ Treat these as distinct categories:
42
+
43
+ - sandbox-only content operations
44
+ `cat`, `write`, `replace`, `mv`, `mkdir`, `rm`, `rmdir`, `info`, `chmod`
45
+ - host-to-sandbox transfer
46
+ `upload`
47
+ - sandbox-to-host transfer
48
+ `download`
49
+ - discovery before modification
50
+ `search`, then `info`
51
+
52
+ If the path is uncertain, search first. If the file boundary crosses between host and sandbox, use `upload` or `download` instead of `write` or `cat`.
53
+
54
+ ## Golden Paths
55
+
56
+ Write and verify inside the sandbox:
57
+
58
+ ```bash
59
+ osb file write <sandbox-id> /workspace/app.txt -c "hello" -o json
60
+ osb file cat <sandbox-id> /workspace/app.txt -o raw
61
+ ```
62
+
63
+ Upload from host and verify in the sandbox:
64
+
65
+ ```bash
66
+ osb file upload <sandbox-id> ./local.txt /workspace/local.txt -o json
67
+ osb file cat <sandbox-id> /workspace/local.txt -o raw
68
+ ```
69
+
70
+ Search before editing:
71
+
72
+ ```bash
73
+ osb file search <sandbox-id> /workspace --pattern "*.py" -o json
74
+ osb file info <sandbox-id> /workspace/main.py -o json
75
+ ```
76
+
77
+ ## Sandbox-Only File Edits
78
+
79
+ Read and write:
80
+
81
+ ```bash
82
+ osb file cat <sandbox-id> /path/to/file -o raw
83
+ osb file write <sandbox-id> /path/to/file -c "hello" -o json
84
+ ```
85
+
86
+ Use `write` with `-c/--content` when the new content is known directly. If the content should come from stdin, omit `-c` and pipe or paste the content into the command.
87
+
88
+ Edit existing content:
89
+
90
+ ```bash
91
+ osb file replace <sandbox-id> /path/to/file --old old --new new -o json
92
+ osb file mv <sandbox-id> /old/path /new/path -o json
93
+ ```
94
+
95
+ Prefer `replace` for small text substitutions and `mv` for rename/path changes. Do not rewrite a full file when a targeted replace is enough.
96
+
97
+ Create directories:
98
+
99
+ ```bash
100
+ osb file mkdir <sandbox-id> /workspace/output -o json
101
+ osb file mkdir <sandbox-id> /workspace/a /workspace/b --mode 755 -o json
102
+ ```
103
+
104
+ ## Host <-> Sandbox Transfer
105
+
106
+ Host to sandbox:
107
+
108
+ ```bash
109
+ osb file upload <sandbox-id> ./local.txt /remote/path/local.txt -o json
110
+ ```
111
+
112
+ Sandbox to host:
113
+
114
+ ```bash
115
+ osb file download <sandbox-id> /remote/path/result.json ./result.json -o json
116
+ ```
117
+
118
+ Rules:
119
+
120
+ - use `upload` when the source file is on the host
121
+ - use `download` when the destination should be written to the host filesystem
122
+ - use `write` and `cat` only when the operation stays entirely inside the sandbox
123
+
124
+ ## Metadata and Permissions
125
+
126
+ Inspect metadata:
127
+
128
+ ```bash
129
+ osb file info <sandbox-id> /path/to/file -o json
130
+ osb file info <sandbox-id> /path/one /path/two -o json
131
+ ```
132
+
133
+ Search by pattern:
134
+
135
+ ```bash
136
+ osb file search <sandbox-id> /workspace --pattern "*.py" -o json
137
+ ```
138
+
139
+ Set permissions:
140
+
141
+ ```bash
142
+ osb file chmod <sandbox-id> /path/to/script --mode 755 -o json
143
+ osb file chmod <sandbox-id> /path/to/file --mode 644 --owner root --group root -o json
144
+ ```
145
+
146
+ Use `info` after `chmod` when the user needs to confirm mode, ownership, or timestamps changed as expected.
147
+
148
+ ## Destructive Operations
149
+
150
+ Delete files or directories only after verifying the target path:
151
+
152
+ ```bash
153
+ osb file info <sandbox-id> /workspace/tmp.txt -o json
154
+ osb file rm <sandbox-id> /workspace/tmp.txt -o json
155
+ ```
156
+
157
+ ```bash
158
+ osb file search <sandbox-id> /workspace --pattern "old-*" -o json
159
+ osb file rmdir <sandbox-id> /workspace/old-dir -o json
160
+ ```
161
+
162
+ Rules:
163
+
164
+ - prefer `info` when the exact path is known
165
+ - prefer `search` when the path is uncertain
166
+ - do not suggest `rm` or `rmdir` until the target has been verified
167
+ - after `mv`, `rm`, or `rmdir`, verify the new state with `info` or `search`
168
+
169
+ ## Failure Semantics
170
+
171
+ - `upload` and `download` have host filesystem side effects; treat them as cross-boundary operations
172
+ - `download` writes to the local path immediately, so be explicit about the destination
173
+ - permission or ownership failures are usually path/runtime permission issues, not a reason to switch away from `osb file`
174
+ - if multiple file commands fail unexpectedly, check sandbox health before assuming a file-command bug
175
+
176
+ ## Response Pattern
177
+
178
+ Structure the answer as:
179
+
180
+ 1. exact `osb file` command
181
+ 2. which operation mode it uses
182
+ 3. the next verification command if the workflow continues
183
+
184
+ Keep command examples concrete and ready to paste.
185
+
186
+ ## Minimal Closed Loops
187
+
188
+ Write and verify:
189
+
190
+ ```bash
191
+ osb file write <sandbox-id> /workspace/app.txt -c "hello" -o json
192
+ osb file cat <sandbox-id> /workspace/app.txt -o raw
193
+ ```
194
+
195
+ Upload and verify:
196
+
197
+ ```bash
198
+ osb file upload <sandbox-id> ./local.txt /workspace/local.txt -o json
199
+ osb file cat <sandbox-id> /workspace/local.txt -o raw
200
+ ```
201
+
202
+ Replace and verify:
203
+
204
+ ```bash
205
+ osb file replace <sandbox-id> /workspace/app.txt --old hello --new world -o json
206
+ osb file cat <sandbox-id> /workspace/app.txt -o raw
207
+ ```
208
+
209
+ Change permissions and inspect:
210
+
211
+ ```bash
212
+ osb file chmod <sandbox-id> /workspace/script.sh --mode 755 -o json
213
+ osb file info <sandbox-id> /workspace/script.sh -o json
214
+ ```
215
+
216
+ Create a directory and inspect it:
217
+
218
+ ```bash
219
+ osb file mkdir <sandbox-id> /workspace/output -o json
220
+ osb file info <sandbox-id> /workspace/output -o json
221
+ ```
222
+
223
+ Move a file and verify the new path:
224
+
225
+ ```bash
226
+ osb file mv <sandbox-id> /workspace/app.txt /workspace/archive/app.txt -o json
227
+ osb file info <sandbox-id> /workspace/archive/app.txt -o json
228
+ ```
229
+
230
+ Delete and verify removal:
231
+
232
+ ```bash
233
+ osb file info <sandbox-id> /workspace/tmp.txt -o json
234
+ osb file rm <sandbox-id> /workspace/tmp.txt -o json
235
+ osb file search <sandbox-id> /workspace --pattern "tmp.txt" -o json
236
+ ```
237
+
238
+ ## Best Practices
239
+
240
+ - prefer `search` before modification when the path is not certain
241
+ - prefer `info` before destructive actions
242
+ - prefer `replace` over full rewrites for small text changes
243
+ - prefer `upload` and `download` only for host boundary crossings
244
+ - prefer explicit verification after `mv`, `chmod`, `rm`, and `rmdir`