very-happy-cli 0.2.156 → 0.2.158
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/{AcpBackend-BHe1_F1h.cjs → AcpBackend-DIDRig5G.cjs} +2 -2
- package/dist/{AcpBackend-C3nc1u-s.mjs → AcpBackend-vDVLHuYJ.mjs} +2 -2
- package/dist/{AcpSessionManager-BVxkQnNf.cjs → AcpSessionManager-C44U99CC.cjs} +1 -1
- package/dist/{AcpSessionManager-dN3k1LbI.mjs → AcpSessionManager-CmRjfhn3.mjs} +1 -1
- package/dist/{agentGuidance-C604n0Zq.mjs → agentGuidance-CrR9l62U.mjs} +562 -30
- package/dist/{agentGuidance-C-CmV03f.cjs → agentGuidance-DwOWbX55.cjs} +581 -29
- package/dist/{agentHome-D2XGsoqs.mjs → agentHome-CyLmGH5L.mjs} +2 -2
- package/dist/{agentHome-CmIJNV7f.cjs → agentHome-Dw8Jx_CJ.cjs} +2 -2
- package/dist/{auto-B1I6G_Nf.mjs → auto-BlMZPwOk.mjs} +19 -10
- package/dist/{auto-CMDq4oIu.cjs → auto-DZbNcTXx.cjs} +19 -10
- package/dist/codex/happyMcpStdioBridge.cjs +2 -2
- package/dist/codex/happyMcpStdioBridge.mjs +2 -2
- package/dist/{config-BkTu25bH.mjs → config-BtPArqRH.mjs} +4 -4
- package/dist/{config-BYHSRY2E.cjs → config-DG84bjwF.cjs} +4 -4
- package/dist/{index-BbtR-Qto.cjs → index-BG932eCh.cjs} +292 -47
- package/dist/{index-DAAreiRF.mjs → index-CVCt3kSt.mjs} +289 -44
- package/dist/{index-CB8QnjvS.cjs → index-D5ScEErM.cjs} +4 -4
- package/dist/{index-B_-7NpGZ.cjs → index-DPCsWlWK.cjs} +7 -7
- package/dist/{index-PYO8Mdz4.mjs → index-DgO0FHOx.mjs} +4 -4
- package/dist/{index-B5QbzEga.mjs → index-ZChGsuB7.mjs} +8 -8
- package/dist/index.cjs +4 -4
- package/dist/index.mjs +4 -4
- package/dist/{install-CNtXzwT_.mjs → install-DarS_Wco.mjs} +1 -1
- package/dist/{install-CO1ps6Wt.cjs → install-wKQGgXvN.cjs} +1 -1
- package/dist/{installTerminalHooks-CMpOV3RU.mjs → installTerminalHooks-CpNbRFPz.mjs} +4 -4
- package/dist/{installTerminalHooks-BOAnQ1Qe.cjs → installTerminalHooks-DcQfee_O.cjs} +4 -4
- package/dist/lib.cjs +2 -2
- package/dist/lib.d.cts +77 -1
- package/dist/lib.d.mts +77 -1
- package/dist/lib.mjs +2 -2
- package/dist/{limits-DmMdchTz.mjs → limits-D2uN090G.mjs} +163 -11
- package/dist/{limits-o29KXBgj.cjs → limits-kqFqhfsh.cjs} +170 -10
- package/dist/{mcp-C_uejotl.mjs → mcp-BN6bKIGb.mjs} +4 -4
- package/dist/{mcp-CwK1nI7Y.cjs → mcp-DGdeUUDJ.cjs} +4 -4
- package/dist/{piRuntime-Dj5WnqzJ.cjs → piRuntime-3qtbKujX.cjs} +2 -2
- package/dist/{piRuntime-Cu2-8nOT.mjs → piRuntime-bkw2NAZU.mjs} +2 -2
- package/dist/{piTerminal-D-BIbov9.cjs → piTerminal-BYxp3LMr.cjs} +6 -6
- package/dist/{piTerminal-DanZeBrO.mjs → piTerminal-DilKwM7B.mjs} +6 -6
- package/dist/{runGemini--QqK-Ap5.cjs → runGemini-28O2sqyI.cjs} +6 -6
- package/dist/{runGemini-CZ_lhMWY.mjs → runGemini-Cjii069p.mjs} +6 -6
- package/dist/{runOpenClaw-C0xLhTvk.cjs → runOpenClaw-CiMZad-H.cjs} +5 -5
- package/dist/{runOpenClaw-CxgLpm38.mjs → runOpenClaw-jaLr7KF1.mjs} +5 -5
- package/dist/{send-BaOkDgH0.mjs → send-C9mOtChi.mjs} +43 -31
- package/dist/{send-CTcGMVuG.cjs → send-D5ejHPXG.cjs} +43 -31
- package/dist/{sessions-OmvyJb0n.cjs → sessions-BNMPQoLE.cjs} +133 -49
- package/dist/{sessions-Bn304xF1.mjs → sessions-W6771g3X.mjs} +133 -49
- package/dist/{spawn-BKNabutV.mjs → spawn-D0QVhzRb.mjs} +4 -4
- package/dist/{spawn-CC6V2KiA.cjs → spawn-Ddtom1cb.cjs} +4 -4
- package/dist/{teams-XtrH3_h3.cjs → teams-BZ1tayg_.cjs} +5 -5
- package/dist/{teams-CLbS2_4Q.mjs → teams-CqSwtw2O.mjs} +5 -5
- package/dist/{todo-CEDvKNXK.cjs → todo-BYMg0fR0.cjs} +2 -2
- package/dist/{todo-BcPbNKt2.mjs → todo-BbusB1zM.mjs} +2 -2
- package/dist/{types-BEVzYSoV.mjs → types-DIBF4U0Y.mjs} +78 -5
- package/dist/{types-CphWjqHc.cjs → types-DnrPeN80.cjs} +78 -5
- package/package.json +7 -7
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { D as DEFAULT_RECENT_LIMIT, M as MAX_READ_LIMIT, l as listAccountSessions, e as listSessions,
|
|
3
|
-
import { d as readPersistedSessions, i as isValidSessionId } from './limits-
|
|
2
|
+
import { D as DEFAULT_RECENT_LIMIT, M as MAX_READ_LIMIT, l as listAccountSessions, f as fillForeignAccountRows, e as listRemoteSessions, g as listSessions, h as cliPeerSelf, i as listRemotePeers, j as listPeerSessions, s as sendPeerMessage, w as waitForTurnEnd, T as TurnWaitTimeoutError, k as readSessionTranscript, m as waitForRemoteTurnEnd, n as readRemoteTranscript, o as stopSession, p as resolvePermissionRequest, q as archiveSession, t as PEER_SCOPES } from './agentGuidance-CrR9l62U.mjs';
|
|
3
|
+
import { d as readPersistedSessions, i as isValidSessionId } from './limits-D2uN090G.mjs';
|
|
4
4
|
import { resolve } from 'node:path';
|
|
5
5
|
import 'zod';
|
|
6
6
|
import 'node:crypto';
|
|
@@ -13,6 +13,7 @@ import '@paralleldrive/cuid2';
|
|
|
13
13
|
import 'tweetnacl';
|
|
14
14
|
import 'node:util';
|
|
15
15
|
|
|
16
|
+
const MACHINE_ID_RE = /^[a-zA-Z0-9_-]{1,128}$/;
|
|
16
17
|
const ACTIONS = ["list", "read", "stop", "archive", "approve", "deny", "peers", "message"];
|
|
17
18
|
const ACTIONS_NEEDING_ID = /* @__PURE__ */ new Set(["read", "stop", "archive", "approve", "deny", "message"]);
|
|
18
19
|
const ACTIONS_NEEDING_REQUEST_ID = /* @__PURE__ */ new Set(["approve", "deny"]);
|
|
@@ -65,6 +66,10 @@ function parseSessionsArgs(args) {
|
|
|
65
66
|
const value = args[++i];
|
|
66
67
|
if (value === void 0 || value.trim() === "") throw new Error("--reply-to requires a value");
|
|
67
68
|
options.replyTo = value;
|
|
69
|
+
} else if (arg === "--machine") {
|
|
70
|
+
const value = args[++i];
|
|
71
|
+
if (value === void 0 || !MACHINE_ID_RE.test(value)) throw new Error("--machine requires a machine id");
|
|
72
|
+
options.machine = value;
|
|
68
73
|
} else if (arg === "--tag") {
|
|
69
74
|
const value = args[++i];
|
|
70
75
|
if (value === void 0) throw new Error("--tag requires a value");
|
|
@@ -130,31 +135,45 @@ function parseSessionsArgs(args) {
|
|
|
130
135
|
if (options.cwd !== void 0) throw new Error("--cwd only applies to `sessions peers`");
|
|
131
136
|
}
|
|
132
137
|
if (options.replyTo !== void 0 && options.action !== "message") throw new Error("--reply-to only applies to `sessions message`");
|
|
138
|
+
if (options.machine !== void 0) {
|
|
139
|
+
if (!MACHINE_ACTIONS.has(options.action)) throw new Error("--machine only applies to `sessions list|read|peers|message`");
|
|
140
|
+
if (options.all) throw new Error("--machine cannot be combined with --all (--all already asks every online machine)");
|
|
141
|
+
}
|
|
133
142
|
return options;
|
|
134
143
|
}
|
|
144
|
+
const MACHINE_ACTIONS = /* @__PURE__ */ new Set(["list", "read", "peers", "message"]);
|
|
135
145
|
const SESSIONS_HELP = `
|
|
136
|
-
${chalk.bold("very-happy sessions")} - Inspect and control sessions on this machine (for automation)
|
|
146
|
+
${chalk.bold("very-happy sessions")} - Inspect and control sessions on this machine and, through their daemons, on the account's other machines (for automation)
|
|
137
147
|
|
|
138
148
|
${chalk.bold("Usage:")}
|
|
139
|
-
very-happy sessions list [--all [--include-archived]] [--tag <name>] [--limit <n>] [--json]
|
|
140
|
-
very-happy sessions read <id> [--limit <n>] [--full] [--answer]
|
|
149
|
+
very-happy sessions list [--all [--include-archived]] [--machine <id>] [--tag <name>] [--limit <n>] [--json]
|
|
150
|
+
very-happy sessions read <id> [--machine <id>] [--limit <n>] [--full] [--answer]
|
|
141
151
|
[--wait [--timeout <s>]] [--json]
|
|
142
152
|
very-happy sessions stop <id> [--json]
|
|
143
153
|
very-happy sessions archive <id> [--json]
|
|
144
154
|
very-happy sessions approve <id> <requestId> [--for-session] [--json]
|
|
145
155
|
very-happy sessions deny <id> <requestId> [--reason <text>] [--json]
|
|
146
|
-
very-happy sessions peers [--scope repo|cwd|machine] [--cwd <dir>] [--json]
|
|
147
|
-
very-happy sessions message <id> <text> [--reply-to <msgId>] [--json]
|
|
156
|
+
very-happy sessions peers [--scope repo|cwd|machine] [--cwd <dir>] [--machine <id>] [--json]
|
|
157
|
+
very-happy sessions message <id> <text> [--reply-to <msgId>] [--machine <id>] [--json]
|
|
148
158
|
|
|
149
159
|
${chalk.bold("Actions:")}
|
|
150
160
|
list Running sessions plus recently seen ones, newest first.
|
|
151
161
|
With --all: every session on the account (newest 150, server
|
|
152
|
-
REST), attention first.
|
|
153
|
-
|
|
162
|
+
REST), attention first. Rows this machine cannot decrypt are
|
|
163
|
+
filled in by the online machine that spawned them (\`readable\`,
|
|
164
|
+
\`via\`, \`machine\` in --json; \`decryptable\` stays "this
|
|
165
|
+
machine's own key"); what no online machine holds shows only
|
|
154
166
|
id / active / archived / timestamps / url.
|
|
167
|
+
With --machine <id>: that machine's own listing (its daemon's
|
|
168
|
+
children plus what it saw lately).
|
|
155
169
|
read The tail of a session as a role-tagged transcript, plus where
|
|
156
170
|
the latest turn stands (\`turn\` in --json: ended, status and
|
|
157
|
-
\`answer\` = the agent's reply to the latest prompt).
|
|
171
|
+
\`answer\` = the agent's reply to the latest prompt). A session
|
|
172
|
+
another machine spawned is read through that machine's daemon.
|
|
173
|
+
Both list and read carry \`backgroundTasks\` ({count, tasks[]}):
|
|
174
|
+
async sub-agents / backgrounded commands still running after
|
|
175
|
+
the turn ended \u2014 such a session is NOT idle (absent = the
|
|
176
|
+
daemon or CLI is too old to report it).
|
|
158
177
|
stop SIGTERM the session's process via the local daemon.
|
|
159
178
|
archive Mark the session inactive server-side (it stays resumable).
|
|
160
179
|
approve Answer a pending permission request \`requestId\` with approve \u2014
|
|
@@ -165,13 +184,17 @@ ${chalk.bold("Actions:")}
|
|
|
165
184
|
the last 30 minutes. Default scope \`repo\`: same git repository,
|
|
166
185
|
including its other worktrees (\`sameWorktree\` says which share
|
|
167
186
|
your checkout); \`cwd\`: same directory; \`machine\`: everything.
|
|
168
|
-
|
|
169
|
-
|
|
187
|
+
With --machine <id>: the live sessions on that machine (scope
|
|
188
|
+
\`machine\` unless --cwd names a directory there).
|
|
189
|
+
message Send <text> into a running session. It arrives tagged with the
|
|
190
|
+
sender (the session named by VH_PEER_SESSION_ID when run from a
|
|
170
191
|
managed session's shell, else this CLI) and tells the peer how to
|
|
171
|
-
reply.
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
192
|
+
reply. A session another machine spawned is delivered through
|
|
193
|
+
that machine's daemon, with this host named in the header.
|
|
194
|
+
Refused for sessions that are not running, or terminal mirrors.
|
|
195
|
+
\`delivered\` follows \`very-happy send\`: true only when a
|
|
196
|
+
wrapper was attached before and after the POST (\`stored\` = it
|
|
197
|
+
is on the server anyway).
|
|
175
198
|
|
|
176
199
|
${chalk.bold("Options:")}
|
|
177
200
|
--all list only: account-wide over REST instead of this
|
|
@@ -197,26 +220,31 @@ ${chalk.bold("Options:")}
|
|
|
197
220
|
--cwd <dir> peers only: directory the scope is computed from
|
|
198
221
|
(default: the process cwd, or HAPPY_SESSION_ID's cwd).
|
|
199
222
|
--reply-to <id> message only: the peer message id being answered.
|
|
223
|
+
--machine <id> list / read / peers / message: go through this account
|
|
224
|
+
machine's daemon (its id as the web's machine list shows
|
|
225
|
+
it). read / message: skip the lookup across machines.
|
|
200
226
|
--json Machine-readable output.
|
|
201
227
|
-h, --help Show this help
|
|
202
228
|
|
|
203
229
|
${chalk.bold("Scope:")}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
the
|
|
230
|
+
Sessions this machine's daemon spawned are handled with the local key in
|
|
231
|
+
~/.happy/sessions.json (pruned after 14 days). Any other session of the
|
|
232
|
+
account is handled by the daemon of the machine that spawned it: that
|
|
233
|
+
machine must be online and on CLI \u2265 0.2.157, the operation runs there with
|
|
234
|
+
its keys, and only the result travels back \u2014 this CLI never holds the
|
|
235
|
+
account content key or another machine's session key. Without --machine
|
|
236
|
+
the online machines are asked in turn which one holds the session.
|
|
237
|
+
approve / deny / stop / archive remain local-only.
|
|
211
238
|
|
|
212
239
|
${chalk.bold("Exit codes:")}
|
|
213
240
|
2 read --wait: the turn had not ended when --timeout ran out (the
|
|
214
241
|
partial state is still printed)
|
|
215
242
|
0 success (approve/deny: the wrapper acknowledged the verdict; check
|
|
216
243
|
\`settled\` in --json to see whether the request has left the pending set)
|
|
217
|
-
1 bad arguments, unknown session, no
|
|
218
|
-
|
|
219
|
-
|
|
244
|
+
1 bad arguments, unknown session, no machine of the account holds it (or
|
|
245
|
+
its machine is offline / too old), request not pending, session not
|
|
246
|
+
online, or the operation failed (including \`stop\` on a session the
|
|
247
|
+
daemon is not running)
|
|
220
248
|
`;
|
|
221
249
|
function formatWait(ms) {
|
|
222
250
|
const minutes = Math.floor(ms / 6e4);
|
|
@@ -225,17 +253,19 @@ function formatWait(ms) {
|
|
|
225
253
|
return `${Math.floor(minutes / 60)}h${minutes % 60}m`;
|
|
226
254
|
}
|
|
227
255
|
function formatAccountSummaryLine(summary) {
|
|
228
|
-
const state = summary.attention ? "[attention]" : summary.live ? "[running here]" : summary.active ? "[active elsewhere]" : summary.archived ? "[archived]" : "[idle]";
|
|
256
|
+
const state = summary.attention ? "[attention]" : summary.live ? summary.machine ? `[running on ${summary.machine.host}]` : "[running here]" : summary.active ? "[active elsewhere]" : summary.archived ? "[archived]" : "[idle]";
|
|
229
257
|
const parts = [summary.id, state];
|
|
230
|
-
if (!summary.
|
|
231
|
-
parts.push("(not
|
|
258
|
+
if (!summary.readable) {
|
|
259
|
+
parts.push("(not readable: no online machine holds its key)");
|
|
232
260
|
} else {
|
|
261
|
+
if (summary.machine) parts.push(`via=${summary.machine.host}`);
|
|
233
262
|
if (summary.title) parts.push(`title="${summary.title}"`);
|
|
234
263
|
if (summary.flavor) parts.push(`agent=${summary.flavor}`);
|
|
235
264
|
if (summary.tags?.length) parts.push(`tags=${summary.tags.join(",")}`);
|
|
236
265
|
if (summary.variant) parts.push(`variant=${summary.variant}`);
|
|
237
266
|
if (summary.machineId) parts.push(`machine=${summary.machineId}`);
|
|
238
267
|
if (summary.cwd) parts.push(`cwd=${summary.cwd}`);
|
|
268
|
+
if (summary.backgroundTasks && summary.backgroundTasks.count > 0) parts.push(formatBackgroundTasks(summary.backgroundTasks));
|
|
239
269
|
for (const request of summary.pending ?? []) {
|
|
240
270
|
parts.push(`pending=${request.id}:${request.tool}${request.waitingMs !== void 0 ? `(${formatWait(request.waitingMs)})` : ""}`);
|
|
241
271
|
}
|
|
@@ -246,6 +276,10 @@ function formatAccountSummaryLine(summary) {
|
|
|
246
276
|
function permissionFailureRecord(sessionId, requestId, error) {
|
|
247
277
|
return { sessionId, requestId, error: error instanceof Error ? error.message : String(error) };
|
|
248
278
|
}
|
|
279
|
+
function formatBackgroundTasks(report) {
|
|
280
|
+
const types = report.tasks.map((task) => task.type).filter(Boolean);
|
|
281
|
+
return `background=${report.count}${types.length ? `(${types.join(",")})` : ""}`;
|
|
282
|
+
}
|
|
249
283
|
function formatSummaryLine(summary) {
|
|
250
284
|
const parts = [summary.id, summary.live ? "[running]" : "[not running]"];
|
|
251
285
|
if (summary.title) parts.push(`title="${summary.title}"`);
|
|
@@ -254,6 +288,7 @@ function formatSummaryLine(summary) {
|
|
|
254
288
|
if (summary.variant) parts.push(`variant=${summary.variant}`);
|
|
255
289
|
if (summary.cwd) parts.push(`cwd=${summary.cwd}`);
|
|
256
290
|
if (summary.pid !== void 0) parts.push(`pid=${summary.pid}`);
|
|
291
|
+
if (summary.backgroundTasks && summary.backgroundTasks.count > 0) parts.push(formatBackgroundTasks(summary.backgroundTasks));
|
|
257
292
|
parts.push(summary.url);
|
|
258
293
|
return parts.join(" ");
|
|
259
294
|
}
|
|
@@ -272,20 +307,48 @@ async function handleSessionsCommand(args) {
|
|
|
272
307
|
}
|
|
273
308
|
try {
|
|
274
309
|
if (options.action === "list" && options.all) {
|
|
275
|
-
const
|
|
310
|
+
const listed = await listAccountSessions({ recentLimit: options.tag ? 150 : options.limit, includeArchived: options.includeArchived });
|
|
311
|
+
const fill = await fillForeignAccountRows(listed);
|
|
312
|
+
let sessions = fill.rows;
|
|
313
|
+
if (options.tag) {
|
|
314
|
+
const wanted = options.tag;
|
|
315
|
+
sessions = sessions.filter((summary) => summary.tags?.includes(wanted) === true);
|
|
316
|
+
const recentLimit = Math.max(0, options.limit ?? DEFAULT_RECENT_LIMIT);
|
|
317
|
+
let idleKept = 0;
|
|
318
|
+
sessions = sessions.filter((summary) => summary.attention || summary.live || idleKept++ < recentLimit);
|
|
319
|
+
}
|
|
276
320
|
if (options.json) {
|
|
277
|
-
console.log(JSON.stringify({ sessions, scope: "account" }));
|
|
321
|
+
console.log(JSON.stringify({ sessions, scope: "account", machines: { asked: fill.asked, skipped: fill.skipped } }));
|
|
278
322
|
} else if (sessions.length === 0) {
|
|
279
|
-
console.log(options.tag ? `No
|
|
323
|
+
console.log(options.tag ? `No readable sessions tagged "${options.tag}" on this account.` : "No sessions found on this account.");
|
|
280
324
|
} else {
|
|
281
325
|
for (const summary of sessions) console.log(formatAccountSummaryLine(summary));
|
|
282
|
-
const foreign = sessions.filter((summary) => !summary.
|
|
326
|
+
const foreign = sessions.filter((summary) => !summary.readable).length;
|
|
327
|
+
const filled = fill.asked.reduce((n, a) => n + a.filled, 0);
|
|
328
|
+
if (filled > 0) {
|
|
329
|
+
console.log(chalk.dim(`${filled} session(s) read through their own machine's daemon (${fill.asked.filter((a) => a.filled > 0).map((a) => a.host ?? a.machineId).join(", ")}).`));
|
|
330
|
+
}
|
|
331
|
+
for (const asked of fill.asked.filter((a) => a.error)) console.log(chalk.dim(`machine ${asked.machineId}: ${asked.error}`));
|
|
283
332
|
if (foreign > 0) {
|
|
284
|
-
|
|
333
|
+
const why = fill.skipped.length > 0 ? ` (${fill.skipped.map((s) => `${s.machineId}: ${s.reason}`).join("; ")})` : "";
|
|
334
|
+
console.log(chalk.dim(`${foreign} session(s) belong to a machine that is not reachable right now${why}.`));
|
|
285
335
|
}
|
|
286
336
|
}
|
|
287
337
|
process.exit(0);
|
|
288
338
|
}
|
|
339
|
+
if (options.action === "list" && options.machine) {
|
|
340
|
+
const listing = await listRemoteSessions(options.machine, { tag: options.tag, limit: options.limit });
|
|
341
|
+
const sessions = listing.sessions;
|
|
342
|
+
if (options.json) {
|
|
343
|
+
console.log(JSON.stringify({ sessions, machine: { id: listing.machineId, host: listing.host } }));
|
|
344
|
+
} else if (sessions.length === 0) {
|
|
345
|
+
console.log(options.tag ? `No sessions tagged "${options.tag}" on ${listing.host}.` : `No sessions found on ${listing.host}.`);
|
|
346
|
+
} else {
|
|
347
|
+
console.log(chalk.dim(`machine ${listing.host} (${listing.machineId})`));
|
|
348
|
+
for (const summary of sessions) console.log(formatSummaryLine(summary));
|
|
349
|
+
}
|
|
350
|
+
process.exit(0);
|
|
351
|
+
}
|
|
289
352
|
if (options.action === "list") {
|
|
290
353
|
const sessions = await listSessions({ tag: options.tag, recentLimit: options.limit });
|
|
291
354
|
if (options.json) {
|
|
@@ -302,12 +365,13 @@ async function handleSessionsCommand(args) {
|
|
|
302
365
|
const persisted = readPersistedSessions();
|
|
303
366
|
const context = { self: () => cliPeerSelf(process.env, cwd, persisted), readPersisted: () => persisted };
|
|
304
367
|
if (options.action === "peers") {
|
|
305
|
-
const listing = await listPeerSessions(context, options.scope ?? "repo");
|
|
368
|
+
const listing = options.machine ? await listRemotePeers(options.machine, { ...options.scope ? { scope: options.scope } : {}, ...options.cwd ? { cwd: options.cwd } : {} }) : await listPeerSessions(context, options.scope ?? "repo");
|
|
306
369
|
if (options.json) {
|
|
307
370
|
console.log(JSON.stringify(listing));
|
|
308
371
|
} else if (listing.peers.length === 0) {
|
|
309
|
-
console.log(`No other live sessions in scope "${listing.scope}"${listing.self.repoRoot ? ` (repo ${listing.self.repoRoot})` : ""}.`);
|
|
372
|
+
console.log(`No other live sessions in scope "${listing.scope}"${listing.self.repoRoot ? ` (repo ${listing.self.repoRoot})` : ""}${"host" in listing ? ` on ${listing.host}` : ""}.`);
|
|
310
373
|
} else {
|
|
374
|
+
if ("host" in listing) console.log(chalk.dim(`machine ${String(listing.host)} (${String(listing.machineId)})`));
|
|
311
375
|
for (const peer of listing.peers) {
|
|
312
376
|
const parts = [peer.sessionId, `[${peer.kind === "mirror" ? "terminal" : "running"}]`];
|
|
313
377
|
if (peer.title) parts.push(`title="${peer.title}"`);
|
|
@@ -322,9 +386,9 @@ async function handleSessionsCommand(args) {
|
|
|
322
386
|
}
|
|
323
387
|
const target = options.sessionId;
|
|
324
388
|
try {
|
|
325
|
-
const result = await sendPeerMessage(context, { to: target, body: options.text, replyTo: options.replyTo });
|
|
389
|
+
const result = await sendPeerMessage(context, { to: target, body: options.text, replyTo: options.replyTo, ...options.machine ? { machineId: options.machine } : {} });
|
|
326
390
|
if (options.json) console.log(JSON.stringify(result));
|
|
327
|
-
else if (result.delivered) console.log(`Message ${result.messageId} delivered to ${target}
|
|
391
|
+
else if (result.delivered) console.log(`Message ${result.messageId} delivered to ${target}${result.machine ? ` on ${result.machine.host}` : ""}
|
|
328
392
|
${result.url}`);
|
|
329
393
|
else console.error(chalk.yellow("Not delivered:"), result.error ?? `session is ${result.status}`, result.stored ? "(stored server-side, unread)" : "");
|
|
330
394
|
process.exit(result.delivered ? 0 : 1);
|
|
@@ -336,24 +400,44 @@ ${result.url}`);
|
|
|
336
400
|
const sessionId = options.sessionId;
|
|
337
401
|
if (options.action === "read") {
|
|
338
402
|
let timedOut = false;
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
403
|
+
const local = !options.machine && readPersistedSessions()[sessionId] !== void 0;
|
|
404
|
+
let result;
|
|
405
|
+
if (local) {
|
|
406
|
+
if (options.wait) {
|
|
407
|
+
try {
|
|
408
|
+
await waitForTurnEnd(sessionId, { timeoutMs: (options.timeoutSec ?? 600) * 1e3 });
|
|
409
|
+
} catch (error) {
|
|
410
|
+
if (!(error instanceof TurnWaitTimeoutError)) throw error;
|
|
411
|
+
timedOut = true;
|
|
412
|
+
console.error(chalk.yellow("Timeout:"), error.message);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
result = await readSessionTranscript(sessionId, options.limit ?? 20, { full: options.full });
|
|
416
|
+
} else {
|
|
417
|
+
const readOptions = { limit: options.limit ?? 20, full: options.full, ...options.machine ? { machineId: options.machine } : {} };
|
|
418
|
+
let remote;
|
|
419
|
+
if (options.wait) {
|
|
420
|
+
const timeoutMs = (options.timeoutSec ?? 600) * 1e3;
|
|
421
|
+
const waited = await waitForRemoteTurnEnd(sessionId, { ...readOptions, timeoutMs });
|
|
422
|
+
remote = waited.read;
|
|
423
|
+
if (waited.timedOut) {
|
|
424
|
+
timedOut = true;
|
|
425
|
+
console.error(chalk.yellow("Timeout:"), `Session ${sessionId} on ${remote.host}: the latest turn did not end within ${Math.round(timeoutMs / 1e3)}s`);
|
|
426
|
+
}
|
|
427
|
+
} else {
|
|
428
|
+
remote = await readRemoteTranscript(sessionId, readOptions);
|
|
346
429
|
}
|
|
430
|
+
const { machineId, host, ...rest } = remote;
|
|
431
|
+
result = { ...rest, machine: { id: machineId, host } };
|
|
347
432
|
}
|
|
348
|
-
const result = await readSessionTranscript(sessionId, options.limit ?? 20, { full: options.full });
|
|
349
433
|
if (options.json) {
|
|
350
|
-
console.log(JSON.stringify(options.answer ? { sessionId, turn: result.turn } : result));
|
|
434
|
+
console.log(JSON.stringify(options.answer ? { sessionId, turn: result.turn, ...result.machine ? { machine: result.machine } : {} } : result));
|
|
351
435
|
} else if (options.answer) {
|
|
352
436
|
if (result.turn.answer.length > 0) console.log(result.turn.answer);
|
|
353
437
|
else console.error(chalk.dim("(the agent has not replied to the latest prompt yet)"));
|
|
354
438
|
} else {
|
|
355
|
-
console.log(formatSummaryLine(result.summary));
|
|
356
|
-
console.log(`--- last ${result.messageCount} message(s) ---`);
|
|
439
|
+
console.log(formatSummaryLine(result.summary) + (result.machine ? chalk.dim(` machine=${result.machine.host}`) : ""));
|
|
440
|
+
console.log(`--- last ${result.messageCount} message(s)${result.truncated ? " (truncated to fit the RPC budget)" : ""} ---`);
|
|
357
441
|
console.log(result.transcript.length > 0 ? result.transcript : "(no readable conversation content in this range)");
|
|
358
442
|
const turn = result.turn;
|
|
359
443
|
console.log(chalk.dim(`--- latest turn: ${turn.ended ? `ended (${turn.status ?? "unknown"})` : "running"}${turn.error ? ` \u2014 ${turn.error}` : ""} ---`));
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { readFileSync, statSync } from 'node:fs';
|
|
3
3
|
import { resolve } from 'node:path';
|
|
4
|
-
import { d as readPersistedSessions, k as checkIfDaemonRunningAndCleanupStaleState, l as logger, m as spawnDaemonSession, n as stopDaemonSession } from './limits-
|
|
5
|
-
import { r as resolveSpawnPermissionMode, c as resolveFirstMessageMeta, i as isValidSpawnOrigin, s as sanitizeSpawnPermissionMode, A as ALLOWED_SPAWN_PERMISSION_MODES, S as SPAWN_AGENTS } from './index-
|
|
6
|
-
import { f as forkSession, g as getProjectPath, F as ForkSourceMissingError, C as CodexAppServerClient, c as forkCodexThread } from './types-
|
|
7
|
-
import {
|
|
4
|
+
import { d as readPersistedSessions, k as checkIfDaemonRunningAndCleanupStaleState, l as logger, m as spawnDaemonSession, n as stopDaemonSession } from './limits-D2uN090G.mjs';
|
|
5
|
+
import { r as resolveSpawnPermissionMode, c as resolveFirstMessageMeta, i as isValidSpawnOrigin, s as sanitizeSpawnPermissionMode, A as ALLOWED_SPAWN_PERMISSION_MODES, S as SPAWN_AGENTS } from './index-CVCt3kSt.mjs';
|
|
6
|
+
import { f as forkSession, g as getProjectPath, F as ForkSourceMissingError, C as CodexAppServerClient, c as forkCodexThread } from './types-DIBF4U0Y.mjs';
|
|
7
|
+
import { F as readSessionMetadata, x as sessionWebUrl, G as waitForSessionKey, H as sendUserMessage } from './agentGuidance-CrR9l62U.mjs';
|
|
8
8
|
import 'zod';
|
|
9
9
|
import '@paralleldrive/cuid2';
|
|
10
10
|
import 'node:fs/promises';
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
var chalk = require('chalk');
|
|
4
4
|
var node_fs = require('node:fs');
|
|
5
5
|
var path = require('node:path');
|
|
6
|
-
var persistence = require('./limits-
|
|
7
|
-
var index = require('./index-
|
|
8
|
-
var index$1 = require('./types-
|
|
9
|
-
var agentGuidance = require('./agentGuidance-
|
|
6
|
+
var persistence = require('./limits-kqFqhfsh.cjs');
|
|
7
|
+
var index = require('./index-BG932eCh.cjs');
|
|
8
|
+
var index$1 = require('./types-DnrPeN80.cjs');
|
|
9
|
+
var agentGuidance = require('./agentGuidance-DwOWbX55.cjs');
|
|
10
10
|
require('zod');
|
|
11
11
|
require('@paralleldrive/cuid2');
|
|
12
12
|
require('node:fs/promises');
|
|
@@ -4,9 +4,9 @@ var os = require('node:os');
|
|
|
4
4
|
var promises = require('node:fs/promises');
|
|
5
5
|
var z = require('zod');
|
|
6
6
|
var node_crypto = require('node:crypto');
|
|
7
|
-
var agentGuidance = require('./agentGuidance-
|
|
8
|
-
var persistence = require('./limits-
|
|
9
|
-
var install = require('./install-
|
|
7
|
+
var agentGuidance = require('./agentGuidance-DwOWbX55.cjs');
|
|
8
|
+
var persistence = require('./limits-kqFqhfsh.cjs');
|
|
9
|
+
var install = require('./install-wKQGgXvN.cjs');
|
|
10
10
|
require('node:fs');
|
|
11
11
|
require('node:path');
|
|
12
12
|
require('axios');
|
|
@@ -15,8 +15,8 @@ require('@paralleldrive/cuid2');
|
|
|
15
15
|
require('tweetnacl');
|
|
16
16
|
require('chalk');
|
|
17
17
|
require('node:util');
|
|
18
|
-
require('./index-
|
|
19
|
-
require('./types-
|
|
18
|
+
require('./index-BG932eCh.cjs');
|
|
19
|
+
require('./types-DnrPeN80.cjs');
|
|
20
20
|
require('node:events');
|
|
21
21
|
require('child_process');
|
|
22
22
|
require('util');
|
|
@@ -2,9 +2,9 @@ import { homedir } from 'node:os';
|
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { randomUUID } from 'node:crypto';
|
|
5
|
-
import {
|
|
6
|
-
import { h as readCredentialsForConfiguredRelay } from './limits-
|
|
7
|
-
import { a as installTeamSkill, i as installAutomationSkill } from './install-
|
|
5
|
+
import { I as teamRequest, J as createTeamsClient } from './agentGuidance-CrR9l62U.mjs';
|
|
6
|
+
import { h as readCredentialsForConfiguredRelay } from './limits-D2uN090G.mjs';
|
|
7
|
+
import { a as installTeamSkill, i as installAutomationSkill } from './install-DarS_Wco.mjs';
|
|
8
8
|
import 'node:fs';
|
|
9
9
|
import 'node:path';
|
|
10
10
|
import 'axios';
|
|
@@ -13,8 +13,8 @@ import '@paralleldrive/cuid2';
|
|
|
13
13
|
import 'tweetnacl';
|
|
14
14
|
import 'chalk';
|
|
15
15
|
import 'node:util';
|
|
16
|
-
import './index-
|
|
17
|
-
import './types-
|
|
16
|
+
import './index-CVCt3kSt.mjs';
|
|
17
|
+
import './types-DIBF4U0Y.mjs';
|
|
18
18
|
import 'node:events';
|
|
19
19
|
import 'child_process';
|
|
20
20
|
import 'util';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var persistence = require('./limits-
|
|
4
|
-
var agentGuidance = require('./agentGuidance-
|
|
3
|
+
var persistence = require('./limits-kqFqhfsh.cjs');
|
|
4
|
+
var agentGuidance = require('./agentGuidance-DwOWbX55.cjs');
|
|
5
5
|
require('zod');
|
|
6
6
|
require('@paralleldrive/cuid2');
|
|
7
7
|
require('node:fs/promises');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { h as readCredentialsForConfiguredRelay, j as createBuiltinTodoClient, c as configuration, B as BUILTIN_TODO_SKILL } from './limits-
|
|
2
|
-
import {
|
|
1
|
+
import { h as readCredentialsForConfiguredRelay, j as createBuiltinTodoClient, c as configuration, B as BUILTIN_TODO_SKILL } from './limits-D2uN090G.mjs';
|
|
2
|
+
import { C as readTeamScope, E as teamScopePath } from './agentGuidance-CrR9l62U.mjs';
|
|
3
3
|
import 'zod';
|
|
4
4
|
import '@paralleldrive/cuid2';
|
|
5
5
|
import 'node:fs/promises';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
-
import {
|
|
2
|
+
import { S as AgentVersionsSchema, l as logger, w as encodeBase64, L as encrypt, N as decrypt, M as decodeBase64, c as configuration, e as createEnvelope, s as streamKeyOf, T as backoff, U as ReleaseDrainNoticeSchema, V as decryptBlob, W as RelayAssignmentResponseSchema, X as notifyDaemonTurnEvent, Y as BackgroundTaskReporter, Z as notifyDaemonBackgroundTasks, _ as prepareClipboardText, $ as prepareClipboardHistoryText, g as delay, r as readSettings, d as readPersistedSessions, a0 as credentialRelayProblem, F as packageJson, a1 as RelayCandidatesResponseSchema, a2 as readTrackedClaudeSessionIds, a3 as readTrackedCodexThreadIds, a4 as isValidTerminalId, a5 as exponentialBackoffDelay, a6 as libsodiumEncryptForPublicKey, a7 as libsodiumPublicKeyFromSecretKey, a8 as getRandomBytes, a9 as redactLogValue } from './limits-D2uN090G.mjs';
|
|
3
3
|
import { EventEmitter } from 'node:events';
|
|
4
4
|
import { io } from 'socket.io-client';
|
|
5
5
|
import { z } from 'zod';
|
|
@@ -417,6 +417,15 @@ async function deriveKey$1(master, usage, path) {
|
|
|
417
417
|
|
|
418
418
|
class RpcHandlerManager {
|
|
419
419
|
handlers = /* @__PURE__ */ new Map();
|
|
420
|
+
/**
|
|
421
|
+
* B-506: methods whose params/result are PLAINTEXT JSON strings instead of
|
|
422
|
+
* machine-key ciphertext. The caller is a CLI on another machine of the
|
|
423
|
+
* same account, which holds neither this machine's key nor the account
|
|
424
|
+
* content key; the trusted relay sees the payload either way (this fork
|
|
425
|
+
* is server-trusted, not e2e). Only `registerPlainHandler` puts a method
|
|
426
|
+
* here; everything else keeps decrypting.
|
|
427
|
+
*/
|
|
428
|
+
plainMethods = /* @__PURE__ */ new Set();
|
|
420
429
|
scopePrefix;
|
|
421
430
|
encryptionKey;
|
|
422
431
|
encryptionVariant;
|
|
@@ -441,14 +450,31 @@ class RpcHandlerManager {
|
|
|
441
450
|
unregisterHandler(method) {
|
|
442
451
|
const prefixedMethod = this.getPrefixedMethod(method);
|
|
443
452
|
this.handlers.delete(prefixedMethod);
|
|
453
|
+
this.plainMethods.delete(prefixedMethod);
|
|
444
454
|
for (const socket of this.sockets) socket.emit("rpc-unregister", { method: prefixedMethod });
|
|
445
455
|
}
|
|
456
|
+
/**
|
|
457
|
+
* B-506: register a method whose request is a plaintext JSON string and
|
|
458
|
+
* whose response is returned as a plaintext JSON string (see
|
|
459
|
+
* `plainMethods`). The handler receives the parsed params; a throw is
|
|
460
|
+
* answered with `{ error }` like the encrypted path.
|
|
461
|
+
*/
|
|
462
|
+
registerPlainHandler(method, handler) {
|
|
463
|
+
this.plainMethods.add(this.getPrefixedMethod(method));
|
|
464
|
+
this.registerHandler(method, handler);
|
|
465
|
+
}
|
|
466
|
+
isPlainMethod(prefixedMethod) {
|
|
467
|
+
return this.plainMethods.has(prefixedMethod);
|
|
468
|
+
}
|
|
446
469
|
/**
|
|
447
470
|
* Handle an incoming RPC request
|
|
448
471
|
* @param request - The RPC request data
|
|
449
472
|
* @param callback - The response callback
|
|
450
473
|
*/
|
|
451
474
|
async handleRequest(request) {
|
|
475
|
+
if (this.plainMethods.has(request.method)) {
|
|
476
|
+
return this.handlePlainRequest(request);
|
|
477
|
+
}
|
|
452
478
|
try {
|
|
453
479
|
const handler = this.handlers.get(request.method);
|
|
454
480
|
if (!handler) {
|
|
@@ -472,6 +498,25 @@ class RpcHandlerManager {
|
|
|
472
498
|
return encodeBase64(encrypt(this.encryptionKey, this.encryptionVariant, errorResponse));
|
|
473
499
|
}
|
|
474
500
|
}
|
|
501
|
+
/** Plaintext variant of handleRequest (B-506). Never throws; never encrypts. */
|
|
502
|
+
async handlePlainRequest(request) {
|
|
503
|
+
const handler = this.handlers.get(request.method);
|
|
504
|
+
if (!handler) return JSON.stringify({ error: "Method not found" });
|
|
505
|
+
let params;
|
|
506
|
+
try {
|
|
507
|
+
params = typeof request.params === "string" && request.params.length > 0 ? JSON.parse(request.params) : {};
|
|
508
|
+
} catch {
|
|
509
|
+
return JSON.stringify({ error: "Invalid JSON params" });
|
|
510
|
+
}
|
|
511
|
+
try {
|
|
512
|
+
this.logger("[RPC] Calling plain handler", { method: request.method });
|
|
513
|
+
const result = await handler(params);
|
|
514
|
+
return JSON.stringify(result === void 0 ? null : result);
|
|
515
|
+
} catch (error) {
|
|
516
|
+
this.logger("[RPC] [ERROR] Error handling plain request", { error });
|
|
517
|
+
return JSON.stringify({ error: error instanceof Error ? error.message : "Unknown error" });
|
|
518
|
+
}
|
|
519
|
+
}
|
|
475
520
|
onSocketConnect(socket) {
|
|
476
521
|
this.sockets.add(socket);
|
|
477
522
|
for (const [prefixedMethod] of this.handlers) {
|
|
@@ -2751,7 +2796,7 @@ class ApiSessionClient extends EventEmitter {
|
|
|
2751
2796
|
const persisted = await axios.post(
|
|
2752
2797
|
`${configuration.serverUrl}/v3/sessions/${encodeURIComponent(this.sessionId)}/messages`,
|
|
2753
2798
|
{ messages: batch },
|
|
2754
|
-
{ headers: this.authHeaders(), timeout: 6e4 }
|
|
2799
|
+
{ headers: { ...this.authHeaders(), "X-Happy-Message-Origin": "relay-client" }, timeout: 6e4 }
|
|
2755
2800
|
);
|
|
2756
2801
|
const stored = Array.isArray(persisted.data.messages) ? persisted.data.messages : [];
|
|
2757
2802
|
const storedLocalIds = new Set(stored.map((message) => message.localId));
|
|
@@ -2979,16 +3024,29 @@ class ApiSessionClient extends EventEmitter {
|
|
|
2979
3024
|
/** B-466: turn edges (and a periodic lease) reported to the local daemon so
|
|
2980
3025
|
* auto-update waits for "no turn in flight" instead of "no sessions". */
|
|
2981
3026
|
turnReporter = new TurnReporter((event) => void notifyDaemonTurnEvent(this.sessionId, event).catch((error) => logger.debug("[API] Failed to report turn event to daemon:", error)));
|
|
2982
|
-
|
|
3027
|
+
/** B-507: background tasks in flight, published on change and renewed every
|
|
3028
|
+
* 60s to the daemon (`/session-event background_tasks`) and into agentState
|
|
3029
|
+
* (`backgroundTasks: { updatedAt, tasks }`) — the two readers that outlive a
|
|
3030
|
+
* single heartbeat. The heartbeat itself carries only the count. */
|
|
3031
|
+
backgroundTaskReporter = new BackgroundTaskReporter((tasks, now) => {
|
|
3032
|
+
void notifyDaemonBackgroundTasks(this.sessionId, tasks).catch((error) => logger.debug("[API] Failed to report background tasks to daemon:", error));
|
|
3033
|
+
this.updateAgentState((state) => ({
|
|
3034
|
+
...state,
|
|
3035
|
+
backgroundTasks: tasks.length > 0 ? { updatedAt: now, tasks } : void 0
|
|
3036
|
+
}));
|
|
3037
|
+
});
|
|
3038
|
+
keepAlive(thinking, mode, backgroundTasks) {
|
|
2983
3039
|
if (process.env.DEBUG) {
|
|
2984
3040
|
logger.debug(`[API] Sending keep alive message: ${thinking}`);
|
|
2985
3041
|
}
|
|
2986
3042
|
this.turnReporter.observe(thinking);
|
|
3043
|
+
if (backgroundTasks) this.backgroundTaskReporter.observe(backgroundTasks);
|
|
2987
3044
|
this.socket.volatile.emit("session-alive", {
|
|
2988
3045
|
sid: this.sessionId,
|
|
2989
3046
|
time: Date.now(),
|
|
2990
3047
|
thinking,
|
|
2991
|
-
mode
|
|
3048
|
+
mode,
|
|
3049
|
+
...backgroundTasks ? { backgroundTasks: backgroundTasks.length } : {}
|
|
2992
3050
|
});
|
|
2993
3051
|
}
|
|
2994
3052
|
/**
|
|
@@ -14335,6 +14393,17 @@ class ApiMachineClient {
|
|
|
14335
14393
|
return { claudeAuth };
|
|
14336
14394
|
});
|
|
14337
14395
|
}
|
|
14396
|
+
/**
|
|
14397
|
+
* B-506: the five `sessions.*` methods a CLI on ANOTHER machine of this
|
|
14398
|
+
* account calls to read / message sessions this daemon spawned. Plaintext
|
|
14399
|
+
* on purpose (no shared key with the caller); see
|
|
14400
|
+
* `api/rpc/RpcHandlerManager.ts` `registerPlainHandler`.
|
|
14401
|
+
*/
|
|
14402
|
+
setRemoteSessionOpsHandlers(handlers) {
|
|
14403
|
+
for (const [method, handler] of Object.entries(handlers)) {
|
|
14404
|
+
this.rpcHandlerManager.registerPlainHandler(method, handler);
|
|
14405
|
+
}
|
|
14406
|
+
}
|
|
14338
14407
|
setRPCHandlers({
|
|
14339
14408
|
spawnSession,
|
|
14340
14409
|
resumeSession,
|
|
@@ -15163,7 +15232,11 @@ class ApiMachineClient {
|
|
|
15163
15232
|
}
|
|
15164
15233
|
bindRpcRequestHandler(socket, transport) {
|
|
15165
15234
|
socket.on("rpc-request", async (data, callback) => {
|
|
15166
|
-
|
|
15235
|
+
if (typeof data?.method === "string" && this.rpcHandlerManager.isPlainMethod(data.method)) {
|
|
15236
|
+
logger.debug(`[API MACHINE] Received plain RPC request via ${transport}: ${data.method}`);
|
|
15237
|
+
} else {
|
|
15238
|
+
logger.debugLargeJson(`[API MACHINE] Received RPC request via ${transport}:`, data);
|
|
15239
|
+
}
|
|
15167
15240
|
await answerSocketRequest(
|
|
15168
15241
|
callback,
|
|
15169
15242
|
() => this.rpcHandlerManager.handleRequest(data),
|