@rethinkingstudio/clawpilot 2.1.20 → 2.1.21-beta.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.
@@ -0,0 +1,193 @@
1
+ import { randomUUID } from "crypto";
2
+ import { homedir } from "os";
3
+ import { existsSync, statSync } from "fs";
4
+ import { spawn } from "node-pty";
5
+ export class PtyManager {
6
+ emitEvent;
7
+ activeSession = null;
8
+ constructor(emitEvent) {
9
+ this.emitEvent = emitEvent;
10
+ }
11
+ open(rawParams) {
12
+ if (this.activeSession) {
13
+ throw new Error("terminal session already active");
14
+ }
15
+ const params = isRecord(rawParams) ? rawParams : {};
16
+ const cols = clampInt(params.cols, 120, 20, 400);
17
+ const rows = clampInt(params.rows, 32, 10, 200);
18
+ const cwd = resolveWorkingDirectory(normalizeString(params.cwd));
19
+ const shellCandidates = resolveShellCandidates(normalizeString(params.shell));
20
+ const sessionId = randomUUID();
21
+ const { pty, shell } = spawnWithFallback(shellCandidates, cwd, cols, rows);
22
+ const session = {
23
+ sessionId,
24
+ shell,
25
+ cwd,
26
+ cols,
27
+ rows,
28
+ pty,
29
+ };
30
+ this.activeSession = session;
31
+ pty.onData((data) => {
32
+ if (this.activeSession?.sessionId !== sessionId)
33
+ return;
34
+ this.emitEvent("terminal.data", { sessionId, data });
35
+ });
36
+ pty.onExit(({ exitCode, signal }) => {
37
+ if (this.activeSession?.sessionId !== sessionId)
38
+ return;
39
+ this.activeSession = null;
40
+ this.emitEvent("terminal.closed", {
41
+ sessionId,
42
+ exitCode,
43
+ signal,
44
+ reason: "process_exited",
45
+ });
46
+ });
47
+ const info = stripPty(session);
48
+ this.emitEvent("terminal.opened", info);
49
+ return info;
50
+ }
51
+ input(rawParams) {
52
+ const session = this.requireSession(rawParams);
53
+ const params = isRecord(rawParams) ? rawParams : {};
54
+ const data = normalizeString(params.data);
55
+ if (!data) {
56
+ throw new Error("terminal input data is required");
57
+ }
58
+ session.pty.write(data);
59
+ }
60
+ resize(rawParams) {
61
+ const session = this.requireSession(rawParams);
62
+ const params = isRecord(rawParams) ? rawParams : {};
63
+ const cols = clampInt(params.cols, session.cols, 20, 400);
64
+ const rows = clampInt(params.rows, session.rows, 10, 200);
65
+ session.pty.resize(cols, rows);
66
+ session.cols = cols;
67
+ session.rows = rows;
68
+ return stripPty(session);
69
+ }
70
+ close(rawParams, reason = "closed_by_client") {
71
+ const session = this.requireSession(rawParams);
72
+ this.activeSession = null;
73
+ session.pty.kill();
74
+ this.emitEvent("terminal.closed", {
75
+ sessionId: session.sessionId,
76
+ reason,
77
+ });
78
+ }
79
+ shutdown(reason = "relay_disconnected") {
80
+ if (!this.activeSession)
81
+ return;
82
+ const session = this.activeSession;
83
+ this.activeSession = null;
84
+ session.pty.kill();
85
+ this.emitEvent("terminal.closed", {
86
+ sessionId: session.sessionId,
87
+ reason,
88
+ });
89
+ }
90
+ currentSession() {
91
+ return this.activeSession ? stripPty(this.activeSession) : null;
92
+ }
93
+ requireSession(rawParams) {
94
+ const params = isRecord(rawParams) ? rawParams : {};
95
+ const sessionId = normalizeString(params.sessionId);
96
+ if (!this.activeSession) {
97
+ throw new Error("terminal session is not active");
98
+ }
99
+ if (sessionId && sessionId != this.activeSession.sessionId) {
100
+ throw new Error("terminal session not found");
101
+ }
102
+ return this.activeSession;
103
+ }
104
+ }
105
+ function stripPty(session) {
106
+ return {
107
+ sessionId: session.sessionId,
108
+ shell: session.shell,
109
+ cwd: session.cwd,
110
+ cols: session.cols,
111
+ rows: session.rows,
112
+ };
113
+ }
114
+ function isRecord(value) {
115
+ return !!value && typeof value === "object" && !Array.isArray(value);
116
+ }
117
+ function normalizeString(value) {
118
+ if (typeof value !== "string") {
119
+ return undefined;
120
+ }
121
+ const trimmed = value.trim();
122
+ return trimmed.length === 0 ? undefined : trimmed;
123
+ }
124
+ function clampInt(value, fallback, min, max) {
125
+ const numeric = typeof value === "number" ? value : Number(value);
126
+ if (!Number.isFinite(numeric))
127
+ return fallback;
128
+ return Math.max(min, Math.min(max, Math.floor(numeric)));
129
+ }
130
+ function defaultShell() {
131
+ if (process.platform === "win32") {
132
+ return "pwsh.exe";
133
+ }
134
+ return process.env.SHELL || "/bin/bash";
135
+ }
136
+ function resolveShellCandidates(requestedShell) {
137
+ if (requestedShell) {
138
+ return dedupe([requestedShell, ...platformShellFallbacks()]);
139
+ }
140
+ return dedupe([defaultShell(), ...platformShellFallbacks()]);
141
+ }
142
+ function platformShellFallbacks() {
143
+ if (process.platform === "win32") {
144
+ return ["powershell.exe", "cmd.exe"];
145
+ }
146
+ return ["/bin/zsh", "/bin/bash", "/bin/sh"];
147
+ }
148
+ function resolveWorkingDirectory(requestedCwd) {
149
+ const candidates = dedupe([
150
+ requestedCwd,
151
+ process.env.HOME,
152
+ homedir(),
153
+ process.cwd(),
154
+ "/",
155
+ ].filter((value) => !!value));
156
+ for (const candidate of candidates) {
157
+ if (directoryExists(candidate)) {
158
+ return candidate;
159
+ }
160
+ }
161
+ return process.cwd();
162
+ }
163
+ function directoryExists(path) {
164
+ try {
165
+ return existsSync(path) && statSync(path).isDirectory();
166
+ }
167
+ catch {
168
+ return false;
169
+ }
170
+ }
171
+ function spawnWithFallback(shellCandidates, cwd, cols, rows) {
172
+ let lastError;
173
+ for (const shell of shellCandidates) {
174
+ try {
175
+ const pty = spawn(shell, [], {
176
+ name: "xterm-256color",
177
+ cols,
178
+ rows,
179
+ cwd,
180
+ env: { ...process.env },
181
+ });
182
+ return { pty, shell };
183
+ }
184
+ catch (error) {
185
+ lastError = error;
186
+ }
187
+ }
188
+ throw new Error(`failed to open terminal: ${String(lastError)}; shells tried: ${shellCandidates.join(", ")}; cwd=${cwd}`);
189
+ }
190
+ function dedupe(values) {
191
+ return [...new Set(values)];
192
+ }
193
+ //# sourceMappingURL=pty-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pty-manager.js","sourceRoot":"","sources":["../../src/terminal/pty-manager.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rethinkingstudio/clawpilot",
3
- "version": "2.1.20",
3
+ "version": "2.1.21-beta.1",
4
4
  "description": "ClawAI relay client for Mac mini",
