lagora-cli 1.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.
Files changed (52) hide show
  1. package/README.md +138 -0
  2. package/dist/help.txt +70 -0
  3. package/dist/lagora.js +342 -0
  4. package/dist/report-help.txt +5 -0
  5. package/dist/scripts/agora_playground_harness.py +263 -0
  6. package/dist/scripts/announce.js +41 -0
  7. package/dist/scripts/check-kernel-submission.py +90 -0
  8. package/dist/scripts/chunk-2EAJVB5D.js +100 -0
  9. package/dist/scripts/chunk-2KTLCUFI.js +29 -0
  10. package/dist/scripts/chunk-AZ3EEBVD.js +137 -0
  11. package/dist/scripts/chunk-NBJMYAOA.js +2128 -0
  12. package/dist/scripts/chunk-NCJMUBTG.js +125 -0
  13. package/dist/scripts/chunk-QJPQHKIO.js +23 -0
  14. package/dist/scripts/chunk-RIR5KGHC.js +33 -0
  15. package/dist/scripts/chunk-TJZVQYBL.js +8 -0
  16. package/dist/scripts/chunk-UHJXD4TG.js +18 -0
  17. package/dist/scripts/chunk-UQ6I6VTY.js +117 -0
  18. package/dist/scripts/cli-auth.js +348 -0
  19. package/dist/scripts/cli-config-IA7EOSYD.js +7 -0
  20. package/dist/scripts/install-skill.js +199 -0
  21. package/dist/scripts/issue-local-client-DZUXZOKY.js +22 -0
  22. package/dist/scripts/issue-search.js +1823 -0
  23. package/dist/scripts/issue.js +386 -0
  24. package/dist/scripts/keycloak-provision.js +986 -0
  25. package/dist/scripts/legato-fsim-runner.py +126 -0
  26. package/dist/scripts/legato-lowering-runner.py +156 -0
  27. package/dist/scripts/legato_runner_annotations.py +235 -0
  28. package/dist/scripts/legato_runner_env.py +91 -0
  29. package/dist/scripts/legato_runner_launchers.py +287 -0
  30. package/dist/scripts/legato_runner_script_wrapper.py +193 -0
  31. package/dist/scripts/notifications-EU43SIEV.js +624 -0
  32. package/dist/scripts/playground.js +408 -0
  33. package/dist/scripts/report-bundle-sync-3U7QTP4Z.js +215 -0
  34. package/dist/scripts/report.js +104 -0
  35. package/dist/scripts/resolve-sdk-package-version.py +151 -0
  36. package/dist/scripts/sdk-runtime-JE6H2PB2.js +992 -0
  37. package/dist/scripts/sdk-runtime-kubernetes-job-KOWL4ITV.js +479 -0
  38. package/dist/scripts/sdk-runtime-smoke.py +168 -0
  39. package/dist/scripts/sdk.js +256 -0
  40. package/dist/scripts/site-feedback-CAPE5MPX.js +136 -0
  41. package/dist/scripts/site-feedback-rate-limit-5BU2WSFE.js +86 -0
  42. package/dist/scripts/site-feedback.js +117 -0
  43. package/dist/scripts/storage-234FBH54.js +67 -0
  44. package/dist/scripts/submit-issue.sh +489 -0
  45. package/dist/scripts/verification-3QCY66QW.js +772 -0
  46. package/dist/scripts/verify-issue.js +144 -0
  47. package/dist/skills/legato-agora-cli/SKILL.md +556 -0
  48. package/dist/skills/legato-agora-cli/agents/openai.yaml +7 -0
  49. package/dist/skills/legato-agora-cli/reference/kernel-with-golden.py +84 -0
  50. package/dist/skills/legato-site-feedback/SKILL.md +49 -0
  51. package/dist/skills/legato-site-feedback/agents/openai.yaml +7 -0
  52. package/package.json +16 -0
