@oxy-hq/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +163 -0
- package/dist/main.mjs +6688 -0
- package/dist/main.mjs.map +1 -0
- package/dist/mcp-VdontkUr.mjs +316 -0
- package/dist/mcp-VdontkUr.mjs.map +1 -0
- package/json-schemas/agent-test.json +104 -0
- package/json-schemas/agentic.json +406 -0
- package/json-schemas/app.json +1884 -0
- package/json-schemas/config.json +2215 -0
- package/json-schemas/flow-test.json +124 -0
- package/json-schemas/global-semantics.json +782 -0
- package/json-schemas/workflow.json +1907 -0
- package/package.json +65 -0
- package/skills/oxy-airway-pipelines/SKILL.md +338 -0
- package/skills/oxy-automations/SKILL.md +67 -0
- package/skills/oxy-cli/SKILL.md +134 -0
- package/skills/oxy-custom-apps/SKILL.md +330 -0
- package/skills/oxy-data-architecture/SKILL.md +225 -0
- package/skills/oxy-semantic-modeling/SKILL.md +216 -0
- package/template/.gitattributes +2 -0
- package/template/.github/scripts/ci-publish.sh +77 -0
- package/template/.github/scripts/discover-apps.sh +175 -0
- package/template/.github/scripts/publish-env.sh +55 -0
- package/template/.github/workflows/publish.yaml +653 -0
- package/template/.github/workflows/validate.yaml +264 -0
- package/template/.nvmrc +1 -0
- package/template/.oxyc-managed +297 -0
- package/template/README.md +414 -0
- package/template/_gitignore +32 -0
- package/template/apps/.gitkeep +0 -0
- package/template/config.yml +53 -0
- package/template/memory/.gitkeep +0 -0
- package/template/package.json +17 -0
- package/template/pipelines/.gitkeep +0 -0
- package/template/pnpm-workspace.yaml +32 -0
- package/template/scripts/dev.sh +443 -0
- package/template/semantics/topics/.gitkeep +0 -0
- package/template/semantics/views/.gitkeep +0 -0
- package/template/workflows/.gitkeep +0 -0
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Run one of this repo's custom apps on your machine, against a cloud oxy's
|
|
3
|
+
# real data.
|
|
4
|
+
#
|
|
5
|
+
# It is two processes, and neither is guessable from the outside — which is
|
|
6
|
+
# why this file exists at all. Before it, running an app locally needed three
|
|
7
|
+
# commands in two terminals and knowledge that was written down nowhere:
|
|
8
|
+
#
|
|
9
|
+
# oxy proxy --env <env> background, listens on :3000. Forwards the app's
|
|
10
|
+
# /api calls to the cloud, signed with the token
|
|
11
|
+
# `oxy login --env <env>` cached.
|
|
12
|
+
# pnpm dev (in the app) foreground, Vite on :5173, which proxies /api to
|
|
13
|
+
# :3000. That target is the Vite plugin's default,
|
|
14
|
+
# so the proxy's port is not really negotiable —
|
|
15
|
+
# hence no --port flag here.
|
|
16
|
+
#
|
|
17
|
+
# Started in that order and torn down together. The proxy is killed on EXIT,
|
|
18
|
+
# INT and TERM, because a proxy left holding :3000 after a Ctrl-C makes the
|
|
19
|
+
# NEXT run fail with a port conflict that names nothing anyone remembers.
|
|
20
|
+
#
|
|
21
|
+
# Usage: pnpm dev --env <env> [app] [--allow-writes] [--allow-events] [--yes]
|
|
22
|
+
|
|
23
|
+
set -euo pipefail
|
|
24
|
+
|
|
25
|
+
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
26
|
+
|
|
27
|
+
# Discovery is NOT reimplemented here.
|
|
28
|
+
#
|
|
29
|
+
# `.github/scripts/discover-apps.sh` is this repo's one answer to "which apps
|
|
30
|
+
# does it hold, and where does each build land", and publish.yaml calls the
|
|
31
|
+
# same file — so a dev run and a publish can never disagree about what counts
|
|
32
|
+
# as an app. Calling a script out of .github/ from a dev script reads oddly,
|
|
33
|
+
# and the tidier-looking fix (move it to scripts/, repoint the workflow) trades
|
|
34
|
+
# that oddness for a workflow whose script path is wrong on the first push.
|
|
35
|
+
# The oddness is cheaper. It lives there because that is where the workflow
|
|
36
|
+
# that also uses it looks for it.
|
|
37
|
+
DISCOVER="$root/.github/scripts/discover-apps.sh"
|
|
38
|
+
|
|
39
|
+
# `oxy proxy`'s own default, and what @oxy-hq/vite-plugin proxies /api to.
|
|
40
|
+
PROXY_PORT=3000
|
|
41
|
+
# Vite's default. Printed, never bound by this script.
|
|
42
|
+
DEV_PORT=5173
|
|
43
|
+
|
|
44
|
+
# --- output -----------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
# Every line this script writes goes through one of these, so a reader can
|
|
47
|
+
# always tell our diagnostics from the two child processes' own output.
|
|
48
|
+
say() { printf '%s\n' "$@"; }
|
|
49
|
+
die() {
|
|
50
|
+
printf 'dev: %s\n' "$1" >&2
|
|
51
|
+
shift
|
|
52
|
+
if [ "$#" -gt 0 ]; then printf '%s\n' "$@" >&2; fi
|
|
53
|
+
exit 1
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
usage() {
|
|
57
|
+
cat <<EOF
|
|
58
|
+
Run one of this repo's custom apps locally, against a cloud oxy's real data.
|
|
59
|
+
|
|
60
|
+
pnpm dev --env <env> [app] [--allow-writes] [--allow-events] [--yes]
|
|
61
|
+
|
|
62
|
+
--env <env> REQUIRED. Which oxy to proxy to. There is deliberately no
|
|
63
|
+
default: \`oxy proxy\`'s own default is production, and a
|
|
64
|
+
\`pnpm dev\` that silently reads production is not something
|
|
65
|
+
this repo will do. One of:
|
|
66
|
+
dev staging production
|
|
67
|
+
a full URL (--env https://acme.oxygen-hq.com), or any name
|
|
68
|
+
the app's own oxy-app.json declares under "environments".
|
|
69
|
+
[app] Which app to run, when this repo holds more than one. Its
|
|
70
|
+
directory (apps/acme/reports) or just its name (reports).
|
|
71
|
+
--allow-writes Forward side-effecting calls instead of holding them.
|
|
72
|
+
--allow-events Forward tracking events instead of dropping them.
|
|
73
|
+
--yes Confirm that --env production is really what you meant.
|
|
74
|
+
-h, --help This text.
|
|
75
|
+
|
|
76
|
+
First run, once per env: oxy login --env <env>
|
|
77
|
+
EOF
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# --- arguments --------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
env_name=""
|
|
83
|
+
app_arg=""
|
|
84
|
+
allow_writes=0
|
|
85
|
+
allow_events=0
|
|
86
|
+
confirm_yes=0
|
|
87
|
+
|
|
88
|
+
while [ "$#" -gt 0 ]; do
|
|
89
|
+
case "$1" in
|
|
90
|
+
--env)
|
|
91
|
+
if [ "$#" -lt 2 ] || [ -z "$2" ]; then
|
|
92
|
+
die "--env needs a value." "Run 'pnpm dev --help' for the choices."
|
|
93
|
+
fi
|
|
94
|
+
env_name="$2"
|
|
95
|
+
shift 2
|
|
96
|
+
;;
|
|
97
|
+
--env=*)
|
|
98
|
+
env_name="${1#--env=}"
|
|
99
|
+
if [ -z "$env_name" ]; then
|
|
100
|
+
die "--env needs a value." "Run 'pnpm dev --help' for the choices."
|
|
101
|
+
fi
|
|
102
|
+
shift
|
|
103
|
+
;;
|
|
104
|
+
--allow-writes) allow_writes=1; shift ;;
|
|
105
|
+
--allow-events) allow_events=1; shift ;;
|
|
106
|
+
--yes|-y) confirm_yes=1; shift ;;
|
|
107
|
+
-h|--help) usage; exit 0 ;;
|
|
108
|
+
--) shift ;;
|
|
109
|
+
-*)
|
|
110
|
+
die "unknown option: $1" "Run 'pnpm dev --help' to see what this accepts."
|
|
111
|
+
;;
|
|
112
|
+
*)
|
|
113
|
+
if [ -n "$app_arg" ]; then
|
|
114
|
+
die "more than one app named: '$app_arg' and '$1'." \
|
|
115
|
+
"This runs one app at a time."
|
|
116
|
+
fi
|
|
117
|
+
app_arg="$1"
|
|
118
|
+
shift
|
|
119
|
+
;;
|
|
120
|
+
esac
|
|
121
|
+
done
|
|
122
|
+
|
|
123
|
+
# --- teardown ---------------------------------------------------------------
|
|
124
|
+
#
|
|
125
|
+
# Armed BEFORE anything is started, so a temp directory is never left behind
|
|
126
|
+
# by an early exit either. `proxy_pid` stays empty until there is something to
|
|
127
|
+
# kill, and cleanup is idempotent — the INT and TERM handlers exit, which runs
|
|
128
|
+
# the EXIT handler on top of them.
|
|
129
|
+
|
|
130
|
+
tmpdir="$(mktemp -d)"
|
|
131
|
+
proxy_pid=""
|
|
132
|
+
|
|
133
|
+
# Has this child finished? An empty state means it is gone from the process
|
|
134
|
+
# table; `Z` means it exited and is waiting for this shell to reap it. `kill -0`
|
|
135
|
+
# can tell neither from a live process — it succeeds on a zombie — which is why
|
|
136
|
+
# this asks the table instead.
|
|
137
|
+
process_finished() {
|
|
138
|
+
local state
|
|
139
|
+
state="$(ps -o state= -p "$1" 2>/dev/null || true)"
|
|
140
|
+
state="${state// /}"
|
|
141
|
+
case "$state" in
|
|
142
|
+
""|Z*) return 0 ;;
|
|
143
|
+
esac
|
|
144
|
+
return 1
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
cleanup() {
|
|
148
|
+
if [ -n "$proxy_pid" ]; then
|
|
149
|
+
# The proxy is the whole reason this trap exists. TERM, then reap it, so
|
|
150
|
+
# :3000 is actually released before this shell returns to the prompt.
|
|
151
|
+
kill "$proxy_pid" 2>/dev/null || true
|
|
152
|
+
|
|
153
|
+
# Bounded, and the bound is the point. A bare `wait` here is unbounded, so
|
|
154
|
+
# a proxy that ignores SIGTERM holds the terminal forever — which is WORSE
|
|
155
|
+
# than leaking one: the person is now stuck in their shell rather than
|
|
156
|
+
# merely running a stale process they can kill later. Ask for ~5s, then
|
|
157
|
+
# stop asking.
|
|
158
|
+
local ticks=0
|
|
159
|
+
while [ "$ticks" -lt 50 ] && ! process_finished "$proxy_pid"; do
|
|
160
|
+
sleep 0.1
|
|
161
|
+
ticks=$((ticks + 1))
|
|
162
|
+
done
|
|
163
|
+
if ! process_finished "$proxy_pid"; then
|
|
164
|
+
printf 'dev: the proxy (pid %s) ignored SIGTERM for 5s; sending SIGKILL.\n' \
|
|
165
|
+
"$proxy_pid" >&2
|
|
166
|
+
kill -9 "$proxy_pid" 2>/dev/null || true
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
wait "$proxy_pid" 2>/dev/null || true
|
|
170
|
+
proxy_pid=""
|
|
171
|
+
fi
|
|
172
|
+
rm -rf "$tmpdir"
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
# The EXIT handler is spelled bare rather than quoted so that shellcheck can
|
|
176
|
+
# see cleanup is reachable (SC2329 otherwise reports it as dead code, and this
|
|
177
|
+
# suite treats a shellcheck finding as a failure). The signal handlers must
|
|
178
|
+
# exit as well as clean up: a returning INT handler would drop the shell back
|
|
179
|
+
# where it was, with the proxy already dead.
|
|
180
|
+
#
|
|
181
|
+
# What each of the three actually buys, measured rather than assumed — bash 3.2,
|
|
182
|
+
# signal delivered while this script sits in the liveness `sleep` below:
|
|
183
|
+
#
|
|
184
|
+
# EXIT does the work on every path tried, INCLUDING death by an untrapped
|
|
185
|
+
# SIGTERM: bash runs it there too. It is the one that matters.
|
|
186
|
+
# INT load-bearing, and not for the reason it looks. A job started in the
|
|
187
|
+
# background inherits SIGINT set to IGNORE, so WITHOUT this line a
|
|
188
|
+
# SIGINT aimed at this script alone is discarded — the script sails on
|
|
189
|
+
# and starts the dev server the person just tried to stop (measured:
|
|
190
|
+
# exit 0, no teardown). Installing the trap un-ignores the signal.
|
|
191
|
+
# TERM defensive, and honestly so: every window tried already produced 143
|
|
192
|
+
# and a clean teardown through EXIT. It is kept because it makes the
|
|
193
|
+
# status explicit instead of leaning on bash running an EXIT handler
|
|
194
|
+
# for a fatal signal, which is not a portable guarantee — but there is
|
|
195
|
+
# no test for it, because none could fail.
|
|
196
|
+
trap cleanup EXIT
|
|
197
|
+
trap 'cleanup; exit 130' INT
|
|
198
|
+
trap 'cleanup; exit 143' TERM
|
|
199
|
+
|
|
200
|
+
# --- which apps does this repo hold? ----------------------------------------
|
|
201
|
+
|
|
202
|
+
if [ ! -f "$DISCOVER" ]; then
|
|
203
|
+
die "this repo has no $DISCOVER." \
|
|
204
|
+
"That script is what both this and .github/workflows/publish.yaml use to" \
|
|
205
|
+
"find apps. Restore it from the customer-tooling template."
|
|
206
|
+
fi
|
|
207
|
+
|
|
208
|
+
discover_status=0
|
|
209
|
+
bash "$DISCOVER" "$root" >"$tmpdir/apps" 2>"$tmpdir/discover.err" || discover_status=$?
|
|
210
|
+
|
|
211
|
+
if [ "$discover_status" -ne 0 ]; then
|
|
212
|
+
cat "$tmpdir/discover.err" >&2
|
|
213
|
+
die "could not work out which apps this repo holds — see above."
|
|
214
|
+
fi
|
|
215
|
+
|
|
216
|
+
# discover-apps.sh speaks GitHub Actions. On the zero-app path it prints
|
|
217
|
+
# "publish: no custom apps under apps/ ...", which is the wrong words for a
|
|
218
|
+
# `pnpm dev` — this script says its own thing below. Everything else it puts
|
|
219
|
+
# on stderr is a real diagnostic (an app directory named `dist` that was
|
|
220
|
+
# skipped, say) and has to survive.
|
|
221
|
+
grep_status=0
|
|
222
|
+
grep -v '^publish: ' "$tmpdir/discover.err" >&2 || grep_status=$?
|
|
223
|
+
# grep exits 1 when it selects NO lines, which is the ordinary answer here:
|
|
224
|
+
# the only line may be the one just filtered out, or there may have been no
|
|
225
|
+
# stderr at all. Exactly that status is tolerated. Anything above it is grep
|
|
226
|
+
# itself failing, and swallowing that would hide a real diagnostic — the
|
|
227
|
+
# failure mode a bare `|| true` produces.
|
|
228
|
+
if [ "$grep_status" -gt 1 ]; then
|
|
229
|
+
die "could not read the discovery diagnostics (grep exited $grep_status)."
|
|
230
|
+
fi
|
|
231
|
+
|
|
232
|
+
app_dirs=()
|
|
233
|
+
while IFS=$'\t' read -r dir _; do
|
|
234
|
+
[ -n "$dir" ] || continue
|
|
235
|
+
app_dirs+=("$dir")
|
|
236
|
+
done < "$tmpdir/apps"
|
|
237
|
+
|
|
238
|
+
# Zero apps is checked FIRST, ahead of --env and ahead of every tool check: a
|
|
239
|
+
# freshly scaffolded repo has none, someone will type `pnpm dev` in it on day
|
|
240
|
+
# one, and "here is how to add an app" is a more useful answer than a lecture
|
|
241
|
+
# about a flag they have nothing to use it on. Exit 0 — day one is green.
|
|
242
|
+
if [ "${#app_dirs[@]}" -eq 0 ]; then
|
|
243
|
+
say "" \
|
|
244
|
+
"dev: this repo has no custom apps yet, so there is nothing to run." \
|
|
245
|
+
"" \
|
|
246
|
+
"An app is a React + Vite bundle in its own directory under apps/:" \
|
|
247
|
+
"" \
|
|
248
|
+
" apps/<org>/<app>/ e.g. apps/acme/sales-dashboard/" \
|
|
249
|
+
" oxy-app.json \"slug\" and \"orgSlug\" — the app's identity" \
|
|
250
|
+
" package.json with a \"dev\" script (Vite)" \
|
|
251
|
+
" src/…" \
|
|
252
|
+
"" \
|
|
253
|
+
"Scaffold one, from the repo root:" \
|
|
254
|
+
"" \
|
|
255
|
+
" pnpm dlx @oxy-hq/create-oxy-app apps/<org>/<app>" \
|
|
256
|
+
" pnpm install # writes pnpm-lock.yaml — commit it" \
|
|
257
|
+
" pnpm dev --env dev" \
|
|
258
|
+
""
|
|
259
|
+
exit 0
|
|
260
|
+
fi
|
|
261
|
+
|
|
262
|
+
# --- which one? -------------------------------------------------------------
|
|
263
|
+
|
|
264
|
+
list_apps() {
|
|
265
|
+
local d
|
|
266
|
+
for d in "${app_dirs[@]}"; do
|
|
267
|
+
printf ' %s\n' "$d"
|
|
268
|
+
done
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
app_dir=""
|
|
272
|
+
if [ -n "$app_arg" ]; then
|
|
273
|
+
# Tab completion adds the trailing slash, and typing the whole path is the
|
|
274
|
+
# obvious thing to do; so is typing just the name.
|
|
275
|
+
want="${app_arg%/}"
|
|
276
|
+
matches=()
|
|
277
|
+
for d in "${app_dirs[@]}"; do
|
|
278
|
+
if [ "$want" = "$d" ] || [ "$want" = "${d#apps/}" ] || [ "$want" = "${d##*/}" ]; then
|
|
279
|
+
matches+=("$d")
|
|
280
|
+
fi
|
|
281
|
+
done
|
|
282
|
+
if [ "${#matches[@]}" -eq 0 ]; then
|
|
283
|
+
die "no app called '$app_arg' in this repo. It holds:" "$(list_apps)"
|
|
284
|
+
fi
|
|
285
|
+
if [ "${#matches[@]}" -gt 1 ]; then
|
|
286
|
+
printf "dev: '%s' matches more than one app — name one by its directory:\n" "$app_arg" >&2
|
|
287
|
+
printf ' %s\n' "${matches[@]}" >&2
|
|
288
|
+
exit 1
|
|
289
|
+
fi
|
|
290
|
+
app_dir="${matches[0]}"
|
|
291
|
+
elif [ "${#app_dirs[@]}" -eq 1 ]; then
|
|
292
|
+
app_dir="${app_dirs[0]}"
|
|
293
|
+
else
|
|
294
|
+
# Refusing rather than picking the first: which app you get would then depend
|
|
295
|
+
# on sort order, and the wrong one starts silently and looks like the right
|
|
296
|
+
# one until you notice the data.
|
|
297
|
+
printf 'dev: this repo holds %s apps — name the one to run:\n' "${#app_dirs[@]}" >&2
|
|
298
|
+
list_apps >&2
|
|
299
|
+
printf '\n pnpm dev --env dev %s\n' "${app_dirs[0]##*/}" >&2
|
|
300
|
+
exit 1
|
|
301
|
+
fi
|
|
302
|
+
|
|
303
|
+
if [ ! -f "$root/$app_dir/package.json" ]; then
|
|
304
|
+
die "$app_dir has an oxy-app.json but no package.json, so there is no dev server to start."
|
|
305
|
+
fi
|
|
306
|
+
|
|
307
|
+
# --- which oxy? -------------------------------------------------------------
|
|
308
|
+
|
|
309
|
+
if [ -z "$env_name" ]; then
|
|
310
|
+
die "--env is required." \
|
|
311
|
+
"" \
|
|
312
|
+
" pnpm dev --env dev ${app_dir##*/}" \
|
|
313
|
+
"" \
|
|
314
|
+
"It says which cloud oxy the proxy reads from: dev, staging, production," \
|
|
315
|
+
"a full URL (--env https://acme.oxygen-hq.com), or a name the app's" \
|
|
316
|
+
"oxy-app.json declares under \"environments\"." \
|
|
317
|
+
"" \
|
|
318
|
+
"There is no default on purpose. 'oxy proxy' defaults to PRODUCTION, and" \
|
|
319
|
+
"a 'pnpm dev' that silently serves production data is not a thing this" \
|
|
320
|
+
"repo will do quietly."
|
|
321
|
+
fi
|
|
322
|
+
|
|
323
|
+
if [ "$env_name" = "production" ] && [ "$confirm_yes" -ne 1 ]; then
|
|
324
|
+
# `oxy proxy` refuses a production target without --yes on its own. Catching
|
|
325
|
+
# it here matters because the proxy runs in the BACKGROUND: its refusal (or
|
|
326
|
+
# its prompt) would scroll past under Vite's banner, and the app would just
|
|
327
|
+
# fail every request with nothing on screen explaining why.
|
|
328
|
+
die "--env production is real customer data." \
|
|
329
|
+
"" \
|
|
330
|
+
" pnpm dev --env production --yes ${app_dir##*/}" \
|
|
331
|
+
"" \
|
|
332
|
+
"Add --yes if that is genuinely what you want. For everyday work use" \
|
|
333
|
+
"--env dev or --env staging."
|
|
334
|
+
fi
|
|
335
|
+
|
|
336
|
+
# --- preconditions ----------------------------------------------------------
|
|
337
|
+
|
|
338
|
+
if ! command -v oxy >/dev/null 2>&1; then
|
|
339
|
+
die "the 'oxy' CLI is not on your PATH, and the proxy is oxy." \
|
|
340
|
+
"" \
|
|
341
|
+
" curl -sSfL https://get.oxy.tech | bash" \
|
|
342
|
+
"" \
|
|
343
|
+
"It installs to ~/.local/bin; add that to PATH if it is not already there."
|
|
344
|
+
fi
|
|
345
|
+
|
|
346
|
+
if ! command -v pnpm >/dev/null 2>&1; then
|
|
347
|
+
die "'pnpm' is not on your PATH, and it is what runs the app's dev server." \
|
|
348
|
+
"This repo is pnpm-only — never npm or yarn." \
|
|
349
|
+
"" \
|
|
350
|
+
" corepack enable pnpm"
|
|
351
|
+
fi
|
|
352
|
+
|
|
353
|
+
# Who, if anyone, is already listening on a port. Empty when the port is free,
|
|
354
|
+
# when lsof is not installed, or when lsof cannot tell — this is a diagnostic,
|
|
355
|
+
# and it must never be the thing that stops a run.
|
|
356
|
+
port_holder=""
|
|
357
|
+
read_port_holder() {
|
|
358
|
+
local out="" st=0
|
|
359
|
+
command -v lsof >/dev/null 2>&1 || return 0
|
|
360
|
+
out="$(lsof -nP -iTCP:"$1" -sTCP:LISTEN 2>/dev/null)" || st=$?
|
|
361
|
+
# lsof exits 1 for "nothing matched" AND for its own errors, and there is no
|
|
362
|
+
# way to tell them apart, so both read as "nothing to report".
|
|
363
|
+
if [ "$st" -ne 0 ] || [ -z "$out" ]; then
|
|
364
|
+
return 0
|
|
365
|
+
fi
|
|
366
|
+
port_holder="$(printf '%s\n' "$out" |
|
|
367
|
+
awk 'NR > 1 { print $1 " (pid " $2 ")" }' | LC_ALL=C sort -u | tr '\n' ' ')"
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
read_port_holder "$PROXY_PORT"
|
|
371
|
+
if [ -n "$port_holder" ]; then
|
|
372
|
+
die "port $PROXY_PORT is already taken by: $port_holder" \
|
|
373
|
+
"" \
|
|
374
|
+
"'oxy proxy' has to listen there — the app's dev server proxies /api to" \
|
|
375
|
+
"that exact port — so this cannot just move out of the way. Usually it is" \
|
|
376
|
+
"a proxy from an earlier run that outlived its terminal, or a local" \
|
|
377
|
+
"'oxy serve'. Stop it and try again:" \
|
|
378
|
+
"" \
|
|
379
|
+
" lsof -nP -iTCP:$PROXY_PORT -sTCP:LISTEN"
|
|
380
|
+
fi
|
|
381
|
+
|
|
382
|
+
# --- go ---------------------------------------------------------------------
|
|
383
|
+
|
|
384
|
+
proxy_args=(proxy --env "$env_name")
|
|
385
|
+
if [ "$allow_writes" -eq 1 ]; then proxy_args+=(--allow-writes); fi
|
|
386
|
+
if [ "$allow_events" -eq 1 ]; then proxy_args+=(--allow-events); fi
|
|
387
|
+
if [ "$confirm_yes" -eq 1 ]; then proxy_args+=(--yes); fi
|
|
388
|
+
|
|
389
|
+
say "" \
|
|
390
|
+
" app $app_dir" \
|
|
391
|
+
" env $env_name" \
|
|
392
|
+
" proxy oxy ${proxy_args[*]} -> http://localhost:$PROXY_PORT" \
|
|
393
|
+
" dev pnpm dev in $app_dir -> http://localhost:$DEV_PORT" \
|
|
394
|
+
""
|
|
395
|
+
|
|
396
|
+
# Said once, up front, and not negotiable: "my function isn't running" with no
|
|
397
|
+
# visible cause is an hour of someone's day. The proxy holds these back so an
|
|
398
|
+
# afternoon of local clicking cannot change cloud state.
|
|
399
|
+
if [ "$allow_writes" -eq 1 ]; then
|
|
400
|
+
say " ! --allow-writes: side-effecting calls (/fn handlers, agent runs," \
|
|
401
|
+
" automation runs) are being FORWARDED to $env_name for real."
|
|
402
|
+
else
|
|
403
|
+
say " Side-effecting calls — /fn handlers, agent runs, automation runs — are" \
|
|
404
|
+
" HELD, not sent. A function that 'does nothing' is usually this, not a" \
|
|
405
|
+
" bug. Pass --allow-writes to forward them."
|
|
406
|
+
fi
|
|
407
|
+
if [ "$allow_events" -eq 1 ]; then
|
|
408
|
+
say " ! --allow-events: tracking events are being FORWARDED to $env_name."
|
|
409
|
+
else
|
|
410
|
+
say " Tracking events are DROPPED, so local clicking never shows up in the" \
|
|
411
|
+
" customer's analytics. Pass --allow-events to forward them."
|
|
412
|
+
fi
|
|
413
|
+
say "" " Ctrl-C stops both. If requests come back 401, run: oxy login --env $env_name" ""
|
|
414
|
+
|
|
415
|
+
oxy "${proxy_args[@]}" &
|
|
416
|
+
proxy_pid=$!
|
|
417
|
+
|
|
418
|
+
# A proxy that dies on the first second — no cached token, a bad --env — would
|
|
419
|
+
# otherwise leave Vite up and every request failing, with the reason scrolled
|
|
420
|
+
# off the top. One second is enough to catch the immediate exits and cannot
|
|
421
|
+
# produce a false alarm: a proxy still running after it is simply left alone.
|
|
422
|
+
sleep 1
|
|
423
|
+
if process_finished "$proxy_pid"; then
|
|
424
|
+
proxy_pid=""
|
|
425
|
+
die "'oxy proxy --env $env_name' exited immediately — see its output above." \
|
|
426
|
+
"" \
|
|
427
|
+
"Most often there is no cached token for that env yet:" \
|
|
428
|
+
"" \
|
|
429
|
+
" oxy login --env $env_name"
|
|
430
|
+
fi
|
|
431
|
+
|
|
432
|
+
# The dev server, in the foreground, and the LAST statement in this file.
|
|
433
|
+
#
|
|
434
|
+
# A subshell so the script keeps its own cwd for the teardown, and `exec` so
|
|
435
|
+
# the dev server IS that process — a wrapper in between would swallow the exit
|
|
436
|
+
# status a Ctrl-C produces.
|
|
437
|
+
#
|
|
438
|
+
# Deliberately not followed by `exit "$status"`. Under errexit this line's
|
|
439
|
+
# status is already the script's, the EXIT trap still runs and still kills the
|
|
440
|
+
# proxy, and a trailing `exit` costs something real: shellcheck 0.11 stops
|
|
441
|
+
# tracing the EXIT trap past one, and reports `cleanup` as dead code (SC2329) —
|
|
442
|
+
# a finding this repo's suite treats as a failure.
|
|
443
|
+
( cd "$root/$app_dir" && exec pnpm dev )
|
|
File without changes
|
|
File without changes
|
|
File without changes
|