@neuralnomads/codenomad-dev 0.18.0-dev-20260716-40cee071 → 0.18.0-dev-20260720-4c50829d
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/dist/auth/manager.js +1 -1
- package/dist/filesystem/__tests__/search-cache.test.js +5 -5
- package/dist/filesystem/search-cache.js +1 -1
- package/dist/index.js +42 -0
- package/dist/index.test.js +40 -10
- package/dist/opencode-update/service.js +150 -0
- package/dist/opencode-update/service.test.js +107 -0
- package/dist/permissions/auto-accept-manager.js +165 -0
- package/dist/permissions/auto-accept-manager.test.js +208 -0
- package/dist/permissions/auto-accept-store.js +5 -2
- package/dist/permissions/opencode-yolo-metadata.js +81 -0
- package/dist/permissions/opencode-yolo-metadata.test.js +54 -0
- package/dist/server/__tests__/remote-proxy.test.js +64 -56
- package/dist/server/http-server.js +12 -15
- package/dist/server/remote-proxy.js +68 -98
- package/dist/server/routes/opencode-update.js +37 -0
- package/dist/server/routes/opencode-update.test.js +34 -0
- package/dist/server/routes/remote-servers.js +1 -1
- package/dist/server/routes/workspaces.test.js +38 -0
- package/dist/server/routes/worktrees.js +19 -0
- package/dist/server/routes/yolo.js +2 -1
- package/dist/server/tls.js +2 -2
- package/dist/settings/binaries.test.js +2 -7
- package/dist/shutdown.js +67 -59
- package/dist/shutdown.test.js +115 -78
- package/dist/ui/remote-ui.js +2 -2
- package/dist/usage/providers/api-key.js +3 -3
- package/dist/usage/providers/google.js +1 -1
- package/dist/usage/providers/oauth.js +1 -36
- package/dist/usage/providers/special.js +3 -3
- package/dist/usage/service.js +1 -1
- package/dist/usage/service.test.js +5 -3
- package/dist/usage/shared.js +4 -4
- package/dist/usage/shared.test.js +4 -4
- package/dist/workspaces/__tests__/spawn.test.js +2 -2
- package/dist/workspaces/__tests__/workspace-identity.test.js +94 -286
- package/dist/workspaces/binary-path.test.js +10 -0
- package/dist/workspaces/instance-client.js +4 -3
- package/dist/workspaces/launch-cleanup.test.js +21 -25
- package/dist/workspaces/manager.js +300 -351
- package/dist/workspaces/manager.test.js +209 -237
- package/dist/workspaces/process-identity.darwin.test.js +88 -0
- package/dist/workspaces/process-identity.js +172 -149
- package/dist/workspaces/process-identity.test.js +86 -140
- package/dist/workspaces/runtime.js +261 -373
- package/dist/workspaces/runtime.test.js +222 -547
- package/package.json +1 -1
- package/public/assets/{FilesTab-BjztpSdj.js → FilesTab-ChfE8-8N.js} +2 -2
- package/public/assets/{GitChangesTab-B9XraSoR.js → GitChangesTab-ntRtkxIF.js} +2 -2
- package/public/assets/{SplitFilePanel-Cu_5-Z-E.js → SplitFilePanel-Dav7VCqK.js} +1 -1
- package/public/assets/StatusTab-D3toOefp.js +1 -0
- package/public/assets/{align-justify-BKS9gyRk.js → align-justify-D91sAsLc.js} +1 -1
- package/public/assets/{bundle-full-CXGXXlzR.js → bundle-full-CWkY9-v4.js} +1 -1
- package/public/assets/{diff-viewer-CSNOYoEB.js → diff-viewer-Cn_41zpc.js} +1 -1
- package/public/assets/index-3dx-hhNQ.js +1 -0
- package/public/assets/index-BKwfZLzw.js +1 -0
- package/public/assets/index-C5IioEFT.js +1 -0
- package/public/assets/index-CZYc0VcU.js +2 -0
- package/public/assets/index-CdCoXYXP.js +1 -0
- package/public/assets/index-CwAVops2.js +1 -0
- package/public/assets/index-CzvCOwKc.js +1 -0
- package/public/assets/index-D-loOWPv.js +1 -0
- package/public/assets/{index-DVc4ls8c.js → index-DM1cpVNo.js} +1 -1
- package/public/assets/{index-sLe9hEfQ.js → index-DOprq6Gt.js} +1 -1
- package/public/assets/index-L7fX3W8k.css +1 -0
- package/public/assets/index-w2KdHEME.js +1 -0
- package/public/assets/{loading-CDmt9OJU.js → loading-BfoDcWcg.js} +1 -1
- package/public/assets/main-CaTCcck1.js +68 -0
- package/public/assets/{markdown-1ZLSxCFT.js → markdown-D33YI2g2.js} +3 -3
- package/public/assets/{monaco-viewer-D6JgH76W.js → monaco-viewer-DM35aobI.js} +4 -4
- package/public/assets/{tool-call-CyXb63LH.js → tool-call-tV85Jv2i.js} +3 -3
- package/public/assets/{unified-picker-DCCdSVwI.js → unified-picker-DSRT9GMh.js} +1 -1
- package/public/assets/{wrap-text-pNMdTIq3.js → wrap-text-Dmrn2DKo.js} +1 -1
- package/public/index.html +4 -4
- package/public/loading.html +4 -4
- package/public/sw.js +1 -1
- package/dist/workspaces/launch-cleanup.js +0 -132
- package/public/assets/StatusTab-D_IE79r-.js +0 -1
- package/public/assets/index-B3YjNCh4.js +0 -1
- package/public/assets/index-B3ZD2_SU.js +0 -1
- package/public/assets/index-Bh2ofaQo.js +0 -1
- package/public/assets/index-CM0c9xRZ.js +0 -1
- package/public/assets/index-CdDxouJl.js +0 -2
- package/public/assets/index-D06Y2cFq.js +0 -1
- package/public/assets/index-PmE51vPh.js +0 -1
- package/public/assets/index-Qj63IiSj.css +0 -1
- package/public/assets/index-VY_zwmsG.js +0 -1
- package/public/assets/index-qiiH7egV.js +0 -1
- package/public/assets/main-CTN6Wwz1.js +0 -68
|
@@ -1,32 +1,26 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
test -n "$boot" || exit 1
|
|
4
|
-
for stat in /proc/[0-9]*/stat; do
|
|
5
|
-
line=$(cat "$stat" 2>/dev/null) || continue
|
|
6
|
-
pid=$(printf '%s\n' "$line" | cut -d' ' -f1)
|
|
7
|
-
rest=$(printf '%s\n' "$line" | sed 's/^.*) //')
|
|
8
|
-
set -- $rest
|
|
9
|
-
printf '%s|%s|%s|%s|%s|%s\n' "$pid" "$2" "$3" "$20" "$boot" "$20"
|
|
10
|
-
done
|
|
11
|
-
`;
|
|
12
|
-
const LINUX_GUARDED_SIGNAL_SCRIPT = String.raw `
|
|
1
|
+
export const LAUNCH_CLEANUP_TOKEN_ENV = "CODENOMAD_LAUNCH_CLEANUP_TOKEN";
|
|
2
|
+
const LINUX_IDENTITY_FUNCTIONS = String.raw `
|
|
13
3
|
boot=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null) || exit 20
|
|
14
4
|
read_stat() {
|
|
15
5
|
line=$(cat "/proc/$1/stat" 2>/dev/null) || return 1
|
|
16
6
|
stat_pid=$(printf '%s\n' "$line" | cut -d' ' -f1); rest=$(printf '%s\n' "$line" | sed 's/^.*) //'); set -- $rest
|
|
17
7
|
stat_ppid=$2; stat_group=$3; stat_start=$20
|
|
18
8
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
9
|
+
emit_linux() { printf '%s|%s|%s|%s|%s|%s|%s\n' "$1" "$stat_pid" "$stat_ppid" "$stat_group" "$stat_start" "$boot" "$stat_start"; }
|
|
10
|
+
`;
|
|
11
|
+
const LINUX_SNAPSHOT_SCRIPT = String.raw `${LINUX_IDENTITY_FUNCTIONS}
|
|
12
|
+
for stat in /proc/[0-9]*/stat; do
|
|
13
|
+
pid=$(basename "$(dirname "$stat")"); read_stat "$pid" && emit_linux "" | cut -c2-
|
|
14
|
+
done
|
|
15
|
+
exit 0
|
|
16
|
+
`;
|
|
17
|
+
const LINUX_GUARDED_SIGNAL_SCRIPT = String.raw `${LINUX_IDENTITY_FUNCTIONS}
|
|
22
18
|
leader_pid=$1; leader_start=$2; leader_boot=$3; expected_group=$4; requested_signal=$5
|
|
23
|
-
shift 5
|
|
24
|
-
matched=0; cutoff=; signal_sent=0
|
|
19
|
+
shift 5; matched=0; cutoff=; signal_sent=0
|
|
25
20
|
if read_stat "$leader_pid" && test "$boot" = "$leader_boot" && test "$stat_start" = "$leader_start" && test "$stat_group" = "$expected_group"; then
|
|
26
21
|
matched=1
|
|
27
22
|
for stat in /proc/[0-9]*/stat; do
|
|
28
|
-
candidate=$(basename "$(dirname "$stat")")
|
|
29
|
-
if read_stat "$candidate" && test "$stat_group" = "$expected_group"; then emit_target; fi
|
|
23
|
+
candidate=$(basename "$(dirname "$stat")"); read_stat "$candidate" && test "$stat_group" = "$expected_group" && emit_linux CODENOMAD_TARGET
|
|
30
24
|
done
|
|
31
25
|
if kill "-$requested_signal" -- "-$expected_group" 2>/dev/null; then
|
|
32
26
|
signal_sent=1
|
|
@@ -38,83 +32,106 @@ else
|
|
|
38
32
|
while test "$#" -ge 3; do
|
|
39
33
|
expected_pid=$1; expected_start=$2; expected_boot=$3; shift 3
|
|
40
34
|
if read_stat "$expected_pid" && test "$boot" = "$expected_boot" && test "$stat_start" = "$expected_start"; then
|
|
41
|
-
|
|
35
|
+
emit_linux CODENOMAD_TARGET
|
|
42
36
|
if kill "-$requested_signal" "$expected_pid" 2>/dev/null; then signal_sent=1; fi
|
|
43
37
|
fi
|
|
44
38
|
done
|
|
45
39
|
fi
|
|
46
40
|
printf 'CODENOMAD_RESULT|%s|%s|%s\n' "$matched" "$cutoff" "$signal_sent"
|
|
47
41
|
`;
|
|
48
|
-
const
|
|
42
|
+
const POSIX_IDENTITY_FUNCTIONS = String.raw `
|
|
43
|
+
LC_ALL=C; export LC_ALL; set -f
|
|
49
44
|
encode() { printf '%s' "$1" | base64 | tr -d '\r\n'; }
|
|
50
45
|
read_identity() {
|
|
51
|
-
current_meta=$(ps -p "$1" -o ppid= -o pgid= -o lstart= 2>/dev/null) || return 1
|
|
52
|
-
|
|
53
|
-
current_verify=$(ps -p "$1" -o ppid= -o pgid= -o lstart= 2>/dev/null) || return 1
|
|
46
|
+
current_meta=$(ps -p "$1" -o ppid= -o pgid= -o lstart= -o comm= 2>/dev/null) || return 1
|
|
47
|
+
current_verify=$(ps -p "$1" -o ppid= -o pgid= -o lstart= -o comm= 2>/dev/null) || return 1
|
|
54
48
|
test "$current_meta" = "$current_verify" || return 1
|
|
55
|
-
set -- $current_meta
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
current_start="$1 $2 $3 $4 $5"
|
|
49
|
+
set -- $current_meta; test "$#" -ge 7 || return 1
|
|
50
|
+
current_ppid=$1; current_group=$2; shift 2; current_start="$1 $2 $3 $4 $5"
|
|
51
|
+
shift 5; current_command="$*"; test -n "$current_command" || return 1
|
|
59
52
|
current_identity=$(printf '%s\t%s' "$current_start" "$current_command")
|
|
60
53
|
}
|
|
61
54
|
emit_target() {
|
|
62
55
|
printf 'CODENOMAD_TARGET_B64|%s|%s|%s|' "$current_pid" "$current_ppid" "$current_group"
|
|
63
56
|
encode "$current_start"; printf '|'; encode "$current_command"; printf '\n'
|
|
64
57
|
}
|
|
65
|
-
|
|
58
|
+
group_pids() { ps -axo pid=,pgid= 2>/dev/null | awk -v group="$1" '$2 == group { print $1 }'; }
|
|
59
|
+
has_cleanup_token() {
|
|
60
|
+
test -n "$cleanup_token" || return 1
|
|
61
|
+
ps eww -p "$1" -o command= 2>/dev/null | tr ' ' '\n' | grep -Fqx -- "${LAUNCH_CLEANUP_TOKEN_ENV}=$cleanup_token"
|
|
62
|
+
}
|
|
63
|
+
`;
|
|
64
|
+
const LINUX_TOKEN_SCRIPT = String.raw `${LINUX_IDENTITY_FUNCTIONS}
|
|
65
|
+
key=$1; expected=$2; requested_signal=$3
|
|
66
|
+
matches_token() { test -r "/proc/$1/environ" && tr '\0' '\n' < "/proc/$1/environ" 2>/dev/null | grep -Fqx -- "$key=$expected"; }
|
|
67
|
+
signal_sent=0; passes=1; test -n "$requested_signal" && passes=3
|
|
68
|
+
pass=0
|
|
69
|
+
while test "$pass" -lt "$passes"; do
|
|
70
|
+
pass=$((pass + 1))
|
|
71
|
+
for environ in /proc/[0-9]*/environ; do
|
|
72
|
+
pid=$(basename "$(dirname "$environ")")
|
|
73
|
+
if matches_token "$pid" && read_stat "$pid"; then
|
|
74
|
+
test -n "$requested_signal" && prefix=CODENOMAD_TARGET || prefix=CODENOMAD_PROCESS
|
|
75
|
+
emit_linux "$prefix"
|
|
76
|
+
if test -n "$requested_signal" && matches_token "$pid" && read_stat "$pid" && kill "-$requested_signal" "$pid" 2>/dev/null; then signal_sent=1; fi
|
|
77
|
+
fi
|
|
78
|
+
done
|
|
79
|
+
done
|
|
80
|
+
if test -n "$requested_signal"; then printf 'CODENOMAD_RESULT|%s\n' "$signal_sent"; fi
|
|
81
|
+
exit 0
|
|
82
|
+
`;
|
|
83
|
+
const POSIX_GUARDED_SIGNAL_SCRIPT = String.raw `${POSIX_IDENTITY_FUNCTIONS}
|
|
84
|
+
leader_pid=$1; leader_start=$2; expected_group=$3; requested_signal=$4; allow_leaderless=$5; cleanup_token=$6; shift 6
|
|
66
85
|
matched=0; signal_sent=0
|
|
67
86
|
if read_identity "$leader_pid" && test "$current_group" = "$expected_group" && test "$current_identity" = "$leader_start"; then
|
|
68
87
|
matched=1
|
|
69
|
-
for current_pid in $(
|
|
70
|
-
|
|
88
|
+
for current_pid in $(group_pids "$expected_group"); do
|
|
89
|
+
read_identity "$current_pid" && test "$current_group" = "$expected_group" && emit_target
|
|
71
90
|
done
|
|
72
91
|
if kill "-$requested_signal" -- "-$expected_group" 2>/dev/null; then signal_sent=1; fi
|
|
92
|
+
elif test "$allow_leaderless" = 1 && ! read_identity "$expected_group"; then
|
|
93
|
+
anchor=0
|
|
94
|
+
while test "$#" -ge 2; do
|
|
95
|
+
expected_pid=$1; expected_start=$2; shift 2; current_pid=$expected_pid
|
|
96
|
+
if read_identity "$expected_pid" && test "$current_group" = "$expected_group" && test "$current_identity" = "$expected_start"; then anchor=1; fi
|
|
97
|
+
done
|
|
98
|
+
if test "$anchor" = 0; then
|
|
99
|
+
for current_pid in $(group_pids "$expected_group"); do
|
|
100
|
+
if has_cleanup_token "$current_pid" && read_identity "$current_pid" && test "$current_group" = "$expected_group"; then anchor=1; break; fi
|
|
101
|
+
done
|
|
102
|
+
fi
|
|
103
|
+
if test "$anchor" = 1; then
|
|
104
|
+
matched=1
|
|
105
|
+
for current_pid in $(group_pids "$expected_group"); do
|
|
106
|
+
read_identity "$current_pid" && test "$current_group" = "$expected_group" && emit_target
|
|
107
|
+
done
|
|
108
|
+
if kill "-$requested_signal" -- "-$expected_group" 2>/dev/null; then signal_sent=1; fi
|
|
109
|
+
fi
|
|
73
110
|
else
|
|
74
111
|
while test "$#" -ge 2; do
|
|
75
|
-
expected_pid=$1; expected_start=$2; shift 2
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
emit_target
|
|
79
|
-
if kill "-$requested_signal" "$expected_pid" 2>/dev/null; then signal_sent=1; fi
|
|
112
|
+
expected_pid=$1; expected_start=$2; shift 2; current_pid=$expected_pid
|
|
113
|
+
if read_identity "$expected_pid" && test "$current_group" = "$expected_group" && test "$current_identity" = "$expected_start"; then
|
|
114
|
+
emit_target; if kill "-$requested_signal" "$expected_pid" 2>/dev/null; then signal_sent=1; fi
|
|
80
115
|
fi
|
|
81
116
|
done
|
|
82
117
|
fi
|
|
83
118
|
printf 'CODENOMAD_RESULT|%s||%s\n' "$matched" "$signal_sent"
|
|
84
119
|
`;
|
|
85
|
-
const POSIX_OWNED_GROUP_SIGNAL_SCRIPT = String.raw
|
|
86
|
-
encode() { printf '%s' "$1" | base64 | tr -d '\r\n'; }
|
|
87
|
-
read_identity() {
|
|
88
|
-
current_meta=$(ps -p "$1" -o ppid= -o pgid= -o lstart= 2>/dev/null) || return 1
|
|
89
|
-
current_command=$(ps -p "$1" -o command= 2>/dev/null) || return 1
|
|
90
|
-
current_verify=$(ps -p "$1" -o ppid= -o pgid= -o lstart= 2>/dev/null) || return 1
|
|
91
|
-
test "$current_meta" = "$current_verify" || return 1
|
|
92
|
-
set -- $current_meta
|
|
93
|
-
test "$#" -ge 7 || return 1
|
|
94
|
-
current_ppid=$1; current_group=$2; shift 2
|
|
95
|
-
current_start="$1 $2 $3 $4 $5"
|
|
96
|
-
current_identity=$(printf '%s\t%s' "$current_start" "$current_command")
|
|
97
|
-
}
|
|
98
|
-
emit_target() {
|
|
99
|
-
printf 'CODENOMAD_TARGET_B64|%s|%s|%s|' "$current_pid" "$current_ppid" "$current_group"
|
|
100
|
-
encode "$current_start"; printf '|'; encode "$current_command"; printf '\n'
|
|
101
|
-
}
|
|
120
|
+
const POSIX_OWNED_GROUP_SIGNAL_SCRIPT = String.raw `${POSIX_IDENTITY_FUNCTIONS}
|
|
102
121
|
root_pid=$1; requested_signal=$2; matched=0; signal_sent=0
|
|
103
122
|
if read_identity "$root_pid" && test "$current_group" = "$root_pid"; then
|
|
104
123
|
matched=1
|
|
105
|
-
for current_pid in $(
|
|
106
|
-
|
|
124
|
+
for current_pid in $(group_pids "$root_pid"); do
|
|
125
|
+
read_identity "$current_pid" && test "$current_group" = "$root_pid" && emit_target
|
|
107
126
|
done
|
|
108
127
|
if kill "-$requested_signal" -- "-$root_pid" 2>/dev/null; then signal_sent=1; fi
|
|
109
|
-
for current_pid in $(
|
|
110
|
-
|
|
128
|
+
for current_pid in $(group_pids "$root_pid"); do
|
|
129
|
+
read_identity "$current_pid" && test "$current_group" = "$root_pid" && emit_target
|
|
111
130
|
done
|
|
112
131
|
fi
|
|
113
132
|
printf 'CODENOMAD_RESULT|%s||%s\n' "$matched" "$signal_sent"
|
|
114
133
|
`;
|
|
115
|
-
|
|
116
|
-
return result.error?.message || String(result.stderr ?? result.stdout ?? "").trim() || `exit code ${result.status}`;
|
|
117
|
-
}
|
|
134
|
+
const commandError = (result) => result.error?.message || String(result.stderr ?? result.stdout ?? "").trim() || `exit code ${result.status}`;
|
|
118
135
|
function parseDelimitedSnapshot(output, requireBootId = false) {
|
|
119
136
|
const processes = new Map();
|
|
120
137
|
for (const line of output.split(/\r?\n/)) {
|
|
@@ -130,14 +147,8 @@ function parseDelimitedSnapshot(output, requireBootId = false) {
|
|
|
130
147
|
if (!/^\d+$/.test(pidText ?? "") || !/^\d+$/.test(parentPidText ?? "") || !/^\d+$/.test(groupIdText ?? "") ||
|
|
131
148
|
!Number.isInteger(pid) || pid <= 0 || !Number.isInteger(parentPid) || !startTime || (requireBootId && !bootId))
|
|
132
149
|
return null;
|
|
133
|
-
processes.set(pid, {
|
|
134
|
-
|
|
135
|
-
parentPid,
|
|
136
|
-
groupId: Number.isInteger(groupId) && groupId > 0 ? groupId : pid,
|
|
137
|
-
startTime,
|
|
138
|
-
...(bootId ? { bootId } : {}),
|
|
139
|
-
...(startOrder ? { startOrder } : {}),
|
|
140
|
-
});
|
|
150
|
+
processes.set(pid, { pid, parentPid, groupId: Number.isInteger(groupId) && groupId > 0 ? groupId : pid, startTime,
|
|
151
|
+
...(bootId ? { bootId } : {}), ...(startOrder ? { startOrder } : {}) });
|
|
141
152
|
}
|
|
142
153
|
return processes;
|
|
143
154
|
}
|
|
@@ -179,50 +190,69 @@ function parseBase64Snapshot(output, prefix = "CODENOMAD_B64|") {
|
|
|
179
190
|
}
|
|
180
191
|
return processes;
|
|
181
192
|
}
|
|
182
|
-
function parsePortablePosixSnapshot(output) {
|
|
193
|
+
function parsePortablePosixSnapshot(output, filter) {
|
|
183
194
|
const processes = new Map();
|
|
195
|
+
const requestedPids = filter?.pids ? new Set(filter.pids) : undefined;
|
|
184
196
|
for (const line of output.split(/\r?\n/)) {
|
|
185
197
|
if (!line.trim())
|
|
186
198
|
continue;
|
|
187
199
|
const match = line.match(/^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\S+\s+\S+\s+\d+\s+\d{2}:\d{2}:\d{2}\s+\d{4})\s+(.+)$/);
|
|
188
|
-
if (!match)
|
|
189
|
-
|
|
200
|
+
if (!match) {
|
|
201
|
+
const numeric = line.match(/^\s*(\d+)\s+(\d+)\s+(\d+)\s+/);
|
|
202
|
+
if (!filter || !numeric || requestedPids?.has(Number(numeric[1])) || Number(numeric[3]) === filter.groupId)
|
|
203
|
+
return null;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
190
206
|
const [, pidText = "", parentPidText = "", groupIdText = "", start = "", command = ""] = match;
|
|
191
207
|
const pid = Number.parseInt(pidText, 10);
|
|
192
208
|
const parentPid = Number.parseInt(parentPidText, 10);
|
|
193
209
|
const groupId = Number.parseInt(groupIdText, 10);
|
|
194
210
|
if (pid <= 0 || parentPid < 0 || groupId <= 0)
|
|
195
211
|
return null;
|
|
212
|
+
if (filter && !requestedPids?.has(pid) && groupId !== filter.groupId)
|
|
213
|
+
continue;
|
|
196
214
|
processes.set(pid, { pid, parentPid, groupId, startTime: `${start}\t${command}` });
|
|
197
215
|
}
|
|
198
216
|
return processes;
|
|
199
217
|
}
|
|
200
|
-
function
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
218
|
+
function querySnapshot(run, parse, options = {}) {
|
|
219
|
+
const sanitize = options.redact ?? ((error) => error);
|
|
220
|
+
try {
|
|
221
|
+
const result = run();
|
|
222
|
+
if (result.status !== 0)
|
|
223
|
+
return { ok: false, error: sanitize(commandError(result)) };
|
|
224
|
+
const processes = parse(String(result.stdout ?? ""));
|
|
225
|
+
if (processes && (options.allowEmpty || processes.size > 0))
|
|
226
|
+
return { ok: true, processes };
|
|
227
|
+
return { ok: false, error: options.malformedError ?? "process identity query returned no parseable processes" };
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
return { ok: false, error: sanitize(error instanceof Error ? error.message : String(error)) };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function parsePrefixedSnapshot(output, prefix) {
|
|
234
|
+
const records = [];
|
|
235
|
+
for (const line of output.split(/\r?\n/)) {
|
|
236
|
+
if (!line)
|
|
237
|
+
continue;
|
|
238
|
+
if (!line.startsWith(prefix))
|
|
239
|
+
return null;
|
|
240
|
+
records.push(line.slice(prefix.length));
|
|
241
|
+
}
|
|
242
|
+
return parseDelimitedSnapshot(records.join("\n"), true);
|
|
204
243
|
}
|
|
205
244
|
function parseGuardedResult(result) {
|
|
206
245
|
const signaled = new Map();
|
|
207
|
-
const failure = (error) => ({
|
|
208
|
-
|
|
209
|
-
error,
|
|
210
|
-
...(signaled.size > 0 ? { observed: Array.from(signaled.values()) } : {}),
|
|
211
|
-
});
|
|
246
|
+
const failure = (error) => ({ ok: false, error,
|
|
247
|
+
...(signaled.size > 0 ? { observed: Array.from(signaled.values()) } : {}) });
|
|
212
248
|
let matched;
|
|
213
249
|
let signalSent = false;
|
|
214
250
|
let cutoff;
|
|
215
251
|
for (const line of String(result.stdout ?? "").split(/\r?\n/)) {
|
|
216
|
-
if (line.startsWith("CODENOMAD_TARGET|")) {
|
|
217
|
-
const parsed =
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
for (const identity of parsed.values())
|
|
221
|
-
signaled.set(identity.pid, identity);
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
if (line.startsWith("CODENOMAD_TARGET_B64|")) {
|
|
225
|
-
const parsed = parseBase64Snapshot(line, "CODENOMAD_TARGET_B64|");
|
|
252
|
+
if (line.startsWith("CODENOMAD_TARGET|") || line.startsWith("CODENOMAD_TARGET_B64|")) {
|
|
253
|
+
const parsed = line.startsWith("CODENOMAD_TARGET_B64|")
|
|
254
|
+
? parseBase64Snapshot(line, "CODENOMAD_TARGET_B64|")
|
|
255
|
+
: parseDelimitedSnapshot(line.slice("CODENOMAD_TARGET|".length));
|
|
226
256
|
if (!parsed)
|
|
227
257
|
return failure("guarded signal command returned a malformed target record");
|
|
228
258
|
for (const identity of parsed.values())
|
|
@@ -249,22 +279,27 @@ function parseGuardedResult(result) {
|
|
|
249
279
|
? failure("guarded signal command returned no structured result")
|
|
250
280
|
: { ok: true, matched, signalSent, signaled: Array.from(signaled.values()), ...(cutoff ? { cutoff } : {}) };
|
|
251
281
|
}
|
|
252
|
-
function runGuardedCommand(
|
|
282
|
+
function runGuardedCommand(run) {
|
|
253
283
|
try {
|
|
254
|
-
return parseGuardedResult(
|
|
284
|
+
return parseGuardedResult(run());
|
|
255
285
|
}
|
|
256
286
|
catch (error) {
|
|
257
287
|
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
258
288
|
}
|
|
259
289
|
}
|
|
260
|
-
|
|
261
|
-
|
|
290
|
+
const signalName = (signal) => signal === "SIGKILL" ? "KILL" : "TERM";
|
|
291
|
+
function runLinuxScript(spawnCommand, script, args, timeoutMs, label, distro) {
|
|
292
|
+
return distro
|
|
293
|
+
? spawnCommand("wsl.exe", ["--distribution", distro, "--exec", "sh", "-c", script, label, ...args], { encoding: "utf8", timeout: timeoutMs })
|
|
294
|
+
: spawnCommand("sh", ["-c", script, label, ...args], { encoding: "utf8", timeout: timeoutMs });
|
|
262
295
|
}
|
|
296
|
+
const redactToken = (value, token) => value.split(token).join("[REDACTED]");
|
|
263
297
|
function shellGuardArgs(request, linux) {
|
|
264
298
|
const leader = request.leader;
|
|
265
299
|
const args = linux
|
|
266
300
|
? [String(leader?.pid ?? 0), leader?.startTime ?? "", leader?.bootId ?? "", String(request.groupId ?? 0), signalName(request.signal)]
|
|
267
|
-
: [String(leader?.pid ?? 0), leader?.startTime ?? "", String(request.groupId ?? 0), signalName(request.signal)
|
|
301
|
+
: [String(leader?.pid ?? 0), leader?.startTime ?? "", String(request.groupId ?? 0), signalName(request.signal),
|
|
302
|
+
request.allowLeaderlessGroup ? "1" : "0", request.cleanupToken ?? ""];
|
|
268
303
|
for (const member of request.members) {
|
|
269
304
|
args.push(String(member.pid), member.startTime);
|
|
270
305
|
if (linux)
|
|
@@ -272,15 +307,11 @@ function shellGuardArgs(request, linux) {
|
|
|
272
307
|
}
|
|
273
308
|
return args;
|
|
274
309
|
}
|
|
275
|
-
|
|
276
|
-
return `'${value.replace(/'/g, "''")}'`;
|
|
277
|
-
}
|
|
310
|
+
const quotePowerShell = (value) => `'${value.replace(/'/g, "''")}'`;
|
|
278
311
|
function buildWindowsGuardedScript(request) {
|
|
279
312
|
const leaderPid = request.leader?.pid ?? 0;
|
|
280
313
|
const leaderStart = quotePowerShell(request.leader?.startTime ?? "");
|
|
281
|
-
const expected = request.members
|
|
282
|
-
.map((identity) => `@{ Pid = ${identity.pid}; Start = ${quotePowerShell(identity.startTime)} }`)
|
|
283
|
-
.join(", ");
|
|
314
|
+
const expected = request.members.map((identity) => `@{ Pid = ${identity.pid}; Start = ${quotePowerShell(identity.startTime)} }`).join(", ");
|
|
284
315
|
return [
|
|
285
316
|
"$ErrorActionPreference = 'Stop'",
|
|
286
317
|
`$leaderPid = ${leaderPid}`,
|
|
@@ -340,69 +371,61 @@ export function descendantsOf(processes, rootPid) {
|
|
|
340
371
|
}
|
|
341
372
|
return descendants;
|
|
342
373
|
}
|
|
343
|
-
export function probePosixProcesses(spawnCommand, timeoutMs, platform = process.platform) {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
if (result.status !== 0)
|
|
351
|
-
return { ok: false, error: commandError(result) };
|
|
352
|
-
return snapshotOrFailure(parseDelimitedSnapshot(String(result.stdout ?? ""), true));
|
|
353
|
-
}
|
|
354
|
-
// POSIX has no portable pidfd/start ticks; collect one coherent table instead of probing every PID.
|
|
355
|
-
const result = spawnCommand("ps", ["-axo", "pid=,ppid=,pgid=,lstart=,command="], {
|
|
356
|
-
encoding: "utf8",
|
|
357
|
-
timeout: timeoutMs,
|
|
358
|
-
});
|
|
359
|
-
if (result.status !== 0)
|
|
360
|
-
return { ok: false, error: commandError(result) };
|
|
361
|
-
return snapshotOrFailure(parsePortablePosixSnapshot(String(result.stdout ?? "")));
|
|
362
|
-
}
|
|
363
|
-
catch (error) {
|
|
364
|
-
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
365
|
-
}
|
|
374
|
+
export function probePosixProcesses(spawnCommand, timeoutMs, platform = process.platform, filter) {
|
|
375
|
+
if (platform === "linux")
|
|
376
|
+
return querySnapshot(() => runLinuxScript(spawnCommand, LINUX_SNAPSHOT_SCRIPT, [], timeoutMs, "codenomad-posix-identity"), (output) => parseDelimitedSnapshot(output, true));
|
|
377
|
+
// POSIX has no portable pidfd/start ticks; collect one coherent table instead of probing every PID.
|
|
378
|
+
return querySnapshot(() => spawnCommand("ps", ["-axo", "pid=,ppid=,pgid=,lstart=,comm="], {
|
|
379
|
+
encoding: "utf8", timeout: timeoutMs, env: { ...process.env, LC_ALL: "C", LANG: "C" },
|
|
380
|
+
}), (output) => parsePortablePosixSnapshot(output, filter), { allowEmpty: Boolean(filter) });
|
|
366
381
|
}
|
|
367
382
|
export function probeWindowsProcesses(spawnCommand, timeoutMs) {
|
|
368
383
|
const script = [
|
|
369
384
|
"$all = @(Get-CimInstance Win32_Process -ErrorAction Stop)",
|
|
370
385
|
"$all | Where-Object { [int]$_.ProcessId -gt 0 } | ForEach-Object { $start = ([datetime]$_.CreationDate).ToUniversalTime().Ticks.ToString(); '{0}|{1}|0|{2}||{2}' -f [int]$_.ProcessId, [int]$_.ParentProcessId, $start }",
|
|
371
386
|
].join("; ");
|
|
372
|
-
|
|
373
|
-
const result = spawnCommand("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], {
|
|
374
|
-
encoding: "utf8",
|
|
375
|
-
timeout: timeoutMs,
|
|
376
|
-
});
|
|
377
|
-
if (result.status !== 0)
|
|
378
|
-
return { ok: false, error: commandError(result) };
|
|
379
|
-
return snapshotOrFailure(parseDelimitedSnapshot(String(result.stdout ?? "")));
|
|
380
|
-
}
|
|
381
|
-
catch (error) {
|
|
382
|
-
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
383
|
-
}
|
|
387
|
+
return querySnapshot(() => spawnCommand("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], { encoding: "utf8", timeout: timeoutMs }), parseDelimitedSnapshot);
|
|
384
388
|
}
|
|
385
389
|
export function probeWslProcesses(spawnCommand, distro, timeoutMs) {
|
|
386
|
-
|
|
387
|
-
const result = spawnCommand("wsl.exe", ["--distribution", distro, "--exec", "sh", "-c", LINUX_SNAPSHOT_SCRIPT, "codenomad-wsl-identity"], { encoding: "utf8", timeout: timeoutMs });
|
|
388
|
-
if (result.status !== 0)
|
|
389
|
-
return { ok: false, error: commandError(result) };
|
|
390
|
-
return snapshotOrFailure(parseDelimitedSnapshot(String(result.stdout ?? ""), true));
|
|
391
|
-
}
|
|
392
|
-
catch (error) {
|
|
393
|
-
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
394
|
-
}
|
|
390
|
+
return querySnapshot(() => runLinuxScript(spawnCommand, LINUX_SNAPSHOT_SCRIPT, [], timeoutMs, "codenomad-wsl-identity", distro), (output) => parseDelimitedSnapshot(output, true));
|
|
395
391
|
}
|
|
396
392
|
export function signalPosixProcesses(spawnCommand, request, timeoutMs, platform) {
|
|
397
393
|
const linux = platform === "linux";
|
|
398
|
-
return runGuardedCommand(spawnCommand
|
|
394
|
+
return runGuardedCommand(() => spawnCommand("sh", ["-c", linux ? LINUX_GUARDED_SIGNAL_SCRIPT : POSIX_GUARDED_SIGNAL_SCRIPT, "codenomad-guarded-signal", ...shellGuardArgs(request, linux)], { encoding: "utf8", timeout: timeoutMs }));
|
|
399
395
|
}
|
|
400
396
|
export function signalOwnedPosixProcessGroup(spawnCommand, rootPid, signal, timeoutMs) {
|
|
401
|
-
return runGuardedCommand(spawnCommand
|
|
397
|
+
return runGuardedCommand(() => spawnCommand("sh", ["-c", POSIX_OWNED_GROUP_SIGNAL_SCRIPT, "codenomad-owned-group-cleanup", String(rootPid), signalName(signal)], { encoding: "utf8", timeout: timeoutMs }));
|
|
402
398
|
}
|
|
403
399
|
export function signalWslProcesses(spawnCommand, distro, request, timeoutMs) {
|
|
404
|
-
return runGuardedCommand(
|
|
400
|
+
return runGuardedCommand(() => runLinuxScript(spawnCommand, LINUX_GUARDED_SIGNAL_SCRIPT, shellGuardArgs(request, true), timeoutMs, "codenomad-wsl-guarded-signal", distro));
|
|
405
401
|
}
|
|
406
402
|
export function signalWindowsProcesses(spawnCommand, request, timeoutMs) {
|
|
407
|
-
return runGuardedCommand(spawnCommand
|
|
403
|
+
return runGuardedCommand(() => spawnCommand("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", buildWindowsGuardedScript(request)], { encoding: "utf8", timeout: timeoutMs }));
|
|
404
|
+
}
|
|
405
|
+
export function probeLaunchCleanupToken(spawnCommand, token, timeoutMs, distro) {
|
|
406
|
+
return querySnapshot(() => runLinuxScript(spawnCommand, LINUX_TOKEN_SCRIPT, [LAUNCH_CLEANUP_TOKEN_ENV, token, ""], timeoutMs, "codenomad-token-cleanup", distro), (output) => parsePrefixedSnapshot(output, "CODENOMAD_PROCESS|"), {
|
|
407
|
+
allowEmpty: true,
|
|
408
|
+
malformedError: "launch cleanup probe returned malformed or unexpected output",
|
|
409
|
+
redact: (error) => redactToken(error, token),
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
export function signalLaunchCleanupToken(spawnCommand, token, signal, timeoutMs, distro) {
|
|
413
|
+
const failed = (error) => ({ ok: false, signalSent: false, targets: [], error });
|
|
414
|
+
try {
|
|
415
|
+
const result = runLinuxScript(spawnCommand, LINUX_TOKEN_SCRIPT, [LAUNCH_CLEANUP_TOKEN_ENV, token, signalName(signal)], timeoutMs, "codenomad-token-cleanup", distro);
|
|
416
|
+
if (result.status !== 0)
|
|
417
|
+
return failed(redactToken(commandError(result), token));
|
|
418
|
+
const lines = String(result.stdout ?? "").split(/\r?\n/).filter(Boolean);
|
|
419
|
+
const resultLines = lines.filter((line) => line.startsWith("CODENOMAD_RESULT|"));
|
|
420
|
+
if (resultLines.length !== 1 || !/^CODENOMAD_RESULT\|[01]$/.test(resultLines[0] ?? "")) {
|
|
421
|
+
return failed("launch cleanup signal returned no valid structured result");
|
|
422
|
+
}
|
|
423
|
+
const targets = parsePrefixedSnapshot(lines.filter((line) => !line.startsWith("CODENOMAD_RESULT|")).join("\n"), "CODENOMAD_TARGET|");
|
|
424
|
+
return targets
|
|
425
|
+
? { ok: true, signalSent: resultLines[0].endsWith("1"), targets: Array.from(targets.values()) }
|
|
426
|
+
: failed("launch cleanup signal returned malformed or unexpected output");
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
return failed(redactToken(error instanceof Error ? error.message : String(error), token));
|
|
430
|
+
}
|
|
408
431
|
}
|