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.
- package/README.md +138 -0
- package/dist/help.txt +70 -0
- package/dist/lagora.js +342 -0
- package/dist/report-help.txt +5 -0
- package/dist/scripts/agora_playground_harness.py +263 -0
- package/dist/scripts/announce.js +41 -0
- package/dist/scripts/check-kernel-submission.py +90 -0
- package/dist/scripts/chunk-2EAJVB5D.js +100 -0
- package/dist/scripts/chunk-2KTLCUFI.js +29 -0
- package/dist/scripts/chunk-AZ3EEBVD.js +137 -0
- package/dist/scripts/chunk-NBJMYAOA.js +2128 -0
- package/dist/scripts/chunk-NCJMUBTG.js +125 -0
- package/dist/scripts/chunk-QJPQHKIO.js +23 -0
- package/dist/scripts/chunk-RIR5KGHC.js +33 -0
- package/dist/scripts/chunk-TJZVQYBL.js +8 -0
- package/dist/scripts/chunk-UHJXD4TG.js +18 -0
- package/dist/scripts/chunk-UQ6I6VTY.js +117 -0
- package/dist/scripts/cli-auth.js +348 -0
- package/dist/scripts/cli-config-IA7EOSYD.js +7 -0
- package/dist/scripts/install-skill.js +199 -0
- package/dist/scripts/issue-local-client-DZUXZOKY.js +22 -0
- package/dist/scripts/issue-search.js +1823 -0
- package/dist/scripts/issue.js +386 -0
- package/dist/scripts/keycloak-provision.js +986 -0
- package/dist/scripts/legato-fsim-runner.py +126 -0
- package/dist/scripts/legato-lowering-runner.py +156 -0
- package/dist/scripts/legato_runner_annotations.py +235 -0
- package/dist/scripts/legato_runner_env.py +91 -0
- package/dist/scripts/legato_runner_launchers.py +287 -0
- package/dist/scripts/legato_runner_script_wrapper.py +193 -0
- package/dist/scripts/notifications-EU43SIEV.js +624 -0
- package/dist/scripts/playground.js +408 -0
- package/dist/scripts/report-bundle-sync-3U7QTP4Z.js +215 -0
- package/dist/scripts/report.js +104 -0
- package/dist/scripts/resolve-sdk-package-version.py +151 -0
- package/dist/scripts/sdk-runtime-JE6H2PB2.js +992 -0
- package/dist/scripts/sdk-runtime-kubernetes-job-KOWL4ITV.js +479 -0
- package/dist/scripts/sdk-runtime-smoke.py +168 -0
- package/dist/scripts/sdk.js +256 -0
- package/dist/scripts/site-feedback-CAPE5MPX.js +136 -0
- package/dist/scripts/site-feedback-rate-limit-5BU2WSFE.js +86 -0
- package/dist/scripts/site-feedback.js +117 -0
- package/dist/scripts/storage-234FBH54.js +67 -0
- package/dist/scripts/submit-issue.sh +489 -0
- package/dist/scripts/verification-3QCY66QW.js +772 -0
- package/dist/scripts/verify-issue.js +144 -0
- package/dist/skills/legato-agora-cli/SKILL.md +556 -0
- package/dist/skills/legato-agora-cli/agents/openai.yaml +7 -0
- package/dist/skills/legato-agora-cli/reference/kernel-with-golden.py +84 -0
- package/dist/skills/legato-site-feedback/SKILL.md +49 -0
- package/dist/skills/legato-site-feedback/agents/openai.yaml +7 -0
- package/package.json +16 -0
package/README.md
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# lagora-cli
|
|
2
|
+
|
|
3
|
+
Lean npm distribution of the Legato Agora CLI. The npm package is `lagora-cli`
|
|
4
|
+
(unscoped); the executable remains `lagora`. Nothing has been published, so
|
|
5
|
+
installing from the public registry doesn't work yet. Until a release exists,
|
|
6
|
+
install from the internal GitHub release tarball or run
|
|
7
|
+
`./bin/lagora` from a repository checkout as described in the
|
|
8
|
+
[root README](../../README.md#install-the-cli-package).
|
|
9
|
+
|
|
10
|
+
After a public release:
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm install -g lagora-cli
|
|
14
|
+
lagora login
|
|
15
|
+
lagora help
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`lagora login` opens the browser flow described in the root README and stores a
|
|
19
|
+
CLI-scoped token; nothing about login changes in the npm build.
|
|
20
|
+
|
|
21
|
+
Requires Node.js 22.16 or newer. Remote login, reports, issue commands,
|
|
22
|
+
Playground upload/status, SDK administration and feedback use compiled Node.js,
|
|
23
|
+
without Next.js, React, tsx, Bash, curl or Python runtime dependencies. Login
|
|
24
|
+
opens the system browser and prints the URL for manual use if opening fails.
|
|
25
|
+
Saved login targets and `LAGORA_CONFIG` / `XDG_CONFIG_HOME` remain supported.
|
|
26
|
+
|
|
27
|
+
`lagora report --store` retains the existing portable shell writer and requires
|
|
28
|
+
Bash, Python 3 and standard Unix utilities. It preserves duplicate merging,
|
|
29
|
+
artifact collision handling and optional `--auto-lowering`. Local reads and
|
|
30
|
+
mutations remain isolated from the working directory's other stores.
|
|
31
|
+
`playground validate` requires Python 3. Real lowering/fSIM and SDK preparation
|
|
32
|
+
also require the separately installed platform-specific SDK, Python and its
|
|
33
|
+
build tools; this package does not install them or promise Windows portability
|
|
34
|
+
for these optional paths. No live SDK build or production service is exercised
|
|
35
|
+
by the package tests. Remote reports do not query GitHub CLI identity.
|
|
36
|
+
|
|
37
|
+
## Self-update
|
|
38
|
+
|
|
39
|
+
The installed CLI keeps itself current from the npm registry. Nothing runs in
|
|
40
|
+
the background: no daemon, cron entry or extra dependency.
|
|
41
|
+
|
|
42
|
+
### Automatic
|
|
43
|
+
|
|
44
|
+
After an ordinary command finishes and prints its own output, the CLI checks
|
|
45
|
+
for a newer stable release and installs it if found. The original command's
|
|
46
|
+
exit code is preserved either way. Checks happen at most once every 24 hours
|
|
47
|
+
per install prefix, and a failed or offline check counts toward that window, so
|
|
48
|
+
a machine without network access sees at most one bounded attempt per day.
|
|
49
|
+
Timeouts are short: the version lookup is capped at 4 seconds and the install
|
|
50
|
+
at 45 seconds, with no retries.
|
|
51
|
+
|
|
52
|
+
Turn it off in one of two ways:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
lagora update --disable # persisted; lagora update --enable turns it back on
|
|
56
|
+
LAGORA_AUTO_UPDATE=0 lagora report ... # per process; false and off also work
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The persisted setting lives in `$XDG_CONFIG_HOME/lagora/update.json`
|
|
60
|
+
(`~/.config/lagora/update.json` by default). It's separate from the login
|
|
61
|
+
config, which the updater never reads or writes.
|
|
62
|
+
|
|
63
|
+
### Manual
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
lagora update # check now and apply a newer stable exact version
|
|
67
|
+
lagora update --help
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`lagora update` ignores the 24-hour throttle and the disabled setting, prints a
|
|
71
|
+
JSON status line, and exits 0 when it updated or was already current, 1 when
|
|
72
|
+
the install is unsupported, another updater holds the lock, or the update
|
|
73
|
+
failed. Reasons are sanitized codes; raw npm output and credentials never
|
|
74
|
+
appear.
|
|
75
|
+
|
|
76
|
+
### Supported installs
|
|
77
|
+
|
|
78
|
+
Self-update, automatic or manual, only touches a canonical `npm install -g`
|
|
79
|
+
of `lagora-cli` on Unix where the global prefix, bin link and package
|
|
80
|
+
directory are owned and writable by the current non-root user and match npm's
|
|
81
|
+
configured global prefix. Everything else still runs every ordinary command but
|
|
82
|
+
never updates itself and makes no registry update requests:
|
|
83
|
+
|
|
84
|
+
- `sudo` or root installs
|
|
85
|
+
- Windows
|
|
86
|
+
- `npx`
|
|
87
|
+
- project-local dependencies
|
|
88
|
+
- `npm link` or a repository checkout
|
|
89
|
+
- read-only prefixes
|
|
90
|
+
|
|
91
|
+
npm itself is invoked with `--ignore-scripts`, `--engine-strict` and an exact
|
|
92
|
+
validated version, using your configured registry, `.npmrc` and npm-managed
|
|
93
|
+
auth. CLI and chat tokens are kept out of npm's environment.
|
|
94
|
+
|
|
95
|
+
### Disk footprint and cleanup
|
|
96
|
+
|
|
97
|
+
Each applied update is staged inside the prefix and then kept as an immutable
|
|
98
|
+
release under `<prefix>/.lagora-update/releases/<uuid>`. Only the small global
|
|
99
|
+
launcher file is switched, atomically, so commands that are still running, or
|
|
100
|
+
suspended, keep the assets they started with. Old releases are intentionally
|
|
101
|
+
not garbage-collected: a paused process may still need them. Each retained
|
|
102
|
+
release is roughly the size of one unpacked package, about 0.6 MB at the current
|
|
103
|
+
version, so the directory grows by that much per update.
|
|
104
|
+
|
|
105
|
+
To reclaim the space:
|
|
106
|
+
|
|
107
|
+
1. Make sure no `lagora` process is running or suspended.
|
|
108
|
+
2. Reinstall normally: `npm install -g lagora-cli`.
|
|
109
|
+
3. Remove `<prefix>/.lagora-update` (find the prefix with `npm prefix -g`).
|
|
110
|
+
|
|
111
|
+
`npm uninstall -g lagora-cli` alone leaves `.lagora-update` behind;
|
|
112
|
+
remove it by hand as well when uninstalling for good.
|
|
113
|
+
|
|
114
|
+
Known limits: the updater's lock doesn't coordinate with a separate `npm
|
|
115
|
+
install`/`uninstall` you run at the same time, and a hard kill between the
|
|
116
|
+
launcher switch and the manifest refresh can leave `npm ls -g` showing a stale
|
|
117
|
+
version while the CLI itself already runs the new release. Both are recovered
|
|
118
|
+
by an ordinary reinstall.
|
|
119
|
+
|
|
120
|
+
## Build and test from this repository
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
npm ci
|
|
124
|
+
npm run build:cli
|
|
125
|
+
npm run test:cli
|
|
126
|
+
npm run pack:cli
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The build reuses the repository's esbuild and compiles only the CLI dependency
|
|
130
|
+
graph. `dist/` and this README plus the manifest are the only shipped files.
|
|
131
|
+
Explicit asset allowlists exclude tests, environment files, local data and
|
|
132
|
+
repository evidence. There are zero runtime npm dependencies and no install
|
|
133
|
+
lifecycle script. The root website remains private and independently built.
|
|
134
|
+
|
|
135
|
+
The runtime test creates a real tarball with `npm pack`, installs it globally
|
|
136
|
+
under a temporary prefix outside the repository, then runs that installed
|
|
137
|
+
`lagora` with temporary HOME/config/data directories and loopback APIs only.
|
|
138
|
+
No npm credentials or registry publishing are needed.
|
package/dist/help.txt
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
Usage: lagora <command> [options]
|
|
2
|
+
|
|
3
|
+
Commands:
|
|
4
|
+
login Sign in through the browser (Keycloak + activity profile)
|
|
5
|
+
logout Revoke and clear CLI login
|
|
6
|
+
whoami Show the verified CLI identity
|
|
7
|
+
tokens List or revoke your active CLI tokens
|
|
8
|
+
report Submit a portable Legato issue bundle
|
|
9
|
+
search Search similar issues
|
|
10
|
+
fetch Fetch an issue and copy artifacts locally
|
|
11
|
+
comment Add an analysis/comment reply
|
|
12
|
+
status Update an issue status
|
|
13
|
+
description Rewrite the issue description
|
|
14
|
+
suggestion Submit or replace a kernel code suggestion
|
|
15
|
+
assign Assign an issue
|
|
16
|
+
blocker Link or sync a GitHub PR blocker
|
|
17
|
+
notifications List or mark remote board notifications read
|
|
18
|
+
announce Publish a site-wide feature announcement
|
|
19
|
+
sdk Inspect or update the site's prepared SDK packages
|
|
20
|
+
playground Upload and inspect Playground runs
|
|
21
|
+
feedback List, fetch, or resolve site feedback
|
|
22
|
+
issue Fetch or update issues
|
|
23
|
+
verify Run worker verification
|
|
24
|
+
keycloak Plan, apply, or verify safe Keycloak provisioning
|
|
25
|
+
skill Install the Legato Agora agent skills
|
|
26
|
+
help Show this help
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
lagora login
|
|
30
|
+
lagora report --kernel kernel.py --log error.log --title "lowering failure" --reporter alice
|
|
31
|
+
lagora tokens list
|
|
32
|
+
lagora tokens revoke --token <token-id>
|
|
33
|
+
lagora tokens revoke --all
|
|
34
|
+
lagora search --query "cgdma Xfer=0 lowering"
|
|
35
|
+
lagora fetch --issue <issue-id> --out ./issue-work
|
|
36
|
+
lagora comment --issue <issue-id> --body "Root cause looks like CORE_IR lowering."
|
|
37
|
+
lagora status --issue <issue-id> --status investigating
|
|
38
|
+
lagora description --issue <issue-id> --body "Updated reproduction steps."
|
|
39
|
+
lagora suggestion --issue <issue-id> --file fixed_kernel.py
|
|
40
|
+
lagora assign --issue <issue-id> --assignee alice
|
|
41
|
+
lagora blocker add --issue <issue-id> --pr 1234
|
|
42
|
+
lagora blocker sync --issue <issue-id> --blocker-id <blocker-id>
|
|
43
|
+
lagora issue notifications
|
|
44
|
+
lagora issue notifications read --notification <notification-id>
|
|
45
|
+
lagora feedback list
|
|
46
|
+
LAGORA_ANNOUNCEMENT_TOKEN=... lagora announce create --title "Playground" --body "Playground is now available." --link /playground
|
|
47
|
+
LAGORA_SDK_TOKEN=... lagora sdk status
|
|
48
|
+
LAGORA_SDK_TOKEN=... lagora sdk list --disk
|
|
49
|
+
LAGORA_SDK_TOKEN=... lagora sdk update --watch
|
|
50
|
+
lagora playground runtimes
|
|
51
|
+
lagora playground validate --kernel kernel.py
|
|
52
|
+
lagora playground run --kernel kernel.py --sdk-name prepared-runtime
|
|
53
|
+
lagora playground status --run <run-id>
|
|
54
|
+
lagora playground report --run <run-id> --title "CORE_IR regression"
|
|
55
|
+
lagora feedback fetch --status open
|
|
56
|
+
lagora feedback resolve --id <feedback-id>
|
|
57
|
+
lagora fetch --store /path/to/issue-store --issue <issue-id> --out ./issue-work
|
|
58
|
+
lagora comment --store /path/to/issue-store --issue <issue-id> --body "Root cause looks like CORE_IR lowering."
|
|
59
|
+
lagora status --store /path/to/issue-store --issue <issue-id> --status investigating
|
|
60
|
+
lagora description --store /path/to/issue-store --issue <issue-id> --body "Updated reproduction steps."
|
|
61
|
+
lagora verify --store /path/to/issue-store --issue <issue-id> --kind lowering --mode real --sdk-branch main
|
|
62
|
+
lagora keycloak plan --state-fixture fixtures/keycloak/empty-realm.fixture.json
|
|
63
|
+
lagora keycloak verify --claims-fixture fixtures/keycloak/tenant-claims.fixture.json
|
|
64
|
+
lagora keycloak verify --live
|
|
65
|
+
lagora skill install
|
|
66
|
+
|
|
67
|
+
Targets:
|
|
68
|
+
Commands talk to https://legato-dev-agora.hyperaccel.net/legato-dev-agora by default.
|
|
69
|
+
Pass --api-url <url> or set LAGORA_API_URL to use another deployment, and
|
|
70
|
+
pass --store <path> to work against a local or mounted issue store instead.
|
package/dist/lagora.js
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// packages/cli/src/lagora.ts
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
// packages/cli/src/dispatch.ts
|
|
9
|
+
var aliases = {
|
|
10
|
+
fetch: "checkout",
|
|
11
|
+
comment: "comment",
|
|
12
|
+
status: "status",
|
|
13
|
+
description: "description",
|
|
14
|
+
suggestion: "suggestion",
|
|
15
|
+
assign: "assign",
|
|
16
|
+
blocker: "blocker",
|
|
17
|
+
notifications: "notifications"
|
|
18
|
+
};
|
|
19
|
+
var scripts = {
|
|
20
|
+
report: "report",
|
|
21
|
+
search: "issue-search",
|
|
22
|
+
issue: "issue",
|
|
23
|
+
verify: "verify-issue",
|
|
24
|
+
announce: "announce",
|
|
25
|
+
sdk: "sdk",
|
|
26
|
+
playground: "playground",
|
|
27
|
+
feedback: "site-feedback",
|
|
28
|
+
keycloak: "keycloak-provision",
|
|
29
|
+
skill: "install-skill"
|
|
30
|
+
};
|
|
31
|
+
var authCommands = /* @__PURE__ */ new Set(["login", "logout", "whoami", "tokens"]);
|
|
32
|
+
function commandTarget(command, argv) {
|
|
33
|
+
if (authCommands.has(command)) return { script: "cli-auth", argv: [command, ...argv] };
|
|
34
|
+
if (Object.hasOwn(aliases, command)) return { script: "issue", argv: [aliases[command], ...argv] };
|
|
35
|
+
if (Object.hasOwn(scripts, command)) return { script: scripts[command], argv: [...argv] };
|
|
36
|
+
throw new Error(`Unknown command: ${command}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// packages/cli/src/update.ts
|
|
40
|
+
import { execFile } from "node:child_process";
|
|
41
|
+
import { randomUUID } from "node:crypto";
|
|
42
|
+
import { accessSync, constants, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readlinkSync, realpathSync, renameSync, rmSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
|
|
43
|
+
import { homedir } from "node:os";
|
|
44
|
+
import path from "node:path";
|
|
45
|
+
import { promisify } from "node:util";
|
|
46
|
+
var execute = promisify(execFile);
|
|
47
|
+
var packageName = "lagora-cli";
|
|
48
|
+
var stable = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
49
|
+
var interval = 24 * 60 * 60 * 1e3;
|
|
50
|
+
var UpdateError = class extends Error {
|
|
51
|
+
constructor(reason) {
|
|
52
|
+
super(reason);
|
|
53
|
+
this.reason = reason;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
function isNewerStable(candidate, current) {
|
|
57
|
+
if (typeof candidate !== "string" || typeof current !== "string") return false;
|
|
58
|
+
const next = stable.exec(candidate);
|
|
59
|
+
const previous = stable.exec(current);
|
|
60
|
+
if (!next || !previous) return false;
|
|
61
|
+
for (const index of [1, 2, 3]) {
|
|
62
|
+
const a = BigInt(next[index] ?? "0");
|
|
63
|
+
const b = BigInt(previous[index] ?? "0");
|
|
64
|
+
if (a !== b) return a > b;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
function code(error, expected) {
|
|
69
|
+
return error instanceof Error && "code" in error && error.code === expected;
|
|
70
|
+
}
|
|
71
|
+
function json(file) {
|
|
72
|
+
const value = JSON.parse(readFileSync(file, "utf8"));
|
|
73
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new UpdateError("invalid-metadata");
|
|
74
|
+
return Object.fromEntries(Object.entries(value));
|
|
75
|
+
}
|
|
76
|
+
function settingsPath(env) {
|
|
77
|
+
return path.join(env.XDG_CONFIG_HOME || path.join(env.HOME || homedir(), ".config"), "lagora", "update.json");
|
|
78
|
+
}
|
|
79
|
+
async function setAutoUpdate(enabled, env = process.env) {
|
|
80
|
+
const file = settingsPath(env);
|
|
81
|
+
mkdirSync(path.dirname(file), { recursive: true, mode: 448 });
|
|
82
|
+
const temporary = `${file}.${randomUUID()}`;
|
|
83
|
+
try {
|
|
84
|
+
writeFileSync(temporary, JSON.stringify({ enabled }), { mode: 384, flag: "wx" });
|
|
85
|
+
renameSync(temporary, file);
|
|
86
|
+
} finally {
|
|
87
|
+
rmSync(temporary, { force: true });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function autoEnabled(env) {
|
|
91
|
+
if (/^(0|false|off)$/i.test(env.LAGORA_AUTO_UPDATE ?? "")) return false;
|
|
92
|
+
try {
|
|
93
|
+
return json(settingsPath(env)).enabled !== false;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if (code(error, "ENOENT")) return true;
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function installation(packageRoot) {
|
|
100
|
+
if (process.platform === "win32" || process.getuid?.() === 0) return void 0;
|
|
101
|
+
const runtime = path.resolve(packageRoot);
|
|
102
|
+
const retained = path.basename(path.dirname(runtime)) === "releases" && path.basename(path.resolve(runtime, "../..")) === ".lagora-update";
|
|
103
|
+
const prefix = path.resolve(runtime, "../../..");
|
|
104
|
+
const root2 = path.join(prefix, "lib/node_modules", packageName);
|
|
105
|
+
if (!retained && runtime !== root2) return void 0;
|
|
106
|
+
try {
|
|
107
|
+
if (realpathSync(root2) !== root2 || realpathSync(runtime) !== runtime) return void 0;
|
|
108
|
+
for (let ancestor = root2; ; ancestor = path.dirname(ancestor)) {
|
|
109
|
+
if (existsSync(path.join(ancestor, ".git"))) return void 0;
|
|
110
|
+
if (ancestor === path.dirname(ancestor)) break;
|
|
111
|
+
}
|
|
112
|
+
for (const directory of [prefix, path.join(prefix, "lib/node_modules"), root2, runtime, path.join(prefix, "bin")]) {
|
|
113
|
+
const info = lstatSync(directory);
|
|
114
|
+
if (!info.isDirectory() || info.uid !== process.getuid?.() || (info.mode & 128) === 0) return void 0;
|
|
115
|
+
accessSync(directory, constants.W_OK);
|
|
116
|
+
}
|
|
117
|
+
const manifest = json(path.join(root2, "package.json"));
|
|
118
|
+
const bin = manifest.bin;
|
|
119
|
+
if (manifest.name !== packageName || typeof manifest.version !== "string" || !stable.test(manifest.version) || !bin || typeof bin !== "object" || Reflect.get(bin, "lagora") !== "dist/lagora.js" || realpathSync(path.join(prefix, "bin/lagora")) !== path.join(root2, "dist/lagora.js")) return void 0;
|
|
120
|
+
return { root: root2, prefix, version: manifest.version, state: path.join(prefix, ".lagora-update") };
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (error instanceof Error) return void 0;
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function alive(owner) {
|
|
127
|
+
const pid = Number(owner.split("-")[0]);
|
|
128
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) return true;
|
|
129
|
+
try {
|
|
130
|
+
process.kill(pid, 0);
|
|
131
|
+
return true;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (code(error, "ESRCH")) return false;
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function stateDirectory(install) {
|
|
138
|
+
mkdirSync(install.state, { recursive: true, mode: 448 });
|
|
139
|
+
const info = lstatSync(install.state);
|
|
140
|
+
if (!info.isDirectory() || info.uid !== process.getuid?.()) throw new UpdateError("unsafe-state-directory");
|
|
141
|
+
}
|
|
142
|
+
function lock(file) {
|
|
143
|
+
const owner = `${process.pid}-${randomUUID()}`;
|
|
144
|
+
try {
|
|
145
|
+
symlinkSync(owner, file);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
if (!code(error, "EEXIST")) throw error;
|
|
148
|
+
let previous;
|
|
149
|
+
try {
|
|
150
|
+
previous = readlinkSync(file);
|
|
151
|
+
} catch (readError) {
|
|
152
|
+
if (code(readError, "ENOENT")) return lock(file);
|
|
153
|
+
throw readError;
|
|
154
|
+
}
|
|
155
|
+
if (!/^\d+-[0-9a-f-]{36}$/.test(previous) || alive(previous)) return void 0;
|
|
156
|
+
const releaseReaper = lock(`${file}.reap-${previous}`);
|
|
157
|
+
if (!releaseReaper) return void 0;
|
|
158
|
+
try {
|
|
159
|
+
try {
|
|
160
|
+
if (readlinkSync(file) === previous) unlinkSync(file);
|
|
161
|
+
} catch (readError) {
|
|
162
|
+
if (!code(readError, "ENOENT")) throw readError;
|
|
163
|
+
}
|
|
164
|
+
} finally {
|
|
165
|
+
releaseReaper();
|
|
166
|
+
}
|
|
167
|
+
return lock(file);
|
|
168
|
+
}
|
|
169
|
+
return () => {
|
|
170
|
+
if (readlinkSync(file) === owner) unlinkSync(file);
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
function npmEnvironment(env) {
|
|
174
|
+
return { NODE_ENV: env.NODE_ENV ?? "production", ...Object.fromEntries(Object.entries(env).filter(([key]) => /^(npm_|path$|home$|userprofile$|appdata$|localappdata$|tmpdir$|temp$|tmp$|systemroot$|comspec$|pathext$|https?_proxy$|no_proxy$|node_extra_ca_certs$|ssl_cert_file$)/i.test(key))) };
|
|
175
|
+
}
|
|
176
|
+
async function npm(args, env, timeout) {
|
|
177
|
+
try {
|
|
178
|
+
const result = await execute("npm", [...args, "--fetch-retries=0", "--fetch-timeout=2000", "--update-notifier=false", "--no-audit", "--no-fund"], {
|
|
179
|
+
cwd: env.HOME || homedir(),
|
|
180
|
+
env: npmEnvironment(env),
|
|
181
|
+
timeout,
|
|
182
|
+
killSignal: "SIGKILL",
|
|
183
|
+
maxBuffer: 1024 * 1024
|
|
184
|
+
});
|
|
185
|
+
return result.stdout.trim();
|
|
186
|
+
} catch (error) {
|
|
187
|
+
if (error instanceof Error) throw new UpdateError("npm-unavailable-or-failed");
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function promote(install, staged) {
|
|
192
|
+
const releases = path.join(install.state, "releases");
|
|
193
|
+
mkdirSync(releases, { recursive: true, mode: 448 });
|
|
194
|
+
const retained = path.join(releases, randomUUID());
|
|
195
|
+
renameSync(staged, retained);
|
|
196
|
+
const launcher = path.join(install.root, "dist/lagora.js");
|
|
197
|
+
const temporary = `${launcher}.${randomUUID()}`;
|
|
198
|
+
const manifest = path.join(install.root, "package.json");
|
|
199
|
+
const metadata = `${manifest}.${randomUUID()}`;
|
|
200
|
+
const relative = path.relative(path.dirname(launcher), path.join(retained, "dist/lagora.js"));
|
|
201
|
+
try {
|
|
202
|
+
writeFileSync(temporary, `#!/usr/bin/env node
|
|
203
|
+
await import(new URL(${JSON.stringify(relative)}, import.meta.url));
|
|
204
|
+
`, { mode: 493, flag: "wx" });
|
|
205
|
+
writeFileSync(metadata, readFileSync(path.join(retained, "package.json")), { mode: 420, flag: "wx" });
|
|
206
|
+
renameSync(temporary, launcher);
|
|
207
|
+
renameSync(metadata, manifest);
|
|
208
|
+
} finally {
|
|
209
|
+
rmSync(temporary, { force: true });
|
|
210
|
+
rmSync(metadata, { force: true });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async function runUpdate(options) {
|
|
214
|
+
const env = options.env ?? process.env;
|
|
215
|
+
let release;
|
|
216
|
+
let staging;
|
|
217
|
+
try {
|
|
218
|
+
if (options.mode === "auto" && !autoEnabled(env)) return { status: "disabled" };
|
|
219
|
+
const install = installation(options.packageRoot);
|
|
220
|
+
if (!install || env.npm_command === "exec") return { status: "unsupported" };
|
|
221
|
+
stateDirectory(install);
|
|
222
|
+
release = lock(path.join(install.state, "install"));
|
|
223
|
+
if (!release) return { status: "busy" };
|
|
224
|
+
const now = (options.now ?? Date.now)();
|
|
225
|
+
const state = path.join(install.state, "checked.json");
|
|
226
|
+
let last;
|
|
227
|
+
try {
|
|
228
|
+
last = json(state).at;
|
|
229
|
+
} catch (error) {
|
|
230
|
+
if (!code(error, "ENOENT") && !(error instanceof SyntaxError)) throw error;
|
|
231
|
+
}
|
|
232
|
+
if (options.mode === "auto" && typeof last === "number" && now >= last && now - last < interval) return { status: "throttled" };
|
|
233
|
+
writeFileSync(state, JSON.stringify({ at: now }), { mode: 384 });
|
|
234
|
+
if (realpathSync(await npm(["prefix", "--global"], env, 3e3)) !== install.prefix) return { status: "unsupported" };
|
|
235
|
+
const raw = JSON.parse(await npm(["view", `${packageName}@latest`, "name", "version", "--json"], env, 4e3));
|
|
236
|
+
if (!raw || typeof raw !== "object" || Reflect.get(raw, "name") !== packageName) throw new UpdateError("registry-identity-mismatch");
|
|
237
|
+
const version = Reflect.get(raw, "version");
|
|
238
|
+
if (typeof version !== "string" || !stable.test(version)) throw new UpdateError("registry-version-invalid");
|
|
239
|
+
if (!isNewerStable(version, install.version)) return { status: "current", version: install.version };
|
|
240
|
+
staging = mkdtempSync(path.join(install.state, "stage-"));
|
|
241
|
+
const globalConfig = await npm(["config", "get", "globalconfig"], env, 3e3);
|
|
242
|
+
await npm(["install", "--global", "--prefix", staging, "--globalconfig", globalConfig, "--ignore-scripts", "--engine-strict", "--install-strategy=nested", "--package-lock=false", `${packageName}@${version}`], env, 45e3);
|
|
243
|
+
const staged = path.join(staging, "lib/node_modules", packageName);
|
|
244
|
+
const manifest = json(path.join(staged, "package.json"));
|
|
245
|
+
if (manifest.name !== packageName || manifest.version !== version || !manifest.bin || typeof manifest.bin !== "object" || Reflect.get(manifest.bin, "lagora") !== "dist/lagora.js" || !lstatSync(path.join(staged, "dist/lagora.js")).isFile()) throw new UpdateError("installed-identity-mismatch");
|
|
246
|
+
promote(install, staged);
|
|
247
|
+
return { status: "updated", version };
|
|
248
|
+
} catch (error) {
|
|
249
|
+
if (error instanceof Error) return { status: "failed", reason: error instanceof UpdateError ? error.reason : "update-io-failed" };
|
|
250
|
+
throw error;
|
|
251
|
+
} finally {
|
|
252
|
+
try {
|
|
253
|
+
if (staging) rmSync(staging, { recursive: true, force: true });
|
|
254
|
+
} finally {
|
|
255
|
+
release?.();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
async function beforeCommand(argv, packageRoot) {
|
|
260
|
+
if (argv[0] === "update") {
|
|
261
|
+
if (argv.length === 2 && ["--enable", "--disable"].includes(argv[1] ?? "")) {
|
|
262
|
+
await setAutoUpdate(argv[1] === "--enable");
|
|
263
|
+
console.log(`Automatic updates ${argv[1] === "--enable" ? "enabled" : "disabled"}.`);
|
|
264
|
+
} else if (argv.length === 2 && ["--help", "-h"].includes(argv[1] ?? "")) {
|
|
265
|
+
console.log("Usage: lagora update [--enable|--disable]\nUpdates a user-owned global npm install. Automatic updates run after commands, at most daily. LAGORA_AUTO_UPDATE=0 disables automatic updates.");
|
|
266
|
+
} else if (argv.length > 1) {
|
|
267
|
+
console.error("Usage: lagora update [--enable|--disable]");
|
|
268
|
+
process.exitCode = 1;
|
|
269
|
+
} else {
|
|
270
|
+
const result = await runUpdate({ packageRoot, mode: "manual" });
|
|
271
|
+
console.log(JSON.stringify(result));
|
|
272
|
+
if (!["updated", "current"].includes(result.status)) process.exitCode = 1;
|
|
273
|
+
}
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
async function afterCommand(packageRoot) {
|
|
279
|
+
try {
|
|
280
|
+
const result = await runUpdate({ packageRoot, mode: "auto" });
|
|
281
|
+
if (result.status === "updated") console.error(`lagora updated to ${result.version}; the next command uses the new version.`);
|
|
282
|
+
if (result.status === "failed") console.error(`lagora auto-update unavailable (${result.reason}); your command result is unchanged.`);
|
|
283
|
+
} catch (error) {
|
|
284
|
+
if (error instanceof Error) console.error("lagora auto-update cleanup failed; your command result is unchanged.");
|
|
285
|
+
else throw error;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// packages/cli/src/lagora.ts
|
|
290
|
+
var root = new URL("./", import.meta.url);
|
|
291
|
+
async function run(script, argv) {
|
|
292
|
+
const child = spawn(process.execPath, [fileURLToPath(new URL(`scripts/${script}.js`, root)), ...argv], { stdio: "inherit" });
|
|
293
|
+
const result = await new Promise((resolve, reject) => {
|
|
294
|
+
child.once("error", reject);
|
|
295
|
+
child.once("exit", (code2, signal) => resolve({ code: code2, signal }));
|
|
296
|
+
});
|
|
297
|
+
if (result.signal) {
|
|
298
|
+
process.kill(process.pid, result.signal);
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
process.exitCode = result.code ?? 1;
|
|
302
|
+
return result.code === 0;
|
|
303
|
+
}
|
|
304
|
+
async function main() {
|
|
305
|
+
const argv = process.argv.slice(2);
|
|
306
|
+
const packageRoot = fileURLToPath(new URL("../", root));
|
|
307
|
+
if (await beforeCommand(argv, packageRoot)) return;
|
|
308
|
+
try {
|
|
309
|
+
await dispatch(argv);
|
|
310
|
+
} finally {
|
|
311
|
+
await afterCommand(packageRoot);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
async function dispatch(argv) {
|
|
315
|
+
const command = argv[0] ?? "help";
|
|
316
|
+
if (command === "--version" || command === "-v") {
|
|
317
|
+
const manifest = JSON.parse(await readFile(new URL("../package.json", root), "utf8"));
|
|
318
|
+
console.log(manifest.version);
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
if (["help", "--help", "-h"].includes(command)) {
|
|
322
|
+
process.stdout.write(await readFile(new URL("help.txt", root), "utf8"));
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
const args = argv.slice(1);
|
|
326
|
+
const target = commandTarget(command, args);
|
|
327
|
+
if (args.includes("--store")) process.env.AGORA_ISSUE_STORE_MODE = "isolated";
|
|
328
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
329
|
+
process.stdout.write(await readFile(new URL(command === "report" ? "report-help.txt" : "help.txt", root), "utf8"));
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (!authCommands.has(command) && !await run("cli-auth", ["require-auth", command, ...args])) return;
|
|
333
|
+
if (["sdk", "announce"].includes(command) && !args.includes("--api-url")) {
|
|
334
|
+
const { LagoraCliConfigStore } = await import("./scripts/cli-config-IA7EOSYD.js");
|
|
335
|
+
target.argv.push("--api-url", (await new LagoraCliConfigStore().apiSession(void 0)).apiUrl);
|
|
336
|
+
}
|
|
337
|
+
await run(target.script, target.argv);
|
|
338
|
+
}
|
|
339
|
+
main().catch((error) => {
|
|
340
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
341
|
+
process.exitCode = 1;
|
|
342
|
+
});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Usage: lagora report --kernel path --title title --reporter name [--log path] [--description text | --description-file path] [--api-url url | --store path] [--idempotency-key key] [--auto-lowering] [--sdk-root path] [--python python3] [--stages MLIR,CORE_IR] [--kernel-function name]
|
|
2
|
+
|
|
3
|
+
Creates a portable issue bundle under issue-store/issues/<id>/ by default.
|
|
4
|
+
Pass --api-url to submit to the internal Agora server.
|
|
5
|
+
Pass --store to write into a local or mounted issue repository.
|