nebula-ai-gateway 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 +23 -0
- package/bin/nebula-gateway +2 -0
- package/bin/nebula-gateway-local +2 -0
- package/package.json +54 -0
- package/src/approval-relay.ts +91 -0
- package/src/auth.ts +334 -0
- package/src/bootstrap.ts +352 -0
- package/src/build-runtime.ts +1087 -0
- package/src/entrypoint.ts +121 -0
- package/src/events.ts +121 -0
- package/src/heartbeat.ts +97 -0
- package/src/index.ts +98 -0
- package/src/local-entrypoint.ts +356 -0
- package/src/real-runtime.ts +319 -0
- package/src/relaunch-script.ts +152 -0
- package/src/runtime.ts +169 -0
- package/src/secrets.ts +38 -0
- package/src/server.ts +598 -0
- package/src/state.ts +103 -0
- package/src/stub-runtime.ts +61 -0
- package/src/upgrade-script.ts +237 -0
package/src/bootstrap.ts
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bootstrap script template for first-cold-start of an nebula harness inside
|
|
3
|
+
* a Mantle Sandbox container. Returned as a string the init/deploy/upgrade
|
|
4
|
+
* commands feed to `provider-client.execInToolbox(id, { command })`.
|
|
5
|
+
*
|
|
6
|
+
* Two modes:
|
|
7
|
+
* - 'git': clones the monorepo + bun install. ~5-8 min cold start. Pins to
|
|
8
|
+
* any branch/SHA.
|
|
9
|
+
* - 'npm': `bun add -g nebula-ai-cli@<version>`. ~30-60 sec cold start.
|
|
10
|
+
* Only published versions.
|
|
11
|
+
*
|
|
12
|
+
* Design constraint: the Daytona toolbox `process/execute` endpoint caps each
|
|
13
|
+
* exec call at ~60s. Whatever install path runs blows that easily, so we
|
|
14
|
+
* detach the slow work into a background subshell via `nohup bash -c '...' &`
|
|
15
|
+
* and return exit 0 immediately. Progress is observable via two files:
|
|
16
|
+
*
|
|
17
|
+
* - `/tmp/nebula-bootstrap-progress.log` (tail-able, line-by-line stages)
|
|
18
|
+
* - `/tmp/nebula-bootstrap-done` (created only on full success, contains harness pid)
|
|
19
|
+
*
|
|
20
|
+
* The caller (`sandbox-provision.ts`) launches the bootstrap, polls for the
|
|
21
|
+
* `done` marker (then for `/bootstrap/pubkey` from the harness HTTP server).
|
|
22
|
+
*
|
|
23
|
+
* Robustness rules:
|
|
24
|
+
* - All variables shell-quote-escaped to defeat injection from operator
|
|
25
|
+
* address or sandbox id (validated upstream, defense-in-depth).
|
|
26
|
+
* - Git mode: always-clone fresh. Daytona occasionally re-uses post-delete
|
|
27
|
+
* volumes whose stale git credential helpers break re-fetch.
|
|
28
|
+
* - Npm mode: `bun add -g nebula-ai-cli@<exact-version>` is idempotent
|
|
29
|
+
* and overwrites. Same version twice = no-op. Different version = clean
|
|
30
|
+
* swap. Lower risk than git's stale-credential failure mode.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
export type BootstrapMode = 'git' | 'npm'
|
|
34
|
+
|
|
35
|
+
export interface BuildBootstrapScriptOpts {
|
|
36
|
+
/** Sandbox UUID returned by provider's createSandbox. */
|
|
37
|
+
sandboxId: string
|
|
38
|
+
/** EIP-191 checksummed operator address. Stored in container env, used by `verifyChatSig`. */
|
|
39
|
+
operatorAddress: string
|
|
40
|
+
/**
|
|
41
|
+
* Bootstrap mode. Defaults to 'npm' (since v0.21.20) because it's ~10x
|
|
42
|
+
* faster. Callers in cli/src always pass `mode` explicitly via
|
|
43
|
+
* `resolveBootstrapMode`; this default is defense-in-depth.
|
|
44
|
+
*/
|
|
45
|
+
mode?: BootstrapMode
|
|
46
|
+
/**
|
|
47
|
+
* Git mode: tag/branch/SHA to clone (e.g. 'v0.15.0', 'main', or commit SHA).
|
|
48
|
+
* Npm mode: ignored (use `packageVersion`).
|
|
49
|
+
*/
|
|
50
|
+
ref: string
|
|
51
|
+
/**
|
|
52
|
+
* Npm mode: the exact published version to install (e.g. '0.21.15').
|
|
53
|
+
* Required when mode='npm'. Ignored in git mode.
|
|
54
|
+
*/
|
|
55
|
+
packageVersion?: string
|
|
56
|
+
/**
|
|
57
|
+
* Public git URL of nebula. Defaults to the canonical hackathon repo.
|
|
58
|
+
* Override only when running against a fork / private mirror. (Git mode only.)
|
|
59
|
+
*/
|
|
60
|
+
repoUrl?: string
|
|
61
|
+
/** Port the harness binds inside the container. Default 8080. */
|
|
62
|
+
port?: number
|
|
63
|
+
/**
|
|
64
|
+
* Extra `apt-get install` packages. Defaults to xvfb + git + ca-certificates
|
|
65
|
+
* + curl + unzip + psmisc. Caller can append.
|
|
66
|
+
*/
|
|
67
|
+
extraAptPackages?: string[]
|
|
68
|
+
/**
|
|
69
|
+
* GitHub PAT for cloning private nebula repos. Embedded in clone URL as
|
|
70
|
+
* `https://x-access-token:TOKEN@github.com/...`. For public repos, leave
|
|
71
|
+
* unset. Token is base64-wrapped inside the inner script (which itself is
|
|
72
|
+
* base64-encoded into the outer command), and the inner script is written
|
|
73
|
+
* to /tmp on the container; ensure the container is single-tenant (Daytona
|
|
74
|
+
* containers are by-operator). Gets cleared from container env after clone.
|
|
75
|
+
* (Git mode only.)
|
|
76
|
+
*/
|
|
77
|
+
githubToken?: string
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface BuildBootstrapScriptResult {
|
|
81
|
+
/** Outer script: launches the inner subshell + returns exit 0. ~1s execution. */
|
|
82
|
+
script: string
|
|
83
|
+
/**
|
|
84
|
+
* Path the caller should poll via `execInToolbox(id, { command: cat <path> })`
|
|
85
|
+
* to detect bootstrap completion. Returns success line `nebula-gateway-pid=<N>`
|
|
86
|
+
* once everything is up; absent until then.
|
|
87
|
+
*/
|
|
88
|
+
doneMarkerPath: string
|
|
89
|
+
/** Path the caller can tail to surface bootstrap progress. */
|
|
90
|
+
progressLogPath: string
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Quote a string for safe inclusion in a single-quoted bash literal.
|
|
95
|
+
* Single-quoted strings forbid `'`; we escape it as `'\''`.
|
|
96
|
+
*/
|
|
97
|
+
function shQuote(s: string): string {
|
|
98
|
+
return `'${s.replace(/'/g, "'\\''")}'`
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const DEFAULT_APT_PACKAGES: readonly string[] = [
|
|
102
|
+
'curl',
|
|
103
|
+
'unzip',
|
|
104
|
+
'ca-certificates',
|
|
105
|
+
'git',
|
|
106
|
+
// psmisc provides `fuser` which the harness launch step uses to free port
|
|
107
|
+
// 8080 if Daytona's snapshot ships a default service squatting on it.
|
|
108
|
+
'psmisc',
|
|
109
|
+
// xvfb retained as headed-browser fallback insurance; agent-browser's
|
|
110
|
+
// installed Chrome-for-Testing runs headless natively but xvfb is cheap
|
|
111
|
+
// (~5MB) and keeps the door open for visual debugging.
|
|
112
|
+
'xvfb',
|
|
113
|
+
] as const
|
|
114
|
+
|
|
115
|
+
const PROGRESS_LOG = '/tmp/nebula-bootstrap-progress.log'
|
|
116
|
+
const DONE_MARKER = '/tmp/nebula-bootstrap-done'
|
|
117
|
+
const FAIL_MARKER = '/tmp/nebula-bootstrap-failed'
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Stage marker bodies emitted as `STAGE: <body>` lines into the progress log.
|
|
121
|
+
* Single source of truth for both the script generator (writes them) and the
|
|
122
|
+
* CLI poll loop (reads them via `extractBootstrapProgressLine` and routes to
|
|
123
|
+
* `BootstrapProgressBox`). Strings are prefixes — the apt/nebula/chrome rows
|
|
124
|
+
* append details after a space.
|
|
125
|
+
*/
|
|
126
|
+
export const BOOTSTRAP_STAGE_MARKERS = {
|
|
127
|
+
aptUpdate: 'updating package index',
|
|
128
|
+
systemDeps: 'installing system deps',
|
|
129
|
+
bunInstall: 'installing bun runtime',
|
|
130
|
+
nebulaInstall: 'installing nebula',
|
|
131
|
+
browserDeps: 'installing chrome for browser tools',
|
|
132
|
+
harnessSpawn: 'starting harness daemon',
|
|
133
|
+
harnessReady: 'harness ready',
|
|
134
|
+
} as const
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Shell literal (NOT a Node path). Where Bun symlinks third-party global bins
|
|
138
|
+
* after `bun add -g`. Don't pass to `path.join` — `$HOME` won't expand.
|
|
139
|
+
*/
|
|
140
|
+
export const BUN_GLOBAL_BIN_SHELL = '$HOME/.bun/install/global/node_modules/.bin'
|
|
141
|
+
|
|
142
|
+
function buildPreambleLines(
|
|
143
|
+
opts: BuildBootstrapScriptOpts,
|
|
144
|
+
modeLabel: string,
|
|
145
|
+
aptList: string,
|
|
146
|
+
): string[] {
|
|
147
|
+
return [
|
|
148
|
+
'#!/bin/bash',
|
|
149
|
+
'set -uo pipefail',
|
|
150
|
+
`exec > ${PROGRESS_LOG} 2>&1`,
|
|
151
|
+
`echo "[$(date -u +%FT%TZ)] bootstrap-start (mode=${modeLabel})"`,
|
|
152
|
+
`echo " sandbox=${opts.sandboxId}"`,
|
|
153
|
+
'retry() {',
|
|
154
|
+
' local L=$1; shift',
|
|
155
|
+
' local n',
|
|
156
|
+
' for n in 1 2 3; do',
|
|
157
|
+
' echo "[$L attempt $n/3]"',
|
|
158
|
+
' "$@" && return 0',
|
|
159
|
+
' [ $n -lt 3 ] && { echo "[$L failed, retry in $((n*5))s]"; sleep $((n*5)); }',
|
|
160
|
+
' done',
|
|
161
|
+
' return 1',
|
|
162
|
+
'}',
|
|
163
|
+
'export DEBIAN_FRONTEND=noninteractive',
|
|
164
|
+
`echo "STAGE: ${BOOTSTRAP_STAGE_MARKERS.aptUpdate}"`,
|
|
165
|
+
`retry 'apt update' sudo -n apt-get update -qq || { echo "apt-update-failed" > ${FAIL_MARKER}; exit 11; }`,
|
|
166
|
+
`echo "STAGE: ${BOOTSTRAP_STAGE_MARKERS.systemDeps} (build-essential, curl, git, xvfb)"`,
|
|
167
|
+
`retry 'apt install' sudo -n apt-get install -y -qq ${aptList} || { echo "apt-install-failed" > ${FAIL_MARKER}; exit 12; }`,
|
|
168
|
+
'install_bun() { curl -fsSL https://bun.sh/install | bash; }',
|
|
169
|
+
'if ! command -v bun >/dev/null 2>&1; then',
|
|
170
|
+
` echo "STAGE: ${BOOTSTRAP_STAGE_MARKERS.bunInstall}"`,
|
|
171
|
+
` retry 'bun binary' install_bun || { echo "bun-install-failed" > ${FAIL_MARKER}; exit 13; }`,
|
|
172
|
+
'fi',
|
|
173
|
+
'export PATH="$HOME/.bun/bin:$PATH"',
|
|
174
|
+
]
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function buildLaunchLines(opts: BuildBootstrapScriptOpts, gatewayLaunchCmd: string): string[] {
|
|
178
|
+
const port = opts.port ?? 8080
|
|
179
|
+
return [
|
|
180
|
+
'mkdir -p "$HOME/nebula-logs" "$HOME/workspace"',
|
|
181
|
+
'',
|
|
182
|
+
`export SANDBOX_ID=${shQuote(opts.sandboxId)}`,
|
|
183
|
+
`export NEBULA_OPERATOR_ADDRESS=${shQuote(opts.operatorAddress)}`,
|
|
184
|
+
`export HARNESS_PORT=${shQuote(String(port))}`,
|
|
185
|
+
"export HARNESS_HOST='0.0.0.0'",
|
|
186
|
+
'',
|
|
187
|
+
`fuser -k ${port}/tcp 2>/dev/null || true`,
|
|
188
|
+
'sleep 2',
|
|
189
|
+
`echo "STAGE: ${BOOTSTRAP_STAGE_MARKERS.harnessSpawn}"`,
|
|
190
|
+
'echo "[launch harness daemon]"',
|
|
191
|
+
'HARNESS_PID=""',
|
|
192
|
+
'HARNESS_OK=0',
|
|
193
|
+
'for h_attempt in 1 2 3; do',
|
|
194
|
+
' echo "[launch attempt $h_attempt/3]"',
|
|
195
|
+
` fuser -k ${port}/tcp 2>/dev/null || true`,
|
|
196
|
+
' sleep 1',
|
|
197
|
+
` nohup ${gatewayLaunchCmd} > "$HOME/nebula-logs/nebula-gateway.log" 2>&1 &`,
|
|
198
|
+
' HARNESS_PID=$!',
|
|
199
|
+
' disown',
|
|
200
|
+
' sleep 10',
|
|
201
|
+
' if kill -0 "$HARNESS_PID" 2>/dev/null; then',
|
|
202
|
+
' HARNESS_OK=1',
|
|
203
|
+
' break',
|
|
204
|
+
' fi',
|
|
205
|
+
' echo "[harness died on attempt $h_attempt, log tail:]"',
|
|
206
|
+
' tail -n 50 "$HOME/nebula-logs/nebula-gateway.log" 2>/dev/null',
|
|
207
|
+
' if [ $h_attempt -lt 3 ]; then',
|
|
208
|
+
' echo "[retrying in 5s]"',
|
|
209
|
+
' sleep 5',
|
|
210
|
+
' fi',
|
|
211
|
+
'done',
|
|
212
|
+
'if [ "$HARNESS_OK" -ne 1 ]; then',
|
|
213
|
+
' echo "[all 3 harness launch attempts failed, full log dump:]"',
|
|
214
|
+
' tail -n 200 "$HOME/nebula-logs/nebula-gateway.log" 2>/dev/null',
|
|
215
|
+
` echo "harness-died-early" > ${FAIL_MARKER}`,
|
|
216
|
+
' exit 18',
|
|
217
|
+
'fi',
|
|
218
|
+
`echo "STAGE: ${BOOTSTRAP_STAGE_MARKERS.harnessReady}"`,
|
|
219
|
+
`echo "nebula-gateway-pid=$HARNESS_PID" > ${DONE_MARKER}`,
|
|
220
|
+
'echo "[$(date -u +%FT%TZ)] bootstrap-done pid=$HARNESS_PID"',
|
|
221
|
+
'',
|
|
222
|
+
]
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function buildGitInnerScript(opts: BuildBootstrapScriptOpts, aptList: string): string {
|
|
226
|
+
const repoUrl = opts.repoUrl ?? 'https://github.com/rstfulzz/nebula.git'
|
|
227
|
+
const cloneUrl = opts.githubToken
|
|
228
|
+
? repoUrl.replace(
|
|
229
|
+
'https://github.com/',
|
|
230
|
+
`https://x-access-token:${opts.githubToken}@github.com/`,
|
|
231
|
+
)
|
|
232
|
+
: repoUrl
|
|
233
|
+
const preamble = buildPreambleLines(opts, 'git', aptList)
|
|
234
|
+
const installLines = [
|
|
235
|
+
`echo " ref=${opts.ref}"`,
|
|
236
|
+
`echo " repo=${repoUrl}"`,
|
|
237
|
+
'NEBULA_DIR="$HOME/nebula"',
|
|
238
|
+
`echo "STAGE: ${BOOTSTRAP_STAGE_MARKERS.nebulaInstall} (git ${opts.ref})"`,
|
|
239
|
+
`git_clone_one() { rm -rf "$NEBULA_DIR"; git clone --depth 1 --branch ${shQuote(opts.ref)} ${shQuote(cloneUrl)} "$NEBULA_DIR"; }`,
|
|
240
|
+
`retry 'git clone' git_clone_one || { echo "git-clone-failed" > ${FAIL_MARKER}; exit 14; }`,
|
|
241
|
+
`cd "$NEBULA_DIR" && git remote set-url origin ${shQuote(repoUrl)}`,
|
|
242
|
+
`retry 'bun deps' bun install --frozen-lockfile || { echo "bun-install-failed" > ${FAIL_MARKER}; exit 17; }`,
|
|
243
|
+
'',
|
|
244
|
+
// Install Chrome-for-Testing for browser tools. `agent-browser install`
|
|
245
|
+
// pulls a Chromium build + Linux system libs (`--with-deps`). `doctor`
|
|
246
|
+
// exits 0 only when the install state is healthy, so re-runs are no-ops
|
|
247
|
+
// on container restarts that share a persisted volume.
|
|
248
|
+
//
|
|
249
|
+
// Invoked via `node_modules/.bin/agent-browser` directly (not `bunx`)
|
|
250
|
+
// because Daytona's `curl bun.sh/install` install path doesn't always
|
|
251
|
+
// ship a `bunx` symlink.
|
|
252
|
+
`echo "STAGE: ${BOOTSTRAP_STAGE_MARKERS.browserDeps}"`,
|
|
253
|
+
'echo "[browser deps]"',
|
|
254
|
+
'if node_modules/.bin/agent-browser doctor >/dev/null 2>&1; then',
|
|
255
|
+
' echo "[browser deps] already installed, skipping"',
|
|
256
|
+
'else',
|
|
257
|
+
` retry 'browser deps' node_modules/.bin/agent-browser install --with-deps || { echo "browser-install-failed" > ${FAIL_MARKER}; exit 19; }`,
|
|
258
|
+
'fi',
|
|
259
|
+
'',
|
|
260
|
+
]
|
|
261
|
+
const launch = buildLaunchLines(opts, 'bun "$NEBULA_DIR/packages/gateway/bin/nebula-gateway"')
|
|
262
|
+
return [...preamble, ...installLines, ...launch].join('\n')
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function buildNpmInnerScript(opts: BuildBootstrapScriptOpts, aptList: string): string {
|
|
266
|
+
if (!opts.packageVersion) {
|
|
267
|
+
throw new Error('buildBootstrapScript: packageVersion is required when mode=npm')
|
|
268
|
+
}
|
|
269
|
+
const preamble = buildPreambleLines(opts, 'npm', aptList)
|
|
270
|
+
const installLines = [
|
|
271
|
+
`echo " package=nebula-ai-cli@${opts.packageVersion}"`,
|
|
272
|
+
`echo "STAGE: ${BOOTSTRAP_STAGE_MARKERS.nebulaInstall} (${opts.packageVersion})"`,
|
|
273
|
+
// Install nebula from npm. `bun add -g <pkg>@<exact-version>` is idempotent
|
|
274
|
+
// and overwrites whatever is in the global store. Atomic on success; on
|
|
275
|
+
// failure the prior version remains (which may be empty on a fresh container).
|
|
276
|
+
`retry 'nebula install' bun add -g ${shQuote(`nebula-ai-cli@${opts.packageVersion}`)} || { echo "nebula-install-failed" > ${FAIL_MARKER}; exit 14; }`,
|
|
277
|
+
// Add Bun's global package binaries to PATH so nebula-gateway + agent-browser
|
|
278
|
+
// resolve. ~/.bun/bin only contains bun's own binary, NOT third-party global
|
|
279
|
+
// package bins (those live at ~/.bun/install/global/node_modules/.bin/).
|
|
280
|
+
`export PATH="${BUN_GLOBAL_BIN_SHELL}:$PATH"`,
|
|
281
|
+
'',
|
|
282
|
+
// Browser deps (Chrome-for-Testing + Linux libs) installed via the global
|
|
283
|
+
// agent-browser binary. `doctor` is the idempotent guard.
|
|
284
|
+
`echo "STAGE: ${BOOTSTRAP_STAGE_MARKERS.browserDeps}"`,
|
|
285
|
+
'echo "[browser deps]"',
|
|
286
|
+
`if ${BUN_GLOBAL_BIN_SHELL}/agent-browser doctor >/dev/null 2>&1; then`,
|
|
287
|
+
' echo "[browser deps] already installed, skipping"',
|
|
288
|
+
'else',
|
|
289
|
+
` retry 'browser deps' ${BUN_GLOBAL_BIN_SHELL}/agent-browser install --with-deps || { echo "browser-install-failed" > ${FAIL_MARKER}; exit 19; }`,
|
|
290
|
+
'fi',
|
|
291
|
+
'',
|
|
292
|
+
]
|
|
293
|
+
const launch = buildLaunchLines(opts, `${BUN_GLOBAL_BIN_SHELL}/nebula-gateway`)
|
|
294
|
+
return [...preamble, ...installLines, ...launch].join('\n')
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export function buildBootstrapScript(opts: BuildBootstrapScriptOpts): BuildBootstrapScriptResult {
|
|
298
|
+
const mode: BootstrapMode = opts.mode ?? 'npm'
|
|
299
|
+
const aptPkgs = [...DEFAULT_APT_PACKAGES, ...(opts.extraAptPackages ?? [])]
|
|
300
|
+
const aptList = [...new Set(aptPkgs)].join(' ')
|
|
301
|
+
const inner =
|
|
302
|
+
mode === 'npm' ? buildNpmInnerScript(opts, aptList) : buildGitInnerScript(opts, aptList)
|
|
303
|
+
|
|
304
|
+
// Daytona's `process/execute` API does NOT run via a shell — it splits the
|
|
305
|
+
// command string argv-style. Heredocs / pipes / `>` redirects fail because
|
|
306
|
+
// they're passed as literal args to the first binary. To run our complex
|
|
307
|
+
// inner script we base64-encode it (yields only [A-Za-z0-9+/=], no shell
|
|
308
|
+
// metachars) and have `bash -c '...'` decode + write + launch. The single-
|
|
309
|
+
// quoted bash -c wrapper has no internal quotes to escape.
|
|
310
|
+
//
|
|
311
|
+
// Sequencing rules:
|
|
312
|
+
// - File-write steps chain with `&&` (must succeed in order).
|
|
313
|
+
// - `nohup ... &` sends the inner script to background. After `&` you
|
|
314
|
+
// CANNOT use `&&` (syntax error: `& && X`) so we use `;` to follow up
|
|
315
|
+
// with the success marker echo. The launching shell exits ~instantly.
|
|
316
|
+
const innerPath = '/tmp/nebula-bootstrap-inner.sh'
|
|
317
|
+
const innerB64 = Buffer.from(inner).toString('base64')
|
|
318
|
+
const fileWrites = [
|
|
319
|
+
`rm -f ${PROGRESS_LOG} ${DONE_MARKER} ${FAIL_MARKER}`,
|
|
320
|
+
`echo ${innerB64} | base64 -d > ${innerPath}`,
|
|
321
|
+
`chmod +x ${innerPath}`,
|
|
322
|
+
].join(' && ')
|
|
323
|
+
const launchBody = `${fileWrites} && nohup bash ${innerPath} >/dev/null 2>&1 & echo bootstrap-launched`
|
|
324
|
+
const outerScript = `bash -c '${launchBody}'`
|
|
325
|
+
|
|
326
|
+
return {
|
|
327
|
+
script: outerScript,
|
|
328
|
+
doneMarkerPath: DONE_MARKER,
|
|
329
|
+
progressLogPath: PROGRESS_LOG,
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export const BOOTSTRAP_DONE_MARKER = DONE_MARKER
|
|
334
|
+
export const BOOTSTRAP_FAIL_MARKER = FAIL_MARKER
|
|
335
|
+
export const BOOTSTRAP_PROGRESS_LOG = PROGRESS_LOG
|
|
336
|
+
export const BOOTSTRAP_SUCCESS_MARKER_PREFIX = 'nebula-gateway-pid='
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* The exact strings the inner subshell writes to FAIL_MARKER on each step
|
|
340
|
+
* failure. Kept in sync with the per-step `echo "X-failed"` calls inside
|
|
341
|
+
* `buildBootstrapScript`. Pollers compare via substring match (the marker
|
|
342
|
+
* file may also contain bash setlocale warnings).
|
|
343
|
+
*/
|
|
344
|
+
export const BOOTSTRAP_FAIL_KEYWORDS = [
|
|
345
|
+
'apt-update-failed',
|
|
346
|
+
'apt-install-failed',
|
|
347
|
+
'bun-install-failed',
|
|
348
|
+
'git-clone-failed',
|
|
349
|
+
'nebula-install-failed',
|
|
350
|
+
'browser-install-failed',
|
|
351
|
+
'harness-died-early',
|
|
352
|
+
] as const
|