@triflux/remote 10.31.0 → 10.33.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.
@@ -0,0 +1,448 @@
1
+ // hub/tray-state.mjs — normalize Hub/CTO/MCP state for the macOS tray UI
2
+
3
+ function toIsoTime(value) {
4
+ if (typeof value === "string" && value.trim()) return value;
5
+ if (typeof value === "number" && Number.isFinite(value)) {
6
+ return new Date(value).toISOString();
7
+ }
8
+ return null;
9
+ }
10
+
11
+ function statusRank(status) {
12
+ switch (status) {
13
+ case "active":
14
+ return 0;
15
+ case "idle":
16
+ return 1;
17
+ case "starting":
18
+ return 2;
19
+ case "stale":
20
+ return 4;
21
+ default:
22
+ return 3;
23
+ }
24
+ }
25
+
26
+ function readSynapseSessions(snapshot) {
27
+ if (Array.isArray(snapshot)) return snapshot;
28
+ if (Array.isArray(snapshot?.sessions)) return snapshot.sessions;
29
+ if (snapshot && typeof snapshot === "object") {
30
+ return Object.values(snapshot).filter(
31
+ (entry) => entry && typeof entry === "object" && entry.sessionId,
32
+ );
33
+ }
34
+ return [];
35
+ }
36
+
37
+ const MCP_CLIENTS = Object.freeze([
38
+ { id: "claude", label: "Claude", icon: "C", color: "#d19a66" },
39
+ { id: "codex", label: "Codex", icon: "X", color: "#ffffff" },
40
+ { id: "antigravity", label: "Antigravity", icon: "A", color: "#61afef" },
41
+ ]);
42
+
43
+ function normalizeAgentId(value) {
44
+ const id = String(value || "")
45
+ .trim()
46
+ .toLowerCase();
47
+ if (id === "agy" || id === "anti") return "antigravity";
48
+ if (id === "claude" || id === "codex" || id === "antigravity") return id;
49
+ return "";
50
+ }
51
+
52
+ function inferAgentId(value = {}) {
53
+ const direct = normalizeAgentId(
54
+ value.agent_id ??
55
+ value.agentId ??
56
+ value.client ??
57
+ value.provider ??
58
+ value.runtimeClient,
59
+ );
60
+ if (direct) return direct;
61
+ const haystack = [
62
+ value.sessionId,
63
+ value.sessionKind,
64
+ value.role,
65
+ value.taskSummary,
66
+ value.command,
67
+ ]
68
+ .filter(Boolean)
69
+ .join(" ")
70
+ .toLowerCase();
71
+ for (const id of ["claude", "codex", "antigravity"]) {
72
+ if (haystack.includes(id)) return id;
73
+ }
74
+ if (haystack.includes("agy")) return "antigravity";
75
+ return "";
76
+ }
77
+
78
+ function agentMeta(agentId) {
79
+ const id = normalizeAgentId(agentId);
80
+ const meta = MCP_CLIENTS.find((client) => client.id === id);
81
+ return meta ? { ...meta } : null;
82
+ }
83
+
84
+ export function normalizeTraySessions(snapshot) {
85
+ return readSynapseSessions(snapshot)
86
+ .map((session) => {
87
+ const agentId = inferAgentId(session);
88
+ return {
89
+ sessionId: String(session?.sessionId || ""),
90
+ host: typeof session?.host === "string" ? session.host : "local",
91
+ status:
92
+ typeof session?.status === "string"
93
+ ? session.status
94
+ : typeof session?.phase === "string"
95
+ ? session.phase
96
+ : "active",
97
+ sessionKind:
98
+ typeof session?.sessionKind === "string"
99
+ ? session.sessionKind
100
+ : "interactive",
101
+ cwd: typeof session?.cwd === "string" ? session.cwd : "",
102
+ worktreePath:
103
+ typeof session?.worktreePath === "string" ? session.worktreePath : "",
104
+ branch: typeof session?.branch === "string" ? session.branch : "",
105
+ taskSummary:
106
+ typeof session?.taskSummary === "string" ? session.taskSummary : "",
107
+ agent_id: agentId || null,
108
+ agent: agentMeta(agentId),
109
+ lastHeartbeat: Number.isFinite(Number(session?.lastHeartbeat))
110
+ ? Number(session.lastHeartbeat)
111
+ : null,
112
+ started_at: toIsoTime(
113
+ session?.started_at ?? session?.startedAt ?? session?.lastHeartbeat,
114
+ ),
115
+ isRemote: session?.isRemote === true,
116
+ };
117
+ })
118
+ .filter((session) => session.sessionId)
119
+ .sort((a, b) => {
120
+ const rankDelta = statusRank(a.status) - statusRank(b.status);
121
+ if (rankDelta !== 0) return rankDelta;
122
+ return (b.lastHeartbeat || 0) - (a.lastHeartbeat || 0);
123
+ });
124
+ }
125
+
126
+ function countBy(items, pickKey) {
127
+ const counts = {};
128
+ for (const item of Array.isArray(items) ? items : []) {
129
+ const key = pickKey(item) || "unknown";
130
+ counts[key] = (counts[key] || 0) + 1;
131
+ }
132
+ return counts;
133
+ }
134
+
135
+ function sanitizeMcpRow(row) {
136
+ return {
137
+ type: String(row?.type || ""),
138
+ name: String(row?.name || ""),
139
+ client: String(row?.client || ""),
140
+ label: String(row?.label || ""),
141
+ filePath: String(row?.filePath || ""),
142
+ expectedUrl: String(row?.expectedUrl || ""),
143
+ expectedCommand: String(row?.expectedCommand || ""),
144
+ actualUrl: String(row?.actualUrl || ""),
145
+ actualCommand: String(row?.actualCommand || ""),
146
+ status: String(row?.status || ""),
147
+ headerStatus: String(row?.headerStatus || ""),
148
+ expectedHeaderNames: Array.isArray(row?.expectedHeaderNames)
149
+ ? row.expectedHeaderNames.map(String)
150
+ : [],
151
+ actualHeaderNames: Array.isArray(row?.actualHeaderNames)
152
+ ? row.actualHeaderNames.map(String)
153
+ : [],
154
+ command: row?.command ? String(row.command) : "",
155
+ };
156
+ }
157
+
158
+ function summarizeMcpClients(rows) {
159
+ return MCP_CLIENTS.map((client) => {
160
+ const clientRows = rows.filter(
161
+ (row) => row.type === "registry" && row.client === client.id,
162
+ );
163
+ const present = clientRows.filter(
164
+ (row) => row.status === "present" || row.status === "ok",
165
+ ).length;
166
+ const total = clientRows.length;
167
+ let status = "off";
168
+ if (total > 0 && present === total) status = "on";
169
+ else if (present > 0) status = "partial";
170
+ return {
171
+ ...client,
172
+ status,
173
+ present,
174
+ total,
175
+ };
176
+ });
177
+ }
178
+
179
+ function summarizeMcpServers(rows) {
180
+ const grouped = new Map();
181
+ for (const row of rows) {
182
+ if (row.type !== "registry" || !row.name) continue;
183
+ if (!grouped.has(row.name)) {
184
+ grouped.set(row.name, {
185
+ name: row.name,
186
+ target:
187
+ row.expectedUrl ||
188
+ row.actualUrl ||
189
+ row.expectedCommand ||
190
+ row.actualCommand ||
191
+ "",
192
+ rows: [],
193
+ });
194
+ }
195
+ const group = grouped.get(row.name);
196
+ if (!group.target) {
197
+ group.target =
198
+ row.expectedUrl ||
199
+ row.actualUrl ||
200
+ row.expectedCommand ||
201
+ row.actualCommand ||
202
+ "";
203
+ }
204
+ group.rows.push(row);
205
+ }
206
+
207
+ return [...grouped.values()]
208
+ .map((server) => {
209
+ const clients = MCP_CLIENTS.map((client) => {
210
+ const clientRows = server.rows.filter(
211
+ (row) => row.client === client.id,
212
+ );
213
+ if (clientRows.length === 0) return null;
214
+ const present = clientRows.filter(
215
+ (row) => row.status === "present" || row.status === "ok",
216
+ ).length;
217
+ let status = "off";
218
+ if (present === clientRows.length) status = "on";
219
+ else if (present > 0) status = "partial";
220
+ return {
221
+ ...client,
222
+ status,
223
+ present,
224
+ total: clientRows.length,
225
+ labels: [
226
+ ...new Set(clientRows.map((row) => row.label).filter(Boolean)),
227
+ ],
228
+ };
229
+ }).filter(Boolean);
230
+ const onCount = clients.filter((client) => client.status === "on").length;
231
+ const partialCount = clients.filter(
232
+ (client) => client.status === "partial",
233
+ ).length;
234
+ let status = "off";
235
+ if (clients.length > 0 && onCount === clients.length) status = "on";
236
+ else if (onCount > 0 || partialCount > 0) status = "partial";
237
+ return {
238
+ name: server.name,
239
+ target: server.target,
240
+ status,
241
+ clients,
242
+ client_count: clients.length,
243
+ present_client_count: onCount,
244
+ };
245
+ })
246
+ .sort((a, b) => {
247
+ if (a.name === "tfx-hub") return -1;
248
+ if (b.name === "tfx-hub") return 1;
249
+ return a.name.localeCompare(b.name);
250
+ });
251
+ }
252
+
253
+ function normalizeMcpStatus(mcpStatus = {}) {
254
+ const rows = Array.isArray(mcpStatus?.rows)
255
+ ? mcpStatus.rows.map(sanitizeMcpRow)
256
+ : [];
257
+ return {
258
+ registry_path: String(
259
+ mcpStatus?.registry_path || mcpStatus?.registryPath || "",
260
+ ),
261
+ server_count: Number.isFinite(Number(mcpStatus?.server_count))
262
+ ? Number(mcpStatus.server_count)
263
+ : Number.isFinite(Number(mcpStatus?.serverCount))
264
+ ? Number(mcpStatus.serverCount)
265
+ : 0,
266
+ rows,
267
+ clients: summarizeMcpClients(rows),
268
+ servers: summarizeMcpServers(rows),
269
+ summary: {
270
+ total: rows.length,
271
+ by_status: countBy(rows, (row) => row.status),
272
+ by_client: countBy(rows, (row) => row.client || row.label),
273
+ },
274
+ ...(mcpStatus?.error ? { error: String(mcpStatus.error) } : {}),
275
+ };
276
+ }
277
+
278
+ function normalizeHub(hub = {}) {
279
+ const id = String(hub?.id ?? hub?.sessionId ?? hub?.session_id ?? "");
280
+ return {
281
+ id,
282
+ pid: Number.isFinite(Number(hub?.pid)) ? Number(hub.pid) : null,
283
+ port: Number.isFinite(Number(hub?.port)) ? Number(hub.port) : null,
284
+ url: String(hub?.url || ""),
285
+ projectRoot: String(hub?.projectRoot || hub?.project_root || ""),
286
+ };
287
+ }
288
+
289
+ function numericPid(value) {
290
+ const text = String(value ?? "").trim();
291
+ if (!text) return null;
292
+ const pid = Number(text);
293
+ return Number.isFinite(pid) && pid > 0 ? pid : null;
294
+ }
295
+
296
+ function normalizeRuntimeStatus(runtimeStatus = {}) {
297
+ const processes = Array.isArray(runtimeStatus?.processes)
298
+ ? runtimeStatus.processes.map((process) => ({
299
+ client: String(process?.client || ""),
300
+ sessionId: String(process?.sessionId || ""),
301
+ source: String(process?.source || ""),
302
+ conversationId: String(process?.conversationId || ""),
303
+ focusable: process?.focusable !== false,
304
+ pid: numericPid(process?.pid),
305
+ ppid: Number.isFinite(Number(process?.ppid))
306
+ ? Number(process.ppid)
307
+ : null,
308
+ phase:
309
+ typeof process?.phase === "string"
310
+ ? process.phase
311
+ : process?.source === "conversation"
312
+ ? "idle"
313
+ : "active",
314
+ elapsed: String(process?.elapsed || ""),
315
+ command: String(process?.command || ""),
316
+ cwd: String(process?.cwd || ""),
317
+ taskSummary: String(process?.taskSummary || process?.command || ""),
318
+ }))
319
+ : [];
320
+ return {
321
+ processes: processes.filter(
322
+ (process) =>
323
+ process.client &&
324
+ (process.pid || process.sessionId || process.conversationId),
325
+ ),
326
+ summary: runtimeStatus?.summary || {
327
+ total: processes.length,
328
+ clients: [],
329
+ },
330
+ ...(runtimeStatus?.error ? { error: String(runtimeStatus.error) } : {}),
331
+ };
332
+ }
333
+
334
+ function sessionsToLiveOverlay(sessions = []) {
335
+ return sessions
336
+ .filter((session) => session.status !== "stale")
337
+ .map((session) => ({
338
+ sessionId: session.sessionId,
339
+ host: session.host,
340
+ agent_id: session.agent_id,
341
+ agent: session.agent,
342
+ phase: session.status,
343
+ started_at: session.started_at,
344
+ }));
345
+ }
346
+
347
+ function runtimeToLiveOverlay(runtime) {
348
+ return runtime.processes.map((process) => ({
349
+ sessionId:
350
+ process.sessionId ||
351
+ (process.pid
352
+ ? `${process.client}-${process.pid}`
353
+ : `${process.client}-${process.conversationId || "session"}`),
354
+ host: "local",
355
+ agent_id: process.client,
356
+ agent: agentMeta(process.client),
357
+ phase: process.phase || "active",
358
+ pid: process.pid,
359
+ command: process.command,
360
+ elapsed: process.elapsed,
361
+ cwd: process.cwd,
362
+ taskSummary: process.taskSummary || process.command || `PID ${process.pid}`,
363
+ source: process.source,
364
+ conversationId: process.conversationId,
365
+ focusable: process.focusable,
366
+ started_at: null,
367
+ }));
368
+ }
369
+
370
+ function normalizeLiveSession(session = {}) {
371
+ const agentId = inferAgentId(session);
372
+ return {
373
+ ...session,
374
+ sessionId: String(session.sessionId || ""),
375
+ host: typeof session.host === "string" ? session.host : "local",
376
+ agent_id: agentId || null,
377
+ agent: session.agent || agentMeta(agentId),
378
+ phase:
379
+ typeof session.phase === "string"
380
+ ? session.phase
381
+ : typeof session.status === "string"
382
+ ? session.status
383
+ : "active",
384
+ started_at: toIsoTime(
385
+ session.started_at ?? session.startedAt ?? session.lastHeartbeat,
386
+ ),
387
+ };
388
+ }
389
+
390
+ function normalizeCtoStatus(ctoStatus = {}, sessions = [], runtime = {}) {
391
+ const explicitLive = Array.isArray(ctoStatus?.live_sessions)
392
+ ? ctoStatus.live_sessions
393
+ : null;
394
+ const sessionLive = sessionsToLiveOverlay(sessions);
395
+ const runtimeLive = runtimeToLiveOverlay(runtime);
396
+ const overlayLive =
397
+ explicitLive && explicitLive.length > 0
398
+ ? explicitLive
399
+ : sessionLive.length > 0
400
+ ? sessionLive
401
+ : runtimeLive;
402
+ const liveSessions =
403
+ runtimeLive.length > overlayLive.length ? runtimeLive : overlayLive;
404
+ const normalizedLiveSessions = liveSessions
405
+ .map(normalizeLiveSession)
406
+ .filter((session) => session.sessionId);
407
+ const activeShards = Array.isArray(ctoStatus?.active_shards)
408
+ ? ctoStatus.active_shards
409
+ : [];
410
+ return {
411
+ schema_version: String(ctoStatus?.schema_version || "cto-lake.v1"),
412
+ generated_at: ctoStatus?.generated_at || null,
413
+ live_sessions: normalizedLiveSessions,
414
+ active_shards: activeShards,
415
+ summary: {
416
+ live_session_count: normalizedLiveSessions.length,
417
+ active_shard_count: activeShards.length,
418
+ tray_session_count: sessions.length,
419
+ tray_sessions_by_status: countBy(sessions, (session) => session.status),
420
+ },
421
+ ...(ctoStatus?.error ? { error: String(ctoStatus.error) } : {}),
422
+ };
423
+ }
424
+
425
+ export function buildTrayStatePayload({
426
+ hub = {},
427
+ qos = {},
428
+ synapseSnapshot = {},
429
+ broker = [],
430
+ ctoStatus = {},
431
+ mcpStatus = {},
432
+ runtimeStatus = {},
433
+ now = Date.now(),
434
+ } = {}) {
435
+ const sessions = normalizeTraySessions(synapseSnapshot);
436
+ const runtime = normalizeRuntimeStatus(runtimeStatus);
437
+ return {
438
+ ok: true,
439
+ hub: normalizeHub(hub),
440
+ qos,
441
+ sessions,
442
+ cto: normalizeCtoStatus(ctoStatus, sessions, runtime),
443
+ mcp: normalizeMcpStatus(mcpStatus),
444
+ runtime,
445
+ broker: Array.isArray(broker) ? broker : [],
446
+ ts: now,
447
+ };
448
+ }
package/hub/tray.mjs CHANGED
@@ -4,16 +4,71 @@ import _SysTrayModule from "systray2";
4
4
 
