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,104 @@
1
+ import {
2
+ LagoraCliConfigStore,
3
+ baseApiUrl,
4
+ requestHeaders
5
+ } from "./chunk-AZ3EEBVD.js";
6
+ import "./chunk-TJZVQYBL.js";
7
+
8
+ // packages/cli/src/report.ts
9
+ import { execFile, spawn } from "node:child_process";
10
+ import { createHash, randomUUID } from "node:crypto";
11
+ import { readFile } from "node:fs/promises";
12
+ import path from "node:path";
13
+ import { fileURLToPath } from "node:url";
14
+ import { promisify } from "node:util";
15
+ var exec = promisify(execFile);
16
+ var valueOptions = /* @__PURE__ */ new Set(["--kernel", "--log", "--title", "--reporter", "--description", "--description-file", "--tags", "--store", "--api-url", "--idempotency-key", "--sdk-root", "--python", "--stages", "--kernel-function"]);
17
+ function parse(argv) {
18
+ const values = /* @__PURE__ */ new Map();
19
+ for (let index = 0; index < argv.length; index++) {
20
+ const option = argv[index];
21
+ if (option === "--auto-lowering") continue;
22
+ if (!valueOptions.has(option)) throw new Error(`Unknown argument: ${option}`);
23
+ const value = argv[++index];
24
+ if (!value || value.startsWith("--")) throw new Error(`${option} requires a value`);
25
+ if (option === "--api-url" && values.has(option)) throw new Error("Pass --api-url at most once");
26
+ values.set(option, value);
27
+ }
28
+ for (const option of ["--kernel", "--title"]) {
29
+ if (!values.get(option)?.trim()) throw new Error(`${option} is required`);
30
+ }
31
+ return values;
32
+ }
33
+ async function probe(command, args) {
34
+ try {
35
+ return (await exec(command, args, { timeout: 2e3, maxBuffer: 64 * 1024 })).stdout.trim() || void 0;
36
+ } catch (error) {
37
+ if (error instanceof Error && "code" in error) return void 0;
38
+ throw error;
39
+ }
40
+ }
41
+ async function main() {
42
+ const argv = process.argv.slice(2);
43
+ const values = parse(argv);
44
+ if (values.has("--store") && !values.has("--api-url")) {
45
+ const child = spawn("bash", [fileURLToPath(new URL("submit-issue.sh", import.meta.url)), ...argv], {
46
+ stdio: "inherit",
47
+ env: { ...process.env, LAGORA_NODE: process.execPath, LAGORA_VERIFY_ENTRY: fileURLToPath(new URL("verify-issue.js", import.meta.url)) }
48
+ });
49
+ const code = await new Promise((resolve, reject) => {
50
+ child.once("error", reject);
51
+ child.once("exit", resolve);
52
+ });
53
+ process.exitCode = code ?? 1;
54
+ return;
55
+ }
56
+ const session = await new LagoraCliConfigStore().apiSession(values.get("--api-url"));
57
+ if (!session.token) throw new Error("Run `lagora login` first");
58
+ const kernelPath = values.get("--kernel");
59
+ const kernel = await readFile(kernelPath);
60
+ const checksum = createHash("sha256").update(kernel).digest("hex");
61
+ const reporter = values.get("--reporter") ?? (await exec(process.execPath, [fileURLToPath(new URL("cli-auth.js", import.meta.url)), "whoami"])).stdout.trim();
62
+ const description = values.has("--description-file") ? await readFile(values.get("--description-file"), "utf8") : values.get("--description") || `Reported from lagora with kernel ${path.basename(kernelPath)}.`;
63
+ const [remoteUrl, branch, commit, dirty, python, legatoVersion] = await Promise.all([
64
+ probe("git", ["config", "--get", "remote.origin.url"]),
65
+ probe("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
66
+ probe("git", ["rev-parse", "HEAD"]),
67
+ probe("git", ["status", "--porcelain"]),
68
+ probe("python3", ["--version"]),
69
+ probe("legato", ["--version"])
70
+ ]);
71
+ const form = new FormData();
72
+ for (const [key, value] of Object.entries({
73
+ title: values.get("--title"),
74
+ reporter,
75
+ description,
76
+ tags: values.get("--tags") ?? "",
77
+ idempotencyKey: values.get("--idempotency-key") || randomUUID(),
78
+ gitMetadata: JSON.stringify({ remoteUrl, branch, commit, dirty: Boolean(dirty), filePath: kernelPath }),
79
+ environmentMetadata: JSON.stringify({
80
+ platform: process.platform,
81
+ arch: process.arch,
82
+ node: process.version,
83
+ python: python ?? "not found",
84
+ kernelChecksum: checksum,
85
+ legatoVersion: legatoVersion ?? "legato binary not found on PATH",
86
+ cliVersion: "lagora/npm"
87
+ })
88
+ })) form.set(key, value);
89
+ form.set("kernelFile", new File([kernel], path.basename(kernelPath), { type: "text/x-python" }));
90
+ const log = values.get("--log");
91
+ if (log) form.set("errorLogFile", new File([await readFile(log)], path.basename(log), { type: "text/plain" }));
92
+ const response = await fetch(`${baseApiUrl(session.apiUrl)}/api/report-bundles`, {
93
+ method: "POST",
94
+ headers: requestHeaders(session),
95
+ body: form,
96
+ signal: AbortSignal.timeout(12e4)
97
+ });
98
+ if (!response.ok) throw new Error(`Report upload failed: HTTP ${response.status}`);
99
+ console.log(await response.text());
100
+ }
101
+ main().catch((error) => {
102
+ console.error(error instanceof Error ? error.message : String(error));
103
+ process.exitCode = 1;
104
+ });
@@ -0,0 +1,151 @@
1
+ """Pick the exact SDK versions to install from the package index.
2
+
3
+ The index proxies public PyPI alongside the SDK team's own builds, so an
4
+ unpinned install silently resolves the wrong distribution: `hart` becomes an
5
+ unrelated public project, `legato` becomes a feature-branch build, and `torch_ha`
6
+ becomes the hardware variant. Everything has to be pinned to one exact version,
7
+ and this is what decides which.
8
+
9
+ There are two version lines, not one. legato carries its own release number --
10
+ `legato/VERSION` in the SDK repo, `0.1.1` at the time of writing -- while the
11
+ support packages (halogger, hart, torch_ha) are all stamped with the shared
12
+ number in their pyproject files, `0.0.4rc4`. Reading them as one line was the
13
+ defect this grew out of: legato was pinned to whatever nightly the support
14
+ packages happened to share, so a released legato was never installed.
15
+
16
+ So the shared line is resolved across the packages that actually have one, and
17
+ legato is resolved on its own with `--independent`.
18
+
19
+ Runs inside the prepare job rather than the web pod: the pod's egress only
20
+ reaches GitHub, while the job runs outside the mesh and can reach the index.
21
+ """
22
+
23
+ import argparse
24
+ import re
25
+ import sys
26
+ import urllib.request
27
+
28
+ # The packages the shared version pins. hapti is not among them: it is never
29
+ # installed here (see SDK_INDEX_INSTALL), and requiring it to have published
30
+ # would let a package no run can use veto the whole resolve.
31
+ DEFAULT_PACKAGES = ("halogger", "hart", "torch_ha")
32
+ # 0.0.5.dev20260806+9714ffc64 -- a nightly and nothing else. Branch builds
33
+ # (0.1.0+soft.5428) and variant builds (...+sha.public, ...+sha.hw) are excluded
34
+ # by requiring the local segment to be a bare hex commit.
35
+ NIGHTLY_VERSION = re.compile(r"^(\d+(?:\.\d+)*)\.dev(\d{8})\+([0-9a-f]+)$")
36
+ # 0.2.0 -- a plain release, which is what legato publishes off main. Anything
37
+ # with a local segment is a branch or variant build: `legato_version.py` appends
38
+ # `+SOFT-<number>` and `+HOTFIX.<string>` for unmerged work, and installing one
39
+ # of those would silently test a branch nobody asked for.
40
+ RELEASE_VERSION = re.compile(r"^(\d+(?:\.\d+)*)$")
41
+ DISTRIBUTION_FILE = re.compile(r">([^<>]+?\.(?:whl|tar\.gz))<")
42
+
43
+
44
+ def distribution_version(filename):
45
+ """The version segment of a wheel or sdist filename."""
46
+ stem = filename[: -len(".whl")] if filename.endswith(".whl") else filename[: -len(".tar.gz")]
47
+ parts = stem.split("-")
48
+ return parts[1] if len(parts) >= 2 else None
49
+
50
+
51
+ def nightly_versions(html):
52
+ found = set()
53
+ for filename in DISTRIBUTION_FILE.findall(html):
54
+ version = distribution_version(filename)
55
+ if version and NIGHTLY_VERSION.match(version):
56
+ found.add(version)
57
+ return found
58
+
59
+
60
+ def sort_key(version):
61
+ match = NIGHTLY_VERSION.match(version)
62
+ if not match:
63
+ return ((), 0)
64
+ base, dated, _ = match.groups()
65
+ return (tuple(int(part) for part in base.split(".")), int(dated))
66
+
67
+
68
+ def release_sort_key(version):
69
+ return tuple(int(part) for part in version.split("."))
70
+
71
+
72
+ def own_versions(html):
73
+ """One package's usable versions, releases first, each newest first.
74
+
75
+ A release is preferred over any nightly. The nightlies are what the shared
76
+ line is built from and they are still accepted as a fallback, but a package
77
+ that has cut a release has said which build it wants people on.
78
+ """
79
+ releases, nightlies = set(), set()
80
+ for filename in DISTRIBUTION_FILE.findall(html):
81
+ version = distribution_version(filename)
82
+ if not version:
83
+ continue
84
+ if RELEASE_VERSION.match(version):
85
+ releases.add(version)
86
+ elif NIGHTLY_VERSION.match(version):
87
+ nightlies.add(version)
88
+ return sorted(releases, key=release_sort_key, reverse=True) + sorted(nightlies, key=sort_key, reverse=True)
89
+
90
+
91
+ def fetch(index_url, package, timeout):
92
+ # PEP 503 normalises the project name: underscores become hyphens.
93
+ url = "{}/{}/".format(index_url.rstrip("/"), package.replace("_", "-").lower())
94
+ with urllib.request.urlopen(url, timeout=timeout) as response:
95
+ return response.read().decode("utf-8", "replace")
96
+
97
+
98
+ def shared_versions(index_url, packages, timeout):
99
+ """Every nightly present for *all* the packages, newest first.
100
+
101
+ Requiring all of them keeps a half-published night -- one package uploaded,
102
+ another still building -- from offering a version that cannot install.
103
+ """
104
+ shared = set()
105
+ for position, package in enumerate(packages):
106
+ versions = nightly_versions(fetch(index_url, package, timeout))
107
+ shared = versions if position == 0 else (shared & versions)
108
+ if not shared:
109
+ return []
110
+ return sorted(shared, key=sort_key, reverse=True)
111
+
112
+
113
+ def main(argv=None):
114
+ parser = argparse.ArgumentParser()
115
+ parser.add_argument("--index-url", required=True)
116
+ parser.add_argument("--package", action="append", default=None)
117
+ parser.add_argument("--timeout", type=float, default=30.0)
118
+ parser.add_argument("--list", action="store_true", help="print every usable version, newest first")
119
+ parser.add_argument("--limit", type=int, default=0)
120
+ parser.add_argument(
121
+ "--independent",
122
+ action="store_true",
123
+ help="resolve the single --package on its own version line, rather than a version shared with the others",
124
+ )
125
+ args = parser.parse_args(argv)
126
+
127
+ packages = args.package or list(DEFAULT_PACKAGES)
128
+ if args.independent and len(packages) != 1:
129
+ print("[agora] --independent resolves one package; pass exactly one --package", file=sys.stderr)
130
+ return 1
131
+
132
+ try:
133
+ if args.independent:
134
+ versions = own_versions(fetch(args.index_url, packages[0], args.timeout))
135
+ else:
136
+ versions = shared_versions(args.index_url, packages, args.timeout)
137
+ except Exception as error: # noqa: BLE001 - the caller falls back to a source build
138
+ print("[agora] package index lookup failed: {}".format(error), file=sys.stderr)
139
+ return 1
140
+ if not versions:
141
+ target = packages[0] if args.independent else "every SDK package"
142
+ print("[agora] no usable version is published for {}".format(target), file=sys.stderr)
143
+ return 1
144
+ if args.limit > 0:
145
+ versions = versions[: args.limit]
146
+ print("\n".join(versions) if args.list else versions[0])
147
+ return 0
148
+
149
+
150
+ if __name__ == "__main__":
151
+ raise SystemExit(main())