opencode-polkit 0.2.0 → 0.3.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/CHANGELOG.md +64 -0
- package/README.md +55 -64
- package/dist/index.d.ts +3 -4
- package/dist/index.js +24 -322
- package/dist/index.js.map +1 -1
- package/dist/shims.d.ts +16 -0
- package/dist/shims.js +218 -0
- package/dist/shims.js.map +1 -0
- package/index.js +5 -5
- package/package.json +7 -6
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.3.0] - 2026-11-01
|
|
9
|
+
|
|
10
|
+
0.3.0 replaces how the plugin decides anything. It used to parse the command
|
|
11
|
+
string to find `sudo`, then parse the output to guess whether authentication
|
|
12
|
+
failed; both are gone. The plugin now only prepends a directory of shims to
|
|
13
|
+
`PATH`, letting the shell's own name resolution pick them, and reads the
|
|
14
|
+
outcome from exit status alone. Everything that existed to support guessing —
|
|
15
|
+
the lexical scanner, the output matchers, the denial cache, the translations —
|
|
16
|
+
is deleted with it. The plugin no longer changes the command the agent wrote.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- `src/shims.ts`: `sudo`, `doas`, `sudoedit` and `visudo` scripts written to a
|
|
21
|
+
private `0700` temporary directory at setup and removed when the plugin is
|
|
22
|
+
disposed. `sudo` and `doas` forward to `pkexec`; `sudoedit` and `visudo`
|
|
23
|
+
refuse, because polkit has no equivalent flow.
|
|
24
|
+
- Deterministic option contract in the shims: `-u USER`, `-uUSER`, `--user USER`
|
|
25
|
+
and `--user=USER` become `pkexec --user USER`, `--` ends option parsing, and
|
|
26
|
+
every other option is refused with exit status `64` and a message naming the
|
|
27
|
+
alternative (`-n` → polkit decides, `-i`/`-s` → `pkexec bash`, `-E`/`-H`/`-P`
|
|
28
|
+
→ pkexec sanitizes the environment, `-b` → append `&`). `pkexec` would
|
|
29
|
+
otherwise reject them with `Cannot run program`, which says nothing about what
|
|
30
|
+
to do next.
|
|
31
|
+
- A one-line explanation on exit status `126`: it is the only pkexec failure
|
|
32
|
+
path that prints nothing of its own, so the shim says the dialog was
|
|
33
|
+
dismissed. Every `127` path already prints its reason.
|
|
34
|
+
- `SHIM_USAGE_EXIT` (64) documents that the refusal status is the shim's own and
|
|
35
|
+
cannot be confused with pkexec's `126`/`127`.
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- **Breaking:** commands are no longer rewritten. The `shell` tool's
|
|
40
|
+
`execute.before`/`execute.after` hooks are replaced by a single
|
|
41
|
+
`shell.create.before` hook that only sets `event.env.PATH`, so the agent's
|
|
42
|
+
command reaches the shell exactly as written, and `sudo` inside quotes,
|
|
43
|
+
comments or heredocs needs no special case.
|
|
44
|
+
- **Breaking:** authentication failures are reported from exit status
|
|
45
|
+
(`126` dismissed, `127` not authorized, `64` refused invocation), never from
|
|
46
|
+
output. The locale-dependent matchers for `Not authorized`,
|
|
47
|
+
`Error executing command as another user` and
|
|
48
|
+
`Error creating textual authentication agent` are gone.
|
|
49
|
+
- **Breaking:** messages are English only. The `Messages` table, the ten
|
|
50
|
+
translations and `detectLocale` are removed; the consumer is the coding agent.
|
|
51
|
+
- **Breaking:** a refused command is no longer remembered. Retrying after a
|
|
52
|
+
dismissed dialog prompts again instead of being rejected by the plugin.
|
|
53
|
+
- PATH interception is idempotent: prepending happens once, so stacked hooks do
|
|
54
|
+
not grow `PATH`.
|
|
55
|
+
|
|
56
|
+
### Removed
|
|
57
|
+
|
|
58
|
+
- `scanPrivileged` (the quote-, escape- and heredoc-aware lexical scanner) and
|
|
59
|
+
`findBadOption`.
|
|
60
|
+
- The `deniedCommands` and `originalCommands` maps, along with the
|
|
61
|
+
`execute.after` result rewriting they supported.
|
|
62
|
+
- The `translations` table for en, zh, ja, ko, de, fr, es, pt, ru, tr and uk.
|
|
63
|
+
- The i18n section of the README, which now documents the exit-status contract
|
|
64
|
+
and the PATH shim's limits instead.
|
package/README.md
CHANGED
|
@@ -1,28 +1,17 @@
|
|
|
1
1
|
# opencode-polkit
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Redirects `sudo` and `doas` to `pkexec` inside OpenCode, so authorisation goes
|
|
4
|
+
through the desktop's polkit agent (KDE, GNOME, …) instead of a terminal the
|
|
5
|
+
shell tool does not have. Applies to commands run by the shell tool; `sudo` in
|
|
6
|
+
other terminals is unchanged.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
`sudo` is always redirected to `pkexec` at execution time, and polkitd routes
|
|
9
|
-
the request to whatever agent is currently registered. Without an agent,
|
|
10
|
-
`pkexec` fails fast with `Error creating textual authentication agent`
|
|
11
|
-
(no TTY); the plugin reports that as a clear denial. A hang (dialog never
|
|
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
|
-
apart from a long-running command.
|
|
15
|
-
|
|
16
|
-
Requires OpenCode >= 2.0 (the plugin uses the `@opencode/plugin` v2 API and
|
|
17
|
-
is skipped by older versions).
|
|
18
|
-
|
|
19
|
-
## Install
|
|
8
|
+
## Installation
|
|
20
9
|
|
|
21
10
|
```sh
|
|
22
11
|
opencode plugin add opencode-polkit
|
|
23
12
|
```
|
|
24
13
|
|
|
25
|
-
Or add to `opencode.json` / `~/.config/opencode/opencode.jsonc`:
|
|
14
|
+
Or add it to `opencode.json` / `~/.config/opencode/opencode.jsonc`:
|
|
26
15
|
|
|
27
16
|
```json
|
|
28
17
|
{
|
|
@@ -30,8 +19,7 @@ Or add to `opencode.json` / `~/.config/opencode/opencode.jsonc`:
|
|
|
30
19
|
}
|
|
31
20
|
```
|
|
32
21
|
|
|
33
|
-
For local
|
|
34
|
-
re-exports `dist/`) and point opencode at the project directory instead:
|
|
22
|
+
For a local checkout, run `npm run build` once, then reference the directory:
|
|
35
23
|
|
|
36
24
|
```json
|
|
37
25
|
{
|
|
@@ -41,51 +29,54 @@ re-exports `dist/`) and point opencode at the project directory instead:
|
|
|
41
29
|
|
|
42
30
|
## Behavior
|
|
43
31
|
|
|
44
|
-
| Command
|
|
45
|
-
|
|
46
|
-
| `sudo
|
|
47
|
-
| `
|
|
48
|
-
| `sudo
|
|
49
|
-
| `
|
|
50
|
-
| `
|
|
51
|
-
| `sudoedit
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
`
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
##
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
32
|
+
| Command | Result |
|
|
33
|
+
|------------------------|---------------------------------------------|
|
|
34
|
+
| `sudo apt install x` | polkit dialog, then runs as root |
|
|
35
|
+
| `sudo -u bob …` | as above, as `bob` |
|
|
36
|
+
| `cat x \| sudo tee y` | dialog for `tee` |
|
|
37
|
+
| `sudo -n …` | refused: pkexec has no non-interactive mode |
|
|
38
|
+
| `sudo -i`, `-s`, `-b` | refused, with the equivalent command |
|
|
39
|
+
| `sudoedit`, `visudo` | refused, with the alternative |
|
|
40
|
+
| `pkexec …` | passed through |
|
|
41
|
+
|
|
42
|
+
Refused invocations exit with status 64 and one line naming the alternative.
|
|
43
|
+
|
|
44
|
+
## How it works
|
|
45
|
+
|
|
46
|
+
`sudo` and `doas` stand-ins are placed first on `PATH`, so the shell resolves
|
|
47
|
+
those instead of the system binaries, and they forward the command to `pkexec`.
|
|
48
|
+
Command text is never rewritten, so quoting, pipes and subshells need no special
|
|
49
|
+
handling.
|
|
50
|
+
|
|
51
|
+
## Authorisation
|
|
52
|
+
|
|
53
|
+
`polkitd` decides whether a password is required. These commands always require
|
|
54
|
+
one; other actions may not, according to policy. The plugin takes no part in
|
|
55
|
+
that decision.
|
|
56
|
+
|
|
57
|
+
## Limitations
|
|
58
|
+
|
|
59
|
+
- Absolute paths (`/usr/bin/sudo`) and relative ones (`./sudo`) bypass `PATH`.
|
|
60
|
+
- Commands that reset `PATH` (`env -i`, `PATH=/usr/bin …`) bypass it as well.
|
|
61
|
+
- Programs that invoke `sudo` with a scrubbed environment bypass it.
|
|
62
|
+
|
|
63
|
+
These bypass the redirect, not polkit, which still decides whether the command
|
|
64
|
+
runs.
|
|
65
|
+
|
|
66
|
+
## Requirements
|
|
67
|
+
|
|
68
|
+
`polkit` and a registered authentication agent. Desktop environments start one;
|
|
69
|
+
without one, `pkexec` fails instead of prompting.
|
|
70
|
+
|
|
71
|
+
## Exit status
|
|
72
|
+
|
|
73
|
+
| Status | Meaning |
|
|
74
|
+
|--------|----------------------------------------------|
|
|
75
|
+
| 126 | the polkit dialog was dismissed |
|
|
76
|
+
| 127 | not authorised, or the program could not run |
|
|
77
|
+
| 64 | the plugin refused the invocation |
|
|
78
|
+
|
|
79
|
+
Messages are in English.
|
|
89
80
|
|
|
90
81
|
## License
|
|
91
82
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import type { Plugin } from "@opencode/plugin";
|
|
2
2
|
/**
|
|
3
|
-
* OpenCode 2.0 requires a default export shaped `{ id, setup }
|
|
4
|
-
*
|
|
5
|
-
* the built plugin has zero runtime dependencies.
|
|
3
|
+
* OpenCode 2.0 requires a default export shaped `{ id, setup }`. Type-only
|
|
4
|
+
* imports keep the built plugin free of runtime dependencies.
|
|
6
5
|
*/
|
|
7
6
|
declare const _default: {
|
|
8
7
|
id: string;
|
|
9
|
-
setup: (ctx: Plugin.Context) => Promise<
|
|
8
|
+
setup: (ctx: Plugin.Context) => Promise<Plugin.Cleanup>;
|
|
10
9
|
};
|
|
11
10
|
export default _default;
|
package/dist/index.js
CHANGED
|
@@ -1,334 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
const PKEXEC_OPTIONS = new Set([
|
|
3
|
-
"--version",
|
|
4
|
-
"--help",
|
|
5
|
-
"--disable-internal-agent",
|
|
6
|
-
"--keep-cwd",
|
|
7
|
-
"--user",
|
|
8
|
-
]);
|
|
1
|
+
import { installPolkitShims, removePolkitShims } from "./shims.js";
|
|
9
2
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* Shell commands may contain `sudo` anywhere a command starts (pipes,
|
|
13
|
-
* `&&`/`||`, subshells, command substitution), and string literals /
|
|
14
|
-
* comments may contain the same words spuriously. A plain regex cannot
|
|
15
|
-
* tell those apart; this scanner tracks quote state (' " and backslash
|
|
16
|
-
* escapes) and only reports keywords in executable position.
|
|
17
|
-
*
|
|
18
|
-
* - `node -e "…sudo…"` → no hit (inside quotes)
|
|
19
|
-
* - `cat x | sudo tee y` → hit at index 8 (command boundary)
|
|
20
|
-
* - `cd /tmp && sudo ./x` → hit (after `&&`)
|
|
21
|
-
* - `--name sudo bash` → no hit (argument position)
|
|
22
|
-
* - `echo x; # sudo y` → no hit (comment)
|
|
3
|
+
* PATH used when the shell tool supplies none, mirroring a normal Linux host.
|
|
4
|
+
* Only the prepend matters; the rest is a last-resort fallback.
|
|
23
5
|
*/
|
|
24
|
-
|
|
25
|
-
const hits = [];
|
|
26
|
-
let inSingle = false;
|
|
27
|
-
let inDouble = false;
|
|
28
|
-
let escaped = false;
|
|
29
|
-
// True right after a command boundary (`;`, `&&`, `||`, `|`, `(`, start).
|
|
30
|
-
let atCommandStart = true;
|
|
31
|
-
// Heredoc delimiters waiting to close (stack; multiple << are legal).
|
|
32
|
-
const heredocs = [];
|
|
33
|
-
for (let i = 0; i < command.length; i++) {
|
|
34
|
-
const ch = command[i];
|
|
35
|
-
// Inside a heredoc body: only a line consisting of the delimiter ends
|
|
36
|
-
// it. Content there is a string literal, never a command. Pop the
|
|
37
|
-
// matching delimiter (nested heredocs close in any order).
|
|
38
|
-
if (heredocs.length > 0) {
|
|
39
|
-
const lineEnd = command.indexOf("\n", i);
|
|
40
|
-
const line = lineEnd === -1 ? command.slice(i) : command.slice(i, lineEnd);
|
|
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;
|
|
46
|
-
}
|
|
47
|
-
i = lineEnd === -1 ? command.length : lineEnd;
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
if (escaped) {
|
|
51
|
-
escaped = false;
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
if (ch === "\\" && !inSingle) {
|
|
55
|
-
escaped = true;
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
if (ch === "'" && !inDouble) {
|
|
59
|
-
inSingle = !inSingle;
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
if (ch === '"' && !inDouble) {
|
|
63
|
-
inDouble = !inDouble;
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
if (inSingle || inDouble)
|
|
67
|
-
continue;
|
|
68
|
-
// Heredoc start: `<<[-]DELIM` (delim may be quoted or unquoted).
|
|
69
|
-
if (ch === "<" && command[i + 1] === "<") {
|
|
70
|
-
let j = i + 2;
|
|
71
|
-
if (command[j] === "-")
|
|
72
|
-
j++;
|
|
73
|
-
while (j < command.length && /\s/.test(command[j]))
|
|
74
|
-
j++;
|
|
75
|
-
if (j < command.length && (command[j] === "'" || command[j] === '"')) {
|
|
76
|
-
const q = command[j];
|
|
77
|
-
const end = command.indexOf(q, j + 1);
|
|
78
|
-
if (end !== -1) {
|
|
79
|
-
heredocs.push(command.slice(j + 1, end));
|
|
80
|
-
i = end;
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
const m = /^[^\s|;&<>()]+/.exec(command.slice(j));
|
|
86
|
-
if (m) {
|
|
87
|
-
heredocs.push(m[0]);
|
|
88
|
-
i = j + m[0].length - 1;
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
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)
|
|
124
|
-
continue;
|
|
125
|
-
for (const kw of ["sudoedit", "visudo", "sudo", "doas", "pkexec"]) {
|
|
126
|
-
if (command.startsWith(kw, i)) {
|
|
127
|
-
const next = command[i + kw.length] ?? "";
|
|
128
|
-
if (!/[-\/.\w]/.test(next)) {
|
|
129
|
-
hits.push({ index: i, kw });
|
|
130
|
-
i += kw.length - 1;
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return hits;
|
|
137
|
-
}
|
|
6
|
+
const FALLBACK_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
138
7
|
/**
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
8
|
+
* Redirects sudo/doas to pkexec without touching the command text.
|
|
9
|
+
*
|
|
10
|
+
* The shell tool spawns `$SHELL -c 'command'` with the environment built here,
|
|
11
|
+
* so putting the shims first on PATH lets the shell's own name resolution pick
|
|
12
|
+
* them up — `sudo`, `$(sudo …)`, `xargs sudo`, `FOO=1 sudo`, nested scripts.
|
|
13
|
+
* Nothing parses the command string, and nothing parses pkexec's output: the
|
|
14
|
+
* shims hand pkexec's exit status (126 dismissed, 127 not authorized) back to
|
|
15
|
+
* the caller unchanged.
|
|
143
16
|
*
|
|
144
|
-
* -
|
|
145
|
-
*
|
|
146
|
-
* - `sudo --user root x` → null (compatible)
|
|
147
|
-
* - `sudo -- x -n` → null (`--` ends sudo options)
|
|
17
|
+
* The shell is non-interactive and non-login, so no profile rewrites PATH and
|
|
18
|
+
* no alias can shadow the shims.
|
|
148
19
|
*/
|
|
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
|
-
}
|
|
170
|
-
const translations = {
|
|
171
|
-
en: {
|
|
172
|
-
blocked: "Privilege escalation command blocked by plugin",
|
|
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).',
|
|
175
|
-
},
|
|
176
|
-
zh: {
|
|
177
|
-
blocked: "权限提升命令已被插件拦截",
|
|
178
|
-
polkitDenied: "Polkit 认证已被用户拒绝",
|
|
179
|
-
badOption: '选项 "{opt}" 不被 pkexec 支持,请移除或改用 pkexec 兼容选项(--user、--keep-cwd、--disable-internal-agent)。',
|
|
180
|
-
},
|
|
181
|
-
ja: {
|
|
182
|
-
blocked: "特権昇格コマンドはプラグインによりブロックされました",
|
|
183
|
-
polkitDenied: "Polkit 認証がユーザーによって拒否されました",
|
|
184
|
-
badOption: 'オプション "{opt}" は pkexec ではサポートされていません。削除するか、pkexec 互換オプション(--user、--keep-cwd、--disable-internal-agent)を使用してください。',
|
|
185
|
-
},
|
|
186
|
-
ko: {
|
|
187
|
-
blocked: "권한 상승 명령이 플러그인에 의해 차단되었습니다",
|
|
188
|
-
polkitDenied: "Polkit 인증이 사용자에 의해 거부되었습니다",
|
|
189
|
-
badOption: '옵션 "{opt}"은(는) pkexec에서 지원되지 않습니다. 제거하거나 pkexec 호환 옵션(--user, --keep-cwd, --disable-internal-agent)을 사용하세요.',
|
|
190
|
-
},
|
|
191
|
-
de: {
|
|
192
|
-
blocked: "Berechtigungseskalation vom Plugin blockiert",
|
|
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).',
|
|
195
|
-
},
|
|
196
|
-
fr: {
|
|
197
|
-
blocked: "Commande d'élévation de privilèges bloquée par le plugin",
|
|
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).',
|
|
200
|
-
},
|
|
201
|
-
es: {
|
|
202
|
-
blocked: "Comando de elevación de privilegios bloqueado por el plugin",
|
|
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).',
|
|
205
|
-
},
|
|
206
|
-
pt: {
|
|
207
|
-
blocked: "Comando de elevação de privilégios bloqueado pelo plugin",
|
|
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).',
|
|
210
|
-
},
|
|
211
|
-
ru: {
|
|
212
|
-
blocked: "Команда повышения привилегий заблокирована плагином",
|
|
213
|
-
polkitDenied: "Аутентификация polkit отклонена пользователем",
|
|
214
|
-
badOption: 'Параметр "{opt}" не поддерживается pkexec. Удалите его или используйте совместимый параметр (--user, --keep-cwd, --disable-internal-agent).',
|
|
215
|
-
},
|
|
216
|
-
tr: {
|
|
217
|
-
blocked: "Yetki yükseltme komutu eklenti tarafından engellendi",
|
|
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).',
|
|
220
|
-
},
|
|
221
|
-
uk: {
|
|
222
|
-
blocked: "Команду підвищення привілеїв заблоковано плагіном",
|
|
223
|
-
polkitDenied: "Автентифікацію polkit відхилено користувачем",
|
|
224
|
-
badOption: 'Параметр "{opt}" не підтримується pkexec. Видаліть його або використайте сумісний параметр (--user, --keep-cwd, --disable-internal-agent).',
|
|
225
|
-
},
|
|
226
|
-
};
|
|
227
|
-
function detectLocale() {
|
|
228
|
-
const raw = process.env.LC_MESSAGES || process.env.LANG || "en";
|
|
229
|
-
const tag = raw.split(".")[0].replace(/_/g, "-").toLowerCase();
|
|
230
|
-
for (const prefix of ["zh", "ja", "ko", "de", "fr", "es", "pt", "ru", "tr", "uk"]) {
|
|
231
|
-
if (tag.startsWith(prefix))
|
|
232
|
-
return prefix;
|
|
233
|
-
}
|
|
234
|
-
return "en";
|
|
235
|
-
}
|
|
236
|
-
const MSG = translations[detectLocale()] ?? translations.en;
|
|
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
20
|
const setup = async (ctx) => {
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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));
|
|
282
|
-
}
|
|
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);
|
|
295
|
-
}
|
|
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);
|
|
313
|
-
}
|
|
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
|
-
}
|
|
21
|
+
const shimDir = installPolkitShims();
|
|
22
|
+
const prefix = `${shimDir}:`;
|
|
23
|
+
// Hooks stack across plugin loads; prepending once keeps PATH duplicate-free.
|
|
24
|
+
await ctx.shell.hook("create.before", (event) => {
|
|
25
|
+
const path = event.env.PATH || FALLBACK_PATH;
|
|
26
|
+
if (!path.startsWith(prefix))
|
|
27
|
+
event.env.PATH = prefix + path;
|
|
326
28
|
});
|
|
29
|
+
return () => removePolkitShims(shimDir);
|
|
327
30
|
};
|
|
328
31
|
/**
|
|
329
|
-
* OpenCode 2.0 requires a default export shaped `{ id, setup }
|
|
330
|
-
*
|
|
331
|
-
* the built plugin has zero runtime dependencies.
|
|
32
|
+
* OpenCode 2.0 requires a default export shaped `{ id, setup }`. Type-only
|
|
33
|
+
* imports keep the built plugin free of runtime dependencies.
|
|
332
34
|
*/
|
|
333
35
|
export default { id: "opencode-polkit", setup };
|
|
334
36
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAElE;;;GAGG;AACH,MAAM,aAAa,GAAG,8DAA8D,CAAA;AAEpF;;;;;;;;;;;;GAYG;AACH,MAAM,KAAK,GAAG,KAAK,EAAE,GAAmB,EAA2B,EAAE;IACnE,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAA;IACpC,MAAM,MAAM,GAAG,GAAG,OAAO,GAAG,CAAA;IAE5B,8EAA8E;IAC9E,MAAM,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,KAAK,EAAE,EAAE;QAC9C,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI,aAAa,CAAA;QAC5C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,KAAK,CAAC,GAAG,CAAC,IAAI,GAAG,MAAM,GAAG,IAAI,CAAA;IAC9D,CAAC,CAAC,CAAA;IAEF,OAAO,GAAG,EAAE,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAA;AACzC,CAAC,CAAA;AAED;;;GAGG;AACH,eAAe,EAAE,EAAE,EAAE,iBAAiB,EAAE,KAAK,EAA0B,CAAA"}
|
package/dist/shims.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exit status every shim uses for an invocation pkexec cannot express
|
|
3
|
+
* (EX_USAGE). Distinct from pkexec's own 126 (dismissed) and 127 (not
|
|
4
|
+
* authorized or failed) so callers can tell the two apart without reading
|
|
5
|
+
* any output.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SHIM_USAGE_EXIT = 64;
|
|
8
|
+
/**
|
|
9
|
+
* Writes the shim scripts into a fresh private directory and returns it.
|
|
10
|
+
* The caller prepends this directory to PATH; the shell then resolves
|
|
11
|
+
* `sudo`/`doas` to these files instead of the system ones. The directory is
|
|
12
|
+
* 0700 so no other local user can rewrite the scripts.
|
|
13
|
+
*/
|
|
14
|
+
export declare function installPolkitShims(): string;
|
|
15
|
+
/** Deletes a directory created by installPolkitShims. */
|
|
16
|
+
export declare function removePolkitShims(directory: string): void;
|
package/dist/shims.js
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { chmodSync, existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { isAbsolute, join } from "node:path";
|
|
4
|
+
/**
|
|
5
|
+
* Exit status every shim uses for an invocation pkexec cannot express
|
|
6
|
+
* (EX_USAGE). Distinct from pkexec's own 126 (dismissed) and 127 (not
|
|
7
|
+
* authorized or failed) so callers can tell the two apart without reading
|
|
8
|
+
* any output.
|
|
9
|
+
*/
|
|
10
|
+
export const SHIM_USAGE_EXIT = 64;
|
|
11
|
+
/**
|
|
12
|
+
* The sudo shim. Lives on PATH ahead of the real sudo, so the shell — not
|
|
13
|
+
* this plugin — decides that `sudo` means this file. It forwards to pkexec
|
|
14
|
+
* and never parses what pkexec prints: the exit status is the whole result.
|
|
15
|
+
*
|
|
16
|
+
* Only options with an exact pkexec equivalent are translated. Everything
|
|
17
|
+
* else is refused with an actionable message rather than forwarded, because
|
|
18
|
+
* pkexec would reject it with a message that does not say what to do instead.
|
|
19
|
+
*/
|
|
20
|
+
const SUDO_SHIM = `#!/bin/sh
|
|
21
|
+
# opencode-polkit: sudo shim. Routes sudo to pkexec so polkit can authorize it.
|
|
22
|
+
# Generated at plugin startup; do not edit.
|
|
23
|
+
|
|
24
|
+
fail() {
|
|
25
|
+
printf 'opencode-polkit: %s\\n' "$1" >&2
|
|
26
|
+
exit ${SHIM_USAGE_EXIT}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
command -v pkexec >/dev/null 2>&1 || fail 'pkexec is not installed; install polkit'
|
|
30
|
+
|
|
31
|
+
user=
|
|
32
|
+
while [ $# -gt 0 ]; do
|
|
33
|
+
case $1 in
|
|
34
|
+
--)
|
|
35
|
+
shift
|
|
36
|
+
break
|
|
37
|
+
;;
|
|
38
|
+
-u)
|
|
39
|
+
[ $# -ge 2 ] || fail 'sudo -u needs a username: -u USER PROGRAM'
|
|
40
|
+
user=$2
|
|
41
|
+
shift 2
|
|
42
|
+
;;
|
|
43
|
+
--user)
|
|
44
|
+
[ $# -ge 2 ] || fail 'sudo --user needs a username: --user USER PROGRAM'
|
|
45
|
+
user=$2
|
|
46
|
+
shift 2
|
|
47
|
+
;;
|
|
48
|
+
-u?*)
|
|
49
|
+
user=\${1#-u}
|
|
50
|
+
[ -n "$user" ] || fail 'sudo -u needs a username: -u USER PROGRAM'
|
|
51
|
+
shift
|
|
52
|
+
;;
|
|
53
|
+
--user=*)
|
|
54
|
+
user=\${1#--user=}
|
|
55
|
+
[ -n "$user" ] || fail 'sudo --user needs a username: --user USER PROGRAM'
|
|
56
|
+
shift
|
|
57
|
+
;;
|
|
58
|
+
-n | --non-interactive)
|
|
59
|
+
fail 'sudo -n is not supported: pkexec has no non-interactive mode, polkit decides whether authentication is needed. Drop -n.'
|
|
60
|
+
;;
|
|
61
|
+
-v | --validate | -k | --reset-timestamp | -K | --remove-timestamp)
|
|
62
|
+
fail 'sudo credential caching does not exist under polkit; drop -v/-k.'
|
|
63
|
+
;;
|
|
64
|
+
-l | --list)
|
|
65
|
+
fail 'sudo -l is not available under polkit; inspect policy with pkaction --verbose.'
|
|
66
|
+
;;
|
|
67
|
+
-e | --edit)
|
|
68
|
+
fail 'sudoedit is not supported; write a temp file and run pkexec install or pkexec cp on it.'
|
|
69
|
+
;;
|
|
70
|
+
-i | --login | -s | --shell)
|
|
71
|
+
fail 'sudo -i/-s opens a login shell; run pkexec bash instead.'
|
|
72
|
+
;;
|
|
73
|
+
-S | --stdin)
|
|
74
|
+
fail 'sudo -S reads the password from stdin; pkexec authenticates through the polkit agent. Drop -S.'
|
|
75
|
+
;;
|
|
76
|
+
-A | --askpass)
|
|
77
|
+
fail 'SUDO_ASKPASS does not apply to pkexec; drop -A.'
|
|
78
|
+
;;
|
|
79
|
+
-b | --background)
|
|
80
|
+
fail 'sudo -b backgrounds the command; append & instead.'
|
|
81
|
+
;;
|
|
82
|
+
-E | --preserve-env | --preserve-env=* | -H | --set-home | -P | --preserve-groups | -N | --no-update)
|
|
83
|
+
fail 'pkexec deliberately sanitizes the environment; drop -E/-H/-P.'
|
|
84
|
+
;;
|
|
85
|
+
-?*)
|
|
86
|
+
fail "option has no pkexec equivalent: $1 (pkexec accepts --user, --keep-cwd, --disable-internal-agent)"
|
|
87
|
+
;;
|
|
88
|
+
*)
|
|
89
|
+
break
|
|
90
|
+
;;
|
|
91
|
+
esac
|
|
92
|
+
done
|
|
93
|
+
|
|
94
|
+
# pkexec is silent when the dialog is dismissed (status 126), so say what the
|
|
95
|
+
# status means. Every other failure path already prints its own reason.
|
|
96
|
+
run_pkexec() {
|
|
97
|
+
pkexec "$@"
|
|
98
|
+
status=$?
|
|
99
|
+
if [ $status -eq 126 ]; then
|
|
100
|
+
printf 'opencode-polkit: %s\n' 'the polkit authentication dialog was dismissed' >&2
|
|
101
|
+
fi
|
|
102
|
+
exit $status
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if [ -n "$user" ]; then
|
|
106
|
+
run_pkexec --user "$user" "$@"
|
|
107
|
+
fi
|
|
108
|
+
run_pkexec "$@"
|
|
109
|
+
`;
|
|
110
|
+
/** The doas shim: same contract, doas option grammar. */
|
|
111
|
+
const DOAS_SHIM = `#!/bin/sh
|
|
112
|
+
# opencode-polkit: doas shim. Routes doas to pkexec so polkit can authorize it.
|
|
113
|
+
# Generated at plugin startup; do not edit.
|
|
114
|
+
|
|
115
|
+
fail() {
|
|
116
|
+
printf 'opencode-polkit: %s\\n' "$1" >&2
|
|
117
|
+
exit ${SHIM_USAGE_EXIT}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
command -v pkexec >/dev/null 2>&1 || fail 'pkexec is not installed; install polkit'
|
|
121
|
+
|
|
122
|
+
user=
|
|
123
|
+
while [ $# -gt 0 ]; do
|
|
124
|
+
case $1 in
|
|
125
|
+
--)
|
|
126
|
+
shift
|
|
127
|
+
break
|
|
128
|
+
;;
|
|
129
|
+
-u)
|
|
130
|
+
[ $# -ge 2 ] || fail 'doas -u needs a username: -u USER PROGRAM'
|
|
131
|
+
user=$2
|
|
132
|
+
shift 2
|
|
133
|
+
;;
|
|
134
|
+
-u?*)
|
|
135
|
+
user=\${1#-u}
|
|
136
|
+
[ -n "$user" ] || fail 'doas -u needs a username: -u USER PROGRAM'
|
|
137
|
+
shift
|
|
138
|
+
;;
|
|
139
|
+
-n)
|
|
140
|
+
fail 'doas -n is not supported: pkexec has no non-interactive mode. Drop -n.'
|
|
141
|
+
;;
|
|
142
|
+
-s)
|
|
143
|
+
fail 'doas -s opens a shell; run pkexec bash instead.'
|
|
144
|
+
;;
|
|
145
|
+
-C | -a | -L)
|
|
146
|
+
fail "option has no pkexec equivalent: $1"
|
|
147
|
+
;;
|
|
148
|
+
-?*)
|
|
149
|
+
fail "unknown doas option: $1"
|
|
150
|
+
;;
|
|
151
|
+
*)
|
|
152
|
+
break
|
|
153
|
+
;;
|
|
154
|
+
esac
|
|
155
|
+
done
|
|
156
|
+
|
|
157
|
+
# pkexec is silent when the dialog is dismissed (status 126), so say what the
|
|
158
|
+
# status means. Every other failure path already prints its own reason.
|
|
159
|
+
run_pkexec() {
|
|
160
|
+
pkexec "$@"
|
|
161
|
+
status=$?
|
|
162
|
+
if [ $status -eq 126 ]; then
|
|
163
|
+
printf 'opencode-polkit: %s\n' 'the polkit authentication dialog was dismissed' >&2
|
|
164
|
+
fi
|
|
165
|
+
exit $status
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if [ -n "$user" ]; then
|
|
169
|
+
run_pkexec --user "$user" "$@"
|
|
170
|
+
fi
|
|
171
|
+
run_pkexec "$@"
|
|
172
|
+
`;
|
|
173
|
+
/** sudoedit edits through sudo's own temporary-file flow; polkit has none. */
|
|
174
|
+
const SUDOEDIT_SHIM = `#!/bin/sh
|
|
175
|
+
printf 'opencode-polkit: sudoedit is not supported; write a temp file and run pkexec install or pkexec cp on it.\\n' >&2
|
|
176
|
+
exit ${SHIM_USAGE_EXIT}
|
|
177
|
+
`;
|
|
178
|
+
/** visudo needs an interactive editor on a terminal the shell tool does not have. */
|
|
179
|
+
const VISUDO_SHIM = `#!/bin/sh
|
|
180
|
+
printf 'opencode-polkit: visudo needs an interactive terminal; validate a candidate file with pkexec visudo -c -f FILE instead.\\n' >&2
|
|
181
|
+
exit ${SHIM_USAGE_EXIT}
|
|
182
|
+
`;
|
|
183
|
+
/** File name → script body. Names must match the commands being redirected. */
|
|
184
|
+
const POLKIT_SHIM_SCRIPTS = {
|
|
185
|
+
sudo: SUDO_SHIM,
|
|
186
|
+
doas: DOAS_SHIM,
|
|
187
|
+
sudoedit: SUDOEDIT_SHIM,
|
|
188
|
+
visudo: VISUDO_SHIM,
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* Writes the shim scripts into a fresh private directory and returns it.
|
|
192
|
+
* The caller prepends this directory to PATH; the shell then resolves
|
|
193
|
+
* `sudo`/`doas` to these files instead of the system ones. The directory is
|
|
194
|
+
* 0700 so no other local user can rewrite the scripts.
|
|
195
|
+
*/
|
|
196
|
+
export function installPolkitShims() {
|
|
197
|
+
const directory = mkdtempSync(join(shimBaseDirectory(), "opencode-polkit-"));
|
|
198
|
+
for (const [name, script] of Object.entries(POLKIT_SHIM_SCRIPTS)) {
|
|
199
|
+
const file = join(directory, name);
|
|
200
|
+
writeFileSync(file, script);
|
|
201
|
+
// writeFileSync's mode is filtered by umask, so set it explicitly.
|
|
202
|
+
chmodSync(file, 0o755);
|
|
203
|
+
}
|
|
204
|
+
return directory;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Where shim directories live. Prefers the login session's runtime directory,
|
|
208
|
+
* which the system clears on logout, over /tmp which keeps leftovers.
|
|
209
|
+
*/
|
|
210
|
+
function shimBaseDirectory() {
|
|
211
|
+
const runtime = process.env.XDG_RUNTIME_DIR;
|
|
212
|
+
return runtime && isAbsolute(runtime) && existsSync(runtime) ? runtime : tmpdir();
|
|
213
|
+
}
|
|
214
|
+
/** Deletes a directory created by installPolkitShims. */
|
|
215
|
+
export function removePolkitShims(directory) {
|
|
216
|
+
rmSync(directory, { recursive: true, force: true });
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=shims.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shims.js","sourceRoot":"","sources":["../src/shims.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACnF,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAChC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAE5C;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAA;AAEjC;;;;;;;;GAQG;AACH,MAAM,SAAS,GAAG;;;;;;QAMV,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmFtB,CAAA;AAED,yDAAyD;AACzD,MAAM,SAAS,GAAG;;;;;;QAMV,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuDtB,CAAA;AAED,8EAA8E;AAC9E,MAAM,aAAa,GAAG;;OAEf,eAAe;CACrB,CAAA;AAED,qFAAqF;AACrF,MAAM,WAAW,GAAG;;OAEb,eAAe;CACrB,CAAA;AAED,+EAA+E;AAC/E,MAAM,mBAAmB,GAA2B;IAClD,IAAI,EAAE,SAAS;IACf,IAAI,EAAE,SAAS;IACf,QAAQ,EAAE,aAAa;IACvB,MAAM,EAAE,WAAW;CACpB,CAAA;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,kBAAkB,CAAC,CAAC,CAAA;IAC5E,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;QAClC,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QAC3B,mEAAmE;QACnE,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACxB,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB;IACxB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAA;IAC3C,OAAO,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,CAAA;AACnF,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,iBAAiB,CAAC,SAAiB;IACjD,MAAM,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;AACrD,CAAC"}
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// Local-directory entrypoint for OpenCode 2.
|
|
2
|
-
// directory
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// `"plugins": ["/path/to/opencode-polkit"]
|
|
1
|
+
// Local-directory entrypoint for OpenCode: OpenCode 2.2 also resolves a
|
|
2
|
+
// directory through a named package.json and its "exports", but older 2.0
|
|
3
|
+
// releases only look for `server`/`index` at the root. npm installs reach the
|
|
4
|
+
// real build through package.json "exports"; this shim keeps
|
|
5
|
+
// `"plugins": ["/path/to/opencode-polkit"]` working on both.
|
|
6
6
|
export { default } from "./dist/index.js"
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-polkit",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Redirect sudo/doas to pkexec in OpenCode
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Redirect sudo/doas to pkexec in OpenCode via PATH shims, so the system polkit agent handles authentication",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"dist",
|
|
16
16
|
"index.js",
|
|
17
17
|
"README.md",
|
|
18
|
+
"CHANGELOG.md",
|
|
18
19
|
"LICENSE"
|
|
19
20
|
],
|
|
20
21
|
"scripts": {
|
|
@@ -22,10 +23,10 @@
|
|
|
22
23
|
"check": "tsc -p tsconfig.json --noEmit"
|
|
23
24
|
},
|
|
24
25
|
"engines": {
|
|
25
|
-
"opencode": ">=2.0.
|
|
26
|
+
"opencode": ">=2.0.29"
|
|
26
27
|
},
|
|
27
28
|
"peerDependencies": {
|
|
28
|
-
"@opencode/plugin": ">=2.0.
|
|
29
|
+
"@opencode/plugin": ">=2.0.29"
|
|
29
30
|
},
|
|
30
31
|
"peerDependenciesMeta": {
|
|
31
32
|
"@opencode/plugin": {
|
|
@@ -33,11 +34,11 @@
|
|
|
33
34
|
}
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
|
-
"@opencode/plugin": "^2.
|
|
37
|
+
"@opencode/plugin": "^2.2.5",
|
|
37
38
|
"@types/node": "^24.0.0",
|
|
38
39
|
"typescript": "^5.5.0"
|
|
39
40
|
},
|
|
40
|
-
"keywords": ["opencode-plugin", "sudo", "pkexec", "polkit", "security"],
|
|
41
|
+
"keywords": ["opencode-plugin", "sudo", "doas", "pkexec", "polkit", "security"],
|
|
41
42
|
"license": "MIT",
|
|
42
43
|
"repository": {
|
|
43
44
|
"type": "git",
|