aiur-cli 0.0.4 → 0.0.5-nightly.32b9365
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 +89 -54
- package/bin/aiur.js +3 -233
- package/libexec/aiur-engine.sh +1930 -139
- package/libexec/guard-pr-deletions.sh +70 -0
- package/package.json +15 -7
- package/scripts/postinstall.mjs +60 -2
package/README.md
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
Oversee a fleet of coding agents that **coordinate via events**.
|
|
6
6
|
|
|
7
|
+
Aiur watches your issue tracker, dispatches one agent per ticket into its own
|
|
8
|
+
workspace and checkout, and gives you four ways to supervise the fleet instead
|
|
9
|
+
of babysitting individual coding sessions.
|
|
10
|
+
|
|
7
11
|
> [!WARNING]
|
|
8
12
|
> **Use at your own risk.** Aiur is an unstable, vibecoded engineering preview for trusted
|
|
9
13
|
> environments only. It **bypasses all agent permission prompts** and has very few efficiency
|
|
@@ -12,73 +16,104 @@ Oversee a fleet of coding agents that **coordinate via events**.
|
|
|
12
16
|
> Provided "as is", without warranty of any kind. You assume all risk for any cost, token
|
|
13
17
|
> spend, data loss, or damage from running it.
|
|
14
18
|
|
|
15
|
-
## Install
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
npm install -g aiur-cli
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
Requires [tmux](https://github.com/tmux/tmux) at runtime. Install
|
|
22
|
-
[opencode](https://opencode.ai) too for in-pane agent chat.
|
|
23
|
-
|
|
24
|
-
When a newer `aiur-cli` is published, a one-line update notice is printed to
|
|
25
|
-
stderr (checked in the background at most once a day, never blocking your
|
|
26
|
-
command). Set `AIUR_NO_UPDATE_NOTIFIER=1` to silence it; it is also skipped under
|
|
27
|
-
CI (`CI=true`).
|
|
28
|
-
|
|
29
19
|
## Get started
|
|
30
20
|
|
|
31
21
|
```bash
|
|
22
|
+
npm install -g aiur-cli # Node.js 18+
|
|
23
|
+
|
|
32
24
|
cd your-repo
|
|
33
|
-
aiur init
|
|
34
|
-
aiur
|
|
25
|
+
aiur init # interactive setup wizard
|
|
26
|
+
aiur # start the run
|
|
35
27
|
```
|
|
36
28
|
|
|
37
|
-
`aiur init` scaffolds `.
|
|
29
|
+
`aiur init` scaffolds `.aiur/config` and provisions the repo: tracker and
|
|
30
|
+
repository, agent backends and routing, concurrency and turn limits, the GitHub
|
|
31
|
+
token, and the `agent:*` lifecycle labels. Re-running resumes from your saved
|
|
32
|
+
answers.
|
|
33
|
+
|
|
34
|
+
When setup finishes, label the issues you want worked `agent:todo` and run
|
|
35
|
+
`aiur`.
|
|
36
|
+
|
|
37
|
+
| Requirement | Why |
|
|
38
|
+
| --- | --- |
|
|
39
|
+
| [tmux](https://github.com/tmux/tmux) | Runtime for the TUI and agent panes |
|
|
40
|
+
| [opencode](https://opencode.ai) | In-pane agent chat (provisioned on install) |
|
|
41
|
+
| Claude Code or Codex | The agents themselves |
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
2. **Tracker** — GitHub or Linear, plus the repo.
|
|
41
|
-
3. **Agents & routing** — Claude and/or Codex, optional per-complexity model
|
|
42
|
-
routing, and the permission mode.
|
|
43
|
-
4. **Limits** — max concurrent agents, turns, duration, pre-warmed sessions, and
|
|
44
|
-
polling interval.
|
|
45
|
-
5. **GitHub token** — to create labels and act as the bot account.
|
|
46
|
-
6. **Labels** — the lifecycle (`agent:*`), complexity, model, and remote-control
|
|
47
|
-
labels aiur routes on. Only missing labels are created; a group that already
|
|
48
|
-
exists is reported as created and skipped.
|
|
43
|
+
## The four surfaces
|
|
49
44
|
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
| Surface | What it gives you | How to open it |
|
|
46
|
+
| --- | --- | --- |
|
|
47
|
+
| **CLI** | Scriptable control of a run from any shell | `aiur status`, `aiur pause 142` |
|
|
48
|
+
| **TUI** | Live agent board plus chat panes, in your terminal | `aiur` (foreground) |
|
|
49
|
+
| **Dashboard** | Browser view of the fleet, decisions, and analytics | `http://127.0.0.1:4000` |
|
|
50
|
+
| **Stream Deck** | Physical keys and dictation for the fleet | Separate sidecar, see below |
|
|
52
51
|
|
|
53
|
-
|
|
52
|
+
### CLI
|
|
54
53
|
|
|
55
54
|
| Command | What it does |
|
|
56
|
-
|
|
57
|
-
| `aiur init` | Interactive setup wizard
|
|
58
|
-
| `aiur` | Start the
|
|
59
|
-
| `aiur --bg` | Start a headless
|
|
60
|
-
| `aiur status` |
|
|
61
|
-
| `aiur alerts
|
|
62
|
-
| `aiur pause <id…>` / `resume <id…>` | Pause or resume agents by issue
|
|
63
|
-
| `aiur
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| `aiur init` | Interactive setup wizard |
|
|
57
|
+
| `aiur` | Start the run in the foreground with the TUI |
|
|
58
|
+
| `aiur --bg` | Start a detached headless run, dashboard still served |
|
|
59
|
+
| `aiur status` | Daemon state, active agents, and the current concurrency cap |
|
|
60
|
+
| `aiur alerts --needs-attention` | Unresolved items waiting on you |
|
|
61
|
+
| `aiur pause <id…>` / `aiur resume <id…>` | Pause or resume agents by issue id |
|
|
62
|
+
| `aiur message <id> "<text>"` | Send text into a live agent session |
|
|
63
|
+
| `aiur set max-agents <n>` | Change the concurrency cap without restarting |
|
|
64
|
+
| `aiur --todo <id…> [--only]` | Queue tickets; `--only` dequeues the rest |
|
|
65
|
+
| `aiur stop` / `aiur restart` | Stop the session, or refresh the release and restart |
|
|
66
|
+
|
|
67
|
+
Add `--no-dashboard` to either mode to skip the web listener.
|
|
68
|
+
|
|
69
|
+
### TUI
|
|
70
|
+
|
|
71
|
+
The foreground run opens a tmux board with one row per ticket, showing runtime,
|
|
72
|
+
turn count, backend, pinned model, and a state glyph. Press `enter` on a row to
|
|
73
|
+
open that agent's chat pane beside the board, `space` to pause or resume, `←`
|
|
74
|
+
and `→` to change the concurrency cap, and `?` for the full keymap.
|
|
75
|
+
|
|
76
|
+
### Dashboard
|
|
77
|
+
|
|
78
|
+
Served on foreground and `--bg` runs at the host and port in your config,
|
|
79
|
+
`http://127.0.0.1:4000` by default. The launch output prints the real URL.
|
|
80
|
+
|
|
81
|
+
| Page | What it shows |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| Units | Fleet table, ticket backlog, provider rate meters |
|
|
84
|
+
| Commands | Durable decision inbox: answer, revise, retry delivery |
|
|
85
|
+
| Build Order | Ticket dependency graph and phases |
|
|
86
|
+
| Analytics | Throughput, complexity tiers, and spend over a chosen window |
|
|
87
|
+
|
|
88
|
+
Writable mode is the default and requires `AIUR_DASHBOARD_USERNAME` and
|
|
89
|
+
`AIUR_DASHBOARD_PASSWORD`, even on loopback. Set
|
|
90
|
+
`observability.dashboard_writable: false` for an unauthenticated read-only
|
|
91
|
+
loopback dashboard. Basic Auth is unencrypted, so put a TLS proxy in front of any
|
|
92
|
+
non-loopback bind.
|
|
93
|
+
|
|
94
|
+
### Stream Deck
|
|
95
|
+
|
|
96
|
+
An Elgato Stream Deck + can drive the fleet: agent keys ranked by urgency, pause
|
|
97
|
+
and resume, a live log surface, and hold-to-dictate voice messages. A browser
|
|
98
|
+
emulator of the same layout is at `/streamdeck` on the dashboard.
|
|
99
|
+
|
|
100
|
+
> The physical sidecar is **experimental** and ships separately from this
|
|
101
|
+
> package, as a Linux x64 archive (glibc 2.28+, systemd user session). It is not
|
|
102
|
+
> installed by `npm install aiur-cli`. See the
|
|
103
|
+
> [Stream Deck guide](https://aiur.team/docs/guide/stream-deck).
|
|
104
|
+
|
|
105
|
+
## Update notices
|
|
106
|
+
|
|
107
|
+
When a newer `aiur-cli` is published, a one-line notice is printed to stderr,
|
|
108
|
+
checked in the background at most once a day and never blocking your command.
|
|
109
|
+
Set `AIUR_NO_UPDATE_NOTIFIER=1` to silence it; it is skipped under CI.
|
|
110
|
+
|
|
111
|
+
Prerelease builds are published under the `nightly` dist-tag and never reach
|
|
112
|
+
`latest`, so a plain `npm install -g aiur-cli` always gives you a stable release.
|
|
113
|
+
To try one: `npm install -g aiur-cli@nightly`.
|
|
79
114
|
|
|
80
115
|
---
|
|
81
116
|
|
|
82
117
|
_Command macro, delegate micro, maximize APM._
|
|
83
118
|
|
|
84
|
-
[github.com/
|
|
119
|
+
[github.com/aiur-team/aiur](https://github.com/aiur-team/aiur)
|
package/bin/aiur.js
CHANGED
|
@@ -49,8 +49,8 @@ function resolveReleaseDir() {
|
|
|
49
49
|
if (!triple) {
|
|
50
50
|
fail(
|
|
51
51
|
`unsupported platform ${process.platform}/${process.arch}. ` +
|
|
52
|
-
"Supported:
|
|
53
|
-
"See https://github.com/
|
|
52
|
+
"Supported: linux (x64/arm64) and darwin (arm64). " +
|
|
53
|
+
"See https://github.com/aiur-team/aiur",
|
|
54
54
|
);
|
|
55
55
|
}
|
|
56
56
|
|
|
@@ -64,7 +64,7 @@ function resolveReleaseDir() {
|
|
|
64
64
|
`platform package "${pkg}" is not installed. ` +
|
|
65
65
|
"This usually means optional dependencies were skipped " +
|
|
66
66
|
"(--no-optional, a shared lockfile, or a known npm install bug). " +
|
|
67
|
-
"Reinstall aiur-cli, or see https://github.com/
|
|
67
|
+
"Reinstall aiur-cli, or see https://github.com/aiur-team/aiur",
|
|
68
68
|
);
|
|
69
69
|
}
|
|
70
70
|
throw err;
|
|
@@ -203,228 +203,6 @@ function ensureOpencode() {
|
|
|
203
203
|
);
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
// ---------------------------------------------------------------------------
|
|
207
|
-
// Update notifier
|
|
208
|
-
//
|
|
209
|
-
// When a newer aiur-cli is published, surface a one-line "update available"
|
|
210
|
-
// notice — npm-update-notifier style: every run reads a small cache file and
|
|
211
|
-
// prints from it; the registry is only hit in a detached background worker,
|
|
212
|
-
// at most once per interval, so the check never blocks or errors the command.
|
|
213
|
-
// The notice always goes to stderr so piped stdout stays clean.
|
|
214
|
-
//
|
|
215
|
-
// This file is its own background worker: main() re-spawns it with
|
|
216
|
-
// AIUR_UPDATE_NOTIFIER_WORKER=1 set, which short-circuits to runUpdateWorker().
|
|
217
|
-
// Keeps the launcher dependency-free (a tiny semver compare + https.get).
|
|
218
|
-
// ---------------------------------------------------------------------------
|
|
219
|
-
|
|
220
|
-
const UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24h
|
|
221
|
-
const REGISTRY_LATEST_URL = "https://registry.npmjs.org/aiur-cli/latest";
|
|
222
|
-
// Unpublished/source builds carry this sentinel; never nag on a dev checkout.
|
|
223
|
-
const DEV_VERSION = "0.0.0";
|
|
224
|
-
|
|
225
|
-
function currentVersion() {
|
|
226
|
-
try {
|
|
227
|
-
const pkg = JSON.parse(
|
|
228
|
-
fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8"),
|
|
229
|
-
);
|
|
230
|
-
return typeof pkg.version === "string" ? pkg.version : null;
|
|
231
|
-
} catch (_) {
|
|
232
|
-
return null;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Minimal semver: parse major.minor.patch with an optional prerelease tag.
|
|
237
|
-
// Enough for the launcher's own clean release versions — not a full spec impl.
|
|
238
|
-
// The regex is fully anchored (and tolerates a +build suffix) so a registry- or
|
|
239
|
-
// cache-supplied string with trailing junk — e.g. control/ANSI bytes meant to
|
|
240
|
-
// hijack the terminal when the notice is printed — fails to parse and is treated
|
|
241
|
-
// as not-newer rather than reaching printUpdateNotice verbatim.
|
|
242
|
-
function parseSemver(value) {
|
|
243
|
-
const match = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/.exec(
|
|
244
|
-
String(value).trim(),
|
|
245
|
-
);
|
|
246
|
-
if (!match) return null;
|
|
247
|
-
return { major: +match[1], minor: +match[2], patch: +match[3], pre: match[4] || "" };
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// True when `a` is strictly newer than `b`. A release outranks a prerelease at
|
|
251
|
-
// the same core version (1.0.0 > 1.0.0-rc.1); unparseable input is never newer.
|
|
252
|
-
function semverGt(a, b) {
|
|
253
|
-
const pa = parseSemver(a);
|
|
254
|
-
const pb = parseSemver(b);
|
|
255
|
-
if (!pa || !pb) return false;
|
|
256
|
-
if (pa.major !== pb.major) return pa.major > pb.major;
|
|
257
|
-
if (pa.minor !== pb.minor) return pa.minor > pb.minor;
|
|
258
|
-
if (pa.patch !== pb.patch) return pa.patch > pb.patch;
|
|
259
|
-
if (pa.pre === pb.pre) return false;
|
|
260
|
-
if (!pa.pre) return true; // a is a release, b is a prerelease
|
|
261
|
-
if (!pb.pre) return false; // a is a prerelease, b is a release
|
|
262
|
-
return pa.pre > pb.pre; // both prereleases: lexical tiebreak
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
function cacheFile() {
|
|
266
|
-
const base = process.env.XDG_CACHE_HOME
|
|
267
|
-
? path.join(process.env.XDG_CACHE_HOME, "aiur")
|
|
268
|
-
: path.join(os.homedir(), ".aiur");
|
|
269
|
-
return path.join(base, "update-check.json");
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
function readCache() {
|
|
273
|
-
try {
|
|
274
|
-
return JSON.parse(fs.readFileSync(cacheFile(), "utf8"));
|
|
275
|
-
} catch (_) {
|
|
276
|
-
return null;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function writeCache(data) {
|
|
281
|
-
try {
|
|
282
|
-
const file = cacheFile();
|
|
283
|
-
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
284
|
-
// Write-then-rename so a reader never sees a half-written file: if the
|
|
285
|
-
// detached worker is killed mid-write, or two workers race, the rename is
|
|
286
|
-
// atomic on POSIX and a torn cache can't trigger a respawn-every-run loop.
|
|
287
|
-
const tmp = `${file}.${process.pid}.tmp`;
|
|
288
|
-
fs.writeFileSync(tmp, JSON.stringify(data));
|
|
289
|
-
fs.renameSync(tmp, file);
|
|
290
|
-
} catch (_) {
|
|
291
|
-
// A missing-cache write must never break the run; silently skip.
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Treat any non-falsy CI value except an explicit "false"/"0" as a CI run.
|
|
296
|
-
function isCI() {
|
|
297
|
-
const value = process.env.CI;
|
|
298
|
-
return !!value && value !== "false" && value !== "0";
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
function notifierDisabled(current) {
|
|
302
|
-
return (
|
|
303
|
-
process.env.AIUR_NO_UPDATE_NOTIFIER === "1" ||
|
|
304
|
-
isCI() ||
|
|
305
|
-
!current ||
|
|
306
|
-
current === DEV_VERSION
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
function printUpdateNotice(current, latest) {
|
|
311
|
-
process.stderr.write(
|
|
312
|
-
`aiur: a new version is available — ${current} → ${latest}\n` +
|
|
313
|
-
" update: npm install -g aiur-cli@latest\n",
|
|
314
|
-
);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Synchronous, fast, never-throwing: print from cache when a newer version is
|
|
318
|
-
// known, then kick off a detached refresh if the cache is stale. The notice is
|
|
319
|
-
// TTY-gated so non-interactive/piped invocations stay quiet.
|
|
320
|
-
function maybeNotifyUpdate() {
|
|
321
|
-
try {
|
|
322
|
-
const current = currentVersion();
|
|
323
|
-
if (notifierDisabled(current)) return;
|
|
324
|
-
|
|
325
|
-
const cache = readCache();
|
|
326
|
-
if (
|
|
327
|
-
cache &&
|
|
328
|
-
cache.latest &&
|
|
329
|
-
process.stderr.isTTY &&
|
|
330
|
-
semverGt(cache.latest, current)
|
|
331
|
-
) {
|
|
332
|
-
printUpdateNotice(current, cache.latest);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
const stale =
|
|
336
|
-
!cache ||
|
|
337
|
-
typeof cache.lastCheck !== "number" ||
|
|
338
|
-
Date.now() - cache.lastCheck > UPDATE_CHECK_INTERVAL_MS;
|
|
339
|
-
if (stale) spawnUpdateWorker();
|
|
340
|
-
} catch (_) {
|
|
341
|
-
// The version check is best-effort; never let it affect the command.
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
function spawnUpdateWorker() {
|
|
346
|
-
try {
|
|
347
|
-
const child = spawn(process.execPath, [__filename], {
|
|
348
|
-
detached: true,
|
|
349
|
-
stdio: "ignore",
|
|
350
|
-
env: { ...process.env, AIUR_UPDATE_NOTIFIER_WORKER: "1" },
|
|
351
|
-
});
|
|
352
|
-
child.unref();
|
|
353
|
-
} catch (_) {
|
|
354
|
-
// If we can't detach a worker, just skip — the next run retries.
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// Fetch the `latest` dist-tag's version. Bounded by a timeout and a response
|
|
359
|
-
// cap; any error resolves to null so the worker stays silent when offline.
|
|
360
|
-
function fetchLatestVersion(url, done) {
|
|
361
|
-
let settled = false;
|
|
362
|
-
const finish = (value) => {
|
|
363
|
-
if (settled) return;
|
|
364
|
-
settled = true;
|
|
365
|
-
done(value);
|
|
366
|
-
};
|
|
367
|
-
try {
|
|
368
|
-
const transport = url.startsWith("http://") ? require("node:http") : require("node:https");
|
|
369
|
-
const req = transport.get(
|
|
370
|
-
url,
|
|
371
|
-
{ headers: { Accept: "application/json" }, timeout: 5000 },
|
|
372
|
-
(res) => {
|
|
373
|
-
if (res.statusCode !== 200) {
|
|
374
|
-
res.resume();
|
|
375
|
-
return finish(null);
|
|
376
|
-
}
|
|
377
|
-
let body = "";
|
|
378
|
-
res.setEncoding("utf8");
|
|
379
|
-
res.on("data", (chunk) => {
|
|
380
|
-
body += chunk;
|
|
381
|
-
// Abort an oversized body. req.destroy() emits only "close" (no "end"
|
|
382
|
-
// or "error"), so resolve here explicitly — otherwise the worker would
|
|
383
|
-
// hang forever and never stamp the cache, respawning on every run.
|
|
384
|
-
if (body.length > 1_000_000) {
|
|
385
|
-
req.destroy();
|
|
386
|
-
finish(null);
|
|
387
|
-
}
|
|
388
|
-
});
|
|
389
|
-
res.on("end", () => {
|
|
390
|
-
try {
|
|
391
|
-
const version = JSON.parse(body).version;
|
|
392
|
-
finish(typeof version === "string" ? version : null);
|
|
393
|
-
} catch (_) {
|
|
394
|
-
finish(null);
|
|
395
|
-
}
|
|
396
|
-
});
|
|
397
|
-
},
|
|
398
|
-
);
|
|
399
|
-
// A timeout's req.destroy() likewise emits only "close", so resolve here too
|
|
400
|
-
// rather than waiting on an event that never comes. "error" covers the
|
|
401
|
-
// connection-failure case (offline, refused).
|
|
402
|
-
req.on("timeout", () => {
|
|
403
|
-
req.destroy();
|
|
404
|
-
finish(null);
|
|
405
|
-
});
|
|
406
|
-
req.on("error", () => finish(null));
|
|
407
|
-
} catch (_) {
|
|
408
|
-
finish(null);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// Detached worker entrypoint: refresh the cache, then exit. Always stamps
|
|
413
|
-
// lastCheck (even on failure) so an offline machine doesn't respawn every run.
|
|
414
|
-
function runUpdateWorker() {
|
|
415
|
-
const previous = readCache();
|
|
416
|
-
const url = process.env.AIUR_REGISTRY_URL || REGISTRY_LATEST_URL;
|
|
417
|
-
fetchLatestVersion(url, (latest) => {
|
|
418
|
-
writeCache({
|
|
419
|
-
lastCheck: Date.now(),
|
|
420
|
-
// Keep the last known `latest` when the fetch fails so a transient outage
|
|
421
|
-
// doesn't drop a pending notice.
|
|
422
|
-
latest: latest || (previous && previous.latest) || null,
|
|
423
|
-
});
|
|
424
|
-
process.exit(0);
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
|
|
428
206
|
// `init` and `--version` run as foreground one-shots that never start the
|
|
429
207
|
// tmux-backed UI, so their tmux/opencode provisioning is irrelevant — and tmux
|
|
430
208
|
// may legitimately be absent on a machine that only ever runs `aiur init`.
|
|
@@ -438,14 +216,6 @@ function isForegroundOneShot(argv) {
|
|
|
438
216
|
}
|
|
439
217
|
|
|
440
218
|
function main() {
|
|
441
|
-
// Detached refresh worker: do only the registry check, never the real launch.
|
|
442
|
-
if (process.env.AIUR_UPDATE_NOTIFIER_WORKER === "1") {
|
|
443
|
-
runUpdateWorker();
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
maybeNotifyUpdate();
|
|
448
|
-
|
|
449
219
|
const releaseDir = resolveReleaseDir();
|
|
450
220
|
|
|
451
221
|
if (!isForegroundOneShot(process.argv.slice(2))) {
|