opencode-polkit 0.1.4 → 0.2.0

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 CHANGED
@@ -9,29 +9,33 @@ Like CachyOS Hello, the plugin does not check for a polkit agent up front:
9
9
  the request to whatever agent is currently registered. Without an agent,
10
10
  `pkexec` fails fast with `Error creating textual authentication agent`
11
11
  (no TTY); the plugin reports that as a clear denial. A hang (dialog never
12
- appears) is bounded by the bash tool's own timeout (default 2 min,
13
- configurable up to 10 min) and left untranslated — it cannot be told
12
+ appears) is bounded by the shell tool's own timeout (default 2 min,
13
+ configurable per call) and left untranslated — it cannot be told
14
14
  apart from a long-running command.
15
15
 
16
+ Requires OpenCode >= 2.0 (the plugin uses the `@opencode/plugin` v2 API and
17
+ is skipped by older versions).
18
+
16
19
  ## Install
17
20
 
18
21
  ```sh
19
- opencode plugin opencode-polkit
22
+ opencode plugin add opencode-polkit
20
23
  ```
21
24
 
22
25
  Or add to `opencode.json` / `~/.config/opencode/opencode.jsonc`:
23
26
 
24
27
  ```json
25
28
  {
26
- "plugin": ["opencode-polkit"]
29
+ "plugins": ["opencode-polkit"]
27
30
  }
28
31
  ```
29
32
 
30
- For local development, point opencode at the project directory instead:
33
+ For local development, build once (`npm run build` — the local entrypoint
34
+ re-exports `dist/`) and point opencode at the project directory instead:
31
35
 
32
36
  ```json
33
37
  {
34
- "plugin": ["/path/to/opencode-polkit"]
38
+ "plugins": ["/path/to/opencode-polkit"]
35
39
  }
36
40
  ```
37
41
 
@@ -41,6 +45,7 @@ For local development, point opencode at the project directory instead:
41
45
  |-----------------------|-------------------------------------------------------|
42
46
  | `sudo xxx` | redirects to `pkexec xxx` |
43
47
  | `doas xxx` | redirects to `pkexec xxx` |
48
+ | `sudo -n xxx` | blocked: pkexec has no short options (unclear failure)|
44
49
  | `cat x \| sudo tee y` | redirects to `cat x \| pkexec tee y` (mid-command) |
45
50
  | `pkexec xxx` | passes through |
46
51
  | `sudoedit` / `visudo` | blocked |
@@ -52,8 +57,10 @@ The command is rewritten **only** by replacing `sudo`/`doas` with
52
57
  command that behaves like the one it wrote. Privilege keywords are found
53
58
  by a lexical scanner (quote/escape/heredoc aware): `sudo` inside string
54
59
  literals, comments or heredocs is ignored; `sudo` in any executable
55
- position (leading, after `&&`/`||`/`|`, in `$(...)` or subshells) is
56
- rewritten. `sudo a && sudo b` rewrites both.
60
+ position (leading, after `&&`/`||`/`|`, in `$(...)` or subshells, after
61
+ an env assignment like `FOO=1 sudo x`) is rewritten, while the same word
62
+ in argument position (`--name sudo bash`) is left alone. `sudo a && sudo b`
63
+ rewrites both.
57
64
 
58
65
  ### Failures
59
66
 
@@ -62,10 +69,17 @@ command is remembered so a retry is rejected without prompting again
62
69
  (cleared when opencode restarts): `Not authorized`, `Error executing
63
70
  command as another user`, `Error creating textual authentication agent`
64
71
  (no polkit agent + no TTY). A hang (dialog never appears) is bounded by
65
- the bash tool's own timeout (default 2 min, configurable up to 10 min)
72
+ the shell tool's own timeout (default 2 min, configurable per call)
66
73
  and is left untranslated — it cannot be told apart from a long-running
67
74
  command.
68
75
 
76
+ Options pkexec does not accept (all short options, plus any long option
77
+ outside `--user`, `--keep-cwd`, `--disable-internal-agent`, `--help`,
78
+ `--version`) are rejected before execution with a clear message, since
79
+ the rewrite would otherwise surface pkexec's own confusing
80
+ `Cannot run program -n` error. `sudo --` and options after the program
81
+ name are left alone.
82
+
69
83
  ## i18n