5
5
  "type": "module",
6
6
  "bin": {
@@ -221,6 +221,16 @@ def resolve_runtime(
221
221
  )
222
222
 
223
223
 
224
+ def resolve_toolsets(cfg: Dict[str, Any]) -> List[str]:
225
+ raw_toolsets = _get_platform_tools(cfg, "cli")
226
+ if raw_toolsets is None:
227
+ return []
228
+ try:
229
+ return [str(toolset) for toolset in raw_toolsets]
230
+ except TypeError as exc:
231
+ raise TypeError("Hermes CLI toolset config must be iterable") from exc
232
+
233
+
224
234
  def derive_agent_inputs(payload: Dict[str, Any], session_db: Optional[SessionDB] = None) -> Dict[str, Any]:
225
235
  user_message = str(payload.get("userMessage") or "").strip()
226
236
  if not user_message:
@@ -230,6 +240,8 @@ def derive_agent_inputs(payload: Dict[str, Any], session_db: Optional[SessionDB]
230
240
  session_title = str(payload.get("sessionTitle") or "").strip() or None
231
241
  model = str(payload.get("model") or "").strip() or None
232
242
  provider = str(payload.get("provider") or "").strip() or None
243
+ base_url = str(payload.get("baseUrl") or "").strip() or None
244
+ api_key = str(payload.get("apiKey") or "").strip() or None
233
245
 
234
246
  inherit_base_env_for_profile()
235
247
 
@@ -248,16 +260,18 @@ def derive_agent_inputs(payload: Dict[str, Any], session_db: Optional[SessionDB]
248
260
  if not default_model:
249
261
  raise ValueError("missing model")
250
262
 
263
+ configured_base_url = str(model_cfg.get("base_url") or "").strip() or None
251
264
  runtime = normalize_runtime_config(
252
- resolve_runtime(
253
- provider or model_cfg.get("provider"),
254
- explicit_base_url=model_cfg.get("base_url"),
265
+ resolve_runtime_provider(
266
+ requested=provider or model_cfg.get("provider"),
267
+ explicit_api_key=api_key,
268
+ explicit_base_url=base_url or configured_base_url,
255
269
  target_model=default_model,
256
270
  )
257
271
  )
258
- if model_cfg.get("base_url") and not runtime.get("base_url"):
259
- runtime["base_url"] = model_cfg.get("base_url")
260
- toolsets = list(_get_platform_tools(cfg, "cli"))
272
+ if configured_base_url and not runtime.get("base_url"):
273
+ runtime["base_url"] = configured_base_url
274
+ toolsets = resolve_toolsets(cfg)
261
275
 
262
276
  history = read_session_history(session_id, session_db)
263
277
  return {
@@ -412,6 +426,16 @@ def main() -> int:
412
426
  task_id=agent.session_id,
413
427
  persist_user_message=agent_inputs["user_message"],
414
428
  )
429
+ if not isinstance(result, dict):
430
+ emit(
431
+ {
432
+ "type": "error",
433
+ "message": "Hermes agent returned no result."
434
+ if result is None
435
+ else f"Hermes agent returned invalid result: {type(result).__name__}.",
436
+ }
437
+ )
438
+ return 1
415
439
  final_text = extract_final_text(result, accumulated_text)
416
440
  if not final_text.strip():
417
441
  error_text = extract_error_text(result)