@termhub/agent 0.1.7 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +49 -1
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -39,6 +39,9 @@ var sessionName = z.string().min(1).max(128).regex(SESSION_RE);
39
39
  var machinePath = z.string().min(1).max(4096).refine((p) => (p === "~" || p.startsWith("~/") || p.startsWith("/")) && !/[\0\n\r]/.test(p), "invalid path");
40
40
  var pasteName = z.string().min(1).max(255).regex(/^[A-Za-z0-9._-]+$/);
41
41
  var aiProvider = z.enum(["claude", "chatgpt", "gemini", "antigravity"]);
42
+ var TMUX_KEYS = ["Enter", "Escape", "C-c", "Up", "Down", "Tab", "y", "n", "1", "2", "3", "4", "5", "6", "7", "8", "9"];
43
+ var tmuxKey = z.enum(TMUX_KEYS);
44
+ var TEXT_MAX_CHARS = 4e3;
42
45
  var rpcErrorSchema = z.object({
43
46
  /** `failed`: the operation ran on the machine and `message` says why it failed, in words meant for the user. */
44
47
  code: z.enum(["eperm", "notfound", "no_tmux", "timeout", "invalid", "internal", "failed"]),
@@ -51,6 +54,11 @@ var RPC = {
51
54
  "tmux.list": def(z.object({}), z.object({ sessions: z.array(sessionName) })),
52
55
  "tmux.kill": def(z.object({ session: sessionName }), z.object({ killed: z.boolean() })),
53
56
  "tmux.capture": def(z.object({ session: sessionName, lines: z.number().int().min(1).max(5e3) }), z.object({ text: z.string() })),
57
+ /** Idempotent: creates the detached session in `cwd` when it is missing. `created` says whether it had to. */
58
+ "tmux.ensure": def(z.object({ session: sessionName, cwd: machinePath }), z.object({ created: z.boolean() }), 1e4),
59
+ /** Types `text` literally, then (with `enter`) presses Enter on its own after a short pause. */
60
+ "tmux.sendText": def(z.object({ session: sessionName, text: z.string().max(TEXT_MAX_CHARS), enter: z.boolean() }), z.object({ sent: z.literal(true) }), 1e4),
61
+ "tmux.sendKey": def(z.object({ session: sessionName, key: tmuxKey }), z.object({ sent: z.literal(true) }), 1e4),
54
62
  "tools.detect": def(z.object({}), z.object({ os: z.string().nullable(), tools: z.array(z.string().max(32)) })),
55
63
  "hw.probe": def(z.object({}), z.object({ stdout: z.string() }), 15e3),
56
64
  "fs.list": def(z.object({ path: machinePath }), z.object({ stdout: z.string() })),
@@ -1117,6 +1125,7 @@ async function pasteFile(params) {
1117
1125
  }
1118
1126
 
1119
1127
  // src/rpc/tmux.ts
1128
+ var ENTER_PAUSE_MS = 300;
1120
1129
  function processFailure2(r) {
1121
1130
  if (r.timedOut) return new RpcFailure("timeout", "tmux timed out");
1122
1131
  if (r.error === "enoent") return new RpcFailure("no_tmux", "tmux not found");
@@ -1144,6 +1153,42 @@ async function capture(params) {
1144
1153
  if (r.code !== 0) throw new RpcFailure("notfound", "session not found");
1145
1154
  return { text: r.stdout };
1146
1155
  }
1156
+ var pane = (session) => `=${session}:`;
1157
+ var why = (stderr, fallback) => stderr.trim().split("\n")[0] || fallback;
1158
+ async function ensure(params) {
1159
+ const has = await run(tmuxPath(), ["has-session", "-t", `=${params.session}`]);
1160
+ const hasFailure = processFailure2(has);
1161
+ if (hasFailure) throw hasFailure;
1162
+ if (has.code === 0) return { created: false };
1163
+ const made = await run(tmuxPath(), ["new-session", "-d", "-s", params.session, "-c", params.cwd]);
1164
+ const madeFailure = processFailure2(made);
1165
+ if (madeFailure) throw madeFailure;
1166
+ if (made.code !== 0) throw new RpcFailure("failed", why(made.stderr, "tmux new-session falhou"), params.cwd);
1167
+ return { created: true };
1168
+ }
1169
+ async function sendText(params) {
1170
+ if (params.text) {
1171
+ const typed = await run(tmuxPath(), ["send-keys", "-t", pane(params.session), "-l", "--", params.text]);
1172
+ const failure = processFailure2(typed);
1173
+ if (failure) throw failure;
1174
+ if (typed.code !== 0) throw new RpcFailure("notfound", why(typed.stderr, "session not found"));
1175
+ if (params.enter) await new Promise((r) => setTimeout(r, ENTER_PAUSE_MS));
1176
+ }
1177
+ if (params.enter) {
1178
+ const entered = await run(tmuxPath(), ["send-keys", "-t", pane(params.session), "Enter"]);
1179
+ const failure = processFailure2(entered);
1180
+ if (failure) throw failure;
1181
+ if (entered.code !== 0) throw new RpcFailure("notfound", why(entered.stderr, "session not found"));
1182
+ }
1183
+ return { sent: true };
1184
+ }
1185
+ async function sendKey(params) {
1186
+ const r = await run(tmuxPath(), ["send-keys", "-t", pane(params.session), params.key]);
1187
+ const failure = processFailure2(r);
1188
+ if (failure) throw failure;
1189
+ if (r.code !== 0) throw new RpcFailure("notfound", why(r.stderr, "session not found"));
1190
+ return { sent: true };
1191
+ }
1147
1192
 
1148
1193
  // src/rpc/tools.ts
1149
1194
  async function detect(_params) {
@@ -1159,6 +1204,9 @@ var handlers = {
1159
1204
  "tmux.list": list2,
1160
1205
  "tmux.kill": kill,
1161
1206
  "tmux.capture": capture,
1207
+ "tmux.ensure": ensure,
1208
+ "tmux.sendText": sendText,
1209
+ "tmux.sendKey": sendKey,
1162
1210
  "tools.detect": detect,
1163
1211
  "hw.probe": probe,
1164
1212
  "fs.list": list,
@@ -1254,7 +1302,7 @@ async function status(deps = {}) {
1254
1302
  }
1255
1303
 
1256
1304
  // src/version.ts
1257
- var AGENT_VERSION = "0.1.7";
1305
+ var AGENT_VERSION = "0.2.0";
1258
1306
 
1259
1307
  // src/run.ts
1260
1308
  function detectOs(platform = process.platform) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@termhub/agent",
3
- "version": "0.1.7",
3
+ "version": "0.2.0",
4
4
  "description": "Agente do termhub: conecta esta máquina ao servidor por WebSocket de saída (sem SSH) e expõe os terminais tmux no navegador.",
5
5
  "license": "MIT",
6
6
  "private": false,