docks-kit 0.1.0 → 0.1.2
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/AGENTS.md +14 -15
- package/README.md +12 -11
- package/SoT/.agents/skills.txt +3 -3
- package/SoT/.claude/settings.json +3 -26
- package/SoT/models.json +1 -1
- package/SoT/toolchain.json +3 -3
- package/cli/docs/install.md +39 -12
- package/cli/docs/overview.md +3 -4
- package/cli/docs/platforms.md +35 -22
- package/cli/src/commands/sync.ts +32 -4
- package/cli/src/commands/update.ts +116 -0
- package/cli/src/engine-native/DESIGN.md +89 -0
- package/cli/src/engine-native/claudeModel.ts +48 -0
- package/cli/src/engine-native/claudeSync.ts +771 -0
- package/cli/src/engine-native/codexSync.ts +439 -0
- package/cli/src/engine-native/codexToml.ts +67 -0
- package/cli/src/engine-native/exec.ts +54 -0
- package/cli/src/engine-native/index.ts +109 -0
- package/cli/src/engine-native/jq.ts +63 -0
- package/cli/src/engine-native/models.ts +73 -0
- package/cli/src/engine-native/modes.ts +133 -0
- package/cli/src/engine-native/output.ts +20 -0
- package/cli/src/engine-native/parseArgs.ts +223 -0
- package/cli/src/engine-native/settings.ts +41 -0
- package/cli/src/engine-native/skillsSync.ts +369 -0
- package/cli/src/engine-native/toolchain.ts +257 -0
- package/cli/src/engine.ts +35 -18
- package/cli/src/kitHome.ts +4 -4
- package/cli/src/main.ts +14 -1
- package/cli/src/manifests.ts +3 -2
- package/cli/tsconfig.json +1 -1
- package/docks-kit +6 -3
- package/package.json +8 -4
- package/lib/claude.sh +0 -846
- package/lib/codex.sh +0 -518
- package/lib/common.sh +0 -229
- package/lib/engine.sh +0 -153
- package/lib/skills.sh +0 -373
- package/lib/toolchain.sh +0 -222
package/lib/codex.sh
DELETED
|
@@ -1,518 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
set -euo pipefail
|
|
3
|
-
|
|
4
|
-
# Source-order guards: this lib depends on common.sh's log/warn/err and on
|
|
5
|
-
# the entry script's REPO_DIR. Fail fast with a clear message if sourced standalone.
|
|
6
|
-
declare -F log >/dev/null 2>&1 || { printf '\033[1;31m[err]\033[0m %s\n' "lib/codex.sh must be sourced after lib/common.sh" >&2; exit 1; }
|
|
7
|
-
[[ -n "${REPO_DIR:-}" ]] || { printf '\033[1;31m[err]\033[0m %s\n' "REPO_DIR must be set before sourcing lib/codex.sh" >&2; exit 1; }
|
|
8
|
-
|
|
9
|
-
codex::sync() {
|
|
10
|
-
CODEX_DIR="$HOME/.codex"
|
|
11
|
-
CODEX_SYNCED=1
|
|
12
|
-
|
|
13
|
-
local codex_settings="$REPO_DIR/SoT/.codex/config.toml"
|
|
14
|
-
local user_codex_settings="$CODEX_DIR/config.toml"
|
|
15
|
-
local codex_rules_dir="$REPO_DIR/SoT/.codex/rules"
|
|
16
|
-
local user_codex_rules_dir="$CODEX_DIR/rules"
|
|
17
|
-
local codex_agents_md="$REPO_DIR/SoT/.codex/AGENTS.md"
|
|
18
|
-
local user_codex_agents_md="$CODEX_DIR/AGENTS.md"
|
|
19
|
-
local codex_marketplace="$REPO_DIR/SoT/.codex/plugins/marketplace.json"
|
|
20
|
-
local user_codex_marketplace="$AGENTS_DIR/plugins/marketplace.json"
|
|
21
|
-
|
|
22
|
-
# Per-stage dry-run pattern: each helper owns its own [[ DRY_RUN ]] echo +
|
|
23
|
-
# early return. codex::sync is the dispatcher and never inspects DRY_RUN.
|
|
24
|
-
codex::ensure_bubblewrap
|
|
25
|
-
[[ "$DRY_RUN" -eq 0 ]] && mkdir -p "$CODEX_DIR"
|
|
26
|
-
codex::sync_config "$codex_settings" "$user_codex_settings"
|
|
27
|
-
codex::sync_model
|
|
28
|
-
codex::sync_rules "$codex_rules_dir" "$user_codex_rules_dir"
|
|
29
|
-
codex::sync_agents_md "$codex_agents_md" "$user_codex_agents_md"
|
|
30
|
-
codex::sync_marketplace "$codex_marketplace" "$user_codex_marketplace"
|
|
31
|
-
codex::remove_legacy_docks_marketplace
|
|
32
|
-
codex::sync_plugins "$codex_settings"
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
# Codex prefers a system bubblewrap on Linux, then falls back to its bundled
|
|
36
|
-
# helper when user namespaces allow it. macOS uses Seatbelt natively.
|
|
37
|
-
codex::ensure_bubblewrap() {
|
|
38
|
-
if [[ "$DRY_RUN" -eq 1 ]]; then
|
|
39
|
-
echo "[dry-run] verify bubblewrap installed (recommended Codex Linux sandbox runtime)"
|
|
40
|
-
return
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
codex::_bwrap_supported_os || return
|
|
44
|
-
command -v bwrap >/dev/null 2>&1 && return
|
|
45
|
-
|
|
46
|
-
if [[ "${SKIP_RTK:-0}" -eq 1 ]]; then
|
|
47
|
-
warn "bubblewrap not installed (--skip-rtk skips auto-install). Codex may use its bundled helper if user namespaces work; recommended install: sudo apt install -y bubblewrap"
|
|
48
|
-
return
|
|
49
|
-
fi
|
|
50
|
-
|
|
51
|
-
local pm_install
|
|
52
|
-
pm_install=$(codex::_bwrap_detect_pm_install_cmd)
|
|
53
|
-
if [[ -z "$pm_install" ]]; then
|
|
54
|
-
warn "bubblewrap not installed and no supported package manager found (apt-get/dnf/pacman/zypper). Codex may use its bundled helper if user namespaces work; install system bubblewrap manually when possible."
|
|
55
|
-
return
|
|
56
|
-
fi
|
|
57
|
-
|
|
58
|
-
warn "bubblewrap not installed - recommended for Codex Linux sandbox. Running: $pm_install (sudo prompt may appear)"
|
|
59
|
-
if ! eval "$pm_install"; then
|
|
60
|
-
warn "Failed to auto-install bubblewrap. Install manually: $pm_install"
|
|
61
|
-
return
|
|
62
|
-
fi
|
|
63
|
-
|
|
64
|
-
if ! command -v bwrap >/dev/null 2>&1; then
|
|
65
|
-
warn "Package install reported success but bwrap not on PATH — check installation manually"
|
|
66
|
-
return
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
codex::_bwrap_verify_userns
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
# Returns 0 if the current OS supports bubblewrap (Linux); 1 otherwise. macOS
|
|
73
|
-
# uses Seatbelt natively so bwrap is not needed; unknown OSes get a warning.
|
|
74
|
-
codex::_bwrap_supported_os() {
|
|
75
|
-
case "$(uname -s)" in
|
|
76
|
-
Darwin*) return 1 ;;
|
|
77
|
-
Linux*) return 0 ;;
|
|
78
|
-
*) warn "Unknown OS — skipping bubblewrap check; Codex sandbox may not work"; return 1 ;;
|
|
79
|
-
esac
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
# Echoes the `sudo <pm> install -y bubblewrap` command for the first PM found
|
|
83
|
-
# on PATH (apt-get -> dnf -> pacman -> zypper); empty string if none found.
|
|
84
|
-
codex::_bwrap_detect_pm_install_cmd() {
|
|
85
|
-
if command -v apt-get >/dev/null 2>&1; then
|
|
86
|
-
echo "sudo apt-get install -y bubblewrap"
|
|
87
|
-
elif command -v dnf >/dev/null 2>&1; then
|
|
88
|
-
echo "sudo dnf install -y bubblewrap"
|
|
89
|
-
elif command -v pacman >/dev/null 2>&1; then
|
|
90
|
-
echo "sudo pacman -S --noconfirm bubblewrap"
|
|
91
|
-
elif command -v zypper >/dev/null 2>&1; then
|
|
92
|
-
echo "sudo zypper install -y bubblewrap"
|
|
93
|
-
fi
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
# Probe whether unprivileged user namespaces work - system bubblewrap and the
|
|
97
|
-
# bundled Codex helper both depend on them at runtime.
|
|
98
|
-
codex::_bwrap_verify_userns() {
|
|
99
|
-
if unshare -Ur true >/dev/null 2>&1; then
|
|
100
|
-
log "bubblewrap installed and functional ($(bwrap --version 2>/dev/null | head -1))"
|
|
101
|
-
else
|
|
102
|
-
warn "bubblewrap installed but unprivileged user namespaces appear blocked. On Ubuntu 24.04+, prefer loading the AppArmor bwrap-userns-restrict profile; fallback: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0"
|
|
103
|
-
fi
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
codex::sync_rules() {
|
|
107
|
-
local codex_rules_dir="$1"
|
|
108
|
-
local user_codex_rules_dir="$2"
|
|
109
|
-
local rule_file user_rule_file rules_synced=0
|
|
110
|
-
|
|
111
|
-
[[ -d "$codex_rules_dir" ]] || return
|
|
112
|
-
|
|
113
|
-
if [[ "$DRY_RUN" -eq 1 ]]; then
|
|
114
|
-
echo "[dry-run] cp $codex_rules_dir/*.rules -> $user_codex_rules_dir/"
|
|
115
|
-
return
|
|
116
|
-
fi
|
|
117
|
-
|
|
118
|
-
mkdir -p "$user_codex_rules_dir"
|
|
119
|
-
while IFS= read -r rule_file; do
|
|
120
|
-
[[ -f "$rule_file" ]] || continue
|
|
121
|
-
user_rule_file="$user_codex_rules_dir/$(basename "$rule_file")"
|
|
122
|
-
[[ -f "$user_rule_file" ]] && cp "$user_rule_file" "$user_rule_file.bak"
|
|
123
|
-
cp "$rule_file" "$user_rule_file"
|
|
124
|
-
rules_synced=1
|
|
125
|
-
done < <(find "$codex_rules_dir" -maxdepth 1 -type f -name '*.rules' | sort)
|
|
126
|
-
|
|
127
|
-
[[ "$rules_synced" -eq 1 ]] && log "Codex rules synced"
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
codex::sync_agents_md() {
|
|
131
|
-
local codex_agents_md="$1"
|
|
132
|
-
local user_codex_agents_md="$2"
|
|
133
|
-
|
|
134
|
-
[[ -f "$codex_agents_md" ]] || return
|
|
135
|
-
|
|
136
|
-
if [[ "$DRY_RUN" -eq 1 ]]; then
|
|
137
|
-
echo "[dry-run] cp $codex_agents_md -> $user_codex_agents_md"
|
|
138
|
-
return
|
|
139
|
-
fi
|
|
140
|
-
|
|
141
|
-
[[ -f "$user_codex_agents_md" ]] && cp "$user_codex_agents_md" "$user_codex_agents_md.bak"
|
|
142
|
-
cp "$codex_agents_md" "$user_codex_agents_md"
|
|
143
|
-
log "Codex AGENTS.md synced"
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
codex::sync_config() {
|
|
147
|
-
local codex_settings="$1"
|
|
148
|
-
local user_codex_settings="$2"
|
|
149
|
-
|
|
150
|
-
[[ -f "$codex_settings" ]] || return
|
|
151
|
-
|
|
152
|
-
if [[ "$DRY_RUN" -eq 1 ]]; then
|
|
153
|
-
echo "[dry-run] merge $codex_settings -> $user_codex_settings"
|
|
154
|
-
return
|
|
155
|
-
fi
|
|
156
|
-
|
|
157
|
-
if [[ ! -f "$user_codex_settings" ]]; then
|
|
158
|
-
cp "$codex_settings" "$user_codex_settings"
|
|
159
|
-
log "Codex config installed"
|
|
160
|
-
return
|
|
161
|
-
fi
|
|
162
|
-
|
|
163
|
-
cp "$user_codex_settings" "$user_codex_settings.bak"
|
|
164
|
-
|
|
165
|
-
# Stage list is the OCP extension point: append a new stage here to grow the
|
|
166
|
-
# merge pipeline without editing the loop. Order matters — scrub deprecated
|
|
167
|
-
# keys before merging so SoT keys don't re-introduce a scrubbed table.
|
|
168
|
-
local stages=(scrub_deprecated_features merge_top_level_settings merge_table_settings) stage
|
|
169
|
-
for stage in "${stages[@]}"; do
|
|
170
|
-
"codex::$stage" "$codex_settings" "$user_codex_settings"
|
|
171
|
-
done
|
|
172
|
-
|
|
173
|
-
# Merge is always additive (SoT keys/tables win; user-only keys and [table] blocks
|
|
174
|
-
# preserved). --force does NOT wholesale-reset Codex config, unlike Claude settings.
|
|
175
|
-
log "Codex config merged (backup at config.toml.bak; user-only keys/tables preserved)"
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
# Drop deprecated [features].use_legacy_landlock and the [features] table if
|
|
179
|
-
# that was its only key. Codex emits a "deprecated" warning on every run while
|
|
180
|
-
# the key is present (value=false still warns). bubblewrap is the default Linux
|
|
181
|
-
# sandbox now; removing the override lets Codex pick it up automatically.
|
|
182
|
-
codex::scrub_deprecated_features() {
|
|
183
|
-
# Unified stage signature: (codex_settings, user_codex_settings). $1 is unused.
|
|
184
|
-
local user_codex_settings="$2"
|
|
185
|
-
|
|
186
|
-
[[ -f "$user_codex_settings" ]] || return 0
|
|
187
|
-
grep -q '^use_legacy_landlock[[:space:]]*=' "$user_codex_settings" || return 0
|
|
188
|
-
|
|
189
|
-
local tmp="$user_codex_settings.tmp"
|
|
190
|
-
awk '
|
|
191
|
-
in_features {
|
|
192
|
-
if (/^\[/) {
|
|
193
|
-
in_features = 0
|
|
194
|
-
if (keep) { print header; printf "%s", body }
|
|
195
|
-
print
|
|
196
|
-
next
|
|
197
|
-
}
|
|
198
|
-
if (/^use_legacy_landlock[[:space:]]*=/) next
|
|
199
|
-
body = body $0 "\n"
|
|
200
|
-
if (/[^[:space:]]/) keep = 1
|
|
201
|
-
next
|
|
202
|
-
}
|
|
203
|
-
/^\[features\][[:space:]]*$/ {
|
|
204
|
-
in_features = 1
|
|
205
|
-
header = $0
|
|
206
|
-
body = ""
|
|
207
|
-
keep = 0
|
|
208
|
-
next
|
|
209
|
-
}
|
|
210
|
-
{ print }
|
|
211
|
-
END {
|
|
212
|
-
if (in_features && keep) { print header; printf "%s", body }
|
|
213
|
-
}
|
|
214
|
-
' "$user_codex_settings" > "$tmp" && mv "$tmp" "$user_codex_settings"
|
|
215
|
-
|
|
216
|
-
log "Codex: scrubbed deprecated [features].use_legacy_landlock"
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
# Replace (or insert) one top-level `key = value` line in a user config.toml,
|
|
220
|
-
# never touching [table] blocks: an existing pre-table line for the key is
|
|
221
|
-
# replaced in place; otherwise the line is inserted just before the first table
|
|
222
|
-
# header (or appended when no tables exist). Shared by the SoT merge loop and
|
|
223
|
-
# the --codex-model deploy-time modifier.
|
|
224
|
-
codex::_replace_top_level_setting() {
|
|
225
|
-
local user_codex_settings="$1" setting_key="$2" setting_line="$3"
|
|
226
|
-
local tmp_file="$user_codex_settings.tmp"
|
|
227
|
-
|
|
228
|
-
awk -v key="$setting_key" -v replacement="$setting_line" '
|
|
229
|
-
BEGIN { in_table = 0; replaced = 0 }
|
|
230
|
-
/^\[/ {
|
|
231
|
-
if (!replaced) {
|
|
232
|
-
print replacement
|
|
233
|
-
replaced = 1
|
|
234
|
-
}
|
|
235
|
-
in_table = 1
|
|
236
|
-
print
|
|
237
|
-
next
|
|
238
|
-
}
|
|
239
|
-
!in_table && $0 ~ ("^" key "[[:space:]]*=") {
|
|
240
|
-
if (!replaced) {
|
|
241
|
-
print replacement
|
|
242
|
-
replaced = 1
|
|
243
|
-
}
|
|
244
|
-
next
|
|
245
|
-
}
|
|
246
|
-
{ print }
|
|
247
|
-
END {
|
|
248
|
-
if (!replaced) {
|
|
249
|
-
print replacement
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
' "$user_codex_settings" > "$tmp_file" && mv "$tmp_file" "$user_codex_settings"
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
codex::merge_top_level_settings() {
|
|
256
|
-
local codex_settings="$1"
|
|
257
|
-
local user_codex_settings="$2"
|
|
258
|
-
local setting_line setting_key
|
|
259
|
-
|
|
260
|
-
while IFS= read -r setting_line; do
|
|
261
|
-
[[ -z "$setting_line" ]] && continue
|
|
262
|
-
setting_key="${setting_line%%=*}"
|
|
263
|
-
setting_key="${setting_key%"${setting_key##*[![:space:]]}"}"
|
|
264
|
-
|
|
265
|
-
codex::_replace_top_level_setting "$user_codex_settings" "$setting_key" "$setting_line"
|
|
266
|
-
done < <(awk '
|
|
267
|
-
/^\[/ { exit }
|
|
268
|
-
/^[[:space:]]*($|#)/ { next }
|
|
269
|
-
/^[A-Za-z0-9_.-]+[[:space:]]*=/ { print }
|
|
270
|
-
' "$codex_settings")
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
# Deploy-time modifier (--codex-model=<m>): set the DEPLOYED model line. Same
|
|
274
|
-
# contract as the Claude modifiers: only ~/.codex/config.toml changes, the SoT
|
|
275
|
-
# keeps its pin, and a later flag-less sync restores the SoT value (the merge
|
|
276
|
-
# re-asserts every SoT top-level key). Also callable standalone
|
|
277
|
-
# (docks-kit model codex <m>) — hence the ${CODEX_DIR:-} fallback.
|
|
278
|
-
codex::sync_model() {
|
|
279
|
-
local user_codex_settings="${CODEX_DIR:-$HOME/.codex}/config.toml"
|
|
280
|
-
|
|
281
|
-
[[ -n "$CODEX_MODEL" ]] || return 0
|
|
282
|
-
|
|
283
|
-
if [[ "$DRY_RUN" -eq 1 ]]; then
|
|
284
|
-
echo "[dry-run] (--codex-model) set model = \"$CODEX_MODEL\" in $user_codex_settings"
|
|
285
|
-
return
|
|
286
|
-
fi
|
|
287
|
-
|
|
288
|
-
[[ -f "$user_codex_settings" ]] || { warn "(--codex-model) $user_codex_settings missing — skipped"; return; }
|
|
289
|
-
codex::_replace_top_level_setting "$user_codex_settings" "model" "model = \"$CODEX_MODEL\""
|
|
290
|
-
log "Model: deployed Codex model set to $CODEX_MODEL (SoT unchanged; flag-less sync reverts)"
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
codex::merge_table_settings() {
|
|
294
|
-
local codex_settings="$1"
|
|
295
|
-
local user_codex_settings="$2"
|
|
296
|
-
local table_header table_block tmp_file
|
|
297
|
-
|
|
298
|
-
while IFS= read -r table_header; do
|
|
299
|
-
[[ -z "$table_header" ]] && continue
|
|
300
|
-
|
|
301
|
-
table_block="$(
|
|
302
|
-
awk -v header="$table_header" '
|
|
303
|
-
$0 == header { printing = 1 }
|
|
304
|
-
printing && $0 ~ /^\[/ && $0 != header { exit }
|
|
305
|
-
printing { print }
|
|
306
|
-
' "$codex_settings"
|
|
307
|
-
)"
|
|
308
|
-
|
|
309
|
-
tmp_file="$user_codex_settings.tmp"
|
|
310
|
-
awk -v header="$table_header" '
|
|
311
|
-
$0 == header { skip = 1; next }
|
|
312
|
-
skip && /^\[/ { skip = 0 }
|
|
313
|
-
!skip { print }
|
|
314
|
-
' "$user_codex_settings" > "$tmp_file" && mv "$tmp_file" "$user_codex_settings"
|
|
315
|
-
|
|
316
|
-
{
|
|
317
|
-
printf '\n'
|
|
318
|
-
printf '%s\n' "$table_block"
|
|
319
|
-
} >> "$user_codex_settings"
|
|
320
|
-
done < <(grep -E '^\[[^]]+\]' "$codex_settings")
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
codex::sync_marketplace() {
|
|
324
|
-
local codex_marketplace="$1"
|
|
325
|
-
local user_codex_marketplace="$2"
|
|
326
|
-
|
|
327
|
-
[[ -f "$codex_marketplace" ]] || return
|
|
328
|
-
|
|
329
|
-
if [[ "$DRY_RUN" -eq 1 ]]; then
|
|
330
|
-
echo "[dry-run] cp $codex_marketplace -> $user_codex_marketplace"
|
|
331
|
-
return
|
|
332
|
-
fi
|
|
333
|
-
|
|
334
|
-
mkdir -p "$AGENTS_DIR/plugins"
|
|
335
|
-
jq . "$codex_marketplace" >/dev/null
|
|
336
|
-
|
|
337
|
-
if [[ -f "$user_codex_marketplace" ]]; then
|
|
338
|
-
if ! jq empty "$user_codex_marketplace" 2>/dev/null; then
|
|
339
|
-
err "Skipping marketplace sync: $user_codex_marketplace is not valid JSON. Fix or delete it."
|
|
340
|
-
return
|
|
341
|
-
fi
|
|
342
|
-
cp "$user_codex_marketplace" "$user_codex_marketplace.bak"
|
|
343
|
-
if ! jq -s '
|
|
344
|
-
.[0] as $repo | .[1] as $user |
|
|
345
|
-
($user * {name: ($user.name // $repo.name), interface: ($user.interface // $repo.interface)}) |
|
|
346
|
-
.plugins = (
|
|
347
|
-
(($user.plugins // []) + ($repo.plugins // []))
|
|
348
|
-
| reverse
|
|
349
|
-
| unique_by(.name)
|
|
350
|
-
| reverse
|
|
351
|
-
)
|
|
352
|
-
' "$codex_marketplace" "$user_codex_marketplace" > "$user_codex_marketplace.tmp"; then
|
|
353
|
-
rm -f "$user_codex_marketplace.tmp"
|
|
354
|
-
err "jq marketplace merge failed — marketplace unchanged (backup at marketplace.json.bak)"
|
|
355
|
-
return
|
|
356
|
-
fi
|
|
357
|
-
mv "$user_codex_marketplace.tmp" "$user_codex_marketplace"
|
|
358
|
-
log "Codex marketplace merged (backup at marketplace.json.bak)"
|
|
359
|
-
else
|
|
360
|
-
cp "$codex_marketplace" "$user_codex_marketplace"
|
|
361
|
-
log "Codex marketplace installed"
|
|
362
|
-
fi
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
codex::_marketplace_source() {
|
|
366
|
-
local marketplace="$1"
|
|
367
|
-
local config_file="$2"
|
|
368
|
-
|
|
369
|
-
[[ -f "$config_file" ]] || return
|
|
370
|
-
|
|
371
|
-
awk -v marketplace="$marketplace" '
|
|
372
|
-
$0 == "[marketplaces." marketplace "]" {
|
|
373
|
-
in_marketplace = 1
|
|
374
|
-
next
|
|
375
|
-
}
|
|
376
|
-
/^\[/ {
|
|
377
|
-
in_marketplace = 0
|
|
378
|
-
}
|
|
379
|
-
in_marketplace && /^[[:space:]]*source[[:space:]]*=/ {
|
|
380
|
-
source = $0
|
|
381
|
-
sub(/^[^=]+=[[:space:]]*/, "", source)
|
|
382
|
-
sub(/[[:space:]]*#.*/, "", source)
|
|
383
|
-
gsub(/^"|"$/, "", source)
|
|
384
|
-
print source
|
|
385
|
-
exit
|
|
386
|
-
}
|
|
387
|
-
' "$config_file"
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
codex::remove_legacy_docks_marketplace() {
|
|
391
|
-
if [[ "$DRY_RUN" -eq 1 ]]; then
|
|
392
|
-
echo "[dry-run] remove legacy configured Codex Docks marketplace when personal marketplace is deployed"
|
|
393
|
-
return
|
|
394
|
-
fi
|
|
395
|
-
|
|
396
|
-
if ! command -v codex >/dev/null 2>&1; then
|
|
397
|
-
return
|
|
398
|
-
fi
|
|
399
|
-
|
|
400
|
-
case "$(codex::_marketplace_source docks "$CODEX_DIR/config.toml")" in
|
|
401
|
-
"https://github.com/DocksDocks/docks.git"|"DocksDocks/docks")
|
|
402
|
-
if codex plugin marketplace remove docks >/dev/null 2>&1; then
|
|
403
|
-
log "Removed legacy configured Codex Docks marketplace; using personal marketplace file"
|
|
404
|
-
else
|
|
405
|
-
warn "Failed to remove legacy configured Codex Docks marketplace"
|
|
406
|
-
fi ;;
|
|
407
|
-
*) ;;
|
|
408
|
-
esac
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
codex::_first_line() {
|
|
412
|
-
local text="$1"
|
|
413
|
-
text="${text%%$'\n'*}"
|
|
414
|
-
printf '%s\n' "$text"
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
codex::_standalone_install_command() {
|
|
418
|
-
printf 'tmp=$(mktemp) && curl -fsSL https://chatgpt.com/codex/install.sh -o "$tmp" && CODEX_NON_INTERACTIVE=1 sh "$tmp"\n'
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
codex::_manual_plugin_refresh_command() {
|
|
422
|
-
local codex_settings="$1"
|
|
423
|
-
local first_plugin
|
|
424
|
-
|
|
425
|
-
first_plugin="$(codex::_enabled_plugin_ids "$codex_settings" | head -n 1)"
|
|
426
|
-
if [[ -n "$first_plugin" ]]; then
|
|
427
|
-
printf 'codex plugin add %s\n' "$first_plugin"
|
|
428
|
-
else
|
|
429
|
-
printf 'codex plugin add <plugin@marketplace>\n'
|
|
430
|
-
fi
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
codex::_enabled_plugin_ids() {
|
|
434
|
-
local codex_settings="$1"
|
|
435
|
-
|
|
436
|
-
[[ -f "$codex_settings" ]] || return
|
|
437
|
-
|
|
438
|
-
awk '
|
|
439
|
-
function flush_plugin() {
|
|
440
|
-
if (plugin != "" && enabled == 1) print plugin
|
|
441
|
-
}
|
|
442
|
-
/^\[plugins\."[^"]+"\][[:space:]]*$/ {
|
|
443
|
-
flush_plugin()
|
|
444
|
-
plugin = $0
|
|
445
|
-
sub(/^\[plugins\."/,"",plugin)
|
|
446
|
-
sub(/"\][[:space:]]*$/,"",plugin)
|
|
447
|
-
enabled = 0
|
|
448
|
-
next
|
|
449
|
-
}
|
|
450
|
-
/^\[/ {
|
|
451
|
-
flush_plugin()
|
|
452
|
-
plugin = ""
|
|
453
|
-
enabled = 0
|
|
454
|
-
next
|
|
455
|
-
}
|
|
456
|
-
plugin != "" && /^[[:space:]]*enabled[[:space:]]*=[[:space:]]*true([[:space:]]*(#.*)?)?$/ {
|
|
457
|
-
enabled = 1
|
|
458
|
-
}
|
|
459
|
-
END {
|
|
460
|
-
flush_plugin()
|
|
461
|
-
}
|
|
462
|
-
' "$codex_settings"
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
codex::sync_plugins() {
|
|
466
|
-
local codex_settings="$1"
|
|
467
|
-
local plugin_id refreshed_plugins=0 failed=0 add_out failure_line
|
|
468
|
-
|
|
469
|
-
if [[ "$DRY_RUN" -eq 1 ]]; then
|
|
470
|
-
echo "[dry-run] add enabled Codex plugins from SoT"
|
|
471
|
-
return
|
|
472
|
-
fi
|
|
473
|
-
|
|
474
|
-
if ! command -v codex >/dev/null 2>&1; then
|
|
475
|
-
warn "codex CLI not in PATH - deployed config/marketplace only; install current standalone Codex with: $(codex::_standalone_install_command); then run: $(codex::_manual_plugin_refresh_command "$codex_settings")"
|
|
476
|
-
return
|
|
477
|
-
fi
|
|
478
|
-
|
|
479
|
-
while IFS= read -r plugin_id; do
|
|
480
|
-
[[ -z "$plugin_id" ]] && continue
|
|
481
|
-
|
|
482
|
-
if add_out=$(codex plugin add "$plugin_id" 2>&1); then
|
|
483
|
-
refreshed_plugins=$((refreshed_plugins + 1))
|
|
484
|
-
elif [[ "$add_out" == *"could not find a Codex CLI binary"* ]]; then
|
|
485
|
-
warn "Codex plugin refresh hit a stale launcher/wrapper on PATH - install current standalone Codex with: $(codex::_standalone_install_command)"
|
|
486
|
-
failed=$((failed + 1))
|
|
487
|
-
else
|
|
488
|
-
failure_line="$(codex::_first_line "$add_out")"
|
|
489
|
-
warn "Codex plugin refresh failed for $plugin_id: ${failure_line:-unknown error}; run manually: codex plugin add $plugin_id"
|
|
490
|
-
failed=$((failed + 1))
|
|
491
|
-
fi
|
|
492
|
-
done < <(codex::_enabled_plugin_ids "$codex_settings")
|
|
493
|
-
|
|
494
|
-
if [[ "$refreshed_plugins" -gt 0 ]]; then
|
|
495
|
-
log "Codex plugins synced (plugins: ~$refreshed_plugins)"
|
|
496
|
-
fi
|
|
497
|
-
if [[ "$failed" -gt 0 ]]; then
|
|
498
|
-
warn "$failed Codex plugin operation(s) failed — re-run sync or install manually"
|
|
499
|
-
fi
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
codex::summary() {
|
|
503
|
-
local codex_config codex_plugin_count
|
|
504
|
-
|
|
505
|
-
[[ "${CODEX_SYNCED:-0}" -eq 1 ]] || return
|
|
506
|
-
echo "Codex: ${CODEX_DIR:-$HOME/.codex}"
|
|
507
|
-
if [[ "$DRY_RUN" -eq 0 ]]; then
|
|
508
|
-
codex_config="${CODEX_DIR:-$HOME/.codex}/config.toml"
|
|
509
|
-
codex_plugin_count=$(codex::_enabled_plugin_ids "$codex_config" | grep -c . || true)
|
|
510
|
-
codex_plugin_count=${codex_plugin_count:-0}
|
|
511
|
-
echo "Codex plugins: $codex_plugin_count enabled in config.toml"
|
|
512
|
-
fi
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
codex::next_steps() {
|
|
516
|
-
[[ "${CODEX_SYNCED:-0}" -eq 1 ]] || return
|
|
517
|
-
echo "Restart Codex to load any refreshed plugins, skills, or tools."
|
|
518
|
-
}
|