@rynx-ai/cli 0.1.11-beta.39 → 0.1.11-beta.40
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/dist/autostart.d.ts +1 -0
- package/dist/commands/lifecycle.js +2 -14
- package/dist/standalone.d.ts +0 -1
- package/dist/standalone.js +0 -4
- package/dist/systemd-service.d.ts +2 -13
- package/dist/systemd-service.js +93 -112
- package/package.json +7 -7
package/dist/autostart.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { startStandaloneDaemon,
|
|
1
|
+
import { startStandaloneDaemon, statusStandaloneDaemon, stopStandaloneDaemon, streamStandaloneDaemonLogs, } from "../standalone.js";
|
|
2
2
|
import { refreshAutostart } from "../autostart.js";
|
|
3
|
-
import { assertLinuxSystemdInvocation
|
|
3
|
+
import { assertLinuxSystemdInvocation } from "../systemd-service.js";
|
|
4
4
|
import { fail } from "./errors.js";
|
|
5
5
|
const SYSTEMD_SERVICE_FLAG = "--systemd-service";
|
|
6
6
|
export async function runLifecycleCommand(command, args, options = {}) {
|
|
@@ -14,18 +14,6 @@ export async function runLifecycleCommand(command, args, options = {}) {
|
|
|
14
14
|
}
|
|
15
15
|
if (args.length > 0)
|
|
16
16
|
fail(`${command}: unexpected argument ${args[0]}`);
|
|
17
|
-
if (linuxUserSystemdAvailable() &&
|
|
18
|
-
(command === "start" ||
|
|
19
|
-
command === "restart" ||
|
|
20
|
-
command === "stop" ||
|
|
21
|
-
command === "status")) {
|
|
22
|
-
return runLinuxSystemdLifecycle(command, {
|
|
23
|
-
restartRequired: standaloneDaemonRestartRequired,
|
|
24
|
-
start: startStandaloneDaemon,
|
|
25
|
-
status: statusStandaloneDaemon,
|
|
26
|
-
stop: stopStandaloneDaemon,
|
|
27
|
-
}, options);
|
|
28
|
-
}
|
|
29
17
|
switch (command) {
|
|
30
18
|
case "start": {
|
|
31
19
|
refreshAutostart();
|
package/dist/standalone.d.ts
CHANGED
|
@@ -4,7 +4,6 @@ export declare function startStandaloneDaemon(options?: {
|
|
|
4
4
|
restart?: boolean;
|
|
5
5
|
quiet?: boolean;
|
|
6
6
|
}): Promise<number>;
|
|
7
|
-
export declare function standaloneDaemonRestartRequired(): Promise<boolean>;
|
|
8
7
|
/** Reuse any healthy owner; otherwise explicitly start the standalone owner. */
|
|
9
8
|
export declare function ensureStandaloneDaemon(): Promise<{
|
|
10
9
|
origin: string;
|
package/dist/standalone.js
CHANGED
|
@@ -22,10 +22,6 @@ export async function startStandaloneDaemon(options = {}) {
|
|
|
22
22
|
},
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
|
-
export async function standaloneDaemonRestartRequired() {
|
|
26
|
-
const { isDaemonStartRequired } = await import("@rynx-ai/daemon/lifecycle");
|
|
27
|
-
return isDaemonStartRequired();
|
|
28
|
-
}
|
|
29
25
|
/** Reuse any healthy owner; otherwise explicitly start the standalone owner. */
|
|
30
26
|
export async function ensureStandaloneDaemon() {
|
|
31
27
|
const current = await resolveDaemonControlEndpoint();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AutostartRenderInput, AutostartState } from "./autostart.js";
|
|
2
2
|
export declare const RYNX_SYSTEMD_SERVICE = "rynx.service";
|
|
3
3
|
export declare const RYNX_SYSTEMD_SERVICE_ENV = "RYNX_SYSTEMD_SERVICE";
|
|
4
|
+
export declare const RYNX_SERVICE_ENV_KEYS = "RYNX_SERVICE_ENV_KEYS";
|
|
4
5
|
export interface LinuxSystemdServiceState {
|
|
5
6
|
loadState: string;
|
|
6
7
|
activeState: string;
|
|
@@ -38,26 +39,14 @@ export interface LinuxPm2InspectionDeps {
|
|
|
38
39
|
statUid?: (file: string) => number;
|
|
39
40
|
currentUid?: number;
|
|
40
41
|
}
|
|
41
|
-
export interface LinuxLifecycleActions {
|
|
42
|
-
restartRequired: () => Promise<boolean>;
|
|
43
|
-
start: (options?: {
|
|
44
|
-
restart?: boolean;
|
|
45
|
-
quiet?: boolean;
|
|
46
|
-
}) => Promise<number>;
|
|
47
|
-
status: () => Promise<number>;
|
|
48
|
-
stop: () => Promise<number>;
|
|
49
|
-
}
|
|
50
42
|
export declare function renderSystemdUnit(input: AutostartRenderInput): string;
|
|
51
43
|
export declare function linuxUserSystemdAvailable(): boolean;
|
|
52
44
|
export declare function inspectLinuxAutostart(): AutostartState;
|
|
53
45
|
export declare function enableLinuxAutostart(): void;
|
|
54
46
|
export declare function disableLinuxAutostart(): void;
|
|
55
|
-
/** Refresh
|
|
47
|
+
/** Refresh an existing autostart unit without changing enablement. */
|
|
56
48
|
export declare function refreshLinuxServiceIfPresent(): boolean;
|
|
57
49
|
export declare function assertLinuxSystemdInvocation(): void;
|
|
58
|
-
export declare function runLinuxSystemdLifecycle(command: "start" | "restart" | "stop" | "status", actions: LinuxLifecycleActions, options?: {
|
|
59
|
-
quiet?: boolean;
|
|
60
|
-
}): Promise<number>;
|
|
61
50
|
export declare function parseLinuxSystemdShow(output: string): LinuxSystemdServiceState;
|
|
62
51
|
export declare function inspectLinuxPm2GodOwnership(home: string, deps?: LinuxPm2InspectionDeps): LinuxPm2GodOwnership;
|
|
63
52
|
export declare function scanLinuxPm2GodPids(home: string, deps?: LinuxPm2InspectionDeps): number[];
|
package/dist/systemd-service.js
CHANGED
|
@@ -6,10 +6,78 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import { rynxHome } from "@rynx-ai/core";
|
|
7
7
|
export const RYNX_SYSTEMD_SERVICE = "rynx.service";
|
|
8
8
|
export const RYNX_SYSTEMD_SERVICE_ENV = "RYNX_SYSTEMD_SERVICE";
|
|
9
|
+
export const RYNX_SERVICE_ENV_KEYS = "RYNX_SERVICE_ENV_KEYS";
|
|
9
10
|
const COMMAND_TIMEOUT_MS = 5_000;
|
|
10
|
-
const
|
|
11
|
+
const AUTOMATIC_SERVICE_ENVIRONMENT = new Set([
|
|
12
|
+
"ALL_PROXY",
|
|
13
|
+
"AWS_CA_BUNDLE",
|
|
14
|
+
"CURL_CA_BUNDLE",
|
|
15
|
+
"GIT_SSL_CAINFO",
|
|
16
|
+
"HTTPS_PROXY",
|
|
17
|
+
"HTTP_PROXY",
|
|
18
|
+
"NODE_EXTRA_CA_CERTS",
|
|
19
|
+
"NO_PROXY",
|
|
20
|
+
"REQUESTS_CA_BUNDLE",
|
|
21
|
+
"SSL_CERT_DIR",
|
|
22
|
+
"SSL_CERT_FILE",
|
|
23
|
+
"all_proxy",
|
|
24
|
+
"https_proxy",
|
|
25
|
+
"http_proxy",
|
|
26
|
+
"no_proxy",
|
|
27
|
+
]);
|
|
28
|
+
const BLOCKED_SERVICE_ENVIRONMENT = new Set([
|
|
29
|
+
"PATH",
|
|
30
|
+
"INVOCATION_ID",
|
|
31
|
+
"JOURNAL_STREAM",
|
|
32
|
+
"LISTEN_FDS",
|
|
33
|
+
"LISTEN_FDNAMES",
|
|
34
|
+
"LISTEN_PID",
|
|
35
|
+
"MAINPID",
|
|
36
|
+
"MANAGERPID",
|
|
37
|
+
"NOTIFY_SOCKET",
|
|
38
|
+
"ELECTRON_RUN_AS_NODE",
|
|
39
|
+
"NODE_CHANNEL_FD",
|
|
40
|
+
"NODE_UNIQUE_ID",
|
|
41
|
+
"PM2_HOME",
|
|
42
|
+
"RYNX_DAEMON_LIFECYCLE",
|
|
43
|
+
"RYNX_HOME",
|
|
44
|
+
"RYNX_REFRESH_LOGIN_SHELL_PATH",
|
|
45
|
+
RYNX_SERVICE_ENV_KEYS,
|
|
46
|
+
RYNX_SYSTEMD_SERVICE_ENV,
|
|
47
|
+
"SYSTEMD_EXEC_PID",
|
|
48
|
+
"SYSTEMD_INVOCATION_ID",
|
|
49
|
+
"WATCHDOG_PID",
|
|
50
|
+
"WATCHDOG_USEC",
|
|
51
|
+
]);
|
|
52
|
+
const TRANSIENT_SERVICE_ENVIRONMENT = new Set([
|
|
53
|
+
"_",
|
|
54
|
+
"CI",
|
|
55
|
+
"CLICOLOR",
|
|
56
|
+
"CLICOLOR_FORCE",
|
|
57
|
+
"CODEX_CI",
|
|
58
|
+
"COLORTERM",
|
|
59
|
+
"FORCE_COLOR",
|
|
60
|
+
"GH_PAGER",
|
|
61
|
+
"GIT_PAGER",
|
|
62
|
+
"NO_COLOR",
|
|
63
|
+
"OLDPWD",
|
|
64
|
+
"PAGER",
|
|
65
|
+
"PWD",
|
|
66
|
+
"SHLVL",
|
|
67
|
+
"SSH_CLIENT",
|
|
68
|
+
"SSH_CONNECTION",
|
|
69
|
+
"SSH_TTY",
|
|
70
|
+
"TERM",
|
|
71
|
+
"TERM_PROGRAM",
|
|
72
|
+
"TERM_PROGRAM_VERSION",
|
|
73
|
+
"TERM_SESSION_ID",
|
|
74
|
+
"TERMINFO",
|
|
75
|
+
"TMUX",
|
|
76
|
+
"TMUX_PANE",
|
|
77
|
+
]);
|
|
11
78
|
export function renderSystemdUnit(input) {
|
|
12
79
|
const pidFile = path.join(input.dataDir, "pm2", "pm2.pid");
|
|
80
|
+
const inheritedEnvironment = renderInheritedEnvironment(input.environment);
|
|
13
81
|
return `[Unit]
|
|
14
82
|
Description=Rynx local control plane
|
|
15
83
|
After=network-online.target
|
|
@@ -26,6 +94,7 @@ SendSIGKILL=no
|
|
|
26
94
|
TimeoutStartSec=60
|
|
27
95
|
TimeoutStopSec=45
|
|
28
96
|
WorkingDirectory=${systemdPath(input.dataDir)}
|
|
97
|
+
${inheritedEnvironment}
|
|
29
98
|
Environment=${systemdQuote(`PATH=${input.pathEnv}`)}
|
|
30
99
|
Environment=${systemdQuote(`RYNX_HOME=${input.dataDir}`)}
|
|
31
100
|
Environment=${systemdQuote(`${RYNX_SYSTEMD_SERVICE_ENV}=${RYNX_SYSTEMD_SERVICE}`)}
|
|
@@ -95,7 +164,7 @@ export function disableLinuxAutostart() {
|
|
|
95
164
|
rmSync(context.unitPath, { force: true });
|
|
96
165
|
requireSuccess(run("systemctl", ["--user", "daemon-reload"]), "systemctl --user daemon-reload");
|
|
97
166
|
}
|
|
98
|
-
/** Refresh
|
|
167
|
+
/** Refresh an existing autostart unit without changing enablement. */
|
|
99
168
|
export function refreshLinuxServiceIfPresent() {
|
|
100
169
|
if (process.platform !== "linux" || !linuxUserSystemdAvailable())
|
|
101
170
|
return false;
|
|
@@ -116,59 +185,6 @@ export function assertLinuxSystemdInvocation() {
|
|
|
116
185
|
throw new Error(`--systemd-service requires the ${RYNX_SYSTEMD_SERVICE} cgroup`);
|
|
117
186
|
}
|
|
118
187
|
}
|
|
119
|
-
export async function runLinuxSystemdLifecycle(command, actions, options = {}) {
|
|
120
|
-
assertUserSystemdAvailable();
|
|
121
|
-
const context = currentContext();
|
|
122
|
-
if (command === "status") {
|
|
123
|
-
return statusLinuxLifecycle(context, actions);
|
|
124
|
-
}
|
|
125
|
-
if (command === "stop") {
|
|
126
|
-
return stopLinuxLifecycle(context, actions);
|
|
127
|
-
}
|
|
128
|
-
const unitChanged = syncLinuxService(context);
|
|
129
|
-
const before = inspectLinuxPm2GodOwnership(context.pm2Home);
|
|
130
|
-
assertSingleGod(before);
|
|
131
|
-
const serviceBefore = inspectLinuxSystemdService();
|
|
132
|
-
if (before.kind === "owned" && serviceOwnsGod(serviceBefore, before.processes[0])) {
|
|
133
|
-
if (command === "start") {
|
|
134
|
-
if (unitChanged || await actions.restartRequired()) {
|
|
135
|
-
systemctlUser(["restart", RYNX_SYSTEMD_SERVICE], LIFECYCLE_TIMEOUT_MS);
|
|
136
|
-
assertRunningServiceOwnership(context);
|
|
137
|
-
return options.quiet ? 0 : actions.status();
|
|
138
|
-
}
|
|
139
|
-
const result = await actions.start(options.quiet ? { quiet: true } : undefined);
|
|
140
|
-
if (result !== 0)
|
|
141
|
-
return result;
|
|
142
|
-
assertRunningServiceOwnership(context);
|
|
143
|
-
return 0;
|
|
144
|
-
}
|
|
145
|
-
const previous = before.processes[0];
|
|
146
|
-
systemctlUser(["restart", RYNX_SYSTEMD_SERVICE], LIFECYCLE_TIMEOUT_MS);
|
|
147
|
-
const current = assertRunningServiceOwnership(context);
|
|
148
|
-
if (current.pid === previous.pid &&
|
|
149
|
-
current.startIdentity === previous.startIdentity) {
|
|
150
|
-
throw new Error("systemd restart did not replace the PM2 supervisor generation");
|
|
151
|
-
}
|
|
152
|
-
return options.quiet ? 0 : actions.status();
|
|
153
|
-
}
|
|
154
|
-
if (before.kind !== "absent") {
|
|
155
|
-
assertMigratableOwnership(before);
|
|
156
|
-
const stopped = await actions.stop();
|
|
157
|
-
if (stopped !== 0)
|
|
158
|
-
return stopped;
|
|
159
|
-
const afterStop = inspectLinuxPm2GodOwnership(context.pm2Home);
|
|
160
|
-
if (afterStop.kind !== "absent") {
|
|
161
|
-
throw new Error("the previous PM2 supervisor is still running after shutdown");
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
if (serviceBefore.activeState !== "inactive") {
|
|
165
|
-
systemctlUser(["stop", RYNX_SYSTEMD_SERVICE], LIFECYCLE_TIMEOUT_MS);
|
|
166
|
-
}
|
|
167
|
-
run("systemctl", ["--user", "reset-failed", RYNX_SYSTEMD_SERVICE]);
|
|
168
|
-
systemctlUser(["start", RYNX_SYSTEMD_SERVICE], LIFECYCLE_TIMEOUT_MS);
|
|
169
|
-
assertRunningServiceOwnership(context);
|
|
170
|
-
return options.quiet ? 0 : actions.status();
|
|
171
|
-
}
|
|
172
188
|
export function parseLinuxSystemdShow(output) {
|
|
173
189
|
const values = new Map();
|
|
174
190
|
for (const line of output.split(/\r?\n/)) {
|
|
@@ -282,6 +298,7 @@ function currentContext() {
|
|
|
282
298
|
dataDir,
|
|
283
299
|
logDir: path.join(dataDir, "logs"),
|
|
284
300
|
cliPath: fileURLToPath(new URL("./cli.js", import.meta.url)),
|
|
301
|
+
environment: process.env,
|
|
285
302
|
nodePath: process.execPath,
|
|
286
303
|
pathEnv: process.env.PATH || defaultLinuxPath(),
|
|
287
304
|
pm2Home,
|
|
@@ -407,64 +424,11 @@ function assertRunningServiceOwnership(context) {
|
|
|
407
424
|
}
|
|
408
425
|
return process;
|
|
409
426
|
}
|
|
410
|
-
async function statusLinuxLifecycle(context, actions) {
|
|
411
|
-
const ownership = inspectLinuxPm2GodOwnership(context.pm2Home);
|
|
412
|
-
if (ownership.kind === "absent") {
|
|
413
|
-
console.log("rynx: stopped (PM2 supervisor is absent)");
|
|
414
|
-
return 1;
|
|
415
|
-
}
|
|
416
|
-
assertSingleGod(ownership);
|
|
417
|
-
const directStatus = await actions.status();
|
|
418
|
-
if (ownership.kind !== "owned") {
|
|
419
|
-
console.error(`rynx: PM2 supervisor is not owned by ${RYNX_SYSTEMD_SERVICE}: ${describeOwnership(ownership)}`);
|
|
420
|
-
return 2;
|
|
421
|
-
}
|
|
422
|
-
try {
|
|
423
|
-
assertRunningServiceOwnership(context);
|
|
424
|
-
}
|
|
425
|
-
catch (error) {
|
|
426
|
-
console.error(`rynx: ${errorMessage(error)}`);
|
|
427
|
-
return 1;
|
|
428
|
-
}
|
|
429
|
-
return directStatus;
|
|
430
|
-
}
|
|
431
|
-
async function stopLinuxLifecycle(context, actions) {
|
|
432
|
-
const ownership = inspectLinuxPm2GodOwnership(context.pm2Home);
|
|
433
|
-
assertSingleGod(ownership);
|
|
434
|
-
if (ownership.kind === "owned") {
|
|
435
|
-
const state = inspectLinuxSystemdService();
|
|
436
|
-
if (serviceOwnsGod(state, ownership.processes[0])) {
|
|
437
|
-
systemctlUser(["stop", RYNX_SYSTEMD_SERVICE], LIFECYCLE_TIMEOUT_MS);
|
|
438
|
-
if (inspectLinuxPm2GodOwnership(context.pm2Home).kind !== "absent") {
|
|
439
|
-
throw new Error("PM2 supervisor survived systemd stop");
|
|
440
|
-
}
|
|
441
|
-
console.log("rynx stopped.");
|
|
442
|
-
return 0;
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
if (ownership.kind === "external")
|
|
446
|
-
assertMigratableOwnership(ownership);
|
|
447
|
-
return actions.stop();
|
|
448
|
-
}
|
|
449
427
|
function assertSingleGod(ownership) {
|
|
450
428
|
if (ownership.kind !== "absent" && ownership.processes.length !== 1) {
|
|
451
429
|
throw new Error(`multiple PM2 supervisors use this RYNX_HOME: ${describeOwnership(ownership)}`);
|
|
452
430
|
}
|
|
453
431
|
}
|
|
454
|
-
function assertMigratableOwnership(ownership) {
|
|
455
|
-
if (ownership.kind !== "external")
|
|
456
|
-
return;
|
|
457
|
-
const externalService = ownership.processes.find((entry) => serviceOwner(entry.cgroup)?.endsWith(".service"));
|
|
458
|
-
if (externalService) {
|
|
459
|
-
throw new Error(`refusing to replace a PM2 supervisor owned by ${serviceOwner(externalService.cgroup)}`);
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
function serviceOwnsGod(state, process) {
|
|
463
|
-
return state.activeState === "active" &&
|
|
464
|
-
state.subState === "running" &&
|
|
465
|
-
state.mainPid === process.pid &&
|
|
466
|
-
cgroupHasService(process.cgroup);
|
|
467
|
-
}
|
|
468
432
|
function describeOwnership(ownership) {
|
|
469
433
|
if (ownership.kind === "absent")
|
|
470
434
|
return "absent";
|
|
@@ -472,9 +436,6 @@ function describeOwnership(ownership) {
|
|
|
472
436
|
.map((entry) => `${entry.pid}@${entry.cgroup}`)
|
|
473
437
|
.join(", ");
|
|
474
438
|
}
|
|
475
|
-
function serviceOwner(cgroup) {
|
|
476
|
-
return cgroup.split("/").reverse().find((part) => part.endsWith(".service") || part.endsWith(".scope"));
|
|
477
|
-
}
|
|
478
439
|
function inspectLinuxLinger() {
|
|
479
440
|
const result = run("loginctl", [
|
|
480
441
|
"show-user",
|
|
@@ -550,7 +511,9 @@ function replaceFileAtomically(file, content) {
|
|
|
550
511
|
writeFileSync(temporary, content, {
|
|
551
512
|
encoding: "utf8",
|
|
552
513
|
flag: "wx",
|
|
553
|
-
|
|
514
|
+
// The generated unit contains the caller's environment and may include
|
|
515
|
+
// runtime credentials. Keep the snapshot private to the owning user.
|
|
516
|
+
mode: 0o600,
|
|
554
517
|
});
|
|
555
518
|
renameSync(temporary, file);
|
|
556
519
|
}
|
|
@@ -586,6 +549,24 @@ function signalMatches(value, number, name) {
|
|
|
586
549
|
function defaultLinuxPath() {
|
|
587
550
|
return "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
588
551
|
}
|
|
552
|
+
function renderInheritedEnvironment(env) {
|
|
553
|
+
if (!env)
|
|
554
|
+
return "";
|
|
555
|
+
const requested = new Set((env[RYNX_SERVICE_ENV_KEYS] ?? "")
|
|
556
|
+
.split(/[\s,]+/)
|
|
557
|
+
.filter((name) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(name)));
|
|
558
|
+
return Object.entries(env)
|
|
559
|
+
.filter(([name, value]) => value !== undefined &&
|
|
560
|
+
/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) &&
|
|
561
|
+
(AUTOMATIC_SERVICE_ENVIRONMENT.has(name) || requested.has(name)) &&
|
|
562
|
+
!name.startsWith("npm_") &&
|
|
563
|
+
!name.startsWith("RYNX_RUNNER_") &&
|
|
564
|
+
!BLOCKED_SERVICE_ENVIRONMENT.has(name) &&
|
|
565
|
+
!TRANSIENT_SERVICE_ENVIRONMENT.has(name))
|
|
566
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
567
|
+
.map(([name, value]) => `Environment=${systemdQuote(`${name}=${value}`)}`)
|
|
568
|
+
.join("\n");
|
|
569
|
+
}
|
|
589
570
|
function systemdQuote(value) {
|
|
590
571
|
return `"${value
|
|
591
572
|
.replaceAll("\\", "\\\\")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rynx-ai/cli",
|
|
3
|
-
"version": "0.1.11-beta.
|
|
3
|
+
"version": "0.1.11-beta.40",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/rynx-ai/rynx.git",
|
|
@@ -51,12 +51,12 @@
|
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@clack/prompts": "^1.6.0",
|
|
53
53
|
"ws": "^8.21.0",
|
|
54
|
-
"@rynx-ai/
|
|
55
|
-
"@rynx-ai/
|
|
56
|
-
"@rynx-ai/
|
|
57
|
-
"@rynx-ai/
|
|
58
|
-
"@rynx-ai/tmux": "0.1.11-beta.
|
|
59
|
-
"@rynx-ai/
|
|
54
|
+
"@rynx-ai/browser-cdp": "0.1.11-beta.40",
|
|
55
|
+
"@rynx-ai/core": "0.1.11-beta.40",
|
|
56
|
+
"@rynx-ai/emulator": "0.1.11-beta.40",
|
|
57
|
+
"@rynx-ai/protocol": "0.1.11-beta.40",
|
|
58
|
+
"@rynx-ai/tmux": "0.1.11-beta.40",
|
|
59
|
+
"@rynx-ai/daemon": "0.1.11-beta.40"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@types/ws": "^8.18.1"
|