@@ -0,0 +1,992 @@
1
+ import {
2
+ sdkPackageIndex
3
+ } from "./chunk-2KTLCUFI.js";
4
+ import {
5
+ loweringWorkerEnv
6
+ } from "./chunk-RIR5KGHC.js";
7
+ import {
8
+ SDK_PREPARE_SOURCE_PATHS,
9
+ readSdkPrepareCacheState,
10
+ sdkPrepareCacheEnv,
11
+ sdkPrepareCacheStateMatches,
12
+ sdkPrepareCacheStatePath,
13
+ sdkPrepareSourceFingerprint,
14
+ writeSdkPrepareCacheState
15
+ } from "./chunk-UQ6I6VTY.js";
16
+ import {
17
+ configuredGithubToken,
18
+ gitEnv
19
+ } from "./chunk-2EAJVB5D.js";
20
+
21
+ // lib/server/sdk-runtime.ts
22
+ import { execFile as execFile2 } from "node:child_process";
23
+ import { access as access2, appendFile, mkdir as mkdir4, readFile as readFile4, readdir, realpath, rename, writeFile as writeFile3 } from "node:fs/promises";
24
+ import path6 from "node:path";
25
+ import { promisify as promisify2 } from "node:util";
26
+
27
+ // lib/server/sdk-runtime-progress.ts
28
+ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
29
+ import path2 from "node:path";
30
+
31
+ // lib/server/sdk-runtime-state.ts
32
+ import { createHash } from "node:crypto";
33
+ import { access, mkdir, readFile, writeFile } from "node:fs/promises";
34
+ import path from "node:path";
35
+ var DEFAULT_REPO_URL = "https://github.com/Hyper-Accel/hyperaccel-sdk.git";
36
+ var DEFAULT_BRANCH = "main";
37
+ var LEGACY_PREPARE_COMMAND = "git config url.https://github.com/.insteadOf git@github.com: && git submodule update --init --recursive && uv sync --group cpu";
38
+ var PREPARE_COMMAND_WITHOUT_LLVM_PATHS = [
39
+ "git config url.https://github.com/.insteadOf git@github.com:",
40
+ "git submodule update --init --recursive",
41
+ "CMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL:-1} uv sync --frozen --inexact --package legato --no-default-groups --group cpu --group build --no-install-project",
42
+ "CMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL:-1} uv sync --frozen --inexact --package legato --no-default-groups --group cpu --group build --no-build-isolation",
43
+ "HART_BUILD_TYPE=release CMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL:-1} uv sync --frozen --inexact --package hart --no-default-groups --group cpu --group build --no-install-project",
44
+ "HART_BUILD_TYPE=release CMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL:-1} uv sync --frozen --inexact --package hart --no-default-groups --group cpu --group build --no-build-isolation",
45
+ ".venv/bin/python -c 'import legato; import hart'"
46
+ ].join(" && ");
47
+ var SDK_INDEX_INSTALL = [
48
+ // Two version lines, not one. legato carries its own release number and the
49
+ // support packages share theirs; pinning legato to the shared one meant a
50
+ // released legato was never installed. AGORA_LEGATO_PACKAGE_VERSION is the
51
+ // version a reader picks in Settings, because "which Legato?" is the question
52
+ // this site exists to answer -- the rest is plumbing and resolves itself.
53
+ 'LEGATO_V="${AGORA_LEGATO_PACKAGE_VERSION:-$(python3 /app/scripts/resolve-sdk-package-version.py --index-url "$AGORA_SDK_INDEX_URL" --package legato --independent)}"',
54
+ // The support packages' shared line, pinnable exactly the way legato's is. A
55
+ // reported failure happened against a pair, so reproducing it needs both
56
+ // halves nameable; pinning only legato left this side floating on whatever
57
+ // nightly the job resolved that day. Unset still resolves the newest shared
58
+ // nightly, which is what every unpinned prepare keeps doing.
59
+ 'SDK_V="${AGORA_SDK_PACKAGE_VERSION:-$(python3 /app/scripts/resolve-sdk-package-version.py --index-url "$AGORA_SDK_INDEX_URL")}"',
60
+ '[ -n "$LEGATO_V" ] && [ -n "$SDK_V" ]',
61
+ `printf '\\n[agora] installing legato %s with SDK support packages %s\\n' "$LEGATO_V" "$SDK_V"`,
62
+ 'uv pip install --prerelease=allow --extra-index-url https://download.pytorch.org/whl/cpu "torch==2.10.0+cpu" "legato===$LEGATO_V" "halogger===$SDK_V" "numpy<2.0.0" "PyYAML==6.0.3" "pybind11>=3.0.0" loguru "fairscale==0.4.13" "llama-models==0.3.0" "transformers>=4.57.1" "accelerate>=0.30.0" ipython',
63
+ // hapti is deliberately absent, as it is from the SDK team's own image. It is
64
+ // the on-hardware profiler, its profiles are blocked in the UI, and its wheel
65
+ // cannot find libha_hapti.so in this layout. The profile gate looks for hapti's
66
+ // artifacts on disk, so leaving it out reports "unavailable" rather than
67
+ // failing the whole prepare on a package no run can use.
68
+ 'uv pip install --prerelease=allow --no-deps "torch_ha===$SDK_V" "hart===$SDK_V"'
69
+ ].join(" && ");
70
+ var SDK_SOURCE_BUILD = [
71
+ "git submodule update --init --recursive",
72
+ "uv lock",
73
+ "CMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL:-1} uv sync --frozen --inexact --package legato --no-default-groups --group cpu --group build --no-install-project",
74
+ "CMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL:-1} uv sync --frozen --inexact --package legato --no-default-groups --group cpu --group build --no-build-isolation",
75
+ "HART_BUILD_TYPE=release CMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL:-1} uv sync --frozen --inexact --package hart --no-default-groups --group cpu --group build --no-install-project",
76
+ "HART_BUILD_TYPE=release CMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL:-1} uv sync --frozen --inexact --package hart --no-default-groups --group cpu --group build --no-build-isolation",
77
+ "HART_BUILD_TYPE=release CMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL:-1} uv sync --frozen --inexact --package torch_ha --no-default-groups --group cpu --group build --no-install-project",
78
+ "HART_BUILD_TYPE=release CMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL:-1} uv sync --frozen --inexact --package torch_ha --no-default-groups --group cpu --group build --no-build-isolation",
79
+ // hapti builds libhapti_tool.so, which the profile gate looks for before it
80
+ // will let a run ask for a trace.
81
+ "HART_BUILD_TYPE=release CMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL:-1} uv sync --frozen --inexact --package hapti --no-default-groups --group cpu --group build --no-install-project",
82
+ "HART_BUILD_TYPE=release CMAKE_BUILD_PARALLEL_LEVEL=${CMAKE_BUILD_PARALLEL_LEVEL:-1} uv sync --frozen --inexact --package hapti --no-default-groups --group cpu --group build --no-build-isolation"
83
+ ].join(" && ");
84
+ var PREPARE_PRELUDE = [
85
+ // --global is load-bearing: two private submodules are declared with git@ URLs,
86
+ // and a submodule fetch runs its own git, which never reads the superproject's
87
+ // local config. Set locally, the rewrite is ignored and the fetch needs an
88
+ // authorised SSH key that prepared environments do not have.
89
+ "git config --global url.https://github.com/.insteadOf git@github.com:",
90
+ // UV_DEFAULT_INDEX empty means "unset" to uv, which is how the SDK repo's own
91
+ // workflows switch between the internal index and plain PyPI.
92
+ "export PATH=/usr/lib/llvm-20/bin:$PATH CC=${CC:-clang-20} CXX=${CXX:-clang++-20} CMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH:-/usr/lib/llvm-20} LLVM_DIR=${LLVM_DIR:-/usr/lib/llvm-20/lib/cmake/llvm} MLIR_DIR=${MLIR_DIR:-/usr/lib/llvm-20/lib/cmake/mlir} OpenMP_ROOT=${OpenMP_ROOT:-/usr/lib/llvm-20} UV_CACHE_DIR=${UV_CACHE_DIR:-$(dirname $PWD)/.agora/cache/uv} CCACHE_DIR=${CCACHE_DIR:-$(dirname $PWD)/.agora/cache/ccache} CCACHE_BASEDIR=${CCACHE_BASEDIR:-$PWD} UV_LINK_MODE=${UV_LINK_MODE:-copy} UV_LOCK_TIMEOUT=${UV_LOCK_TIMEOUT:-900} UV_DEFAULT_INDEX=${AGORA_SDK_INDEX_URL:-} UV_INSECURE_HOST=${AGORA_SDK_INDEX_HOST:-}",
93
+ // --allow-existing reuses a venv a previous prepare left behind. Without it uv
94
+ // warns that a future release will require --clear to touch an existing venv,
95
+ // which would turn a uv upgrade in the image into a failing prepare.
96
+ "uv venv --python 3.10 --allow-existing",
97
+ // uv downloads its own CPython, so libpython lives under UV_PYTHON_INSTALL_DIR
98
+ // rather than a system library directory. torch_ha's CMake runs the gtest binary
99
+ // it has just built to enumerate test cases, and that binary links libpython --
100
+ // the loader cannot find it otherwise, and the build stops there. Resolved at
101
+ // this point rather than in the export block above because the venv that names
102
+ // the interpreter does not exist until uv creates it.
103
+ `export LD_LIBRARY_PATH="$(.venv/bin/python -c 'import sysconfig; print(sysconfig.get_config_var("LIBDIR") or "")')\${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"`
104
+ ];
105
+ var PREPARE_EPILOGUE = [
106
+ // torch must be imported before torch_ha: torch autoloads the torch_ha device
107
+ // backend, and importing torch_ha first re-enters a half-initialised torch.
108
+ // hapti is not verified: it needs real hardware, its profiles are blocked in
109
+ // the UI, and the profile gate decides its availability from disk on its own.
110
+ ".venv/bin/python -c 'import torch; import torch_ha; import legato; import hart'",
111
+ // Which versions actually landed -- the wheel path resolves them from the index,
112
+ // so the log is the only place that records what this runtime really contains.
113
+ //
114
+ // Reports what is installed rather than asserting a fixed list. Naming packages
115
+ // directly made this step fail the whole prepare on a fresh root once hapti was
116
+ // dropped from the install, and the two lists have no reason to stay in step:
117
+ // the source build installs one set, the index install another.
118
+ `.venv/bin/python -c 'import importlib.metadata as m; d={(x.metadata["Name"] or "").lower().replace("-","_"): x.version for x in m.distributions()}; print("[agora] installed: " + ", ".join(f"{k}=={d[k]}" for k in ["legato","hart","torch_ha","hapti","torch"] if k in d))'`
119
+ ];
120
+ var DEFAULT_PREPARE_COMMAND = [
121
+ ...PREPARE_PRELUDE,
122
+ `( [ -n "\${AGORA_SDK_INDEX_URL:-}" ] && ${SDK_INDEX_INSTALL} ) || ( printf '\\n[agora] package index install unavailable; building from source\\n' && ${SDK_SOURCE_BUILD} )`,
123
+ ...PREPARE_EPILOGUE
124
+ ].join(" && ");
125
+ var PACKAGE_PREPARE_COMMAND = [
126
+ ...PREPARE_PRELUDE.filter((part) => !part.startsWith("git config")),
127
+ SDK_INDEX_INSTALL,
128
+ ...PREPARE_EPILOGUE
129
+ ].join(" && ");
130
+ async function readSdkRuntimeSettings(overrides = {}) {
131
+ const defaults = defaultSdkRuntimeSettings();
132
+ let settings = defaults;
133
+ try {
134
+ const raw = await readFile(settingsPath(), "utf8");
135
+ settings = parseStoredSettings(JSON.parse(raw), defaults);
136
+ } catch {
137
+ settings = defaults;
138
+ }
139
+ return applyOverrides(settings, overrides);
140
+ }
141
+ async function writeSdkRuntimeSettings(input) {
142
+ const runtimeRoot = path.resolve(normalizeRequired(input.runtimeRoot, defaultRuntimeRoot()));
143
+ const repoUrl = normalizeRequired(input.repoUrl, DEFAULT_REPO_URL);
144
+ const branch = normalizeRequired(input.branch, DEFAULT_BRANCH);
145
+ const preparedRoot = preparedRuntimeRoot(runtimeRoot, repoUrl, branch);
146
+ const settings = {
147
+ repoUrl,
148
+ branch,
149
+ runtimeRoot,
150
+ preparedRoot,
151
+ // Both the interpreter and the extra PYTHONPATH live inside the branch/commit-specific prepared
152
+ // worktree, so they are derived from preparedRoot and never user-configurable. A stored absolute
153
+ // path would point at a stale .venv / legato_aten_lib whenever the selected branch or commit changes.
154
+ pythonPath: defaultPythonPath(preparedRoot),
155
+ prepareCommand: normalizePrepareCommand(input.prepareCommand, defaultPrepareCommand()),
156
+ extraPythonPaths: defaultExtraPythonPaths(preparedRoot),
157
+ autoPrepareOnStart: input.autoPrepareOnStart,
158
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
159
+ };
160
+ await mkdir(settingsRoot(), { recursive: true });
161
+ await writeFile(settingsPath(), `${JSON.stringify(settings, null, 2)}
162
+ `);
163
+ return settings;
164
+ }
165
+ async function readSdkRuntimeStatus(overrides = {}) {
166
+ const settings = await readSdkRuntimeSettings(overrides);
167
+ try {
168
+ const raw = await readFile(statusPath(settings.preparedRoot), "utf8");
169
+ const status = parseStoredStatus(JSON.parse(raw), settings);
170
+ if (!statusMatchesSettings(status, settings)) {
171
+ return notPreparedStatus(settings, "SDK runtime settings changed. Prepare the selected branch before lowering.");
172
+ }
173
+ if (status.status === "ready" && !await pathExists(status.pythonPath)) {
174
+ return notPreparedStatus(settings, `SDK runtime Python runtime is missing: ${status.pythonPath}`);
175
+ }
176
+ return status;
177
+ } catch {
178
+ return notPreparedStatus(settings, "SDK source worktree has not been prepared yet.");
179
+ }
180
+ }
181
+ async function writeSdkRuntimeStatus(settings, input, statusRoot = settings.preparedRoot) {
182
+ const status = {
183
+ ...input,
184
+ repoUrl: settings.repoUrl,
185
+ branch: settings.branch,
186
+ runtimeRoot: settings.runtimeRoot,
187
+ preparedRoot: settings.preparedRoot,
188
+ pythonPath: settings.pythonPath
189
+ };
190
+ await mkdir(statusRoot, { recursive: true });
191
+ await writeFile(statusPath(statusRoot), `${JSON.stringify(status, null, 2)}
192
+ `);
193
+ return status;
194
+ }
195
+ function sdkRuntimeExtraPythonPath(settings) {
196
+ return settings.extraPythonPaths.split(path.delimiter).map((item) => item.trim()).filter(Boolean).join(path.delimiter);
197
+ }
198
+ function sdkRuntimeSettingsWithPreparedRoot(settings, preparedRoot) {
199
+ const resolvedPreparedRoot = path.resolve(preparedRoot);
200
+ return {
201
+ ...settings,
202
+ preparedRoot: resolvedPreparedRoot,
203
+ pythonPath: defaultPythonPath(resolvedPreparedRoot),
204
+ extraPythonPaths: defaultExtraPythonPaths(resolvedPreparedRoot)
205
+ };
206
+ }
207
+ function installLogPath(runtimeRoot) {
208
+ return path.join(runtimeRoot, "install.log");
209
+ }
210
+ function defaultSdkRuntimeSettings() {
211
+ const runtimeRoot = defaultRuntimeRoot();
212
+ const repoUrl = process.env.AGORA_SDK_REPO_URL ?? DEFAULT_REPO_URL;
213
+ const branch = process.env.AGORA_SDK_BRANCH ?? DEFAULT_BRANCH;
214
+ const preparedRoot = preparedRuntimeRoot(runtimeRoot, repoUrl, branch);
215
+ return {
216
+ repoUrl,
217
+ branch,
218
+ runtimeRoot,
219
+ preparedRoot,
220
+ pythonPath: defaultPythonPath(preparedRoot),
221
+ prepareCommand: process.env.AGORA_SDK_PREPARE_COMMAND ?? defaultPrepareCommand(),
222
+ extraPythonPaths: defaultExtraPythonPaths(preparedRoot),
223
+ autoPrepareOnStart: readBoolean(process.env.AGORA_SDK_PREPARE_ON_START) ?? true
224
+ };
225
+ }
226
+ async function pathExists(candidate) {
227
+ try {
228
+ await access(candidate);
229
+ return true;
230
+ } catch {
231
+ return false;
232
+ }
233
+ }
234
+ function defaultRuntimeRoot() {
235
+ const configuredRoot = process.env.AGORA_SDK_RUNTIME_ROOT ?? process.env.AGORA_SDK_ROOT;
236
+ return configuredRoot ? path.resolve(configuredRoot) : path.join(
237
+ /*turbopackIgnore: true*/
238
+ process.cwd(),
239
+ ".local-data",
240
+ "sdk-source"
241
+ );
242
+ }
243
+ function defaultPrepareCommand() {
244
+ return DEFAULT_PREPARE_COMMAND;
245
+ }
246
+ function parseStoredSettings(value, defaults) {
247
+ if (!isRecord(value)) return defaults;
248
+ const runtimeRoot = path.resolve(readString(value, "runtimeRoot", defaults.runtimeRoot));
249
+ const repoUrl = readString(value, "repoUrl", defaults.repoUrl);
250
+ const branch = readString(value, "branch", defaults.branch);
251
+ const preparedRoot = path.resolve(readString(value, "preparedRoot", preparedRuntimeRoot(runtimeRoot, repoUrl, branch)));
252
+ return {
253
+ repoUrl,
254
+ branch,
255
+ runtimeRoot,
256
+ preparedRoot,
257
+ pythonPath: defaultPythonPath(preparedRoot),
258
+ prepareCommand: normalizePrepareCommand(readString(value, "prepareCommand", defaults.prepareCommand), defaults.prepareCommand),
259
+ extraPythonPaths: defaultExtraPythonPaths(preparedRoot),
260
+ autoPrepareOnStart: readBooleanValue(value.autoPrepareOnStart) ?? defaults.autoPrepareOnStart,
261
+ updatedAt: readOptionalString(value, "updatedAt")
262
+ };
263
+ }
264
+ function parseStoredStatus(value, settings) {
265
+ if (!isRecord(value)) return notPreparedStatus(settings);
266
+ const status = value.status === "preparing" || value.status === "ready" || value.status === "failed" ? value.status : "not_prepared";
267
+ return {
268
+ status,
269
+ repoUrl: readString(value, "repoUrl", settings.repoUrl),
270
+ branch: readString(value, "branch", settings.branch),
271
+ runtimeRoot: readString(value, "runtimeRoot", settings.runtimeRoot),
272
+ preparedRoot: path.resolve(readString(value, "preparedRoot", settings.preparedRoot)),
273
+ pythonPath: readString(value, "pythonPath", settings.pythonPath),
274
+ commitSha: readOptionalString(value, "commitSha"),
275
+ startedAt: readOptionalString(value, "startedAt"),
276
+ completedAt: readOptionalString(value, "completedAt"),
277
+ message: readOptionalString(value, "message")
278
+ };
279
+ }
280
+ function settingsRoot() {
281
+ return process.env.AGORA_SETTINGS_DIR ? path.resolve(process.env.AGORA_SETTINGS_DIR) : path.join(
282
+ /*turbopackIgnore: true*/
283
+ process.cwd(),
284
+ ".local-data",
285
+ "settings"
286
+ );
287
+ }
288
+ function settingsPath() {
289
+ return path.join(settingsRoot(), "sdk-runtime.json");
290
+ }
291
+ function statusPath(runtimeRoot) {
292
+ return path.join(runtimeRoot, "runtime.json");
293
+ }
294
+ function applyOverrides(settings, overrides) {
295
+ const repoUrl = normalizeRequired(overrides.repoUrl ?? settings.repoUrl, settings.repoUrl);
296
+ const branch = normalizeRequired(overrides.branch ?? settings.branch, settings.branch);
297
+ const runtimeRoot = path.resolve(normalizeRequired(overrides.runtimeRoot ?? settings.runtimeRoot, settings.runtimeRoot));
298
+ const defaultPreparedRoot = preparedRuntimeRoot(runtimeRoot, repoUrl, branch);
299
+ const preparedRoot = path.resolve(normalizeRequired(overrides.preparedRoot ?? defaultPreparedRoot, defaultPreparedRoot));
300
+ return {
301
+ ...settings,
302
+ repoUrl,
303
+ branch,
304
+ runtimeRoot,
305
+ preparedRoot,
306
+ pythonPath: defaultPythonPath(preparedRoot),
307
+ prepareCommand: normalizePrepareCommand(overrides.prepareCommand ?? settings.prepareCommand, settings.prepareCommand),
308
+ extraPythonPaths: defaultExtraPythonPaths(preparedRoot)
309
+ };
310
+ }
311
+ function notPreparedStatus(settings, message) {
312
+ return {
313
+ status: "not_prepared",
314
+ repoUrl: settings.repoUrl,
315
+ branch: settings.branch,
316
+ runtimeRoot: settings.runtimeRoot,
317
+ preparedRoot: settings.preparedRoot,
318
+ pythonPath: settings.pythonPath,
319
+ message
320
+ };
321
+ }
322
+ function statusMatchesSettings(status, settings) {
323
+ const preparedRoot = path.resolve(status.preparedRoot);
324
+ const sameRef = status.branch === settings.branch || status.commitSha === settings.branch;
325
+ return status.repoUrl === settings.repoUrl && sameRef && path.resolve(status.runtimeRoot) === settings.runtimeRoot && (preparedRoot === settings.preparedRoot && status.pythonPath === settings.pythonPath || isRuntimeRootChild(settings.runtimeRoot, preparedRoot) && status.pythonPath === defaultPythonPath(preparedRoot));
326
+ }
327
+ function isRuntimeRootChild(runtimeRoot, preparedRoot) {
328
+ const relative = path.relative(runtimeRoot, preparedRoot);
329
+ return Boolean(relative) && !relative.startsWith("..") && !path.isAbsolute(relative);
330
+ }
331
+ function preparedRuntimeRoot(runtimeRoot, repoUrl, branch) {
332
+ return path.join(runtimeRoot, runtimeKey(repoUrl, branch));
333
+ }
334
+ function runtimeKey(repoUrl, branch) {
335
+ const branchSlug = branch.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "ref";
336
+ const repoHash2 = createHash("sha256").update(repoUrl).digest("hex").slice(0, 10);
337
+ return `${branchSlug}-${repoHash2}`;
338
+ }
339
+ function defaultPythonPath(preparedRoot) {
340
+ return path.join(preparedRoot, ".venv", "bin", "python");
341
+ }
342
+ function defaultExtraPythonPaths(preparedRoot) {
343
+ return path.join(preparedRoot, "legato_aten_lib");
344
+ }
345
+ function normalizeRequired(value, fallback) {
346
+ const normalized = value.trim();
347
+ return normalized ? normalized : fallback;
348
+ }
349
+ function normalizePrepareCommand(value, fallback) {
350
+ const normalized = normalizeRequired(value, fallback);
351
+ if (normalized === LEGACY_PREPARE_COMMAND || normalized === PREPARE_COMMAND_WITHOUT_LLVM_PATHS || /(?:clang-14|llvm-14|MLIR_DIR=\$\{MLIR_DIR:-\/usr\/lib\/llvm-14)/.test(normalized) || /llvm-20|clang-20|uv sync --frozen --package (?:legato|hart)/.test(normalized) && (!normalized.includes("--inexact") || !normalized.includes("PATH=/usr/lib/llvm-20/bin:$PATH") || !normalized.includes("OpenMP_ROOT=${OpenMP_ROOT:-/usr/lib/llvm-20}") || !normalized.includes("UV_CACHE_DIR=${UV_CACHE_DIR:-$(dirname $PWD)/.agora/cache/uv}") || !normalized.includes("CCACHE_DIR=${CCACHE_DIR:-$(dirname $PWD)/.agora/cache/ccache}") || !normalized.includes("UV_LINK_MODE=${UV_LINK_MODE:-copy}") || !normalized.includes("uv lock") || !normalized.includes("uv sync --frozen --inexact --package legato --no-default-groups --group cpu --group build --no-install-project") || !normalized.includes("uv sync --frozen --inexact --package legato --no-default-groups --group cpu --group build --no-build-isolation") || !normalized.includes("--package torch_ha") || !normalized.includes("--package hapti") || !normalized.includes('sysconfig.get_config_var("LIBDIR")') || !normalized.includes("AGORA_SDK_INDEX_URL") || !normalized.includes("legato===$LEGATO_V") || !normalized.includes("AGORA_SDK_PACKAGE_VERSION") || !normalized.includes("import legato; import hart'") || !normalized.includes("m.distributions()") || !normalized.includes("git config --global url.https://github.com/.insteadOf"))) {
352
+ return DEFAULT_PREPARE_COMMAND;
353
+ }
354
+ return normalized;
355
+ }
356
+ function readString(record, key, fallback) {
357
+ const value = record[key];
358
+ return typeof value === "string" && value.trim() ? value.trim() : fallback;
359
+ }
360
+ function readOptionalString(record, key) {
361
+ const value = record[key];
362
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
363
+ }
364
+ function readBoolean(value) {
365
+ if (value === "1" || value === "true") return true;
366
+ if (value === "0" || value === "false") return false;
367
+ return void 0;
368
+ }
369
+ function readBooleanValue(value) {
370
+ return typeof value === "boolean" ? value : void 0;
371
+ }
372
+ function isRecord(value) {
373
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
374
+ }
375
+
376
+ // lib/server/sdk-runtime-progress.ts
377
+ async function createSdkPrepareRun(settings, startedAt) {
378
+ const runId = `${compactUtc(startedAt)}-${process.pid}`;
379
+ const runDir = path2.join(sdkPrepareRunsRoot(settings), runId);
380
+ const run = {
381
+ runId,
382
+ runDir,
383
+ progressPath: path2.join(runDir, "events.jsonl"),
384
+ liveLogPath: path2.join(runDir, "combined.log"),
385
+ resultPath: path2.join(runDir, "result.json"),
386
+ startedAt
387
+ };
388
+ await mkdir2(runDir, { recursive: true });
389
+ await writeFile2(path2.join(runDir, "manifest.json"), `${JSON.stringify({
390
+ runId,
391
+ repoUrl: settings.repoUrl,
392
+ branch: settings.branch,
393
+ runtimeRoot: settings.runtimeRoot,
394
+ preparedRoot: settings.preparedRoot,
395
+ prepareCommand: settings.prepareCommand,
396
+ startedAt
397
+ }, null, 2)}
398
+ `);
399
+ await writeFile2(sdkPrepareCurrentPath(settings), `${JSON.stringify(run, null, 2)}
400
+ `);
401
+ return run;
402
+ }
403
+ function sdkPrepareRunsRoot(settings) {
404
+ return path2.join(settings.runtimeRoot, ".agora", "sdk-prepare-runs", path2.basename(settings.preparedRoot));
405
+ }
406
+ function sdkPrepareCurrentPath(settings) {
407
+ return path2.join(sdkPrepareRunsRoot(settings), "current.json");
408
+ }
409
+ function compactUtc(value) {
410
+ return value.replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z");
411
+ }
412
+
413
+ // lib/server/sdk-runtime-lock.ts
414
+ import { mkdir as mkdir3, open, readFile as readFile3, rm, stat } from "node:fs/promises";
415
+ import path3 from "node:path";
416
+ import { setTimeout as delay } from "node:timers/promises";
417
+ import { randomUUID } from "node:crypto";
418
+ var DEFAULT_LOCK_STALE_MS = 144e5;
419
+ var DEFAULT_LOCK_WAIT_MS = 144e5;
420
+ var POLL_INTERVAL_MS = 1e3;
421
+ async function withSdkPrepareLock(settings, callback) {
422
+ const lock = await acquireSdkPrepareLock(settings);
423
+ try {
424
+ return await callback();
425
+ } finally {
426
+ await lock.release();
427
+ }
428
+ }
429
+ async function acquireSdkPrepareLock(settings) {
430
+ const lockPath = sdkPrepareLockPath(settings);
431
+ const owner = `${process.pid}-${randomUUID()}`;
432
+ const started = Date.now();
433
+ while (Date.now() - started < lockWaitMs()) {
434
+ const handle = await tryCreateLock(lockPath, owner);
435
+ if (handle) return lockHandle(lockPath, owner, handle);
436
+ await removeStaleLock(lockPath);
437
+ await delay(POLL_INTERVAL_MS);
438
+ }
439
+ throw new Error(`Timed out waiting for SDK prepare lock: ${lockPath}`);
440
+ }
441
+ function sdkPrepareLockPath(settings) {
442
+ return path3.join(settings.runtimeRoot, ".agora", "sdk-prepare.lock");
443
+ }
444
+ async function tryCreateLock(lockPath, owner) {
445
+ try {
446
+ const handle = await open(lockPath, "wx");
447
+ await handle.writeFile(`${JSON.stringify({ owner, pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString() })}
448
+ `);
449
+ return handle;
450
+ } catch (error) {
451
+ if (isNodeError(error) && error.code === "ENOENT") {
452
+ await mkdir3(path3.dirname(lockPath), { recursive: true });
453
+ return await tryCreateLock(lockPath, owner);
454
+ }
455
+ if (isNodeError(error) && error.code === "EEXIST") return void 0;
456
+ throw error;
457
+ }
458
+ }
459
+ async function removeStaleLock(lockPath) {
460
+ try {
461
+ const details = await stat(lockPath);
462
+ if (Date.now() - details.mtimeMs <= lockStaleMs()) return;
463
+ await rm(lockPath, { force: true });
464
+ } catch (error) {
465
+ if (isNodeError(error) && error.code === "ENOENT") return;
466
+ throw error;
467
+ }
468
+ }
469
+ function lockHandle(lockPath, owner, handle) {
470
+ return {
471
+ release: async () => {
472
+ await handle.close();
473
+ try {
474
+ const current = JSON.parse(await readFile3(lockPath, "utf8"));
475
+ if (isRecord2(current) && current.owner === owner) await rm(lockPath, { force: true });
476
+ } catch (error) {
477
+ if (isNodeError(error) && error.code === "ENOENT") return;
478
+ throw error;
479
+ }
480
+ }
481
+ };
482
+ }
483
+ function lockStaleMs() {
484
+ return readPositiveInt(process.env.AGORA_SDK_PREPARE_LOCK_STALE_MS, DEFAULT_LOCK_STALE_MS);
485
+ }
486
+ function lockWaitMs() {
487
+ return readPositiveInt(process.env.AGORA_SDK_PREPARE_LOCK_WAIT_MS, DEFAULT_LOCK_WAIT_MS);
488
+ }
489
+ function readPositiveInt(value, fallback) {
490
+ if (!value) return fallback;
491
+ const parsed = Number.parseInt(value, 10);
492
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
493
+ }
494
+ function isNodeError(error) {
495
+ return error instanceof Error && "code" in error;
496
+ }
497
+ function isRecord2(value) {
498
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
499
+ }
500
+
501
+ // lib/server/sdk-runtime-smoke.ts
502
+ import { fileURLToPath as packageFilePath } from "node:url";
503
+ import { execFile } from "node:child_process";
504
+ import path4 from "node:path";
505
+ import { promisify } from "node:util";
506
+ var execFileAsync = promisify(execFile);
507
+ var DEFAULT_SMOKE_STAGES = "MLIR,CORE_IR";
508
+ var DEFAULT_SMOKE_TIMEOUT_MS = 12e4;
509
+ async function runPreparedRuntimeSmoke(settings) {
510
+ const scriptPath = process.env.AGORA_SDK_PREPARE_SMOKE_SCRIPT ?? path4.join(
511
+ /*turbopackIgnore: true*/
512
+ packageFilePath(new URL(".", import.meta.url)),
513
+ "sdk-runtime-smoke.py"
514
+ );
515
+ const outputRoot = path4.join(settings.preparedRoot, ".agora", "smoke");
516
+ const stages = process.env.AGORA_SDK_PREPARE_SMOKE_STAGES ?? DEFAULT_SMOKE_STAGES;
517
+ return await execFileAsync(settings.pythonPath, [scriptPath, "--repo-root", settings.preparedRoot, "--output-root", outputRoot, "--stages", stages], {
518
+ cwd: settings.preparedRoot,
519
+ env: loweringWorkerEnv(settings, { ...process.env, ...sdkPrepareCacheEnv(settings) }),
520
+ timeout: readPositiveInt2(process.env.AGORA_SDK_PREPARE_SMOKE_TIMEOUT_MS, DEFAULT_SMOKE_TIMEOUT_MS),
521
+ maxBuffer: 8 * 1024 * 1024
522
+ });
523
+ }
524
+ function readPositiveInt2(value, fallback) {
525
+ if (!value) return fallback;
526
+ const parsed = Number.parseInt(value, 10);
527
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
528
+ }
529
+
530
+ // lib/server/sdk-runtime-source-root.ts
531
+ import { createHash as createHash2 } from "node:crypto";
532
+ import path5 from "node:path";
533
+ var FINGERPRINT_SLICE_LENGTH = 12;
534
+ var REPO_HASH_LENGTH = 10;
535
+ function sdkRuntimeSettingsForSourceFingerprint(settings, sourceFingerprint) {
536
+ return sdkRuntimeSettingsWithPreparedRoot(
537
+ settings,
538
+ path5.join(settings.runtimeRoot, sourceRuntimeKey(settings.repoUrl, settings.branch, sourceFingerprint))
539
+ );
540
+ }
541
+ function sdkRuntimeSettingsForPackageVersion(settings, pin) {
542
+ const versions = typeof pin === "string" ? { legatoVersion: pin } : pin;
543
+ return sdkRuntimeSettingsWithPreparedRoot(
544
+ settings,
545
+ path5.join(settings.runtimeRoot, packageRuntimeKey(versions, settings.repoUrl))
546
+ );
547
+ }
548
+ function packageRuntimeKey(pin, repoUrl) {
549
+ const legato = pin.legatoVersion?.trim();
550
+ const sdk = pin.sdkVersion?.trim();
551
+ if (!sdk) return `pkg-${versionSlug(legato ?? "")}-${repoHash(repoUrl)}`;
552
+ const legatoSegment = legato ? versionSlug(legato) : "latest";
553
+ return `pkg-${legatoSegment}-sdk-${versionSlug(sdk)}-${repoHash(repoUrl)}`;
554
+ }
555
+ function versionSlug(version) {
556
+ return version.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "version";
557
+ }
558
+ function sourceRuntimeKey(repoUrl, ref, sourceFingerprint) {
559
+ return `${refSlug(ref)}-${sourceFingerprint.slice(0, FINGERPRINT_SLICE_LENGTH)}-${repoHash(repoUrl)}`;
560
+ }
561
+ function refSlug(ref) {
562
+ return ref.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "ref";
563
+ }
564
+ function repoHash(repoUrl) {
565
+ return createHash2("sha256").update(repoUrl).digest("hex").slice(0, REPO_HASH_LENGTH);
566
+ }
567
+
568
+ // lib/server/sdk-runtime.ts
569
+ var execFileAsync2 = promisify2(execFile2);
570
+ function prepareVersionPin(options) {
571
+ return {
572
+ legatoVersion: trimmedVersion(options.legatoVersion ?? options.packageVersion),
573
+ sdkVersion: trimmedVersion(options.sdkVersion)
574
+ };
575
+ }
576
+ function trimmedVersion(value) {
577
+ const text = value?.trim();
578
+ return text ? text : void 0;
579
+ }
580
+ function isVersionPinned(pin) {
581
+ return Boolean(pin.legatoVersion ?? pin.sdkVersion);
582
+ }
583
+ function describeVersionPin(pin) {
584
+ return `legato ${pin.legatoVersion ?? "latest"} with SDK support packages ${pin.sdkVersion ?? "latest"}`;
585
+ }
586
+ function prepareVersionEnv(pin) {
587
+ return {
588
+ ...pin.legatoVersion ? { AGORA_LEGATO_PACKAGE_VERSION: pin.legatoVersion } : {},
589
+ ...pin.sdkVersion ? { AGORA_SDK_PACKAGE_VERSION: pin.sdkVersion } : {}
590
+ };
591
+ }
592
+ async function prepareSdkRuntime(overrides = {}, options = {}) {
593
+ const refSettings = await readSdkRuntimeSettings(overrides);
594
+ return await withSdkPrepareLock(refSettings, async () => await prepareSdkRuntimeUnlocked(refSettings, options));
595
+ }
596
+ async function prepareSdkRuntimeUnlocked(refSettings, options = {}) {
597
+ await mkdir4(path6.dirname(refSettings.preparedRoot), { recursive: true });
598
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
599
+ const prepareRun = await createSdkPrepareRun(refSettings, startedAt);
600
+ await writeSdkRuntimeStatus(refSettings, { status: "preparing", startedAt, message: "Resolving SDK source ref." });
601
+ let activeSettings = refSettings;
602
+ try {
603
+ const token = await configuredGithubToken();
604
+ const logParts = prepareLogHeader(refSettings, startedAt);
605
+ const pin = prepareVersionPin(options);
606
+ const pinned = isVersionPinned(pin);
607
+ const fromIndex = pinned && Boolean(sdkPackageIndex());
608
+ let commitSha;
609
+ let sourceFingerprint = "";
610
+ if (!fromIndex) {
611
+ await verifyGitAccess(refSettings, token);
612
+ logParts.push("gitAccess: ok");
613
+ await syncRepository(refSettings, token);
614
+ commitSha = await gitOutput(refSettings.preparedRoot, ["rev-parse", "HEAD"], token);
615
+ sourceFingerprint = await prepareSourceFingerprint(refSettings, token);
616
+ }
617
+ activeSettings = pinned ? sdkRuntimeSettingsForPackageVersion(refSettings, pin) : sdkRuntimeSettingsForSourceFingerprint(refSettings, sourceFingerprint);
618
+ if (fromIndex) {
619
+ if (activeSettings.prepareCommand === DEFAULT_PREPARE_COMMAND) {
620
+ activeSettings = { ...activeSettings, prepareCommand: PACKAGE_PREPARE_COMMAND };
621
+ }
622
+ await mkdir4(activeSettings.preparedRoot, { recursive: true });
623
+ }
624
+ logParts.push(
625
+ fromIndex ? `legatoVersion: ${pin.legatoVersion ?? "latest"}` : `commitSha: ${commitSha}`,
626
+ ...fromIndex ? [`sdkVersion: ${pin.sdkVersion ?? "latest"}`] : [],
627
+ fromIndex ? "source: package index (no checkout)" : `sourceFingerprint: ${sourceFingerprint}`,
628
+ `sourcePreparedRoot: ${activeSettings.preparedRoot}`,
629
+ ""
630
+ );
631
+ await writeSdkRuntimeStatus(activeSettings, { status: "preparing", startedAt, commitSha, message: fromIndex ? "Installing published SDK packages." : "Preparing SDK source runtime." }, refSettings.preparedRoot);
632
+ if (process.env.AGORA_SDK_PREPARE_BACKEND === "kubernetes-job") {
633
+ if (!options.force && !pinned && await preparedRuntimeMarkedReady(activeSettings, commitSha ?? "", sourceFingerprint)) {
634
+ const completedAt2 = (/* @__PURE__ */ new Date()).toISOString();
635
+ await writeSdkPrepareCacheState(activeSettings, commitSha ?? "", sourceFingerprint, completedAt2);
636
+ logParts.push(
637
+ "cache:",
638
+ `hit: ${sdkPrepareCacheStatePath(activeSettings)}`,
639
+ `preparedRoot: ${activeSettings.preparedRoot}`,
640
+ "prepare job skipped; existing prepared-runtime marker reused (no smoke run in web pod).",
641
+ `completedAt: ${completedAt2}`
642
+ );
643
+ await writeInstallLog(activeSettings, refSettings, logParts.join("\n"));
644
+ const status2 = await writePreparedStatus(activeSettings, refSettings, { status: "ready", startedAt, completedAt: completedAt2, commitSha, message: "SDK source runtime reused from prepared marker." });
645
+ return { settings: activeSettings, status: status2, logPath: installLogPath(activeSettings.preparedRoot) };
646
+ }
647
+ return await prepareSdkRuntimeWithKubernetesJob(activeSettings, refSettings, startedAt, prepareRun, logParts, pin);
648
+ }
649
+ if (!fromIndex && activeSettings.preparedRoot !== refSettings.preparedRoot) {
650
+ await syncRepository(activeSettings, token);
651
+ }
652
+ if (!options.force && !pinned && await canReusePreparedRuntime(activeSettings, commitSha ?? "", sourceFingerprint, token)) {
653
+ const completedAt2 = (/* @__PURE__ */ new Date()).toISOString();
654
+ await writeSdkPrepareCacheState(activeSettings, commitSha ?? "", sourceFingerprint, completedAt2);
655
+ logParts.push(
656
+ "cache:",
657
+ `hit: ${sdkPrepareCacheStatePath(activeSettings)}`,
658
+ `preparedRoot: ${activeSettings.preparedRoot}`,
659
+ "prepare command skipped; existing SDK runtime smoke passed.",
660
+ `completedAt: ${completedAt2}`
661
+ );
662
+ await writeInstallLog(activeSettings, refSettings, logParts.join("\n"));
663
+ const status2 = await writePreparedStatus(activeSettings, refSettings, { status: "ready", startedAt, completedAt: completedAt2, commitSha, message: "SDK source runtime reused from cache." });
664
+ return { settings: activeSettings, status: status2, logPath: installLogPath(activeSettings.preparedRoot) };
665
+ }
666
+ const install = await execFileAsync2("sh", ["-lc", activeSettings.prepareCommand], {
667
+ cwd: activeSettings.preparedRoot,
668
+ env: { ...prepareEnv(activeSettings, token), ...prepareVersionEnv(pin) },
669
+ // A cold prepared root builds legato (~23 min), hart and torch_ha (~11 min)
670
+ // from source, and `uv lock` alone is allowed 15 min, so observed cold runs
671
+ // pass an hour. Kept below the 4h orphan window in sdk-runtime-inventory.
672
+ timeout: readPositiveInt3(process.env.AGORA_SDK_PREPARE_TIMEOUT_MS, 108e5),
673
+ maxBuffer: 32 * 1024 * 1024
674
+ });
675
+ await probePreparedRuntime(activeSettings, token);
676
+ const smoke = await runPreparedRuntimeSmoke(activeSettings);
677
+ const completedAt = (/* @__PURE__ */ new Date()).toISOString();
678
+ await writeSdkPrepareCacheState(activeSettings, commitSha ?? "", sourceFingerprint, completedAt);
679
+ logParts.push(
680
+ "stdout:",
681
+ install.stdout || "<empty>",
682
+ "",
683
+ "stderr:",
684
+ install.stderr || "<empty>",
685
+ "",
686
+ "smokeStdout:",
687
+ smoke.stdout || "<empty>",
688
+ "",
689
+ "smokeStderr:",
690
+ smoke.stderr || "<empty>",
691
+ `completedAt: ${completedAt}`
692
+ );
693
+ await writeInstallLog(activeSettings, refSettings, logParts.join("\n"));
694
+ const status = await writePreparedStatus(activeSettings, refSettings, { status: "ready", startedAt, completedAt, commitSha, message: fromIndex ? `Installed ${describeVersionPin(pin)} from the package index.` : "SDK source runtime is ready." });
695
+ return { settings: activeSettings, status, logPath: installLogPath(activeSettings.preparedRoot) };
696
+ } catch (error) {
697
+ const status = await recordPrepareFailure(activeSettings, refSettings, startedAt, error);
698
+ return { settings: activeSettings, status, logPath: installLogPath(activeSettings.preparedRoot) };
699
+ }
700
+ }
701
+ async function prepareSdkRuntimeWithKubernetesJob(settings, refSettings, startedAt, prepareRun, logParts, pin) {
702
+ const { runSdkPrepareJob } = await import("./sdk-runtime-kubernetes-job-KOWL4ITV.js");
703
+ logParts.push(`prepareRunId: ${prepareRun.runId}`, `prepareRunDir: ${prepareRun.runDir}`);
704
+ logParts.push("backend: kubernetes-job", "");
705
+ const result = await runSdkPrepareJob(settings, prepareRun, refSettings.preparedRoot, pin);
706
+ const completedAt = (/* @__PURE__ */ new Date()).toISOString();
707
+ const liveLog = await readOptionalTail(prepareRun.liveLogPath, 32e3);
708
+ logParts.push(
709
+ "jobLogs:",
710
+ result.logs || "<pod log empty; prepare command output is captured in prepare-live.log>",
711
+ "",
712
+ "prepareLiveLog:",
713
+ liveLog || "<empty>",
714
+ `completedAt: ${completedAt}`
715
+ );
716
+ await writeInstallLog(settings, refSettings, logParts.join("\n"));
717
+ if (!result.succeeded) {
718
+ await appendPrepareRunFailureLog(prepareRun, result.logs || result.message || "SDK prepare job failed.");
719
+ await ensurePrepareRunFailureResult(prepareRun, completedAt, result.message ?? "SDK prepare job failed.");
720
+ const status2 = await writePreparedStatus(settings, refSettings, {
721
+ status: "failed",
722
+ startedAt,
723
+ completedAt,
724
+ message: firstLogLine(result.logs) ?? result.message ?? "SDK prepare job failed."
725
+ });
726
+ return { settings, status: status2, logPath: installLogPath(settings.preparedRoot) };
727
+ }
728
+ const prepareResult = await readPrepareRunResult(prepareRun.resultPath);
729
+ const prepareFailure = prepareResult?.status === "failed" || sdkPrepareLogLooksFailed(liveLog);
730
+ if (prepareFailure) {
731
+ const message = prepareResult?.message ?? firstPrepareLogFailure(liveLog) ?? "SDK prepare command failed.";
732
+ await ensurePrepareRunFailureResult(prepareRun, completedAt, message);
733
+ const status2 = await writePreparedStatus(settings, refSettings, {
734
+ status: "failed",
735
+ startedAt,
736
+ completedAt,
737
+ message
738
+ });
739
+ return { settings, status: status2, logPath: installLogPath(settings.preparedRoot) };
740
+ }
741
+ const commitSha = await gitOutput(settings.preparedRoot, ["rev-parse", "HEAD"], void 0);
742
+ try {
743
+ await probePreparedRuntime(settings, void 0);
744
+ await runPreparedRuntimeSmoke(settings);
745
+ } catch (error) {
746
+ const message = `SDK runtime smoke probe failed after prepare command completed. ${prepareFailureMessage(error)}`;
747
+ await appendPrepareRunFailureLog(prepareRun, message);
748
+ await ensurePrepareRunFailureResult(prepareRun, completedAt, message);
749
+ const status2 = await writePreparedStatus(settings, refSettings, {
750
+ status: "failed",
751
+ startedAt,
752
+ completedAt,
753
+ commitSha,
754
+ message
755
+ });
756
+ return { settings, status: status2, logPath: installLogPath(settings.preparedRoot) };
757
+ }
758
+ const status = await writePreparedStatus(settings, refSettings, {
759
+ status: "ready",
760
+ startedAt,
761
+ completedAt,
762
+ commitSha,
763
+ message: "SDK source runtime is ready."
764
+ });
765
+ return { settings, status, logPath: installLogPath(settings.preparedRoot) };
766
+ }
767
+ async function pathExists2(candidate) {
768
+ try {
769
+ await access2(candidate);
770
+ return true;
771
+ } catch {
772
+ return false;
773
+ }
774
+ }
775
+ async function preparedRuntimeMarkedReady(settings, commitSha, sourceFingerprint) {
776
+ const state = await readSdkPrepareCacheState(settings);
777
+ if (!sdkPrepareCacheStateMatches(settings, state, commitSha, sourceFingerprint)) return false;
778
+ return pathExists2(settings.pythonPath);
779
+ }
780
+ async function canReusePreparedRuntime(settings, commitSha, sourceFingerprint, token) {
781
+ const state = await readSdkPrepareCacheState(settings);
782
+ if (!sdkPrepareCacheStateMatches(settings, state, commitSha, sourceFingerprint)) return false;
783
+ try {
784
+ await probePreparedRuntime(settings, token);
785
+ await runPreparedRuntimeSmoke(settings);
786
+ return true;
787
+ } catch {
788
+ return false;
789
+ }
790
+ }
791
+ async function writePreparedStatus(settings, refSettings, input) {
792
+ const status = await writeSdkRuntimeStatus(settings, input);
793
+ if (settings.preparedRoot !== refSettings.preparedRoot) {
794
+ await writeSdkRuntimeStatus(settings, input, refSettings.preparedRoot);
795
+ }
796
+ return status;
797
+ }
798
+ async function writeInstallLog(settings, refSettings, text) {
799
+ await writeFile3(installLogPath(settings.preparedRoot), text);
800
+ if (settings.preparedRoot !== refSettings.preparedRoot) {
801
+ await writeFile3(installLogPath(refSettings.preparedRoot), text);
802
+ }
803
+ }
804
+ async function probePreparedRuntime(settings, token) {
805
+ await execFileAsync2(settings.pythonPath, ["-c", "import legato; import hart"], {
806
+ cwd: settings.preparedRoot,
807
+ env: prepareEnv(settings, token),
808
+ timeout: 6e4,
809
+ maxBuffer: 2 * 1024 * 1024
810
+ });
811
+ }
812
+ async function prepareSourceFingerprint(settings, token) {
813
+ const treeListing = await gitOutput(settings.preparedRoot, ["ls-tree", "HEAD", "--", ...SDK_PREPARE_SOURCE_PATHS], token);
814
+ return sdkPrepareSourceFingerprint(treeListing);
815
+ }
816
+ function prepareEnv(settings, token) {
817
+ return { ...gitEnv(token), ...sdkPrepareCacheEnv(settings) };
818
+ }
819
+ async function appendPrepareRunFailureLog(prepareRun, details) {
820
+ const trimmed = details.trim();
821
+ if (!trimmed) return;
822
+ await appendFile(prepareRun.liveLogPath, `
823
+
824
+ ===== SDK prepare failure diagnostics =====
825
+ ${trimmed}
826
+ `);
827
+ }
828
+ async function ensurePrepareRunFailureResult(prepareRun, completedAt, message) {
829
+ const fallback = { status: "failed", completedAt, message };
830
+ try {
831
+ const parsed = JSON.parse(await readFile4(prepareRun.resultPath, "utf8"));
832
+ if (isRecord3(parsed) && typeof parsed.message === "string" && parsed.message.trim()) return;
833
+ const exitCode = isRecord3(parsed) && typeof parsed.exitCode === "number" ? parsed.exitCode : void 0;
834
+ await writeFile3(prepareRun.resultPath, `${JSON.stringify({ ...fallback, exitCode })}
835
+ `);
836
+ } catch {
837
+ await writeFile3(prepareRun.resultPath, `${JSON.stringify(fallback)}
838
+ `);
839
+ }
840
+ }
841
+ async function readPrepareRunResult(resultPath) {
842
+ try {
843
+ const parsed = JSON.parse(await readFile4(resultPath, "utf8"));
844
+ if (!isRecord3(parsed)) return void 0;
845
+ return {
846
+ status: typeof parsed.status === "string" ? parsed.status : void 0,
847
+ message: typeof parsed.message === "string" ? parsed.message : void 0
848
+ };
849
+ } catch {
850
+ return void 0;
851
+ }
852
+ }
853
+ async function verifyGitAccess(settings, token) {
854
+ await execFileAsync2("git", ["ls-remote", settings.repoUrl, settings.branch], {
855
+ env: gitEnv(token),
856
+ timeout: 6e4,
857
+ maxBuffer: 2 * 1024 * 1024
858
+ });
859
+ }
860
+ async function syncRepository(settings, token) {
861
+ await mkdir4(settings.preparedRoot, { recursive: true });
862
+ if (!await hasManagedGitWorktree(settings.preparedRoot, settings.repoUrl)) {
863
+ await prepareCloneTarget(settings.preparedRoot);
864
+ await execFileAsync2("git", ["clone", "--no-checkout", settings.repoUrl, settings.preparedRoot], {
865
+ env: gitEnv(token),
866
+ timeout: readPositiveInt3(process.env.AGORA_SDK_CLONE_TIMEOUT_MS, 6e5),
867
+ maxBuffer: 8 * 1024 * 1024
868
+ });
869
+ }
870
+ await execFileAsync2("git", ["-C", settings.preparedRoot, "remote", "set-url", "origin", settings.repoUrl], { env: gitEnv(token), timeout: 3e4 });
871
+ await execFileAsync2("git", ["-C", settings.preparedRoot, "reset", "--hard"], {
872
+ env: gitEnv(token),
873
+ timeout: 6e4,
874
+ maxBuffer: 2 * 1024 * 1024
875
+ });
876
+ await execFileAsync2("git", ["-C", settings.preparedRoot, "clean", "-ffd", "-e", ".venv/"], {
877
+ env: gitEnv(token),
878
+ timeout: 6e4,
879
+ maxBuffer: 2 * 1024 * 1024
880
+ });
881
+ await execFileAsync2("git", ["-C", settings.preparedRoot, "fetch", "origin", settings.branch], {
882
+ env: gitEnv(token),
883
+ timeout: readPositiveInt3(process.env.AGORA_SDK_FETCH_TIMEOUT_MS, 6e5),
884
+ maxBuffer: 8 * 1024 * 1024
885
+ });
886
+ await execFileAsync2("git", ["-C", settings.preparedRoot, "checkout", "--force", "--detach", "FETCH_HEAD"], {
887
+ env: gitEnv(token),
888
+ timeout: 6e4,
889
+ maxBuffer: 2 * 1024 * 1024
890
+ });
891
+ await execFileAsync2("git", ["-C", settings.preparedRoot, "reset", "--hard", "FETCH_HEAD"], {
892
+ env: gitEnv(token),
893
+ timeout: 6e4,
894
+ maxBuffer: 2 * 1024 * 1024
895
+ });
896
+ }
897
+ async function hasManagedGitWorktree(worktree, repoUrl) {
898
+ try {
899
+ const topLevel = await gitOutput(worktree, ["rev-parse", "--show-toplevel"], void 0);
900
+ if (await realpath(topLevel) !== await realpath(worktree)) return false;
901
+ const remoteUrl = await gitOutput(worktree, ["remote", "get-url", "origin"], void 0);
902
+ return normalizeGitRemote(remoteUrl) === normalizeGitRemote(repoUrl);
903
+ } catch {
904
+ return false;
905
+ }
906
+ }
907
+ async function prepareCloneTarget(worktree) {
908
+ const entries = await readdir(worktree);
909
+ if (entries.length === 0) return;
910
+ const backupPath = `${worktree}.pre-sdk-clone-${Date.now()}-${process.pid}`;
911
+ await rename(worktree, backupPath);
912
+ await mkdir4(worktree, { recursive: true });
913
+ }
914
+ async function gitOutput(worktree, args, token) {
915
+ const output = await execFileAsync2("git", ["-C", worktree, ...args], { env: gitEnv(token), timeout: 3e4 });
916
+ return output.stdout.trim();
917
+ }
918
+ async function recordPrepareFailure(settings, refSettings, startedAt, error) {
919
+ const failure = error;
920
+ const completedAt = (/* @__PURE__ */ new Date()).toISOString();
921
+ const logText = [
922
+ ...prepareLogHeader(settings, startedAt),
923
+ `completedAt: ${completedAt}`,
924
+ "",
925
+ "stdout:",
926
+ failure.stdout || "<empty>",
927
+ "",
928
+ "stderr:",
929
+ sanitizeFailureText([failure.stderr, failure.message].filter(Boolean).join("\n") || "<empty>")
930
+ ].join("\n");
931
+ await mkdir4(settings.preparedRoot, { recursive: true });
932
+ await writeInstallLog(settings, refSettings, logText);
933
+ return await writePreparedStatus(settings, refSettings, {
934
+ status: "failed",
935
+ startedAt,
936
+ completedAt,
937
+ message: prepareFailureMessage(failure)
938
+ });
939
+ }
940
+ function prepareLogHeader(settings, startedAt) {
941
+ return [
942
+ `repoUrl: ${settings.repoUrl}`,
943
+ `branch: ${settings.branch}`,
944
+ `runtimeRoot: ${settings.runtimeRoot}`,
945
+ `preparedRoot: ${settings.preparedRoot}`,
946
+ `prepareCommand: ${settings.prepareCommand}`,
947
+ `startedAt: ${startedAt}`,
948
+ ""
949
+ ];
950
+ }
951
+ async function readOptionalTail(filePath, maxChars) {
952
+ try {
953
+ const text = await readFile4(filePath, "utf8");
954
+ return text.length > maxChars ? text.slice(-maxChars) : text;
955
+ } catch {
956
+ return "";
957
+ }
958
+ }
959
+ function readPositiveInt3(value, fallback) {
960
+ if (!value) return fallback;
961
+ const parsed = Number.parseInt(value, 10);
962
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
963
+ }
964
+ function sanitizeFailureText(message) {
965
+ return message.replace(/(AUTHORIZATION: basic )[A-Za-z0-9+/=]+/gi, "$1[redacted]");
966
+ }
967
+ function prepareFailureMessage(failure) {
968
+ const text = sanitizeFailureText([failure.stderr, failure.message].filter(Boolean).join("\n"));
969
+ return firstLogLine(text) ?? "SDK source runtime preparation failed.";
970
+ }
971
+ function sdkPrepareLogLooksFailed(text) {
972
+ return /(?:× Failed to build|Failed to build `|Prepare command failed with exit|returned non-zero exit status|CMake Error|error: invalid linker name)/.test(text);
973
+ }
974
+ function firstPrepareLogFailure(text) {
975
+ return text.split("\n").map((line) => line.trim()).find((line) => sdkPrepareLogLooksFailed(line));
976
+ }
977
+ function firstLogLine(text) {
978
+ return text.split("\n").map((line) => line.trim()).find(Boolean);
979
+ }
980
+ function isRecord3(value) {
981
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
982
+ }
983
+ function normalizeGitRemote(remote) {
984
+ return remote.trim().replace(/\.git$/, "");
985
+ }
986
+ export {
987
+ prepareSdkRuntime,
988
+ readSdkRuntimeSettings,
989
+ readSdkRuntimeStatus,
990
+ sdkRuntimeExtraPythonPath,
991
+ writeSdkRuntimeSettings
992
+ };