@rynx-ai/cli 0.1.11-beta.5 → 0.1.11-beta.51

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.
@@ -0,0 +1,509 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
3
+ import { homedir, userInfo } from "node:os";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { rynxHome } from "@rynx-ai/core";
7
+ import { autostartLauncherArguments, currentShell, renderAutostartEnvironmentFile as renderSystemdEnvironmentFile, serviceEnvironmentPath, } from "./autostart-environment.js";
8
+ export { renderSystemdEnvironmentFile };
9
+ export const RYNX_SYSTEMD_SERVICE = "rynx.service";
10
+ export const RYNX_SYSTEMD_SERVICE_ENV = "RYNX_SYSTEMD_SERVICE";
11
+ const COMMAND_TIMEOUT_MS = 5_000;
12
+ export function renderSystemdUnit(input) {
13
+ const argv = [input.nodePath, ...autostartLauncherArguments(input, "--systemd-service")];
14
+ return `[Unit]
15
+ Description=Rynx local control plane
16
+ After=network-online.target
17
+ Wants=network-online.target
18
+
19
+ [Service]
20
+ Type=oneshot
21
+ RemainAfterExit=yes
22
+ KillMode=process
23
+ KillSignal=SIGCONT
24
+ RestartKillSignal=SIGCONT
25
+ FinalKillSignal=SIGCONT
26
+ SendSIGKILL=no
27
+ TimeoutStartSec=60
28
+ TimeoutStopSec=45
29
+ WorkingDirectory=${systemdPath(input.dataDir)}
30
+ Environment=${systemdQuote(`PATH=${input.pathEnv}`)}
31
+ Environment=${systemdQuote(`RYNX_HOME=${input.dataDir}`)}
32
+ Environment=${systemdQuote(`${RYNX_SYSTEMD_SERVICE_ENV}=${RYNX_SYSTEMD_SERVICE}`)}
33
+ ExecStart=:${argv.map(systemdQuote).join(" ")}
34
+ ExecStop=:${systemdQuote(input.nodePath)} ${systemdQuote(input.cliPath)} stop --systemd-service
35
+
36
+ [Install]
37
+ WantedBy=default.target
38
+ `;
39
+ }
40
+ export function linuxUserSystemdAvailable() {
41
+ return process.platform === "linux" &&
42
+ run("systemctl", ["--user", "show-environment"]).status === 0;
43
+ }
44
+ export function inspectLinuxAutostart() {
45
+ const context = currentContext();
46
+ if (!linuxUserSystemdAvailable()) {
47
+ return {
48
+ supported: false,
49
+ registered: existsSync(context.unitPath),
50
+ manager: "systemd-user",
51
+ registrationPath: context.unitPath,
52
+ detail: "user systemd is unavailable in this login session",
53
+ };
54
+ }
55
+ const lingerEnabled = inspectLinuxLinger();
56
+ const active = run("systemctl", ["--user", "is-active", RYNX_SYSTEMD_SERVICE]).status === 0;
57
+ let detail;
58
+ if (active) {
59
+ try {
60
+ assertActiveServiceLifecycle(context);
61
+ }
62
+ catch (error) {
63
+ detail = errorMessage(error);
64
+ }
65
+ }
66
+ return {
67
+ supported: true,
68
+ registered: run("systemctl", ["--user", "is-enabled", RYNX_SYSTEMD_SERVICE]).status === 0,
69
+ active,
70
+ ...(lingerEnabled === undefined
71
+ ? {}
72
+ : {
73
+ lingerEnabled,
74
+ ...(!lingerEnabled
75
+ ? { lingerEnableCommand: `sudo loginctl enable-linger ${userInfo().username}` }
76
+ : {}),
77
+ }),
78
+ manager: "systemd-user",
79
+ registrationPath: context.unitPath,
80
+ ...(detail ? { detail } : {}),
81
+ };
82
+ }
83
+ export function enableLinuxAutostart() {
84
+ assertUserSystemdAvailable();
85
+ const context = currentContext();
86
+ syncLinuxService(context);
87
+ requireSuccess(run("systemctl", ["--user", "enable", RYNX_SYSTEMD_SERVICE]), "systemctl --user enable");
88
+ }
89
+ export function disableLinuxAutostart() {
90
+ assertUserSystemdAvailable();
91
+ const context = currentContext();
92
+ const disabled = run("systemctl", ["--user", "disable", RYNX_SYSTEMD_SERVICE]);
93
+ if (disabled.status !== 0 && existsSync(context.unitPath)) {
94
+ requireSuccess(disabled, "systemctl --user disable");
95
+ }
96
+ rmSync(context.unitPath, { force: true });
97
+ rmSync(serviceEnvironmentPath(context), { force: true });
98
+ requireSuccess(run("systemctl", ["--user", "daemon-reload"]), "systemctl --user daemon-reload");
99
+ }
100
+ /** Refresh an existing autostart unit without changing enablement. */
101
+ export function refreshLinuxServiceIfPresent() {
102
+ if (process.platform !== "linux")
103
+ return false;
104
+ const context = currentContext();
105
+ if (!existsSync(context.unitPath))
106
+ return false;
107
+ return syncLinuxService(context, linuxUserSystemdAvailable());
108
+ }
109
+ export function assertLinuxSystemdInvocation() {
110
+ if (process.platform !== "linux") {
111
+ throw new Error("--systemd-service is only valid on Linux");
112
+ }
113
+ if (process.env[RYNX_SYSTEMD_SERVICE_ENV] !== RYNX_SYSTEMD_SERVICE) {
114
+ throw new Error("--systemd-service requires the Rynx systemd environment");
115
+ }
116
+ const cgroup = linuxSystemdCgroupForPid(process.pid);
117
+ if (!cgroupHasService(cgroup)) {
118
+ throw new Error(`--systemd-service requires the ${RYNX_SYSTEMD_SERVICE} cgroup`);
119
+ }
120
+ }
121
+ export function parseLinuxSystemdShow(output) {
122
+ const values = new Map();
123
+ for (const line of output.split(/\r?\n/)) {
124
+ const separator = line.indexOf("=");
125
+ if (separator <= 0)
126
+ continue;
127
+ values.set(line.slice(0, separator), line.slice(separator + 1));
128
+ }
129
+ return {
130
+ loadState: values.get("LoadState") ?? "",
131
+ activeState: values.get("ActiveState") ?? "",
132
+ subState: values.get("SubState") ?? "",
133
+ type: values.get("Type") ?? "",
134
+ remainAfterExit: values.get("RemainAfterExit") ?? "",
135
+ killMode: values.get("KillMode") ?? "",
136
+ killSignal: values.get("KillSignal") ?? "",
137
+ restartKillSignal: values.get("RestartKillSignal") ?? "",
138
+ finalKillSignal: values.get("FinalKillSignal") ?? "",
139
+ sendSigkill: values.get("SendSIGKILL") ?? "",
140
+ workingDirectory: values.get("WorkingDirectory") ?? "",
141
+ environment: values.get("Environment") ?? "",
142
+ execStart: values.get("ExecStart") ?? "",
143
+ execStop: values.get("ExecStop") ?? "",
144
+ };
145
+ }
146
+ export function inspectLinuxPm2GodProcesses(home, deps = {}) {
147
+ const readText = deps.readText ?? ((file) => readFileSync(file, "utf8"));
148
+ const entries = scanLinuxPm2GodPids(home, deps);
149
+ const processes = [];
150
+ for (const pid of entries) {
151
+ try {
152
+ const startBefore = linuxProcStartIdentity(readText(`/proc/${pid}/stat`));
153
+ const cmdline = readText(`/proc/${pid}/cmdline`).replaceAll("\u0000", " ").trim();
154
+ const cgroup = linuxSystemdCgroupForPid(pid, readText);
155
+ const startAfter = linuxProcStartIdentity(readText(`/proc/${pid}/stat`));
156
+ if (!startBefore || startBefore !== startAfter) {
157
+ throw new Error(`PM2 supervisor pid ${pid} changed during inspection`);
158
+ }
159
+ if (!isDedicatedPm2God(cmdline, home))
160
+ continue;
161
+ processes.push({ pid, cgroup, startIdentity: startBefore });
162
+ }
163
+ catch (error) {
164
+ if (!processDisappeared(error))
165
+ throw procReadError(`/proc/${pid}`, error);
166
+ }
167
+ }
168
+ if (processes.length === 0)
169
+ return { kind: "absent" };
170
+ const external = processes.filter((entry) => !cgroupHasService(entry.cgroup));
171
+ return external.length === 0
172
+ ? { kind: "service-cgroup", processes }
173
+ : { kind: "external", processes };
174
+ }
175
+ export function scanLinuxPm2GodPids(home, deps = {}) {
176
+ const procEntries = deps.procEntries ?? (() => readdirSync("/proc"));
177
+ const readText = deps.readText ?? ((file) => readFileSync(file, "utf8"));
178
+ const statUid = deps.statUid ?? ((file) => statSync(file).uid);
179
+ const currentUid = deps.currentUid ?? process.getuid?.();
180
+ let entries;
181
+ try {
182
+ entries = procEntries();
183
+ }
184
+ catch (error) {
185
+ throw new Error(`cannot inspect /proc: ${errorMessage(error)}`);
186
+ }
187
+ const pids = [];
188
+ for (const entry of entries) {
189
+ if (!/^\d+$/.test(entry))
190
+ continue;
191
+ const pid = Number(entry);
192
+ if (!Number.isSafeInteger(pid) || pid <= 1)
193
+ continue;
194
+ if (currentUid !== undefined) {
195
+ try {
196
+ if (statUid(`/proc/${pid}`) !== currentUid)
197
+ continue;
198
+ }
199
+ catch (error) {
200
+ if (processDisappeared(error))
201
+ continue;
202
+ throw procReadError(`/proc/${pid}`, error);
203
+ }
204
+ }
205
+ try {
206
+ const cmdline = readText(`/proc/${pid}/cmdline`).replaceAll("\u0000", " ").trim();
207
+ if (isDedicatedPm2God(cmdline, home))
208
+ pids.push(pid);
209
+ }
210
+ catch (error) {
211
+ if (!processDisappeared(error)) {
212
+ throw procReadError(`/proc/${pid}/cmdline`, error);
213
+ }
214
+ }
215
+ }
216
+ return [...new Set(pids)].sort((left, right) => left - right);
217
+ }
218
+ export function linuxSystemdCgroupForPid(pid, readText = (file) => readFileSync(file, "utf8")) {
219
+ return normalizedCgroupPath(readText(`/proc/${pid}/cgroup`)) ?? "(unreadable)";
220
+ }
221
+ function currentContext() {
222
+ const dataDir = rynxHome();
223
+ const homeDir = homedir();
224
+ const pm2Home = path.join(dataDir, "pm2");
225
+ return {
226
+ homeDir,
227
+ dataDir,
228
+ logDir: path.join(dataDir, "logs"),
229
+ cliPath: fileURLToPath(new URL("./cli.js", import.meta.url)),
230
+ environment: process.env,
231
+ nodePath: process.execPath,
232
+ pathEnv: process.env.PATH || defaultLinuxPath(),
233
+ shellPath: currentShell(),
234
+ pm2Home,
235
+ unitPath: path.join(homeDir, ".config", "systemd", "user", RYNX_SYSTEMD_SERVICE),
236
+ };
237
+ }
238
+ function syncLinuxService(context, reload = true) {
239
+ mkdirSync(context.dataDir, { recursive: true, mode: 0o700 });
240
+ mkdirSync(context.logDir, { recursive: true, mode: 0o700 });
241
+ const content = renderSystemdUnit(context);
242
+ const previous = existsSync(context.unitPath)
243
+ ? readFileSync(context.unitPath, "utf8")
244
+ : undefined;
245
+ const changed = previous !== content;
246
+ const environmentPath = serviceEnvironmentPath(context);
247
+ const environment = renderSystemdEnvironmentFile(context);
248
+ const previousEnvironment = existsSync(environmentPath)
249
+ ? readFileSync(environmentPath, "utf8")
250
+ : undefined;
251
+ const environmentChanged = previousEnvironment !== environment;
252
+ try {
253
+ if (environmentChanged)
254
+ replaceFileAtomically(environmentPath, environment);
255
+ if (changed)
256
+ replaceFileAtomically(context.unitPath, content);
257
+ if (!reload)
258
+ return changed || environmentChanged;
259
+ requireSuccess(run("systemctl", ["--user", "daemon-reload"]), "systemctl --user daemon-reload");
260
+ assertStaticServiceDefinition(context, inspectLinuxSystemdService());
261
+ return changed || environmentChanged;
262
+ }
263
+ catch (error) {
264
+ if (environmentChanged) {
265
+ if (previousEnvironment === undefined)
266
+ rmSync(environmentPath, { force: true });
267
+ else
268
+ replaceFileAtomically(environmentPath, previousEnvironment);
269
+ }
270
+ if (changed) {
271
+ if (previous === undefined)
272
+ rmSync(context.unitPath, { force: true });
273
+ else
274
+ replaceFileAtomically(context.unitPath, previous);
275
+ const rollback = run("systemctl", ["--user", "daemon-reload"]);
276
+ if (rollback.status !== 0) {
277
+ throw new AggregateError([error, new Error(`systemd rollback failed: ${commandDetail(rollback)}`)], "could not update or restore the Rynx systemd service");
278
+ }
279
+ }
280
+ throw error;
281
+ }
282
+ }
283
+ function inspectLinuxSystemdService() {
284
+ const output = systemctlUser([
285
+ "show",
286
+ RYNX_SYSTEMD_SERVICE,
287
+ "--property=LoadState",
288
+ "--property=ActiveState",
289
+ "--property=SubState",
290
+ "--property=Type",
291
+ "--property=RemainAfterExit",
292
+ "--property=KillMode",
293
+ "--property=KillSignal",
294
+ "--property=RestartKillSignal",
295
+ "--property=FinalKillSignal",
296
+ "--property=SendSIGKILL",
297
+ "--property=WorkingDirectory",
298
+ "--property=Environment",
299
+ "--property=ExecStart",
300
+ "--property=ExecStop",
301
+ ]);
302
+ return parseLinuxSystemdShow(output);
303
+ }
304
+ function assertStaticServiceDefinition(context, state) {
305
+ const errors = [];
306
+ if (state.loadState !== "loaded")
307
+ errors.push(`LoadState=${state.loadState || "(empty)"}`);
308
+ if (state.type !== "oneshot")
309
+ errors.push(`Type=${state.type || "(empty)"}`);
310
+ if (state.remainAfterExit !== "yes") {
311
+ errors.push(`RemainAfterExit=${state.remainAfterExit || "(empty)"}`);
312
+ }
313
+ if (state.workingDirectory !== context.dataDir) {
314
+ errors.push(`WorkingDirectory=${state.workingDirectory || "(empty)"}`);
315
+ }
316
+ if (state.killMode !== "process")
317
+ errors.push(`KillMode=${state.killMode || "(empty)"}`);
318
+ if (!signalMatches(state.killSignal, "18", "SIGCONT")) {
319
+ errors.push(`KillSignal=${state.killSignal || "(empty)"}`);
320
+ }
321
+ if (!signalMatches(state.restartKillSignal, "18", "SIGCONT")) {
322
+ errors.push(`RestartKillSignal=${state.restartKillSignal || "(empty)"}`);
323
+ }
324
+ if (!signalMatches(state.finalKillSignal, "18", "SIGCONT")) {
325
+ errors.push(`FinalKillSignal=${state.finalKillSignal || "(empty)"}`);
326
+ }
327
+ if (state.sendSigkill !== "no")
328
+ errors.push(`SendSIGKILL=${state.sendSigkill || "(empty)"}`);
329
+ for (const expected of [
330
+ `RYNX_HOME=${context.dataDir}`,
331
+ `${RYNX_SYSTEMD_SERVICE_ENV}=${RYNX_SYSTEMD_SERVICE}`,
332
+ `PATH=${context.pathEnv}`,
333
+ ]) {
334
+ if (!state.environment.includes(expected))
335
+ errors.push(`Environment missing ${expected}`);
336
+ }
337
+ for (const [name, value, action] of [
338
+ ["ExecStart", state.execStart, "start"],
339
+ ["ExecStop", state.execStop, "stop"],
340
+ ]) {
341
+ if (!value.includes(context.nodePath) ||
342
+ !value.includes(context.cliPath) ||
343
+ (action === "start" && !value.includes(autostartLauncherArguments(context, "--systemd-service")[0])) ||
344
+ !value.includes(action) ||
345
+ !value.includes("--systemd-service")) {
346
+ errors.push(`${name}=${value || "(empty)"}`);
347
+ }
348
+ }
349
+ if (errors.length > 0) {
350
+ throw new Error(`${RYNX_SYSTEMD_SERVICE} effective configuration is invalid: ${errors.join("; ")}`);
351
+ }
352
+ }
353
+ function assertActiveServiceLifecycle(context) {
354
+ const inspection = inspectLinuxPm2GodProcesses(context.pm2Home);
355
+ assertSingleGod(inspection);
356
+ if (inspection.kind === "absent") {
357
+ throw new Error("systemd started without a PM2 supervisor");
358
+ }
359
+ const process = inspection.processes[0];
360
+ const state = inspectLinuxSystemdService();
361
+ assertStaticServiceDefinition(context, state);
362
+ if (state.activeState !== "active" ||
363
+ state.subState !== "exited") {
364
+ throw new Error(`${RYNX_SYSTEMD_SERVICE} lifecycle is not active: ` +
365
+ `ActiveState=${state.activeState}, SubState=${state.subState}`);
366
+ }
367
+ return process;
368
+ }
369
+ function assertSingleGod(inspection) {
370
+ if (inspection.kind !== "absent" && inspection.processes.length !== 1) {
371
+ throw new Error(`multiple PM2 supervisors use this RYNX_HOME: ${describeInspection(inspection)}`);
372
+ }
373
+ }
374
+ function describeInspection(inspection) {
375
+ if (inspection.kind === "absent")
376
+ return "absent";
377
+ return inspection.processes
378
+ .map((entry) => `${entry.pid}@${entry.cgroup}`)
379
+ .join(", ");
380
+ }
381
+ function inspectLinuxLinger() {
382
+ const result = run("loginctl", [
383
+ "show-user",
384
+ userInfo().username,
385
+ "--property=Linger",
386
+ ]);
387
+ if (result.status !== 0)
388
+ return undefined;
389
+ const value = result.stdout?.trim();
390
+ if (value === "Linger=yes")
391
+ return true;
392
+ if (value === "Linger=no")
393
+ return false;
394
+ return undefined;
395
+ }
396
+ function normalizedCgroupPath(raw) {
397
+ const trimmed = raw.trim();
398
+ if (!trimmed)
399
+ return undefined;
400
+ if (trimmed.startsWith("/"))
401
+ return trimmed;
402
+ let unified;
403
+ for (const line of trimmed.split(/\r?\n/)) {
404
+ const first = line.indexOf(":");
405
+ const second = first < 0 ? -1 : line.indexOf(":", first + 1);
406
+ if (second < 0)
407
+ continue;
408
+ const hierarchy = line.slice(0, first);
409
+ const controllers = line.slice(first + 1, second);
410
+ const cgroup = line.slice(second + 1).trim();
411
+ if (!cgroup)
412
+ continue;
413
+ if (controllers.split(",").includes("name=systemd"))
414
+ return cgroup;
415
+ if (hierarchy === "0" && controllers === "")
416
+ unified = cgroup;
417
+ }
418
+ return unified;
419
+ }
420
+ function cgroupHasService(cgroup) {
421
+ return cgroup?.split("/").includes(RYNX_SYSTEMD_SERVICE) === true;
422
+ }
423
+ function linuxProcStartIdentity(raw) {
424
+ const closeParen = raw.lastIndexOf(")");
425
+ if (closeParen < 0)
426
+ return undefined;
427
+ return raw.slice(closeParen + 2).trim().split(/\s+/)[19];
428
+ }
429
+ function isDedicatedPm2God(cmdline, home) {
430
+ return cmdline.includes("PM2 v") && cmdline.includes(`God Daemon (${home})`);
431
+ }
432
+ function processDisappeared(error) {
433
+ const code = error?.code;
434
+ return code === "ENOENT" || code === "ESRCH" || code === "ENOTDIR";
435
+ }
436
+ function procReadError(file, error) {
437
+ return new Error(`cannot inspect ${file}: ${errorMessage(error)}`);
438
+ }
439
+ function assertUserSystemdAvailable() {
440
+ if (!linuxUserSystemdAvailable()) {
441
+ throw new Error("user systemd is unavailable in this login session");
442
+ }
443
+ }
444
+ function systemctlUser(args, timeout = COMMAND_TIMEOUT_MS) {
445
+ const result = run("systemctl", ["--user", ...args], timeout);
446
+ requireSuccess(result, `systemctl --user ${args.join(" ")}`);
447
+ return result.stdout ?? "";
448
+ }
449
+ function replaceFileAtomically(file, content) {
450
+ mkdirSync(path.dirname(file), { recursive: true });
451
+ const temporary = `${file}.${process.pid}.${Date.now()}.tmp`;
452
+ try {
453
+ writeFileSync(temporary, content, {
454
+ encoding: "utf8",
455
+ flag: "wx",
456
+ // Environment snapshots may include runtime credentials.
457
+ mode: 0o600,
458
+ });
459
+ renameSync(temporary, file);
460
+ }
461
+ finally {
462
+ rmSync(temporary, { force: true });
463
+ }
464
+ }
465
+ function run(command, args, timeout = COMMAND_TIMEOUT_MS) {
466
+ return spawnSync(command, [...args], {
467
+ encoding: "utf8",
468
+ stdio: "pipe",
469
+ timeout,
470
+ killSignal: "SIGTERM",
471
+ });
472
+ }
473
+ function requireSuccess(result, description) {
474
+ if (result.status === 0)
475
+ return;
476
+ throw new Error(`${description} failed: ${commandDetail(result)}`);
477
+ }
478
+ function commandDetail(result) {
479
+ return result.error?.message
480
+ || result.stderr?.trim()
481
+ || result.stdout?.trim()
482
+ || `exit ${result.status ?? "unknown"}`;
483
+ }
484
+ function errorMessage(error) {
485
+ return error instanceof Error ? error.message : String(error);
486
+ }
487
+ function signalMatches(value, number, name) {
488
+ return value === number || value === name;
489
+ }
490
+ function defaultLinuxPath() {
491
+ return "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
492
+ }
493
+ function systemdQuote(value) {
494
+ return `"${value
495
+ .replaceAll("\\", "\\\\")
496
+ .replaceAll("\"", "\\\"")
497
+ .replaceAll("%", "%%")
498
+ .replaceAll("\t", "\\t")
499
+ .replaceAll("\n", "\\n")
500
+ .replaceAll("\r", "\\r")}"`;
501
+ }
502
+ function systemdPath(value) {
503
+ // These scalar path directives expand specifiers, but do not unquote words
504
+ // or decode the C-style escapes accepted by ExecStart/Environment.
505
+ if (/[\r\n]/.test(value)) {
506
+ throw new Error("systemd autostart paths cannot contain line breaks");
507
+ }
508
+ return value.replaceAll("%", "%%");
509
+ }
package/dist/usage.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const USAGE = "Usage: rynx <command>\n\nGeneral:\n version [--json] | --version\n print the installed Rynx version\n\nSetup:\n setup [--non-interactive] [--default-runtime <codex|traex|claude>]\n [--host <host>] [--port <port>] [--log-level <level>]\n [--install-browser|--skip-browser] [--json]\n initialize configuration and local dependencies\n doctor read-only health check\n\nLifecycle:\n start | restart | stop | status | logs\n update [version] [--check] [--json]\n\nPlugins:\n market list\n market add <git-or-local-source> [--alias <id>]\n market refresh [market-id]\n market remove <market-id>\n plugin list\n plugin install <source|plugin@market> [--force] [--expect-digest <sha256-...>]\n plugin update <plugin@market> [--expect-digest <sha256-...>]\n plugin enable|disable|uninstall <plugin@market>\n plugin <plugin@market> <command> invoke a plugin-owned command\n\nAgents:\n agent list\n agent show <id>\n agent add <id>\n agent rm <id>\n\nBuiltin Skills:\n skills list [--json]\n skills get <browser|emulator> [--full] [--json]\n\nMaintenance:\n cleanup sessions [--dry-run]\n\nEmulator:\n emulator <args...>\n\nRemote Runtime:\n runtime share --address <host|ws-url> [--label <label>] [--json]\n runtime add --pairing-code <rynx://...> [--name <name>]\n runtime list [--json]\n runtime test <local|daemon-id> [--json]\n runtime forget <daemon-id>\n runtime clients list [--json]\n runtime clients revoke <grant-id>\n\nSessions:\n session fork <session-id> [--title <title>] [--json]\n\nBrowser:\n browser install|update|version|clean [...]\n browser open [url] [--session <id>] [--runtime <local|daemon-id>] [--json]\n browser status|pages|close [--session <id>] [--runtime <local|daemon-id>] [--json]\n browser endpoint [--ensure] [--session <local-id>] [--json]\n browser snapshot [--session <local-id>] [--json]\n browser navigate <url> [--session <local-id>] [--json]\n browser click (--ref <ref>|--selector <css>|--x <n> --y <n>) [--session <local-id>] [--json]\n browser type (--ref <ref>|--selector <css>) --text <text> [--session <local-id>] [--json]\n browser screenshot --output <absolute-path> [--session <local-id>] [--json]\n";
1
+ export declare const USAGE = "Usage: rynx <command>\n\nGeneral:\n version [--json] | --version\n print the installed Rynx version\n\nSetup:\n setup [--non-interactive] [--default-runtime <codex|traex|claude>]\n [--host <host>] [--port <port>] [--log-level <level>]\n [--install-browser|--skip-browser] [--json|--result-file <path>]\n initialize configuration and local dependencies\n doctor read-only health check\n\nLifecycle:\n start | restart | stop [--if-idle] | status [--json] | logs\n autostart enable|disable|status\n update [version] [--check] [--json]\n\nPlugins:\n market list\n market add <git-or-local-source> [--alias <id>]\n market refresh [market-id]\n market remove <market-id>\n plugin list\n plugin install <source|plugin@market> [--force] [--expect-digest <sha256-...>]\n plugin update <plugin@market> [--expect-digest <sha256-...>]\n plugin enable|disable|uninstall <plugin@market>\n plugin <plugin@market> <command> invoke a plugin-owned command\n\nAgents:\n agent list\n agent show <id>\n agent add <id>\n agent rm <id>\n\nBuiltin Skills:\n skills list [--json]\n skills get <browser|emulator> [--full] [--json]\n\nMaintenance:\n cleanup sessions [--dry-run]\n\nEmulator:\n emulator <args...>\n\nRemote Runtime:\n runtime share --address <host|ws-url> [--label <label>] [--json]\n runtime add --pairing-code <rynx://...> [--name <name>]\n runtime list [--json]\n runtime test <local|daemon-id> [--json]\n runtime forget <daemon-id>\n runtime clients list [--json]\n runtime clients revoke <grant-id>\n\nSessions:\n session fork <session-id> [--title <title>] [--json]\n\nBrowser:\n browser install|update|version|clean [...]\n browser open [url] [--session <id>] [--runtime <local|daemon-id>] [--json]\n browser status|pages|close [--session <id>] [--runtime <local|daemon-id>] [--json]\n browser endpoint [--ensure] [--session <local-id>] [--json]\n browser exec [--page <id>] [--session <local-id>] [--json] -- <agent-browser command and args>\n";
package/dist/usage.js CHANGED
@@ -7,12 +7,13 @@ General:
7
7
  Setup:
8
8
  setup [--non-interactive] [--default-runtime <codex|traex|claude>]
9
9
  [--host <host>] [--port <port>] [--log-level <level>]
10
- [--install-browser|--skip-browser] [--json]
10
+ [--install-browser|--skip-browser] [--json|--result-file <path>]
11
11
  initialize configuration and local dependencies
12
12
  doctor read-only health check
13
13
 
14
14
  Lifecycle:
15
- start | restart | stop | status | logs
15
+ start | restart | stop [--if-idle] | status [--json] | logs
16
+ autostart enable|disable|status
16
17
  update [version] [--check] [--json]
17
18
 
18
19
  Plugins:
@@ -59,9 +60,5 @@ Browser:
59
60
  browser open [url] [--session <id>] [--runtime <local|daemon-id>] [--json]
60
61
  browser status|pages|close [--session <id>] [--runtime <local|daemon-id>] [--json]
61
62
  browser endpoint [--ensure] [--session <local-id>] [--json]
62
- browser snapshot [--session <local-id>] [--json]
63
- browser navigate <url> [--session <local-id>] [--json]
64
- browser click (--ref <ref>|--selector <css>|--x <n> --y <n>) [--session <local-id>] [--json]
65
- browser type (--ref <ref>|--selector <css>) --text <text> [--session <local-id>] [--json]
66
- browser screenshot --output <absolute-path> [--session <local-id>] [--json]
63
+ browser exec [--page <id>] [--session <local-id>] [--json] -- <agent-browser command and args>
67
64
  `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rynx-ai/cli",
3
- "version": "0.1.11-beta.5",
3
+ "version": "0.1.11-beta.51",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/rynx-ai/rynx.git",
@@ -10,7 +10,7 @@
10
10
  "license": "MIT",
11
11
  "type": "module",
12
12
  "engines": {
13
- "node": ">=22"
13
+ "node": ">=22.16"
14
14
  },
15
15
  "publishConfig": {
16
16
  "registry": "https://registry.npmjs.org/",
@@ -51,11 +51,11 @@
51
51
  "dependencies": {
52
52
  "@clack/prompts": "^1.6.0",
53
53
  "ws": "^8.21.0",
54
- "@rynx-ai/browser-cdp": "0.1.11-beta.5",
55
- "@rynx-ai/core": "0.1.11-beta.5",
56
- "@rynx-ai/daemon": "0.1.11-beta.5",
57
- "@rynx-ai/emulator": "0.1.11-beta.5",
58
- "@rynx-ai/protocol": "0.1.11-beta.5"
54
+ "@rynx-ai/core": "0.1.11-beta.51",
55
+ "@rynx-ai/emulator": "0.1.11-beta.51",
56
+ "@rynx-ai/daemon": "0.1.11-beta.51",
57
+ "@rynx-ai/tmux": "0.1.11-beta.51",
58
+ "@rynx-ai/protocol": "0.1.11-beta.51"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@types/ws": "^8.18.1"