70
84
 
71
85
  Messages adapt to `$LC_MESSAGES` / `$LANG`. Currently supports:
@@ -0,0 +1,11 @@
1
+ import type { Plugin } from "@opencode/plugin";
2
+ /**
3
+ * OpenCode 2.0 requires a default export shaped `{ id, setup }` (the loader
4
+ * rejects anything else). Deliberately a plain object with type-only imports:
5
+ * the built plugin has zero runtime dependencies.
6
+ */
7
+ declare const _default: {
8
+ id: string;
9
+ setup: (ctx: Plugin.Context) => Promise<void>;
10
+ };
11
+ export default _default;
package/dist/index.js CHANGED
@@ -1,3 +1,11 @@
1
+ /** Long options pkexec accepts (all of them; pkexec has no short options). */
2
+ const PKEXEC_OPTIONS = new Set([
3
+ "--version",
4
+ "--help",
5
+ "--disable-internal-agent",
6
+ "--keep-cwd",
7
+ "--user",
8
+ ]);
1
9
  /**
2
10
  * Lexical scan for privilege-escalation keywords OUTSIDE quotes.
3
11
  *
@@ -10,23 +18,31 @@
10
18
  * - `node -e "…sudo…"` → no hit (inside quotes)
11
19
  * - `cat x | sudo tee y` → hit at index 8 (command boundary)
12
20
  * - `cd /tmp && sudo ./x` → hit (after `&&`)
21
+ * - `--name sudo bash` → no hit (argument position)
22
+ * - `echo x; # sudo y` → no hit (comment)
13
23
  */
