@apex-deck/cli-darwin-arm64 0.0.0-stage → 0.9.9
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/INSTALL.txt +10 -0
- package/README.md +12 -2
- package/apex +0 -0
- package/apex-daemon +4 -0
- package/apex-provenance +0 -0
- package/install-cli-provenance.sh +418 -0
- package/manifest.json +20 -0
- package/package.json +25 -4
package/INSTALL.txt
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Apex CLI 0.9.9, macOS Apple Silicon
|
|
2
|
+
|
|
3
|
+
This archive contains Developer ID signed binaries. It is not a notarized installer.
|
|
4
|
+
Install explicitly from this extracted directory:
|
|
5
|
+
sudo /bin/bash ./install-cli-provenance.sh "$PWD"
|
|
6
|
+
Then launch in your normal user terminal:
|
|
7
|
+
apex
|
|
8
|
+
|
|
9
|
+
The installer preserves existing Deck authority keys, journal and exact pins, adding separate standalone pins. It restarts the authority service. Upgrades: an identical build is a no-op, a newer version or a rebuilt same-version payload replaces Standalone and swaps standalone pins, older is refused. Quit apex and run apex backend stop first; the installer refuses while the CLI or its backend is running and restores the previous install if anything fails. Flags: --no-path-link (skip /usr/local/bin/apex link), --uninstall (remove Standalone and standalone pins, keep data).
|
|
10
|
+
Provider API-key setup is unfinished; use official provider subscription sign-in.
|
package/README.md
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @apex-deck/cli-darwin-arm64
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Signed binaries for Apex Deck CLI, macOS Apple Silicon.
|
|
4
|
+
|
|
5
|
+
Version 0.9.9. This package is an optionalDependency of @apex-deck/cli and is not intended to be installed directly.
|
|
6
|
+
|
|
7
|
+
Contains:
|
|
8
|
+
- apex, apex-daemon, apex-provenance (Developer ID signed, hardened runtime)
|
|
9
|
+
- manifest.json (sha256 hashes)
|
|
10
|
+
- install-cli-provenance.sh (root enrollment, supports --no-path-link and --uninstall)
|
|
11
|
+
- INSTALL.txt
|
|
12
|
+
|
|
13
|
+
Security: binaries are verified by the installer via codesign and exact CDHash pins.
|
package/apex
ADDED
|
Binary file
|
package/apex-daemon
ADDED
package/apex-provenance
ADDED
|
Binary file
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
|
|
4
|
+
unset NODE_OPTIONS ELECTRON_RUN_AS_NODE PYTHONPATH PYTHONHOME DYLD_INSERT_LIBRARIES DYLD_LIBRARY_PATH
|
|
5
|
+
|
|
6
|
+
root='/Library/Application Support/Apex Deck Provenance'
|
|
7
|
+
plist='/Library/LaunchDaemons/dev.apexdeck.provenance.plist'
|
|
8
|
+
product="$root/Standalone"
|
|
9
|
+
team='75N5HW62JH'
|
|
10
|
+
|
|
11
|
+
no_path_link=0
|
|
12
|
+
uninstall=0
|
|
13
|
+
src=""
|
|
14
|
+
|
|
15
|
+
for arg in "$@"; do
|
|
16
|
+
case "$arg" in
|
|
17
|
+
--no-path-link) no_path_link=1 ;;
|
|
18
|
+
--uninstall) uninstall=1 ;;
|
|
19
|
+
--*) echo "Unknown flag $arg" >&2; exit 1 ;;
|
|
20
|
+
*) if [[ -z "$src" ]]; then src="$arg"; else echo "Too many positional arguments" >&2; exit 1; fi ;;
|
|
21
|
+
esac
|
|
22
|
+
done
|
|
23
|
+
|
|
24
|
+
[[ $EUID == 0 ]] || { echo 'Run explicitly as administrator.' >&2; exit 1; }
|
|
25
|
+
|
|
26
|
+
# Replacing or removing Standalone swaps the CDHash pins, so any process still
|
|
27
|
+
# running the installed binaries would lose authority mid-turn. Refuse instead
|
|
28
|
+
# of stopping anything; fail closed if the process list cannot be read.
|
|
29
|
+
refuse_if_standalone_running() {
|
|
30
|
+
local procs
|
|
31
|
+
procs=$(/bin/ps -axo comm=) || { echo 'Cannot inspect running processes; refusing.' >&2; exit 1; }
|
|
32
|
+
if printf '%s\n' "$procs" | /usr/bin/grep -Fqx -e "$product/apex" -e "$product/apex-daemon"; then
|
|
33
|
+
echo 'Apex CLI is still running. Quit every apex window, run: apex backend stop' >&2
|
|
34
|
+
echo 'then run this command again. Nothing was changed.' >&2
|
|
35
|
+
exit 1
|
|
36
|
+
fi
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# ---------- UNINSTALL ----------
|
|
40
|
+
if [[ $uninstall == 1 ]]; then
|
|
41
|
+
if [[ -n "$src" && ! -d "$src" ]]; then
|
|
42
|
+
echo 'Expected platform directory to be a directory when provided.' >&2; exit 1
|
|
43
|
+
fi
|
|
44
|
+
if [[ ! -e "$root" ]]; then
|
|
45
|
+
echo 'No Apex authority to uninstall.' >&2; exit 0
|
|
46
|
+
fi
|
|
47
|
+
/usr/bin/python3 -I - "$root" "$plist" <<'PY'
|
|
48
|
+
import os,sys,stat
|
|
49
|
+
root,plist=sys.argv[1:]
|
|
50
|
+
for path in [root,root+'/config.json',root+'/apex-provenance',plist]:
|
|
51
|
+
if not os.path.lexists(path): continue
|
|
52
|
+
s=os.lstat(path)
|
|
53
|
+
if stat.S_ISLNK(s.st_mode) or s.st_uid!=0 or s.st_mode&0o022:
|
|
54
|
+
raise SystemExit('Unsafe existing authority path; refusing uninstall: '+path)
|
|
55
|
+
PY
|
|
56
|
+
/bin/mkdir "$root/.cli-enrollment.lock" 2>/dev/null || { echo 'Another enrollment is in progress; inspect authority before retrying.' >&2; exit 1; }
|
|
57
|
+
stage=$(/usr/bin/mktemp -d "$root/.cli-install.XXXXXX")
|
|
58
|
+
/bin/chmod 700 "$stage"
|
|
59
|
+
committed=0
|
|
60
|
+
rollback() {
|
|
61
|
+
st=$?
|
|
62
|
+
if [[ $st != 0 && $committed == 1 ]]; then
|
|
63
|
+
/bin/launchctl bootout system "$plist" >/dev/null 2>&1 || true
|
|
64
|
+
[[ -f "$stage/config.before" ]] && /bin/cp -p "$stage/config.before" "$root/config.json" 2>/dev/null || true
|
|
65
|
+
[[ -f "$stage/broker.before" ]] && /bin/cp -p "$stage/broker.before" "$root/apex-provenance" 2>/dev/null || true
|
|
66
|
+
[[ -f "$stage/plist.before" ]] && /bin/cp -p "$stage/plist.before" "$plist" 2>/dev/null || true
|
|
67
|
+
if [[ -e "$stage/product.before" ]]; then
|
|
68
|
+
/bin/rm -rf "$product" 2>/dev/null || true
|
|
69
|
+
/bin/mv "$stage/product.before" "$product" 2>/dev/null || true
|
|
70
|
+
fi
|
|
71
|
+
/bin/launchctl bootstrap system "$plist" >/dev/null 2>&1 || true
|
|
72
|
+
echo 'Uninstall failed; previous install restored.' >&2
|
|
73
|
+
fi
|
|
74
|
+
if [[ $st == 0 || $committed == 0 ]]; then /bin/rm -rf "$stage"; /bin/rmdir "$root/.cli-enrollment.lock" 2>/dev/null || true; fi
|
|
75
|
+
exit $st
|
|
76
|
+
}
|
|
77
|
+
trap rollback EXIT
|
|
78
|
+
[[ -f "$root/config.json" ]] && /bin/cp -p "$root/config.json" "$stage/config.before"
|
|
79
|
+
[[ -f "$root/apex-provenance" ]] && /bin/cp -p "$root/apex-provenance" "$stage/broker.before"
|
|
80
|
+
[[ -f "$plist" ]] && /bin/cp -p "$plist" "$stage/plist.before"
|
|
81
|
+
refuse_if_standalone_running
|
|
82
|
+
/bin/launchctl bootout system "$plist" >/dev/null 2>&1 || true
|
|
83
|
+
committed=1
|
|
84
|
+
if [[ -e "$product" || -L "$product" ]]; then /bin/mv "$product" "$stage/product.before"; fi
|
|
85
|
+
|
|
86
|
+
if [[ -L /usr/local/bin/apex ]]; then
|
|
87
|
+
target=$(/bin/readlink /usr/local/bin/apex || true)
|
|
88
|
+
case "$target" in
|
|
89
|
+
*Standalone/apex*|*Apex\ Deck\ Provenance/Standalone/apex*) /bin/rm -f /usr/local/bin/apex ;;
|
|
90
|
+
esac
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
decision=$(/usr/bin/python3 -I - "$root" "$stage" <<'PY'
|
|
94
|
+
import json,os,sys
|
|
95
|
+
root,stage=sys.argv[1:]
|
|
96
|
+
before=os.path.join(stage,'config.before')
|
|
97
|
+
if not os.path.exists(before):
|
|
98
|
+
print('full-uninstall'); sys.exit(0)
|
|
99
|
+
with open(before) as f: old=json.load(f)
|
|
100
|
+
if old.get('cliStandaloneCdhash') or old.get('daemonStandaloneCdhash'):
|
|
101
|
+
print('remove-standalone-pins')
|
|
102
|
+
else:
|
|
103
|
+
if old.get('uiCdhash') is None:
|
|
104
|
+
print('full-uninstall')
|
|
105
|
+
else:
|
|
106
|
+
print('remove-standalone-pins')
|
|
107
|
+
PY
|
|
108
|
+
)
|
|
109
|
+
if [[ "$decision" == "full-uninstall" ]]; then
|
|
110
|
+
echo "Removing standalone authority (fresh-only install)."
|
|
111
|
+
/bin/rm -f "$root/config.json" "$root/apex-provenance" "$plist"
|
|
112
|
+
# Keep private-key, public-key, journal.json, journal.log, host-locks
|
|
113
|
+
echo 'Standalone uninstalled. Authority keys and journal were preserved.'
|
|
114
|
+
else
|
|
115
|
+
/usr/bin/python3 -I - "$root" <<'PY'
|
|
116
|
+
import json,os,sys
|
|
117
|
+
root=sys.argv[1]
|
|
118
|
+
path=os.path.join(root,'config.json')
|
|
119
|
+
with open(path) as f: c=json.load(f)
|
|
120
|
+
c.pop('cliStandaloneCdhash',None)
|
|
121
|
+
c.pop('daemonStandaloneCdhash',None)
|
|
122
|
+
tmp=path+'.next'
|
|
123
|
+
fd=os.open(tmp,os.O_WRONLY|os.O_CREAT|os.O_EXCL,0o600)
|
|
124
|
+
with os.fdopen(fd,'w') as out:
|
|
125
|
+
json.dump(c,out); out.write('\n'); out.flush(); os.fsync(out.fileno())
|
|
126
|
+
os.rename(tmp,path)
|
|
127
|
+
PY
|
|
128
|
+
/bin/launchctl bootstrap system "$plist" >/dev/null 2>&1 || true
|
|
129
|
+
/usr/bin/python3 -I - <<'PY'
|
|
130
|
+
import socket,time
|
|
131
|
+
for _ in range(50):
|
|
132
|
+
try:
|
|
133
|
+
with socket.socket(socket.AF_UNIX) as s:
|
|
134
|
+
s.settimeout(.2); s.connect('/var/run/dev.apexdeck.provenance.sock')
|
|
135
|
+
break
|
|
136
|
+
except OSError:
|
|
137
|
+
time.sleep(.1)
|
|
138
|
+
else:
|
|
139
|
+
raise SystemExit('Authority did not become ready after uninstall')
|
|
140
|
+
PY
|
|
141
|
+
echo 'Standalone uninstalled. Desktop pins and authority keys were preserved.'
|
|
142
|
+
fi
|
|
143
|
+
echo 'Data directory was preserved.'
|
|
144
|
+
exit 0
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
# ---------- INSTALL / UPGRADE ----------
|
|
148
|
+
[[ -n "$src" ]] || { echo 'Run with one extracted CLI directory.' >&2; exit 1; }
|
|
149
|
+
[[ -d "$src" && ! -L "$src" ]] || { echo 'Expected an extracted CLI directory.' >&2; exit 1; }
|
|
150
|
+
|
|
151
|
+
existing=0
|
|
152
|
+
is_upgrade=0
|
|
153
|
+
installed_version=""
|
|
154
|
+
if [[ -e "$root" || -L "$root" ]]; then
|
|
155
|
+
existing=1
|
|
156
|
+
/usr/bin/python3 -I - "$root" "$plist" <<'PY'
|
|
157
|
+
import os,sys,stat
|
|
158
|
+
root,plist=sys.argv[1:]
|
|
159
|
+
for path in [root,root+'/config.json',root+'/apex-provenance',plist]:
|
|
160
|
+
if not os.path.lexists(path): continue
|
|
161
|
+
s=os.lstat(path)
|
|
162
|
+
if stat.S_ISLNK(s.st_mode) or s.st_uid!=0 or s.st_mode&0o022:
|
|
163
|
+
raise SystemExit('Unsafe existing authority path; refusing enrollment: '+path)
|
|
164
|
+
PY
|
|
165
|
+
# Fresh-only uninstall removes config.json and broker but preserves the
|
|
166
|
+
# authority directory (keys, journal, locks). Reinstalling from that
|
|
167
|
+
# preserved state must not attempt to verify a missing broker.
|
|
168
|
+
if [[ -f "$root/apex-provenance" ]]; then
|
|
169
|
+
/usr/bin/codesign --verify --strict -R="anchor apple generic and certificate leaf[subject.OU] = \"$team\"" "$root/apex-provenance"
|
|
170
|
+
fi
|
|
171
|
+
if [[ -e "$product/manifest.json" ]]; then
|
|
172
|
+
is_upgrade=1
|
|
173
|
+
installed_version=$(/usr/bin/python3 -I - "$product/manifest.json" <<'PY'
|
|
174
|
+
import json,sys
|
|
175
|
+
with open(sys.argv[1]) as f: print(json.load(f).get('version',''))
|
|
176
|
+
PY
|
|
177
|
+
)
|
|
178
|
+
fi
|
|
179
|
+
else
|
|
180
|
+
[[ ! -e "$plist" && ! -L "$plist" ]] || { echo 'Existing service without authority directory; refusing enrollment.' >&2; exit 1; }
|
|
181
|
+
/usr/bin/install -d -o root -g wheel -m 755 "$root"
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
/bin/mkdir "$root/.cli-enrollment.lock" || { echo 'Another enrollment exists; inspect authority before retrying.' >&2; exit 1; }
|
|
185
|
+
stage=$(/usr/bin/mktemp -d "$root/.cli-install.XXXXXX")
|
|
186
|
+
/bin/chmod 700 "$stage"
|
|
187
|
+
committed=0
|
|
188
|
+
plist_created=0
|
|
189
|
+
product_placed=0
|
|
190
|
+
rollback() {
|
|
191
|
+
st=$?
|
|
192
|
+
if [[ $st != 0 && $committed == 1 ]]; then
|
|
193
|
+
/bin/launchctl bootout system "$plist" >/dev/null 2>&1 || true
|
|
194
|
+
[[ -f "$stage/config.before" ]] && /bin/cp -p "$stage/config.before" "$root/config.json" 2>/dev/null || true
|
|
195
|
+
[[ -f "$stage/broker.before" ]] && /bin/cp -p "$stage/broker.before" "$root/apex-provenance" 2>/dev/null || true
|
|
196
|
+
if [[ -e "$stage/product.before" ]]; then
|
|
197
|
+
/bin/rm -rf "$product" 2>/dev/null || true
|
|
198
|
+
/bin/mv "$stage/product.before" "$product" 2>/dev/null || true
|
|
199
|
+
elif [[ $product_placed == 1 ]]; then
|
|
200
|
+
# No earlier product existed; remove the one this run placed.
|
|
201
|
+
/bin/rm -rf "$product" 2>/dev/null || true
|
|
202
|
+
fi
|
|
203
|
+
if [[ $plist_created == 1 ]]; then
|
|
204
|
+
# This run wrote the service file; there was no service before it.
|
|
205
|
+
/bin/rm -f "$plist" 2>/dev/null || true
|
|
206
|
+
else
|
|
207
|
+
/bin/launchctl bootstrap system "$plist" >/dev/null 2>&1 || true
|
|
208
|
+
fi
|
|
209
|
+
echo 'CLI enrollment failed. Inspect authority before retrying.' >&2
|
|
210
|
+
fi
|
|
211
|
+
if [[ $st == 0 || $committed == 0 ]]; then /bin/rm -rf "$stage"; /bin/rmdir "$root/.cli-enrollment.lock" 2>/dev/null || true; fi
|
|
212
|
+
exit $st
|
|
213
|
+
}
|
|
214
|
+
trap rollback EXIT
|
|
215
|
+
|
|
216
|
+
for binary in apex apex-daemon apex-provenance; do
|
|
217
|
+
[[ -f "$src/$binary" && ! -L "$src/$binary" ]] || { echo "Missing regular binary: $binary" >&2; exit 1; }
|
|
218
|
+
/usr/bin/install -o root -g wheel -m 755 "$src/$binary" "$stage/$binary"
|
|
219
|
+
req="anchor apple generic and certificate leaf[subject.OU] = \"$team\""
|
|
220
|
+
[[ $binary == apex ]] && req="$req and identifier \"dev.apexdeck.cli\""
|
|
221
|
+
/usr/bin/lipo -verify_arch arm64 "$stage/$binary"
|
|
222
|
+
/usr/bin/codesign --verify --strict -R="$req" "$stage/$binary"
|
|
223
|
+
d=$(/usr/bin/codesign -d --verbose=4 "$stage/$binary" 2>&1)
|
|
224
|
+
[[ "$d" == *'(runtime)'* ]] || { echo 'Hardened runtime is required.' >&2; exit 1; }
|
|
225
|
+
ent=$(/usr/bin/codesign -d --entitlements :- "$stage/$binary" 2>/dev/null || true)
|
|
226
|
+
[[ "$ent" != *'com.apple.security.get-task-allow'* ]] || { echo 'Debugger entitlement refused.' >&2; exit 1; }
|
|
227
|
+
done
|
|
228
|
+
|
|
229
|
+
[[ -f "$src/manifest.json" && ! -L "$src/manifest.json" ]] || { echo 'Missing manifest.json' >&2; exit 1; }
|
|
230
|
+
/usr/bin/install -o root -g wheel -m 600 "$src/manifest.json" "$stage/manifest.json"
|
|
231
|
+
/usr/bin/python3 -I - "$stage" <<'PY'
|
|
232
|
+
import json,hashlib,sys
|
|
233
|
+
root=sys.argv[1]
|
|
234
|
+
with open(root+'/manifest.json') as f: m=json.load(f)
|
|
235
|
+
if m.get('formatVersion')!=1 or m.get('product')!='apex-cli' or m.get('architecture')!='arm64':
|
|
236
|
+
raise SystemExit('Unsupported CLI manifest')
|
|
237
|
+
files=m.get('files',[])
|
|
238
|
+
if len(files)!=3 or {f.get('name') for f in files}!={'apex','apex-daemon','apex-provenance'}:
|
|
239
|
+
raise SystemExit('Incomplete CLI manifest')
|
|
240
|
+
for f in files:
|
|
241
|
+
with open(root+'/'+f['name'],'rb') as b: d=hashlib.sha256(b.read()).hexdigest()
|
|
242
|
+
if d!=f.get('sha256'): raise SystemExit('CLI archive hash mismatch')
|
|
243
|
+
PY
|
|
244
|
+
|
|
245
|
+
new_version=$(/usr/bin/python3 -I - "$stage/manifest.json" <<'PY'
|
|
246
|
+
import json,sys
|
|
247
|
+
with open(sys.argv[1]) as f: print(json.load(f).get('version',''))
|
|
248
|
+
PY
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
if [[ $is_upgrade == 1 ]]; then
|
|
252
|
+
decision=$(/usr/bin/python3 -I - "$installed_version" "$new_version" <<'PY'
|
|
253
|
+
import sys
|
|
254
|
+
old,new=sys.argv[1],sys.argv[2]
|
|
255
|
+
def parts(v):
|
|
256
|
+
base=v.split('-')[0]
|
|
257
|
+
p=[int(x) if x.isdigit() else 0 for x in base.split('.')]
|
|
258
|
+
while len(p)<3: p.append(0)
|
|
259
|
+
return p
|
|
260
|
+
if old==new:
|
|
261
|
+
print('same'); sys.exit(0)
|
|
262
|
+
op=parts(old); np=parts(new)
|
|
263
|
+
if np<op:
|
|
264
|
+
print('older'); sys.exit(0)
|
|
265
|
+
if np==op:
|
|
266
|
+
old_pre='-' in old; new_pre='-' in new
|
|
267
|
+
if not old_pre and new_pre:
|
|
268
|
+
print('older'); sys.exit(0)
|
|
269
|
+
print('newer')
|
|
270
|
+
PY
|
|
271
|
+
)
|
|
272
|
+
if [[ "$decision" == "older" ]]; then
|
|
273
|
+
echo "Incoming version $new_version is older than installed $installed_version; refusing downgrade." >&2
|
|
274
|
+
exit 1
|
|
275
|
+
fi
|
|
276
|
+
fi
|
|
277
|
+
|
|
278
|
+
cli_hash=$(/usr/bin/codesign -d --verbose=4 "$stage/apex" 2>&1 | /usr/bin/awk -F= '/^CDHash=/{print $2}')
|
|
279
|
+
daemon_hash=$(/usr/bin/codesign -d --verbose=4 "$stage/apex-daemon" 2>&1 | /usr/bin/awk -F= '/^CDHash=/{print $2}')
|
|
280
|
+
[[ "$cli_hash" =~ ^[0-9a-fA-F]{40}$ && "$daemon_hash" =~ ^[0-9a-fA-F]{40}$ ]] || { echo 'Invalid CDHash' >&2; exit 1; }
|
|
281
|
+
|
|
282
|
+
# Same version string is not proof of same build: a rebuilt package keeps its
|
|
283
|
+
# version but changes every hash. Skip only when the installed files, broker
|
|
284
|
+
# and pins all match this payload exactly.
|
|
285
|
+
if [[ $is_upgrade == 1 && "$decision" == "same" && -f "$plist" ]]; then
|
|
286
|
+
if /usr/bin/python3 -I - "$root" "$product" "$stage" "$cli_hash" "$daemon_hash" <<'PY'
|
|
287
|
+
import hashlib,json,os,sys
|
|
288
|
+
root,product,stage,cli,daemon=sys.argv[1:]
|
|
289
|
+
def sha(p):
|
|
290
|
+
with open(p,'rb') as f: return hashlib.sha256(f.read()).hexdigest()
|
|
291
|
+
try:
|
|
292
|
+
with open(stage+'/manifest.json') as f: m=json.load(f)
|
|
293
|
+
where={'apex':product+'/apex','apex-daemon':product+'/apex-daemon','apex-provenance':root+'/apex-provenance'}
|
|
294
|
+
for f in m['files']:
|
|
295
|
+
if sha(where[f['name']])!=f['sha256']: sys.exit(1)
|
|
296
|
+
if sha(product+'/manifest.json')!=sha(stage+'/manifest.json'): sys.exit(1)
|
|
297
|
+
with open(root+'/config.json') as f: c=json.load(f)
|
|
298
|
+
# A CLI-only (fresh) install pins under cliCdhash/daemonCdhash; once the
|
|
299
|
+
# desktop or a later CLI install shares the authority, standalone pins win.
|
|
300
|
+
solo=c.get('uiCdhash') is None
|
|
301
|
+
pin_cli=c.get('cliStandaloneCdhash') or (c.get('cliCdhash') if solo else None)
|
|
302
|
+
pin_daemon=c.get('daemonStandaloneCdhash') or (c.get('daemonCdhash') if solo else None)
|
|
303
|
+
if pin_cli!=cli or pin_daemon!=daemon: sys.exit(1)
|
|
304
|
+
except Exception:
|
|
305
|
+
sys.exit(1)
|
|
306
|
+
PY
|
|
307
|
+
then
|
|
308
|
+
echo "Standalone already at version $installed_version with this exact build; no changes needed."
|
|
309
|
+
exit 0
|
|
310
|
+
fi
|
|
311
|
+
echo "Replacing standalone $installed_version with a different build of the same version."
|
|
312
|
+
fi
|
|
313
|
+
if [[ $is_upgrade == 1 ]]; then refuse_if_standalone_running; fi
|
|
314
|
+
|
|
315
|
+
/usr/bin/python3 -I - /usr/local /usr/local/bin <<'PY'
|
|
316
|
+
import os,stat,sys
|
|
317
|
+
for p in sys.argv[1:]:
|
|
318
|
+
if not os.path.lexists(p):
|
|
319
|
+
try: os.mkdir(p,0o755)
|
|
320
|
+
except FileExistsError: pass
|
|
321
|
+
s=os.lstat(p)
|
|
322
|
+
if stat.S_ISLNK(s.st_mode) or s.st_uid!=0 or s.st_mode&0o022:
|
|
323
|
+
raise SystemExit('Unsafe PATH directory: '+p)
|
|
324
|
+
PY
|
|
325
|
+
|
|
326
|
+
if [[ $no_path_link == 0 ]]; then
|
|
327
|
+
if [[ -L /usr/local/bin/apex ]]; then
|
|
328
|
+
t=$(/bin/readlink /usr/local/bin/apex || true)
|
|
329
|
+
case "$t" in
|
|
330
|
+
*Standalone/apex*|*Apex\ Deck\ Provenance/Standalone/apex*) ;;
|
|
331
|
+
*) echo '/usr/local/bin/apex exists and does not point to Standalone; refusing replacement.' >&2; exit 1 ;;
|
|
332
|
+
esac
|
|
333
|
+
elif [[ -e /usr/local/bin/apex ]]; then
|
|
334
|
+
echo '/usr/local/bin/apex already exists; refusing replacement.' >&2; exit 1
|
|
335
|
+
fi
|
|
336
|
+
fi
|
|
337
|
+
|
|
338
|
+
if [[ $existing == 1 ]]; then
|
|
339
|
+
[[ -f "$root/config.json" ]] && /bin/cp -p "$root/config.json" "$stage/config.before"
|
|
340
|
+
[[ -f "$root/apex-provenance" ]] && /bin/cp -p "$root/apex-provenance" "$stage/broker.before"
|
|
341
|
+
fi
|
|
342
|
+
|
|
343
|
+
/usr/bin/python3 -I - "$root" "$stage" "$cli_hash" "$daemon_hash" "$existing" <<'PY'
|
|
344
|
+
import json,os,sys
|
|
345
|
+
root,stage,cli,daemon,existing=sys.argv[1:]
|
|
346
|
+
config_path=os.path.join(root,'config.json')
|
|
347
|
+
if existing=='1' and os.path.exists(config_path):
|
|
348
|
+
with open(config_path) as f: c=json.load(f)
|
|
349
|
+
c['cliStandaloneCdhash']=cli
|
|
350
|
+
c['daemonStandaloneCdhash']=daemon
|
|
351
|
+
if c.get('uiCdhash') is None:
|
|
352
|
+
c['cliCdhash']=cli
|
|
353
|
+
c['daemonCdhash']=daemon
|
|
354
|
+
else:
|
|
355
|
+
# Fresh install or reinstall after full uninstall: no config exists; start fresh.
|
|
356
|
+
c={'uiCdhash':None,'cliCdhash':cli,'daemonCdhash':daemon}
|
|
357
|
+
p=stage+'/config.next'
|
|
358
|
+
fd=os.open(p,os.O_WRONLY|os.O_CREAT|os.O_EXCL,0o600)
|
|
359
|
+
with os.fdopen(fd,'w') as f: json.dump(c,f); f.write('\n'); f.flush(); os.fsync(f.fileno())
|
|
360
|
+
PY
|
|
361
|
+
|
|
362
|
+
/usr/bin/install -d -o root -g wheel -m 755 "$stage/product"
|
|
363
|
+
/usr/bin/install -o root -g wheel -m 755 "$stage/apex" "$stage/product/apex"
|
|
364
|
+
/usr/bin/install -o root -g wheel -m 755 "$stage/apex-daemon" "$stage/product/apex-daemon"
|
|
365
|
+
/usr/bin/install -o root -g wheel -m 644 "$stage/manifest.json" "$stage/product/manifest.json"
|
|
366
|
+
|
|
367
|
+
# Uninstall removes the service file but keeps the authority directory, so a
|
|
368
|
+
# reinstall (existing=1) can also find it missing. Recreate it whenever absent.
|
|
369
|
+
if [[ ! -e "$plist" && ! -L "$plist" ]]; then
|
|
370
|
+
plist_created=1
|
|
371
|
+
/bin/cat > "$plist" <<'PLIST'
|
|
372
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
373
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
374
|
+
<plist version="1.0"><dict>
|
|
375
|
+
<key>Label</key><string>dev.apexdeck.provenance</string>
|
|
376
|
+
<key>ProgramArguments</key><array><string>/Library/Application Support/Apex Deck Provenance/apex-provenance</string></array>
|
|
377
|
+
<key>UserName</key><string>root</string><key>RunAtLoad</key><true/><key>KeepAlive</key><true/>
|
|
378
|
+
<key>StandardErrorPath</key><string>/var/log/apex-provenance.log</string>
|
|
379
|
+
<key>ProcessType</key><string>Background</string>
|
|
380
|
+
</dict></plist>
|
|
381
|
+
PLIST
|
|
382
|
+
/usr/sbin/chown root:wheel "$plist"; /bin/chmod 644 "$plist"
|
|
383
|
+
fi
|
|
384
|
+
|
|
385
|
+
if [[ $existing == 1 ]]; then /bin/launchctl bootout system "$plist" >/dev/null 2>&1 || true; fi
|
|
386
|
+
committed=1
|
|
387
|
+
if [[ -e "$product" || -L "$product" ]]; then /bin/mv "$product" "$stage/product.before"; fi
|
|
388
|
+
/bin/mv "$stage/product" "$product"
|
|
389
|
+
product_placed=1
|
|
390
|
+
/bin/mv "$stage/config.next" "$root/config.json"
|
|
391
|
+
/usr/bin/install -o root -g wheel -m 755 "$stage/apex-provenance" "$root/apex-provenance.next"
|
|
392
|
+
/bin/mv "$root/apex-provenance.next" "$root/apex-provenance"
|
|
393
|
+
/bin/launchctl bootstrap system "$plist"
|
|
394
|
+
/bin/launchctl print system/dev.apexdeck.provenance >/dev/null
|
|
395
|
+
/usr/bin/python3 -I - <<'PY'
|
|
396
|
+
import socket,time
|
|
397
|
+
for _ in range(50):
|
|
398
|
+
try:
|
|
399
|
+
with socket.socket(socket.AF_UNIX) as s:
|
|
400
|
+
s.settimeout(.2); s.connect('/var/run/dev.apexdeck.provenance.sock')
|
|
401
|
+
break
|
|
402
|
+
except OSError:
|
|
403
|
+
time.sleep(.1)
|
|
404
|
+
else:
|
|
405
|
+
raise SystemExit('Authority did not become ready; rolling back')
|
|
406
|
+
PY
|
|
407
|
+
|
|
408
|
+
if [[ $no_path_link == 0 ]]; then
|
|
409
|
+
[[ -L /usr/local/bin/apex ]] && /bin/rm -f /usr/local/bin/apex
|
|
410
|
+
if [[ ! -e /usr/local/bin/apex ]]; then /bin/ln -s "$product/apex" /usr/local/bin/apex; fi
|
|
411
|
+
fi
|
|
412
|
+
|
|
413
|
+
if [[ $is_upgrade == 1 ]]; then
|
|
414
|
+
echo "Upgraded standalone Apex CLI from $installed_version to $new_version. Existing authority keys, journal and desktop pins were preserved."
|
|
415
|
+
else
|
|
416
|
+
echo 'Installed standalone Apex CLI. Existing authority keys, journal and desktop pins were preserved.'
|
|
417
|
+
fi
|
|
418
|
+
echo 'Run apex as your normal user. Provider sign-in is initiated explicitly with /login.'
|
package/manifest.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"formatVersion": 1,
|
|
3
|
+
"product": "apex-cli",
|
|
4
|
+
"version": "0.9.9",
|
|
5
|
+
"architecture": "arm64",
|
|
6
|
+
"files": [
|
|
7
|
+
{
|
|
8
|
+
"name": "apex",
|
|
9
|
+
"sha256": "dd2e6aaddb114a1b70f905ea0d4254d90b57865ef7b8772d2c8ef37c415ff4d6"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"name": "apex-daemon",
|
|
13
|
+
"sha256": "e131ad2406690a734c44aa8acee72a4fa7aaf544315f1d1639d8e119ece4d750"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"name": "apex-provenance",
|
|
17
|
+
"sha256": "5ff39c926f87da89ce38b3644d969b959356c070cdfe9b847cc52c9b9fb565e2"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apex-deck/cli-darwin-arm64",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.9.9",
|
|
4
|
+
"description": "Apex Deck CLI signed binaries for macOS Apple Silicon",
|
|
5
|
+
"os": [
|
|
6
|
+
"darwin"
|
|
7
|
+
],
|
|
8
|
+
"cpu": [
|
|
9
|
+
"arm64"
|
|
10
|
+
],
|
|
11
|
+
"files": [
|
|
12
|
+
"apex",
|
|
13
|
+
"apex-daemon",
|
|
14
|
+
"apex-provenance",
|
|
15
|
+
"manifest.json",
|
|
16
|
+
"install-cli-provenance.sh",
|
|
17
|
+
"INSTALL.txt"
|
|
18
|
+
],
|
|
19
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/apex-deck/apex-deck"
|
|
23
|
+
},
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
}
|
|
27
|
+
}
|