5
5
  const SysTray = _SysTrayModule.default || _SysTrayModule;
6
6
 
7
- import { exec } from "node:child_process";
7
+ import { exec, execFileSync, spawn } from "node:child_process";
8
8
  import { existsSync, readFileSync } from "node:fs";
9
9
  import { homedir } from "node:os";
10
- import { join, resolve } from "node:path";
10
+ import { dirname, join, resolve } from "node:path";
11
11
  import { fileURLToPath } from "node:url";
12
- import { IS_WINDOWS } from "@triflux/core/hub/platform.mjs";
12
+ import { IS_MAC, IS_WINDOWS } from "@triflux/core/hub/platform.mjs";
13
+ import { ensureHubForTray } from "./tray-lifecycle.mjs";
13
14
 
14
15
  const HUB_PID_FILE = join(homedir(), ".claude", "cache", "tfx-hub", "hub.pid");
15
16
  const DEFAULT_HUB_PORT = "27888";
16
17
 
18
+ function sleep(ms) {
19
+ return new Promise((resolveSleep) => setTimeout(resolveSleep, ms));
20
+ }
21
+
22
+ export function collectMacTrayProcesses(
23
+ psOutput = "",
24
+ {
25
+ scriptPath = fileURLToPath(import.meta.url),
26
+ currentPid = process.pid,
27
+ } = {},
28
+ ) {
29
+ const target = String(scriptPath || "");
30
+ if (!target) return [];
31
+ const ownPid = Number(currentPid);
32
+ return String(psOutput)
33
+ .split(/\r?\n/u)
34
+ .flatMap((line) => {
35
+ const match = line.match(/^\s*(\d+)\s+(\d+)\s+(.+)$/u);
36
+ if (!match) return [];
37
+ const pid = Number.parseInt(match[1], 10);
38
+ const ppid = Number.parseInt(match[2], 10);
39
+ const command = match[3].trim();
40
+ if (!Number.isFinite(pid) || pid === ownPid) return [];
41
+ if (!command.includes(target)) return [];
42
+ if (!/\bnode(?:\s|$)|\/node(?:\s|$)/u.test(command)) return [];
43
+ return [{ pid, ppid, command }];
44
+ });
45
+ }
46
+
47
+ export function reapExistingMacTrayProcesses({
48
+ scriptPath = fileURLToPath(import.meta.url),
49
+ currentPid = process.pid,
50
+ execFileSyncFn = execFileSync,
51
+ killFn = process.kill,
52
+ } = {}) {
53
+ let output = "";
54
+ try {
55
+ output = execFileSyncFn("ps", ["-axo", "pid,ppid,command"], {
56
+ encoding: "utf8",
57
+ timeout: 1000,
58
+ maxBuffer: 1024 * 1024,
59
+ });
60
+ } catch {
61
+ return [];
62
+ }
63
+ const existing = collectMacTrayProcesses(output, { scriptPath, currentPid });
64
+ for (const processInfo of existing) {
65
+ try {
66
+ killFn(processInfo.pid, "SIGTERM");
67
+ } catch {}
68
+ }
69
+ return existing;
70
+ }
71
+
17
72
  function getHubBaseUrl() {
18
73
  if (process.env.TFX_HUB_URL)
19
74
  return process.env.TFX_HUB_URL.replace(/\/+$/, "");
@@ -354,8 +409,42 @@ async function shutdown(reason = "shutdown") {
354
409
  }
355
410
 
356
411
  export async function startTray() {
412
+ if (IS_MAC) {
413
+ const trayScript = fileURLToPath(import.meta.url);
414
+ const port = process.env.TFX_HUB_PORT || DEFAULT_HUB_PORT;
415
+ const serverPath = join(dirname(trayScript), "server.mjs");
416
+ await ensureHubForTray({ port, serverPath });
417
+ const reaped = reapExistingMacTrayProcesses({ scriptPath: trayScript });
418
+ if (reaped.length > 0) await sleep(350);
419
+ const swiftScript = join(dirname(trayScript), "mac-tray.swift");
420
+
421
+ // Spawn swift script detached or as a child
422
+ const trayProc = spawn("swift", [swiftScript, port], {
423
+ stdio: "ignore",
424
+ detached: false,
425
+ });
426
+
427
+ trayProc.on("error", (err) =>
428
+ console.error("[tfx-tray-mac] start failed:", err),
429
+ );
430
+
431
+ trayProc.on("exit", (code) => {
432
+ console.error(`[tfx-tray-mac] swift script exited (code ${code})`);
433
+ process.exit(typeof code === "number" ? code : 1);
434
+ });
435
+
436
+ const cleanup = () => trayProc.kill();
437
+ process.on("SIGINT", cleanup);
438
+ process.on("SIGTERM", cleanup);
439
+
440
+ return {
441
+ systray: null,
442
+ stop: () => trayProc.kill(),
443
+ };
444
+ }
445
+
357
446
  if (!IS_WINDOWS) {
358
- throw new Error("tray command is only supported on Windows.");
447
+ throw new Error("systray2 tray command is only supported on Windows.");
359
448
  }
360
449
 
361
450
  systray = new SysTray({
@@ -16,7 +16,11 @@ function resolveCandidatePath(candidate, cwd = process.cwd()) {
16
16
  return existsSync(normalized) ? normalized : null;
17
17
  }
18
18
 
19
- function resolveRouteScript(explicitPath, cwd = process.cwd(), env = process.env) {
19
+ function resolveRouteScript(
20
+ explicitPath,
21
+ cwd = process.cwd(),
22
+ env = process.env,
23
+ ) {
20
24
  const candidates = [
21
25
  explicitPath,
22
26
  env.TFX_DELEGATOR_ROUTE_SCRIPT,
@@ -57,10 +61,15 @@ export class AntigravityRouteWorker {
57
61
  constructor(options = {}) {
58
62
  this.cwd = options.cwd || process.cwd();
59
63
  this.env = { ...process.env, ...(options.env || {}) };
60
- this.routeScript = resolveRouteScript(options.routeScript, this.cwd, this.env);
64
+ this.routeScript = resolveRouteScript(
65
+ options.routeScript,
66
+ this.cwd,
67
+ this.env,
68
+ );
61
69
  this.bashCommand = options.bashCommand || this.env.BASH_BIN || "bash";
62
70
  this.timeoutMs =
63
- Number.isFinite(Number(options.timeoutMs)) && Number(options.timeoutMs) > 0
71
+ Number.isFinite(Number(options.timeoutMs)) &&
72
+ Number(options.timeoutMs) > 0
64
73
  ? Number(options.timeoutMs)
65
74
  : DEFAULT_TIMEOUT_MS;
66
75
  this.children = new Set();
@@ -89,7 +98,8 @@ export class AntigravityRouteWorker {
89
98
  }
90
99
 
91
100
  const timeoutMs =
92
- Number.isFinite(Number(options.timeoutMs)) && Number(options.timeoutMs) > 0
101
+ Number.isFinite(Number(options.timeoutMs)) &&
102
+ Number(options.timeoutMs) > 0
93
103
  ? Number(options.timeoutMs)
94
104
  : this.timeoutMs;
95
105
  const timeoutSec = Math.max(1, Math.ceil(timeoutMs / 1000));
@@ -29,11 +29,8 @@ if (!mcpFilterPath) {
29
29
  `mcp-filter.mjs not found. candidates: ${MCP_FILTER_CANDIDATES.join(", ")}`,
30
30
  );
31
31
  }
32
- const {
33
- buildPromptHint,
34
- getCodexMcpConfig,
35
- SUPPORTED_MCP_PROFILES,
36
- } = await import(pathToFileURL(mcpFilterPath).href);
32
+ const { buildPromptHint, getCodexMcpConfig, SUPPORTED_MCP_PROFILES } =
33
+ await import(pathToFileURL(mcpFilterPath).href);
37
34
  const SERVER_INFO = { name: "triflux-delegator", version: "1.0.0" };
38
35
  const DEFAULT_CONTEXT_BYTES = 32 * 1024;
39
36
  const DEFAULT_ROUTE_TIMEOUT_SEC = 120;
@@ -14,8 +14,8 @@
14
14
  // existing `requestJson` helper. Callers can override by passing their own
15
15
  // `publishCallback` or swap the transport with `requestJsonFn`.
16
16
 
17
- import { ClaudeWorker } from "./claude-worker.mjs";
18
17
  import { AntigravityRouteWorker } from "./antigravity-route-worker.mjs";
18
+ import { ClaudeWorker } from "./claude-worker.mjs";
19
19
  import { CodexAppServerWorker } from "./codex-app-server-worker.mjs";
20
20
 
21
21
  /**
@@ -102,7 +102,8 @@ function detectAntigravityCategory(error) {
102
102
  function buildAntigravityErrorInfo(error, attempts) {
103
103
  const category = detectAntigravityCategory(error);
104
104
  const retryable = isAntigravityRetryable(error);
105
- let recovery = "Retry the Antigravity route worker after correcting the issue.";
105
+ let recovery =
106
+ "Retry the Antigravity route worker after correcting the issue.";
106
107
 
107
108
  if (category === "auth") {
108
109
  recovery = "Refresh the Antigravity authentication state and retry.";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@triflux/remote",
3
- "version": "10.31.0",
3
+ "version": "10.33.0",
4
4
  "description": "triflux remote — team mode, psmux, MCP workers, SQLite store.",
5
5
  "type": "module",
6
6
  "main": "hub/index.mjs",
@@ -16,11 +16,16 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "@modelcontextprotocol/sdk": "^1.27.1",
19
+ "ajv": "^8.18.0",
19
20
  "better-sqlite3": "^12.10.0",
20
- "systray2": "^2.1.4"
21
+ "pino": "^10.3.1",
22
+ "pino-pretty": "^13.1.3",
23
+ "systray2": "^2.1.4",
24
+ "zod": "^4.0.0"
21
25
  },
22
26
  "files": [
23
27
  "hub",
28
+ "cto",
24
29
  "scripts/lib"
25
30
  ],
26
31
  "keywords": [