14
24
  function scanPrivileged(command) {
15
25
  const hits = [];
16
26
  let inSingle = false;
17
27
  let inDouble = false;
18
28
  let escaped = false;
29
+ // True right after a command boundary (`;`, `&&`, `||`, `|`, `(`, start).
30
+ let atCommandStart = true;
19
31
  // Heredoc delimiters waiting to close (stack; multiple << are legal).
20
32
  const heredocs = [];
21
33
  for (let i = 0; i < command.length; i++) {
22
34
  const ch = command[i];
23
35
  // Inside a heredoc body: only a line consisting of the delimiter ends
24
- // it. Content there is a string literal, never a command.
36
+ // it. Content there is a string literal, never a command. Pop the
37
+ // matching delimiter (nested heredocs close in any order).
25
38
  if (heredocs.length > 0) {
26
39
  const lineEnd = command.indexOf("\n", i);
27
40
  const line = lineEnd === -1 ? command.slice(i) : command.slice(i, lineEnd);
28
- if (heredocs.some((d) => line === d)) {
29
- heredocs.pop();
41
+ const idx = heredocs.lastIndexOf(line);
42
+ if (idx !== -1) {
43
+ heredocs.splice(idx);
44
+ // The line after the delimiter starts a new command position.
45
+ atCommandStart = true;
30
46
  }
31
47
  i = lineEnd === -1 ? command.length : lineEnd;
32
48
  continue;
@@ -43,7 +59,7 @@ function scanPrivileged(command) {
43
59
  inSingle = !inSingle;
44
60
  continue;
45
61
  }
46
- if (ch === '"' && !inSingle) {
62
+ if (ch === '"' && !inDouble) {
47
63
  inDouble = !inDouble;
48
64
  continue;
49
65
  }
@@ -74,8 +90,37 @@ function scanPrivileged(command) {
74
90
  }
75
91
  }
76
92
  }
77
- const prev = i > 0 ? command[i - 1] : "";
78
- if (/[-\/.\w]/.test(prev))
93
+ // Command boundaries open a new executable position.
94
+ if (ch === ";" || ch === "|" || ch === "&" || ch === "(" || ch === "\n") {
95
+ atCommandStart = true;
96
+ continue;
97
+ }
98
+ // `#` at a word start begins a comment that runs to end of line.
99
+ if (ch === "#") {
100
+ const prev = command[i - 1] ?? "";
101
+ if (i === 0 || /\s/.test(prev) || /[;|&(]/.test(prev)) {
102
+ const nl = command.indexOf("\n", i);
103
+ i = nl === -1 ? command.length : nl;
104
+ continue;
105
+ }
106
+ }
107
+ if (/\s/.test(ch))
108
+ continue;
109
+ // An env assignment (`FOO=1 sudo x`) still starts a command; any other
110
+ // word in argument position (`--name sudo`) does not.
111
+ let isCommandPos = atCommandStart;
112
+ if (!isCommandPos) {
113
+ let j = i - 1;
114
+ while (j >= 0 && /\s/.test(command[j]))
115
+ j--;
116
+ while (j >= 0 && !/[\s;|&()]/.test(command[j]))
117
+ j--;
118
+ const token = command.slice(j + 1, i);
119
+ if (token.includes("="))
120
+ isCommandPos = true;
121
+ }
122
+ atCommandStart = false;
123
+ if (!isCommandPos)
79
124
  continue;
80
125
  for (const kw of ["sudoedit", "visudo", "sudo", "doas", "pkexec"]) {
81
126
  if (command.startsWith(kw, i)) {
@@ -90,50 +135,93 @@ function scanPrivileged(command) {
90
135
  }
91
136
  return hits;
92
137
  }
138
+ /**
139
+ * First option token after a privilege keyword that pkexec does not
140
+ * accept, or null when the command's options are pkexec-compatible.
141
+ * Scanning stops at the program name (`--` also terminates options, as
142
+ * in sudo); tokens after it belong to the program, not to pkexec.
143
+ *
144
+ * - `sudo -n true` → `-n` (pkexec has no short options)
145
+ * - `sudo --non-interactive x` → `--non-interactive`
146
+ * - `sudo --user root x` → null (compatible)
147
+ * - `sudo -- x -n` → null (`--` ends sudo options)
148
+ */
149
+ function findBadOption(command, hit) {
150
+ let i = hit.index + hit.kw.length;
151
+ while (i < command.length && /\s/.test(command[i]))
152
+ i++;
153
+ while (i < command.length && command[i] === "-") {
154
+ let end = i + 1;
155
+ while (end < command.length && !/\s/.test(command[end]) && !/[;|&()]/.test(command[end]))
156
+ end++;
157
+ const token = command.slice(i, end);
158
+ if (token === "--")
159
+ break;
160
+ if (!token.startsWith("--"))
161
+ return token;
162
+ if (!PKEXEC_OPTIONS.has(token.split("=")[0]))
163
+ return token;
164
+ i = end;
165
+ while (i < command.length && /\s/.test(command[i]))
166
+ i++;
167
+ }
168
+ return null;
169
+ }
93
170
  const translations = {
94
171
  en: {
95
172
  blocked: "Privilege escalation command blocked by plugin",
96
173
  polkitDenied: "Polkit authentication denied by user",
174
+ badOption: 'Option "{opt}" is not supported by pkexec. Remove it or use a pkexec-compatible one (--user, --keep-cwd, --disable-internal-agent).',
97
175
  },
98
176
  zh: {
99
177
  blocked: "权限提升命令已被插件拦截",
100
178
  polkitDenied: "Polkit 认证已被用户拒绝",
179
+ badOption: '选项 "{opt}" 不被 pkexec 支持,请移除或改用 pkexec 兼容选项(--user、--keep-cwd、--disable-internal-agent)。',
101
180
  },
102
181
  ja: {
103
182
  blocked: "特権昇格コマンドはプラグインによりブロックされました",
104
183
  polkitDenied: "Polkit 認証がユーザーによって拒否されました",
184
+ badOption: 'オプション "{opt}" は pkexec ではサポートされていません。削除するか、pkexec 互換オプション(--user、--keep-cwd、--disable-internal-agent)を使用してください。',
105
185
  },
106
186
  ko: {
107
187
  blocked: "권한 상승 명령이 플러그인에 의해 차단되었습니다",
108
188
  polkitDenied: "Polkit 인증이 사용자에 의해 거부되었습니다",
189
+ badOption: '옵션 "{opt}"은(는) pkexec에서 지원되지 않습니다. 제거하거나 pkexec 호환 옵션(--user, --keep-cwd, --disable-internal-agent)을 사용하세요.',
109
190
  },
110
191
  de: {
111
192
  blocked: "Berechtigungseskalation vom Plugin blockiert",
112
193
  polkitDenied: "Polkit-Authentifizierung vom Benutzer abgelehnt",
194
+ badOption: 'Option "{opt}" wird von pkexec nicht unterstützt. Entfernen Sie sie oder verwenden Sie eine pkexec-kompatible Option (--user, --keep-cwd, --disable-internal-agent).',
113
195
  },
114
196
  fr: {
115
197
  blocked: "Commande d'élévation de privilèges bloquée par le plugin",
116
198
  polkitDenied: "Authentification polkit refusée par l'utilisateur",
199
+ badOption: 'Option "{opt}" non prise en charge par pkexec. Supprimez-la ou utilisez une option compatible pkexec (--user, --keep-cwd, --disable-internal-agent).',
117
200
  },
118
201
  es: {
119
202
  blocked: "Comando de elevación de privilegios bloqueado por el plugin",
120
203
  polkitDenied: "Autenticación polkit denegada por el usuario",
204
+ badOption: 'Opción "{opt}" no admitida por pkexec. Elimínela o use una opción compatible con pkexec (--user, --keep-cwd, --disable-internal-agent).',
121
205
  },
122
206
  pt: {
123
207
  blocked: "Comando de elevação de privilégios bloqueado pelo plugin",
124
208
  polkitDenied: "Autenticação polkit negada pelo usuário",
209
+ badOption: 'Opção "{opt}" não suportada pelo pkexec. Remova-a ou use uma opção compatível com pkexec (--user, --keep-cwd, --disable-internal-agent).',
125
210
  },
126
211
  ru: {
127
212
  blocked: "Команда повышения привилегий заблокирована плагином",
128
213
  polkitDenied: "Аутентификация polkit отклонена пользователем",
214
+ badOption: 'Параметр "{opt}" не поддерживается pkexec. Удалите его или используйте совместимый параметр (--user, --keep-cwd, --disable-internal-agent).',
129
215
  },
130
216
  tr: {
131
217
  blocked: "Yetki yükseltme komutu eklenti tarafından engellendi",
132
218
  polkitDenied: "Polkit kimlik doğrulaması kullanıcı tarafından reddedildi",
219
+ badOption: '"{opt}" seçeneği pkexec tarafından desteklenmiyor. Kaldırın veya pkexec uyumlu bir seçenek kullanın (--user, --keep-cwd, --disable-internal-agent).',
133
220
  },
134
221
  uk: {
135
222
  blocked: "Команду підвищення привілеїв заблоковано плагіном",
136
223
  polkitDenied: "Автентифікацію polkit відхилено користувачем",
224
+ badOption: 'Параметр "{opt}" не підтримується pkexec. Видаліть його або використайте сумісний параметр (--user, --keep-cwd, --disable-internal-agent).',
137
225
  },
138
226
  };
139
227
  function detectLocale() {
@@ -146,63 +234,101 @@ function detectLocale() {
146
234
  return "en";
147
235
  }
148
236
  const MSG = translations[detectLocale()] ?? translations.en;
149
- export const PolkitPlugin = async (_input) => {
237
+ /** The command string of a shell tool call, or "" for anything else. */
238
+ function shellCommand(input) {
239
+ if (typeof input !== "object" || input === null)
240
+ return "";
241
+ const command = input.command;
242
+ return typeof command === "string" ? command : "";
243
+ }
244
+ /** Plain text of a tool result, whether content is a string or Content[]. */
245
+ function resultText(result) {
246
+ const content = result.content;
247
+ if (typeof content === "string")
248
+ return content;
249
+ if (!Array.isArray(content))
250
+ return "";
251
+ return content
252
+ .map((part) => typeof part === "object" && part !== null && "type" in part && part.type === "text" && "text" in part
253
+ ? String(part.text)
254
+ : "")
255
+ .join("\n");
256
+ }
257
+ const setup = async (ctx) => {
150
258
  const deniedCommands = new Set();
151
- return {
152
- "tool.execute.before": async (hookInput, hookOutput) => {
153
- if (hookInput.tool !== "bash")
154
- return;
155
- const command = hookOutput?.args?.command ?? "";
156
- const hits = scanPrivileged(command);
157
- if (hits.length === 0)
158
- return;
159
- if (deniedCommands.has(command)) {
160
- throw new Error(MSG.polkitDenied);
161
- }
162
- if (hits.some((h) => h.kw === "sudoedit" || h.kw === "visudo")) {
163
- throw new Error(MSG.blocked);
164
- }
165
- // Already pkexec (leading or mid-command): pass through untouched.
166
- if (hits.every((h) => h.kw === "pkexec"))
167
- return;
168
- // Rewrite every in-command sudo/doas to pkexec (back to front so
169
- // indices stay valid): `cat x | sudo tee y` -> `cat x | pkexec tee y`.
170
- // No other rewriting: the command keeps its exact shape so the agent
171
- // sees only the minimal change (sudo -> pkexec).
172
- let rewritten = command;
173
- for (const h of [...hits].reverse()) {
174
- if (h.kw === "sudo" || h.kw === "doas") {
175
- rewritten =
176
- rewritten.slice(0, h.index) + "pkexec" + rewritten.slice(h.index + h.kw.length);
177
- }
259
+ await ctx.tool.hook("execute.before", (event) => {
260
+ if (event.tool !== "shell")
261
+ return;
262
+ // Mutating the input object in place rewrites the executed command;
263
+ // core uses `event.input` after every hook has run.
264
+ const args = event.input;
265
+ const command = shellCommand(args);
266
+ const hits = scanPrivileged(command);
267
+ if (hits.length === 0)
268
+ return;
269
+ if (deniedCommands.has(command)) {
270
+ throw new Error(MSG.polkitDenied);
271
+ }
272
+ if (hits.some((h) => h.kw === "sudoedit" || h.kw === "visudo")) {
273
+ throw new Error(MSG.blocked);
274
+ }
275
+ // Rewrite would produce `pkexec <bad option>`: fail with a clear
276
+ // message instead of a confusing `Cannot run program` error.
277
+ for (const h of hits) {
278
+ if (h.kw === "sudo" || h.kw === "doas" || h.kw === "pkexec") {
279
+ const bad = findBadOption(command, h);
280
+ if (bad)
281
+ throw new Error(MSG.badOption.replace("{opt}", bad));
178
282
  }
179
- hookOutput.args.command = rewritten;
180
- },
181
- "tool.execute.after": async (hookInput, hookOutput) => {
182
- if (hookInput.tool !== "bash")
183
- return;
184
- const command = hookInput.args?.command ?? "";
185
- const hits = scanPrivileged(command);
186
- if (hits.length === 0)
187
- return;
188
- // Reconstruct the user's original command for the deny list: map
189
- // pkexec back to sudo.
190
- let original = command;
191
- for (const h of [...scanPrivileged(original)].reverse()) {
192
- if (h.kw === "pkexec") {
193
- original = original.slice(0, h.index) + "sudo" + original.slice(h.index + 6);
194
- }
283
+ }
284
+ // Already pkexec (leading or mid-command): pass through untouched.
285
+ if (hits.every((h) => h.kw === "pkexec"))
286
+ return;
287
+ // Rewrite every in-command sudo/doas to pkexec (back to front so
288
+ // indices stay valid): `cat x | sudo tee y` -> `cat x | pkexec tee y`.
289
+ // No other rewriting: the command keeps its exact shape so the agent
290
+ // sees only the minimal change (sudo -> pkexec).
291
+ let rewritten = command;
292
+ for (const h of [...hits].reverse()) {
293
+ if (h.kw === "sudo" || h.kw === "doas") {
294
+ rewritten = rewritten.slice(0, h.index) + "pkexec" + rewritten.slice(h.index + h.kw.length);
195
295
  }
196
- // Only unambiguous authentication failures are reported and deny
197
- // listed. A hang (dialog never appears) is bounded by the bash tool's
198
- // own timeout (default 2 min) and is NOT translated here: it cannot
199
- // be told apart from a long-running command.
200
- if (/\bNot authorized\b/.test(hookOutput.output) ||
201
- /\bError executing command as another user\b/.test(hookOutput.output) ||
202
- /\bError creating textual authentication agent\b/.test(hookOutput.output)) {
203
- deniedCommands.add(original);
204
- throw new Error(MSG.polkitDenied);
296
+ }
297
+ ;
298
+ args.command = rewritten;
299
+ });
300
+ await ctx.tool.hook("execute.after", (event) => {
301
+ if (event.tool !== "shell")
302
+ return;
303
+ // `event.input` is the (possibly rewritten) command the shell ran:
304
+ // map pkexec back to sudo to reconstruct the original for the deny list.
305
+ const command = shellCommand(event.input);
306
+ const hits = scanPrivileged(command);
307
+ if (hits.length === 0)
308
+ return;
309
+ let original = command;
310
+ for (const h of [...hits].reverse()) {
311
+ if (h.kw === "pkexec") {
312
+ original = original.slice(0, h.index) + "sudo" + original.slice(h.index + 6);
205
313
  }
206
- },
207
- };
314
+ }
315
+ const text = event.status === "completed" ? resultText(event.result) : event.status === "error" ? event.error.message : "";
316
+ // Only unambiguous authentication failures are reported and deny
317
+ // listed. A hang (dialog never appears) is bounded by the shell tool's
318
+ // own timeout (default 2 min) and is NOT translated here: it cannot
319
+ // be told apart from a long-running command.
320
+ if (/\bNot authorized\b/.test(text) ||
321
+ /\bError executing command as another user\b/.test(text) ||
322
+ /\bError creating textual authentication agent\b/.test(text)) {
323
+ deniedCommands.add(original);
324
+ throw new Error(MSG.polkitDenied);
325
+ }
326
+ });
208
327
  };
328
+ /**
329
+ * OpenCode 2.0 requires a default export shaped `{ id, setup }` (the loader
330
+ * rejects anything else). Deliberately a plain object with type-only imports:
331
+ * the built plugin has zero runtime dependencies.
332
+ */
333
+ export default { id: "opencode-polkit", setup };
334
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/index.js ADDED
@@ -0,0 +1,6 @@
1
+ // Local-directory entrypoint for OpenCode 2.0: the loader resolves a plugin
2
+ // directory by looking for `server`/`index` at its root (package.json "main"
3
+ // and "exports" are only consulted for npm package names). npm installs reach
4
+ // the real build through package.json "exports"; this shim covers
5
+ // `"plugins": ["/path/to/opencode-polkit"]`.
6
+ export { default } from "./dist/index.js"
package/package.json CHANGED
@@ -1,34 +1,40 @@
1
1
  {
2
2
  "name": "opencode-polkit",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "description": "Redirect sudo/doas to pkexec in OpenCode — uses system polkit agent for native auth dialogs",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
- "import": "./dist/index.js",
11
- "types": "./dist/index.d.ts"
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
12
  }
13
13
  },
14
14
  "files": [
15
15
  "dist",
16
+ "index.js",
16
17
  "README.md",
17
18
  "LICENSE"
18
19
  ],
19
20
  "scripts": {
20
- "build": "deno task build",
21
- "check": "deno task check"
21
+ "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
22
+ "check": "tsc -p tsconfig.json --noEmit"
22
23
  },
23
24
  "engines": {
24
- "opencode": ">=1.0.0"
25
+ "opencode": ">=2.0.0"
25
26
  },
26
27
  "peerDependencies": {
27
- "@opencode-ai/plugin": ">=1.0.0"
28
+ "@opencode/plugin": ">=2.0.0"
29
+ },
30
+ "peerDependenciesMeta": {
31
+ "@opencode/plugin": {
32
+ "optional": true
33
+ }
28
34
  },
29
35
  "devDependencies": {
30
- "@opencode-ai/plugin": "^1.0.0",
31
- "bun-types": "^1.2.0",
36
+ "@opencode/plugin": "^2.0.12",
37
+ "@types/node": "^24.0.0",
32
38
  "typescript": "^5.5.0"
33
39
  },
34
40
  "keywords": ["opencode-plugin", "sudo", "pkexec", "polkit", "security"],