@junyoung-kim/reins 0.1.0 → 0.1.1
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/cli.mjs +279 -148
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -2211,9 +2211,9 @@ var AuthorityResolver = class _AuthorityResolver {
|
|
|
2211
2211
|
* `mobileMeta` 는 `state.leader === 'mobile'` 일 때만 채움 — `'handed_off'` placeholder 의
|
|
2212
2212
|
* heartbeat / "cols×rows" 표시 데이터.
|
|
2213
2213
|
*/
|
|
2214
|
-
_broadcastViewerMode(machineId,
|
|
2214
|
+
_broadcastViewerMode(machineId, mode2, meta) {
|
|
2215
2215
|
if (!this._a2SpikeEnabled) return;
|
|
2216
|
-
if (
|
|
2216
|
+
if (mode2 === "handed_off") {
|
|
2217
2217
|
this._ensureHeartbeatTicker(machineId);
|
|
2218
2218
|
} else {
|
|
2219
2219
|
this._stopHeartbeatTicker(machineId);
|
|
@@ -2226,13 +2226,13 @@ var AuthorityResolver = class _AuthorityResolver {
|
|
|
2226
2226
|
lastActivityAt: s.lastActivityAt
|
|
2227
2227
|
} : void 0;
|
|
2228
2228
|
log3.info(
|
|
2229
|
-
`broadcast viewer_mode machine=${machineId} mode=${
|
|
2229
|
+
`broadcast viewer_mode machine=${machineId} mode=${mode2}` + (mobileMeta ? ` lastActivityAt=${mobileMeta.lastActivityAt} cols=${mobileMeta.cols} rows=${mobileMeta.rows}` : "") + (meta?.graceUntil ? ` graceUntil=${meta.graceUntil}` : "") + (meta?.cooldownUntil ? ` cooldownUntil=${meta.cooldownUntil}` : "") + (meta?.reason ? ` reason=${meta.reason}` : "")
|
|
2230
2230
|
);
|
|
2231
2231
|
this.deps.broadcast(
|
|
2232
2232
|
{
|
|
2233
2233
|
type: "desktop.viewer_mode",
|
|
2234
2234
|
machineId,
|
|
2235
|
-
mode,
|
|
2235
|
+
mode: mode2,
|
|
2236
2236
|
mobileMeta,
|
|
2237
2237
|
graceUntil: meta?.graceUntil,
|
|
2238
2238
|
cooldownUntil: meta?.cooldownUntil,
|
|
@@ -6946,10 +6946,19 @@ function loadDotEnv(files = defaultEnvFiles(), allowed = ALLOWED_ENV_KEYS) {
|
|
|
6946
6946
|
return loaded;
|
|
6947
6947
|
}
|
|
6948
6948
|
|
|
6949
|
+
// src/entry-mode.ts
|
|
6950
|
+
function resolveEntryMode(i) {
|
|
6951
|
+
if (!i.isTTY) {
|
|
6952
|
+
if (i.isServiceProcess) return "daemon";
|
|
6953
|
+
return i.isServiceActive() ? "serviceBusy" : "daemon";
|
|
6954
|
+
}
|
|
6955
|
+
return i.isServiceActive() ? "manager" : "tui";
|
|
6956
|
+
}
|
|
6957
|
+
|
|
6949
6958
|
// package.json
|
|
6950
6959
|
var package_default = {
|
|
6951
6960
|
name: "@junyoung-kim/reins",
|
|
6952
|
-
version: "0.1.
|
|
6961
|
+
version: "0.1.1",
|
|
6953
6962
|
type: "module",
|
|
6954
6963
|
description: "\uD3F0\uC5D0\uC11C AI \uCF54\uB529 \uC5D0\uC774\uC804\uD2B8\uC758 \uACE0\uC090\uB97C \uC950\uB2E4 \u2014 \uD5E4\uB4DC\uB9AC\uC2A4 TUI (npx @junyoung-kim/reins)",
|
|
6955
6964
|
bin: {
|
|
@@ -7075,6 +7084,16 @@ import fs9 from "fs";
|
|
|
7075
7084
|
// src/service/unit-template.ts
|
|
7076
7085
|
import os9 from "os";
|
|
7077
7086
|
import path11 from "path";
|
|
7087
|
+
|
|
7088
|
+
// src/service/service-process.ts
|
|
7089
|
+
var SERVICE_ENV_KEY = "REINS_RUN_AS_SERVICE";
|
|
7090
|
+
var SERVICE_ENV_VALUE = "1";
|
|
7091
|
+
var SERVICE_ENV_MARKER = `${SERVICE_ENV_KEY}=${SERVICE_ENV_VALUE}`;
|
|
7092
|
+
function isServiceProcess(env) {
|
|
7093
|
+
return env[SERVICE_ENV_KEY] === SERVICE_ENV_VALUE || !!env.INVOCATION_ID;
|
|
7094
|
+
}
|
|
7095
|
+
|
|
7096
|
+
// src/service/unit-template.ts
|
|
7078
7097
|
var SERVICE_NAME = "reins";
|
|
7079
7098
|
var UNIT_FILE_NAME = `${SERVICE_NAME}.service`;
|
|
7080
7099
|
function renderUnit(p2) {
|
|
@@ -7087,6 +7106,7 @@ function renderUnit(p2) {
|
|
|
7087
7106
|
"",
|
|
7088
7107
|
"[Service]",
|
|
7089
7108
|
"Type=simple",
|
|
7109
|
+
`Environment=${SERVICE_ENV_MARKER}`,
|
|
7090
7110
|
`ExecStart=${exec2}`,
|
|
7091
7111
|
"Restart=always",
|
|
7092
7112
|
"RestartSec=3",
|
|
@@ -7353,8 +7373,8 @@ var HeadlessStore = class extends EventEmitter {
|
|
|
7353
7373
|
};
|
|
7354
7374
|
|
|
7355
7375
|
// src/ui/App.tsx
|
|
7356
|
-
import { Box as Box9, Text as
|
|
7357
|
-
import { useEffect as
|
|
7376
|
+
import { Box as Box9, Text as Text10, useApp } from "ink";
|
|
7377
|
+
import { useEffect as useEffect5, useState as useState8 } from "react";
|
|
7358
7378
|
|
|
7359
7379
|
// src/ui/screens/InfoScreen.tsx
|
|
7360
7380
|
import os11 from "os";
|
|
@@ -7919,7 +7939,7 @@ function MachinesScreen({
|
|
|
7919
7939
|
onBack
|
|
7920
7940
|
}) {
|
|
7921
7941
|
const [selected, setSelected] = useState4(0);
|
|
7922
|
-
const [
|
|
7942
|
+
const [mode2, setMode] = useState4("list");
|
|
7923
7943
|
const [editing, setEditing] = useState4(null);
|
|
7924
7944
|
const count = machines.length;
|
|
7925
7945
|
const sel = Math.min(selected, Math.max(0, count - 1));
|
|
@@ -7943,15 +7963,15 @@ function MachinesScreen({
|
|
|
7943
7963
|
if (machines[sel]) setMode("confirm");
|
|
7944
7964
|
}
|
|
7945
7965
|
},
|
|
7946
|
-
{ isActive: active &&
|
|
7966
|
+
{ isActive: active && mode2 === "list" }
|
|
7947
7967
|
);
|
|
7948
7968
|
useInput3(
|
|
7949
7969
|
(_input, key) => {
|
|
7950
7970
|
if (key.escape) setMode("list");
|
|
7951
7971
|
},
|
|
7952
|
-
{ isActive: active &&
|
|
7972
|
+
{ isActive: active && mode2 === "confirm" }
|
|
7953
7973
|
);
|
|
7954
|
-
if (
|
|
7974
|
+
if (mode2 === "form") {
|
|
7955
7975
|
return /* @__PURE__ */ jsx6(
|
|
7956
7976
|
MachineFormScreen,
|
|
7957
7977
|
{
|
|
@@ -7989,13 +8009,13 @@ function MachinesScreen({
|
|
|
7989
8009
|
" more"
|
|
7990
8010
|
] }) : null,
|
|
7991
8011
|
/* @__PURE__ */ jsx6(Text6, { color: "gray", children: DIVIDER }),
|
|
7992
|
-
|
|
8012
|
+
mode2 === "confirm" && target ? /* @__PURE__ */ jsx6(
|
|
7993
8013
|
ConfirmDialog,
|
|
7994
8014
|
{
|
|
7995
8015
|
message: `Delete "${target.name}"?`,
|
|
7996
8016
|
confirmLabel: "Delete",
|
|
7997
8017
|
warning: deleteWarning,
|
|
7998
|
-
isActive: active &&
|
|
8018
|
+
isActive: active && mode2 === "confirm",
|
|
7999
8019
|
onConfirm: () => {
|
|
8000
8020
|
try {
|
|
8001
8021
|
core.ssh.removeMachine(target.id);
|
|
@@ -8012,8 +8032,55 @@ function MachinesScreen({
|
|
|
8012
8032
|
}
|
|
8013
8033
|
|
|
8014
8034
|
// src/ui/screens/MainScreen.tsx
|
|
8015
|
-
import { Box as Box7, Text as
|
|
8016
|
-
import { useEffect as
|
|
8035
|
+
import { Box as Box7, Text as Text8, useInput as useInput4 } from "ink";
|
|
8036
|
+
import { useEffect as useEffect4, useMemo as useMemo2, useState as useState6 } from "react";
|
|
8037
|
+
|
|
8038
|
+
// src/ui/components/PairQrCell.tsx
|
|
8039
|
+
import { Text as Text7 } from "ink";
|
|
8040
|
+
|
|
8041
|
+
// src/ui/qr-fit.ts
|
|
8042
|
+
var RESERVED_ROWS = 10;
|
|
8043
|
+
function qrDimensions(qrText) {
|
|
8044
|
+
const lines = qrText.split("\n");
|
|
8045
|
+
let width = 0;
|
|
8046
|
+
for (const line of lines) {
|
|
8047
|
+
const w2 = [...line].length;
|
|
8048
|
+
if (w2 > width) width = w2;
|
|
8049
|
+
}
|
|
8050
|
+
return { width, height: lines.length };
|
|
8051
|
+
}
|
|
8052
|
+
function qrLayout(qrText, columns, rows, reserveRows = RESERVED_ROWS) {
|
|
8053
|
+
const { width, height } = qrDimensions(qrText);
|
|
8054
|
+
return { width, height, fits: width <= columns && height <= rows - reserveRows };
|
|
8055
|
+
}
|
|
8056
|
+
|
|
8057
|
+
// src/ui/components/PairQrCell.tsx
|
|
8058
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
8059
|
+
function PairQrCell({
|
|
8060
|
+
qrText,
|
|
8061
|
+
visible,
|
|
8062
|
+
columns,
|
|
8063
|
+
rows,
|
|
8064
|
+
reserveRows
|
|
8065
|
+
}) {
|
|
8066
|
+
if (!qrText) return null;
|
|
8067
|
+
const { width, height, fits } = qrLayout(qrText, columns, rows, reserveRows);
|
|
8068
|
+
if (!visible) {
|
|
8069
|
+
return /* @__PURE__ */ jsx7(Text7, { color: "gray", children: fits ? "(press c to show QR)" : "(terminal too small for QR \u2014 use URL)" });
|
|
8070
|
+
}
|
|
8071
|
+
if (fits) return /* @__PURE__ */ jsx7(Text7, { children: qrText });
|
|
8072
|
+
return /* @__PURE__ */ jsxs7(Text7, { color: "yellow", children: [
|
|
8073
|
+
"\u26A0 QR needs ",
|
|
8074
|
+
width,
|
|
8075
|
+
"x",
|
|
8076
|
+
height,
|
|
8077
|
+
", terminal is ",
|
|
8078
|
+
columns,
|
|
8079
|
+
"x",
|
|
8080
|
+
rows,
|
|
8081
|
+
" \u2014 widen the window or use the URL below."
|
|
8082
|
+
] });
|
|
8083
|
+
}
|
|
8017
8084
|
|
|
8018
8085
|
// src/ui/qr.ts
|
|
8019
8086
|
import qrcode from "qrcode-terminal";
|
|
@@ -8034,8 +8101,31 @@ function scrollWindow(total, scroll, rows) {
|
|
|
8034
8101
|
return { start, end, hiddenAbove: start, hiddenBelow: total - end, maxScroll };
|
|
8035
8102
|
}
|
|
8036
8103
|
|
|
8104
|
+
// src/ui/useTerminalSize.ts
|
|
8105
|
+
import { useStdout } from "ink";
|
|
8106
|
+
import { useEffect as useEffect3, useState as useState5 } from "react";
|
|
8107
|
+
var FALLBACK = { columns: 80, rows: 24 };
|
|
8108
|
+
function useTerminalSize() {
|
|
8109
|
+
const { stdout } = useStdout();
|
|
8110
|
+
const [size, setSize] = useState5(() => ({
|
|
8111
|
+
columns: stdout?.columns ?? FALLBACK.columns,
|
|
8112
|
+
rows: stdout?.rows ?? FALLBACK.rows
|
|
8113
|
+
}));
|
|
8114
|
+
useEffect3(() => {
|
|
8115
|
+
if (!stdout) return;
|
|
8116
|
+
const onResize = () => {
|
|
8117
|
+
setSize({ columns: stdout.columns ?? FALLBACK.columns, rows: stdout.rows ?? FALLBACK.rows });
|
|
8118
|
+
};
|
|
8119
|
+
stdout.on("resize", onResize);
|
|
8120
|
+
return () => {
|
|
8121
|
+
stdout.off("resize", onResize);
|
|
8122
|
+
};
|
|
8123
|
+
}, [stdout]);
|
|
8124
|
+
return size;
|
|
8125
|
+
}
|
|
8126
|
+
|
|
8037
8127
|
// src/ui/screens/MainScreen.tsx
|
|
8038
|
-
import { jsx as
|
|
8128
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
8039
8129
|
var VERBOSE_ROWS = 16;
|
|
8040
8130
|
var PTY_COLOR = {
|
|
8041
8131
|
idle: "gray",
|
|
@@ -8049,45 +8139,46 @@ function DefaultBody({
|
|
|
8049
8139
|
qrText,
|
|
8050
8140
|
qrUrl,
|
|
8051
8141
|
qrMode,
|
|
8052
|
-
qrVisible
|
|
8142
|
+
qrVisible,
|
|
8143
|
+
columns,
|
|
8144
|
+
rows
|
|
8053
8145
|
}) {
|
|
8054
|
-
return /* @__PURE__ */
|
|
8055
|
-
/* @__PURE__ */
|
|
8056
|
-
qrUrl ? /* @__PURE__ */
|
|
8057
|
-
/* @__PURE__ */
|
|
8058
|
-
|
|
8059
|
-
/* @__PURE__ */
|
|
8060
|
-
/* @__PURE__ */
|
|
8061
|
-
/* @__PURE__ */
|
|
8062
|
-
qrMode ? /* @__PURE__ */
|
|
8146
|
+
return /* @__PURE__ */ jsxs8(Box7, { flexDirection: "column", children: [
|
|
8147
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: DIVIDER }),
|
|
8148
|
+
qrUrl ? /* @__PURE__ */ jsxs8(Box7, { flexDirection: "column", children: [
|
|
8149
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: "Scan to pair:" }),
|
|
8150
|
+
/* @__PURE__ */ jsx8(PairQrCell, { qrText, visible: qrVisible, columns, rows }),
|
|
8151
|
+
/* @__PURE__ */ jsxs8(Box7, { children: [
|
|
8152
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: "URL " }),
|
|
8153
|
+
/* @__PURE__ */ jsx8(Text8, { color: "cyan", children: qrUrl }),
|
|
8154
|
+
qrMode ? /* @__PURE__ */ jsxs8(Text8, { color: "gray", children: [
|
|
8063
8155
|
" (",
|
|
8064
8156
|
qrMode,
|
|
8065
8157
|
")"
|
|
8066
8158
|
] }) : null
|
|
8067
|
-
] })
|
|
8068
|
-
|
|
8069
|
-
|
|
8070
|
-
/* @__PURE__ */
|
|
8071
|
-
/* @__PURE__ */
|
|
8072
|
-
|
|
8073
|
-
/* @__PURE__ */
|
|
8074
|
-
/* @__PURE__ */
|
|
8075
|
-
/* @__PURE__ */
|
|
8076
|
-
/* @__PURE__ */ jsx7(Text7, { color: PTY_COLOR[sess.pty], children: sess.pty })
|
|
8159
|
+
] })
|
|
8160
|
+
] }) : /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "Preparing QR\u2026 (waiting for relay)" }),
|
|
8161
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: DIVIDER }),
|
|
8162
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: "Sessions" }),
|
|
8163
|
+
sessions.map((sess) => /* @__PURE__ */ jsxs8(Box7, { children: [
|
|
8164
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: " " }),
|
|
8165
|
+
/* @__PURE__ */ jsx8(Text8, { children: sess.name }),
|
|
8166
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: " \xB7 " }),
|
|
8167
|
+
/* @__PURE__ */ jsx8(Text8, { color: PTY_COLOR[sess.pty], children: sess.pty })
|
|
8077
8168
|
] }, sess.id)),
|
|
8078
|
-
/* @__PURE__ */
|
|
8079
|
-
/* @__PURE__ */
|
|
8080
|
-
/* @__PURE__ */
|
|
8169
|
+
/* @__PURE__ */ jsxs8(Box7, { children: [
|
|
8170
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: "Clients " }),
|
|
8171
|
+
/* @__PURE__ */ jsx8(Text8, { color: clients > 0 ? "green" : "gray", children: clients })
|
|
8081
8172
|
] })
|
|
8082
8173
|
] });
|
|
8083
8174
|
}
|
|
8084
8175
|
function VerboseLog({ logs, scroll }) {
|
|
8085
8176
|
const { start, end, hiddenAbove, hiddenBelow } = scrollWindow(logs.length, scroll, VERBOSE_ROWS);
|
|
8086
8177
|
const view = logs.slice(start, end);
|
|
8087
|
-
return /* @__PURE__ */
|
|
8088
|
-
/* @__PURE__ */
|
|
8089
|
-
view.length === 0 ? /* @__PURE__ */
|
|
8090
|
-
hiddenBelow > 0 ? /* @__PURE__ */
|
|
8178
|
+
return /* @__PURE__ */ jsxs8(Box7, { flexDirection: "column", children: [
|
|
8179
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: hiddenAbove > 0 ? `\u2191 ${hiddenAbove} more` : DIVIDER }),
|
|
8180
|
+
view.length === 0 ? /* @__PURE__ */ jsx8(Text8, { color: "gray", children: "\u2026" }) : view.map((line, i) => /* @__PURE__ */ jsx8(Text8, { color: "gray", children: line }, `${start + i}-${line}`)),
|
|
8181
|
+
hiddenBelow > 0 ? /* @__PURE__ */ jsxs8(Text8, { color: "gray", children: [
|
|
8091
8182
|
"\u2193 ",
|
|
8092
8183
|
hiddenBelow,
|
|
8093
8184
|
" more"
|
|
@@ -8101,13 +8192,14 @@ function MainScreen({
|
|
|
8101
8192
|
onNavigate,
|
|
8102
8193
|
onExit
|
|
8103
8194
|
}) {
|
|
8104
|
-
const
|
|
8105
|
-
const [
|
|
8106
|
-
const [
|
|
8107
|
-
const [
|
|
8108
|
-
const [
|
|
8109
|
-
const [
|
|
8110
|
-
|
|
8195
|
+
const { columns, rows } = useTerminalSize();
|
|
8196
|
+
const [verbose, setVerbose] = useState6(false);
|
|
8197
|
+
const [scroll, setScroll] = useState6(0);
|
|
8198
|
+
const [clients, setClients] = useState6(0);
|
|
8199
|
+
const [qrVisible, setQrVisible] = useState6(false);
|
|
8200
|
+
const [handoffOnQuit, setHandoffOnQuit] = useState6(false);
|
|
8201
|
+
const [autostartNotice, setAutostartNotice] = useState6(null);
|
|
8202
|
+
useEffect4(() => {
|
|
8111
8203
|
const t = setInterval(() => {
|
|
8112
8204
|
setClients(core.ws.getActiveMobileClientCount());
|
|
8113
8205
|
}, 1e3);
|
|
@@ -8177,8 +8269,8 @@ function MainScreen({
|
|
|
8177
8269
|
pty: state.sessions[m2.id] ?? "idle"
|
|
8178
8270
|
}));
|
|
8179
8271
|
const qrText = useMemo2(() => state.qr ? renderQr(state.qr.url) : null, [state.qr?.url]);
|
|
8180
|
-
return /* @__PURE__ */
|
|
8181
|
-
verbose ? /* @__PURE__ */
|
|
8272
|
+
return /* @__PURE__ */ jsxs8(Box7, { flexDirection: "column", children: [
|
|
8273
|
+
verbose ? /* @__PURE__ */ jsx8(VerboseLog, { logs: state.logs, scroll }) : /* @__PURE__ */ jsx8(
|
|
8182
8274
|
DefaultBody,
|
|
8183
8275
|
{
|
|
8184
8276
|
sessions,
|
|
@@ -8186,12 +8278,14 @@ function MainScreen({
|
|
|
8186
8278
|
qrText,
|
|
8187
8279
|
qrUrl: state.qr ? prettyUrl(state.qr.url) : null,
|
|
8188
8280
|
qrMode: state.qr?.mode ?? null,
|
|
8189
|
-
qrVisible
|
|
8281
|
+
qrVisible,
|
|
8282
|
+
columns,
|
|
8283
|
+
rows
|
|
8190
8284
|
}
|
|
8191
8285
|
),
|
|
8192
|
-
/* @__PURE__ */
|
|
8193
|
-
autostartNotice ? /* @__PURE__ */
|
|
8194
|
-
/* @__PURE__ */
|
|
8286
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: DIVIDER }),
|
|
8287
|
+
autostartNotice ? /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: autostartNotice }) : null,
|
|
8288
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "cyan", children: [
|
|
8195
8289
|
"q quit \xB7 r reconnect \xB7 n new QR \xB7 m machines \xB7 o options \xB7 i info \xB7 a auto-start \xB7 c",
|
|
8196
8290
|
" ",
|
|
8197
8291
|
qrVisible ? "hide" : "show",
|
|
@@ -8203,9 +8297,9 @@ function MainScreen({
|
|
|
8203
8297
|
}
|
|
8204
8298
|
|
|
8205
8299
|
// src/ui/screens/OptionsScreen.tsx
|
|
8206
|
-
import { Box as Box8, Text as
|
|
8207
|
-
import { useState as
|
|
8208
|
-
import { jsx as
|
|
8300
|
+
import { Box as Box8, Text as Text9 } from "ink";
|
|
8301
|
+
import { useState as useState7 } from "react";
|
|
8302
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
8209
8303
|
var ORDER = ["relayUrls", "wsPort", "heartbeat", "pong", "auth"];
|
|
8210
8304
|
function loadForm(config2) {
|
|
8211
8305
|
return {
|
|
@@ -8222,9 +8316,9 @@ function OptionsScreen({
|
|
|
8222
8316
|
active,
|
|
8223
8317
|
onBack
|
|
8224
8318
|
}) {
|
|
8225
|
-
const [form, setForm] =
|
|
8226
|
-
const [errors, setErrors] =
|
|
8227
|
-
const [saved, setSaved] =
|
|
8319
|
+
const [form, setForm] = useState7(() => loadForm(config2));
|
|
8320
|
+
const [errors, setErrors] = useState7([]);
|
|
8321
|
+
const [saved, setSaved] = useState7(false);
|
|
8228
8322
|
const set = (patch) => {
|
|
8229
8323
|
setForm((f) => ({ ...f, ...patch }));
|
|
8230
8324
|
setSaved(false);
|
|
@@ -8268,10 +8362,10 @@ function OptionsScreen({
|
|
|
8268
8362
|
onEscape: onBack
|
|
8269
8363
|
});
|
|
8270
8364
|
const invalid = (k2) => errors.includes(k2);
|
|
8271
|
-
return /* @__PURE__ */
|
|
8272
|
-
/* @__PURE__ */
|
|
8273
|
-
/* @__PURE__ */
|
|
8274
|
-
/* @__PURE__ */
|
|
8365
|
+
return /* @__PURE__ */ jsxs9(Box8, { flexDirection: "column", children: [
|
|
8366
|
+
/* @__PURE__ */ jsx9(Text9, { color: "gray", children: DIVIDER }),
|
|
8367
|
+
/* @__PURE__ */ jsx9(Text9, { children: "Options" }),
|
|
8368
|
+
/* @__PURE__ */ jsx9(
|
|
8275
8369
|
Field,
|
|
8276
8370
|
{
|
|
8277
8371
|
label: "Relay",
|
|
@@ -8283,7 +8377,7 @@ function OptionsScreen({
|
|
|
8283
8377
|
invalid: invalid("relayUrls")
|
|
8284
8378
|
}
|
|
8285
8379
|
),
|
|
8286
|
-
/* @__PURE__ */
|
|
8380
|
+
/* @__PURE__ */ jsx9(
|
|
8287
8381
|
Field,
|
|
8288
8382
|
{
|
|
8289
8383
|
label: "WS Port",
|
|
@@ -8294,7 +8388,7 @@ function OptionsScreen({
|
|
|
8294
8388
|
invalid: invalid("wsPort")
|
|
8295
8389
|
}
|
|
8296
8390
|
),
|
|
8297
|
-
/* @__PURE__ */
|
|
8391
|
+
/* @__PURE__ */ jsx9(
|
|
8298
8392
|
Field,
|
|
8299
8393
|
{
|
|
8300
8394
|
label: "Heartbeat",
|
|
@@ -8305,7 +8399,7 @@ function OptionsScreen({
|
|
|
8305
8399
|
invalid: invalid("heartbeat")
|
|
8306
8400
|
}
|
|
8307
8401
|
),
|
|
8308
|
-
/* @__PURE__ */
|
|
8402
|
+
/* @__PURE__ */ jsx9(
|
|
8309
8403
|
Field,
|
|
8310
8404
|
{
|
|
8311
8405
|
label: "Pong",
|
|
@@ -8316,7 +8410,7 @@ function OptionsScreen({
|
|
|
8316
8410
|
invalid: invalid("pong")
|
|
8317
8411
|
}
|
|
8318
8412
|
),
|
|
8319
|
-
/* @__PURE__ */
|
|
8413
|
+
/* @__PURE__ */ jsx9(
|
|
8320
8414
|
Field,
|
|
8321
8415
|
{
|
|
8322
8416
|
label: "Auth",
|
|
@@ -8327,29 +8421,29 @@ function OptionsScreen({
|
|
|
8327
8421
|
invalid: invalid("auth")
|
|
8328
8422
|
}
|
|
8329
8423
|
),
|
|
8330
|
-
/* @__PURE__ */
|
|
8331
|
-
/* @__PURE__ */
|
|
8332
|
-
/* @__PURE__ */
|
|
8424
|
+
/* @__PURE__ */ jsx9(Text9, { color: "gray", children: "\u26A0 WS Port\xB7timeouts apply after restart (relay reconnects immediately)." }),
|
|
8425
|
+
/* @__PURE__ */ jsx9(Text9, { color: "gray", children: DIVIDER }),
|
|
8426
|
+
/* @__PURE__ */ jsxs9(Text9, { color: "gray", children: [
|
|
8333
8427
|
"install_id ",
|
|
8334
8428
|
config2.getInstallId().slice(0, 8),
|
|
8335
8429
|
"\u2026"
|
|
8336
8430
|
] }),
|
|
8337
|
-
/* @__PURE__ */
|
|
8431
|
+
/* @__PURE__ */ jsxs9(Text9, { color: "gray", children: [
|
|
8338
8432
|
"FCM tokens ",
|
|
8339
8433
|
config2.getFcmTokens().length
|
|
8340
8434
|
] }),
|
|
8341
|
-
/* @__PURE__ */
|
|
8342
|
-
errors.length > 0 ? /* @__PURE__ */
|
|
8343
|
-
saved ? /* @__PURE__ */
|
|
8344
|
-
/* @__PURE__ */
|
|
8435
|
+
/* @__PURE__ */ jsx9(Text9, { color: "gray", children: DIVIDER }),
|
|
8436
|
+
errors.length > 0 ? /* @__PURE__ */ jsx9(Text9, { color: "red", children: errors.includes("save") ? "Save failed \u2014 please retry" : `Format error: ${errors.join(", ")}` }) : null,
|
|
8437
|
+
saved ? /* @__PURE__ */ jsx9(Text9, { color: "green", children: "Saved (port/timeouts apply after restart)" }) : null,
|
|
8438
|
+
/* @__PURE__ */ jsx9(Text9, { color: "cyan", children: "Tab move \xB7 Enter next/save \xB7 Esc back" })
|
|
8345
8439
|
] });
|
|
8346
8440
|
}
|
|
8347
8441
|
|
|
8348
8442
|
// src/ui/App.tsx
|
|
8349
|
-
import { jsx as
|
|
8443
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
8350
8444
|
function useStore(store) {
|
|
8351
|
-
const [state, setState] =
|
|
8352
|
-
|
|
8445
|
+
const [state, setState] = useState8(store.getState());
|
|
8446
|
+
useEffect5(() => {
|
|
8353
8447
|
const onChange = () => setState({ ...store.getState() });
|
|
8354
8448
|
store.on("change", onChange);
|
|
8355
8449
|
return () => {
|
|
@@ -8371,37 +8465,37 @@ function App({
|
|
|
8371
8465
|
}) {
|
|
8372
8466
|
const state = useStore(store);
|
|
8373
8467
|
const { exit } = useApp();
|
|
8374
|
-
const [screen, setScreen] =
|
|
8375
|
-
|
|
8468
|
+
const [screen, setScreen] = useState8("main");
|
|
8469
|
+
useEffect5(() => {
|
|
8376
8470
|
if (state.relayStatus === "connected") core.ws.requestQr();
|
|
8377
8471
|
}, [state.relayStatus, core]);
|
|
8378
8472
|
const s = STATUS[state.relayStatus] ?? { dot: "\xB7", color: "gray" };
|
|
8379
8473
|
const retry = state.relayRetry ? ` (retry ${state.relayRetry})` : "";
|
|
8380
8474
|
const relayTarget = config2.getRelayUrls().join(", ");
|
|
8381
|
-
return /* @__PURE__ */
|
|
8382
|
-
/* @__PURE__ */
|
|
8383
|
-
/* @__PURE__ */
|
|
8384
|
-
/* @__PURE__ */
|
|
8475
|
+
return /* @__PURE__ */ jsxs10(Box9, { flexDirection: "column", borderStyle: "round", paddingX: 1, children: [
|
|
8476
|
+
/* @__PURE__ */ jsxs10(Box9, { justifyContent: "space-between", children: [
|
|
8477
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, children: "reins" }),
|
|
8478
|
+
/* @__PURE__ */ jsxs10(Text10, { color: "gray", children: [
|
|
8385
8479
|
"v",
|
|
8386
8480
|
identity.version
|
|
8387
8481
|
] })
|
|
8388
8482
|
] }),
|
|
8389
|
-
/* @__PURE__ */
|
|
8390
|
-
/* @__PURE__ */
|
|
8391
|
-
/* @__PURE__ */
|
|
8483
|
+
/* @__PURE__ */ jsxs10(Box9, { children: [
|
|
8484
|
+
/* @__PURE__ */ jsx10(Text10, { color: "gray", children: "Relay " }),
|
|
8485
|
+
/* @__PURE__ */ jsxs10(Text10, { color: s.color, children: [
|
|
8392
8486
|
s.dot,
|
|
8393
8487
|
" ",
|
|
8394
8488
|
state.relayStatus,
|
|
8395
8489
|
retry
|
|
8396
8490
|
] }),
|
|
8397
|
-
state.qr ? /* @__PURE__ */
|
|
8491
|
+
state.qr ? /* @__PURE__ */ jsxs10(Text10, { color: "gray", children: [
|
|
8398
8492
|
" (",
|
|
8399
8493
|
state.qr.mode,
|
|
8400
8494
|
")"
|
|
8401
8495
|
] }) : null
|
|
8402
8496
|
] }),
|
|
8403
|
-
/* @__PURE__ */
|
|
8404
|
-
screen === "main" ? /* @__PURE__ */
|
|
8497
|
+
/* @__PURE__ */ jsx10(Text10, { color: "gray", children: relayTarget }),
|
|
8498
|
+
screen === "main" ? /* @__PURE__ */ jsx10(
|
|
8405
8499
|
MainScreen,
|
|
8406
8500
|
{
|
|
8407
8501
|
state,
|
|
@@ -8411,7 +8505,7 @@ function App({
|
|
|
8411
8505
|
onExit: exit
|
|
8412
8506
|
}
|
|
8413
8507
|
) : null,
|
|
8414
|
-
screen === "machines" ? /* @__PURE__ */
|
|
8508
|
+
screen === "machines" ? /* @__PURE__ */ jsx10(
|
|
8415
8509
|
MachinesScreen,
|
|
8416
8510
|
{
|
|
8417
8511
|
machines: core.ssh.getMachines(),
|
|
@@ -8420,8 +8514,8 @@ function App({
|
|
|
8420
8514
|
onBack: () => setScreen("main")
|
|
8421
8515
|
}
|
|
8422
8516
|
) : null,
|
|
8423
|
-
screen === "options" ? /* @__PURE__ */
|
|
8424
|
-
screen === "info" ? /* @__PURE__ */
|
|
8517
|
+
screen === "options" ? /* @__PURE__ */ jsx10(OptionsScreen, { config: config2, core, active: true, onBack: () => setScreen("main") }) : null,
|
|
8518
|
+
screen === "info" ? /* @__PURE__ */ jsx10(
|
|
8425
8519
|
InfoScreen,
|
|
8426
8520
|
{
|
|
8427
8521
|
state,
|
|
@@ -8435,8 +8529,8 @@ function App({
|
|
|
8435
8529
|
}
|
|
8436
8530
|
|
|
8437
8531
|
// src/ui/ManagerApp.tsx
|
|
8438
|
-
import { Box as Box10, Text as
|
|
8439
|
-
import { useEffect as
|
|
8532
|
+
import { Box as Box10, Text as Text11, useApp as useApp2, useInput as useInput5 } from "ink";
|
|
8533
|
+
import { useEffect as useEffect6, useMemo as useMemo3, useState as useState9 } from "react";
|
|
8440
8534
|
|
|
8441
8535
|
// src/ui/log-tail.ts
|
|
8442
8536
|
import fs11 from "fs";
|
|
@@ -8462,15 +8556,17 @@ function tailLog(component, lines) {
|
|
|
8462
8556
|
}
|
|
8463
8557
|
|
|
8464
8558
|
// src/ui/ManagerApp.tsx
|
|
8465
|
-
import { jsx as
|
|
8559
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
8466
8560
|
var LOG_ROWS = 8;
|
|
8561
|
+
var QR_RESERVE_ROWS = 10 + LOG_ROWS;
|
|
8467
8562
|
function ManagerApp({ config: config2 }) {
|
|
8468
8563
|
const { exit } = useApp2();
|
|
8469
|
-
const
|
|
8470
|
-
const [
|
|
8471
|
-
const [
|
|
8472
|
-
const [
|
|
8473
|
-
|
|
8564
|
+
const { columns, rows } = useTerminalSize();
|
|
8565
|
+
const [status, setStatus] = useState9(() => serviceStatus());
|
|
8566
|
+
const [logs, setLogs] = useState9(() => tailLog("ws", LOG_ROWS));
|
|
8567
|
+
const [qrVisible, setQrVisible] = useState9(false);
|
|
8568
|
+
const [notice, setNotice] = useState9(null);
|
|
8569
|
+
useEffect6(() => {
|
|
8474
8570
|
const t = setInterval(() => {
|
|
8475
8571
|
setStatus(serviceStatus());
|
|
8476
8572
|
setLogs(tailLog("ws", LOG_ROWS));
|
|
@@ -8495,43 +8591,51 @@ function ManagerApp({ config: config2 }) {
|
|
|
8495
8591
|
setStatus(serviceStatus());
|
|
8496
8592
|
}
|
|
8497
8593
|
});
|
|
8498
|
-
return /* @__PURE__ */
|
|
8499
|
-
/* @__PURE__ */
|
|
8500
|
-
/* @__PURE__ */
|
|
8501
|
-
/* @__PURE__ */
|
|
8594
|
+
return /* @__PURE__ */ jsxs11(Box10, { flexDirection: "column", borderStyle: "round", paddingX: 1, children: [
|
|
8595
|
+
/* @__PURE__ */ jsxs11(Box10, { justifyContent: "space-between", children: [
|
|
8596
|
+
/* @__PURE__ */ jsx11(Text11, { bold: true, children: "reins \u2014 running as service" }),
|
|
8597
|
+
/* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
|
|
8502
8598
|
"v",
|
|
8503
8599
|
identity.version
|
|
8504
8600
|
] })
|
|
8505
8601
|
] }),
|
|
8506
|
-
/* @__PURE__ */
|
|
8507
|
-
/* @__PURE__ */
|
|
8508
|
-
/* @__PURE__ */
|
|
8509
|
-
/* @__PURE__ */
|
|
8602
|
+
/* @__PURE__ */ jsxs11(Box10, { children: [
|
|
8603
|
+
/* @__PURE__ */ jsx11(Text11, { color: "gray", children: "Service " }),
|
|
8604
|
+
/* @__PURE__ */ jsx11(Text11, { color: status.active ? "green" : "red", children: status.active ? "\u25CF active" : "\u25CB inactive" }),
|
|
8605
|
+
/* @__PURE__ */ jsx11(Text11, { color: "gray", children: status.enabled ? " (enabled)" : " (disabled)" })
|
|
8510
8606
|
] }),
|
|
8511
|
-
/* @__PURE__ */
|
|
8512
|
-
/* @__PURE__ */
|
|
8513
|
-
status.linger ? /* @__PURE__ */
|
|
8607
|
+
/* @__PURE__ */ jsxs11(Box10, { children: [
|
|
8608
|
+
/* @__PURE__ */ jsx11(Text11, { color: "gray", children: "Linger " }),
|
|
8609
|
+
status.linger ? /* @__PURE__ */ jsx11(Text11, { color: "green", children: "on \u2192 survives SSH logout & reboot" }) : /* @__PURE__ */ jsxs11(Text11, { color: "yellow", children: [
|
|
8514
8610
|
"off \u2192 run: sudo loginctl enable-linger ",
|
|
8515
8611
|
status.user
|
|
8516
8612
|
] })
|
|
8517
8613
|
] }),
|
|
8518
|
-
/* @__PURE__ */
|
|
8519
|
-
pairUrl ? /* @__PURE__ */
|
|
8520
|
-
/* @__PURE__ */
|
|
8521
|
-
|
|
8522
|
-
|
|
8523
|
-
|
|
8524
|
-
|
|
8614
|
+
/* @__PURE__ */ jsx11(Text11, { color: "gray", children: DIVIDER }),
|
|
8615
|
+
pairUrl ? /* @__PURE__ */ jsxs11(Box10, { flexDirection: "column", children: [
|
|
8616
|
+
/* @__PURE__ */ jsx11(Text11, { color: "gray", children: "Scan to pair:" }),
|
|
8617
|
+
/* @__PURE__ */ jsx11(
|
|
8618
|
+
PairQrCell,
|
|
8619
|
+
{
|
|
8620
|
+
qrText,
|
|
8621
|
+
visible: qrVisible,
|
|
8622
|
+
columns,
|
|
8623
|
+
rows,
|
|
8624
|
+
reserveRows: QR_RESERVE_ROWS
|
|
8625
|
+
}
|
|
8626
|
+
),
|
|
8627
|
+
/* @__PURE__ */ jsxs11(Box10, { children: [
|
|
8628
|
+
/* @__PURE__ */ jsx11(Text11, { color: "gray", children: "URL " }),
|
|
8629
|
+
/* @__PURE__ */ jsx11(Text11, { color: "cyan", children: prettyUrl(pairUrl) })
|
|
8525
8630
|
] }),
|
|
8526
|
-
multiRelay ? /* @__PURE__ */
|
|
8527
|
-
|
|
8528
|
-
|
|
8529
|
-
/* @__PURE__ */
|
|
8530
|
-
/* @__PURE__ */
|
|
8531
|
-
|
|
8532
|
-
/* @__PURE__ */
|
|
8533
|
-
|
|
8534
|
-
/* @__PURE__ */ jsxs10(Text10, { color: "cyan", children: [
|
|
8631
|
+
multiRelay ? /* @__PURE__ */ jsx11(Text11, { color: "yellow", children: "(assumes primary relay \u2014 may differ if failed over)" }) : null
|
|
8632
|
+
] }) : /* @__PURE__ */ jsx11(Text11, { color: "yellow", children: "Pair URL unavailable (session-token / relay URL \uD655\uC778)" }),
|
|
8633
|
+
/* @__PURE__ */ jsx11(Text11, { color: "gray", children: DIVIDER }),
|
|
8634
|
+
/* @__PURE__ */ jsx11(Text11, { color: "gray", children: "Logs (ws.log tail)" }),
|
|
8635
|
+
logs.length === 0 ? /* @__PURE__ */ jsx11(Text11, { color: "gray", children: "\u2026" }) : logs.map((line, i) => /* @__PURE__ */ jsx11(Text11, { color: "gray", children: line }, `${i}-${line}`)),
|
|
8636
|
+
/* @__PURE__ */ jsx11(Text11, { color: "gray", children: DIVIDER }),
|
|
8637
|
+
notice ? /* @__PURE__ */ jsx11(Text11, { color: "green", children: notice }) : null,
|
|
8638
|
+
/* @__PURE__ */ jsxs11(Text11, { color: "cyan", children: [
|
|
8535
8639
|
"r restart \xB7 x stop \xB7 u disable \xB7 c ",
|
|
8536
8640
|
qrVisible ? "hide" : "show",
|
|
8537
8641
|
" qr \xB7 q quit"
|
|
@@ -8540,24 +8644,14 @@ function ManagerApp({ config: config2 }) {
|
|
|
8540
8644
|
}
|
|
8541
8645
|
|
|
8542
8646
|
// src/cli.tsx
|
|
8543
|
-
import { jsx as
|
|
8647
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
8544
8648
|
var loadedEnvFiles = loadDotEnv();
|
|
8545
|
-
setConsoleLogging(false);
|
|
8546
8649
|
var config = new HeadlessConfigStore();
|
|
8547
8650
|
var args = process.argv.slice(2);
|
|
8548
8651
|
if (args[0] === "service") {
|
|
8549
8652
|
process.exit(runServiceCommand(args[1], config));
|
|
8550
8653
|
}
|
|
8551
|
-
|
|
8552
|
-
process.stdout.write("\x1B[?1049h");
|
|
8553
|
-
process.on("exit", () => process.stdout.write("\x1B[?1049l"));
|
|
8554
|
-
}
|
|
8555
|
-
if (isServiceActive()) {
|
|
8556
|
-
const { waitUntilExit } = render(/* @__PURE__ */ jsx11(ManagerApp, { config }));
|
|
8557
|
-
process.on("SIGTERM", () => process.exit(0));
|
|
8558
|
-
void waitUntilExit().finally(() => process.exit(0));
|
|
8559
|
-
} else {
|
|
8560
|
-
const store = new HeadlessStore();
|
|
8654
|
+
function buildCore(store) {
|
|
8561
8655
|
const logDir = path16.join(os14.homedir(), ".ai_remote_vibe_agent", "logs");
|
|
8562
8656
|
const relayTarget = config.getRelayUrls().join(", ");
|
|
8563
8657
|
for (const f of loadedEnvFiles) store.log(`[boot] .env loaded: ${f}`);
|
|
@@ -8571,13 +8665,50 @@ if (isServiceActive()) {
|
|
|
8571
8665
|
});
|
|
8572
8666
|
store.apply({ type: "machine_list", machines: core.ssh.getMachines() });
|
|
8573
8667
|
core.ws.requestQr();
|
|
8574
|
-
|
|
8575
|
-
|
|
8668
|
+
return core;
|
|
8669
|
+
}
|
|
8670
|
+
var mode = resolveEntryMode({
|
|
8671
|
+
isTTY: !!process.stdout.isTTY,
|
|
8672
|
+
isServiceProcess: isServiceProcess(process.env),
|
|
8673
|
+
isServiceActive
|
|
8674
|
+
});
|
|
8675
|
+
if (mode === "daemon") {
|
|
8676
|
+
runDaemon();
|
|
8677
|
+
} else if (mode === "serviceBusy") {
|
|
8678
|
+
console.error("reins: \uC11C\uBE44\uC2A4\uAC00 \uC774\uBBF8 \uAC00\uB3D9 \uC911\uC774\uB77C \uB450 \uBC88\uC9F8 \uC778\uC2A4\uD134\uC2A4\uB97C \uC2DC\uC791\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 (\uC774\uC911 relay\xB7\uD3EC\uD2B8 \uCDA9\uB3CC \uBC29\uC9C0).");
|
|
8679
|
+
console.error(" \uC7AC\uC811\uC18D\uD558\uB824\uBA74 TTY \uC5D0\uC11C `reins` \uB97C, \uB85C\uADF8\uB294 `journalctl --user -u reins -f` \uB97C \uC0AC\uC6A9\uD558\uC138\uC694.");
|
|
8680
|
+
process.exit(0);
|
|
8681
|
+
} else {
|
|
8682
|
+
setConsoleLogging(false);
|
|
8683
|
+
process.stdout.write("\x1B[?1049h");
|
|
8684
|
+
process.on("exit", () => process.stdout.write("\x1B[?1049l"));
|
|
8685
|
+
if (mode === "manager") {
|
|
8686
|
+
const { waitUntilExit } = render(/* @__PURE__ */ jsx12(ManagerApp, { config }));
|
|
8687
|
+
process.on("SIGTERM", () => process.exit(0));
|
|
8688
|
+
void waitUntilExit().finally(() => process.exit(0));
|
|
8689
|
+
} else {
|
|
8690
|
+
const store = new HeadlessStore();
|
|
8691
|
+
const core = buildCore(store);
|
|
8692
|
+
const { waitUntilExit } = render(/* @__PURE__ */ jsx12(App, { store, core, config }));
|
|
8693
|
+
const finish = (code) => {
|
|
8694
|
+
core.cleanup();
|
|
8695
|
+
process.exit(code);
|
|
8696
|
+
};
|
|
8697
|
+
process.on("SIGTERM", () => finish(0));
|
|
8698
|
+
void waitUntilExit().then(() => finish(0)).catch(() => finish(1));
|
|
8699
|
+
}
|
|
8700
|
+
}
|
|
8701
|
+
function runDaemon() {
|
|
8702
|
+
const log10 = createLogger("ws");
|
|
8703
|
+
const store = new HeadlessStore();
|
|
8704
|
+
const core = buildCore(store);
|
|
8705
|
+
log10.info(`[daemon] headless service started \u2014 relay=${config.getRelayUrls().join(", ")}`);
|
|
8706
|
+
const shutdown = () => {
|
|
8576
8707
|
core.cleanup();
|
|
8577
|
-
process.exit(
|
|
8708
|
+
process.exit(0);
|
|
8578
8709
|
};
|
|
8579
|
-
process.on("SIGTERM",
|
|
8580
|
-
|
|
8710
|
+
process.on("SIGTERM", shutdown);
|
|
8711
|
+
process.on("SIGINT", shutdown);
|
|
8581
8712
|
}
|
|
8582
8713
|
/*! Bundled license information:
|
|
8583
8714
|
|