svamp-cli 0.2.92 → 0.2.93
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/{agentCommands-t6HbehoC.mjs → agentCommands-5UkFbjCr.mjs} +2 -2
- package/dist/{auth-Dx09pKt3.mjs → auth-DIlY0Nww.mjs} +1 -2
- package/dist/cli.mjs +50 -51
- package/dist/{commands-BHSvizSp.mjs → commands-BjtQRoGi.mjs} +1 -2
- package/dist/{commands-BR_lAgZ0.mjs → commands-D-m22ZpN.mjs} +1 -2
- package/dist/{commands-BxluPors.mjs → commands-Do8wtW5P.mjs} +1 -2
- package/dist/{commands-pVw3CcRo.mjs → commands-POgHLHF-.mjs} +2 -3
- package/dist/{commands-Dfyuenmi.mjs → commands-yzDiaiRy.mjs} +5 -5
- package/dist/{fleet-C3KLMFeD.mjs → fleet-ChKjkV1V.mjs} +1 -2
- package/dist/{frpc-D_1pEpUY.mjs → frpc-glQKHUBu.mjs} +1 -2
- package/dist/{headlessCli-D-unuCQt.mjs → headlessCli-D2ZOlMNL.mjs} +2 -1
- package/dist/index.mjs +1 -2
- package/dist/{package-_Qk8lnvq.mjs → package-Caxptr7d.mjs} +2 -2
- package/dist/{run-l1OtW948.mjs → run-CuHAFrIm.mjs} +1 -2
- package/dist/{run-BF4AmFz8.mjs → run-CvIUzIMP.mjs} +6 -445
- package/dist/{serveCommands-CvhlM4Iz.mjs → serveCommands-1TJus4ZX.mjs} +5 -5
- package/dist/{serveManager-DNlqB0r6.mjs → serveManager-CoVwgYS_.mjs} +2 -3
- package/dist/sideband-7glSvpTW.mjs +79 -0
- package/package.json +2 -2
|
@@ -2,7 +2,7 @@ import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(im
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import { resolve, join } from 'node:path';
|
|
4
4
|
import { existsSync, readFileSync, watch } from 'node:fs';
|
|
5
|
-
import { c as connectToHypha, a as registerSessionService,
|
|
5
|
+
import { c as connectToHypha, a as registerSessionService, P as generateHookSettings } from './run-CvIUzIMP.mjs';
|
|
6
6
|
import { createServer } from 'node:http';
|
|
7
7
|
import { spawn } from 'node:child_process';
|
|
8
8
|
import { createInterface } from 'node:readline';
|
|
@@ -13,7 +13,6 @@ import 'path';
|
|
|
13
13
|
import 'url';
|
|
14
14
|
import 'child_process';
|
|
15
15
|
import 'crypto';
|
|
16
|
-
import 'ws';
|
|
17
16
|
import 'util';
|
|
18
17
|
import '@agentclientprotocol/sdk';
|
|
19
18
|
import '@modelcontextprotocol/sdk/client/index.js';
|
|
@@ -7,7 +7,6 @@ import { execFile, spawn as spawn$1, execSync as execSync$1 } from 'child_proces
|
|
|
7
7
|
import { randomUUID as randomUUID$1 } from 'crypto';
|
|
8
8
|
import { existsSync, readFileSync, mkdirSync as mkdirSync$1, readdirSync, writeFileSync as writeFileSync$1, renameSync as renameSync$1, rmSync, appendFileSync, unlinkSync } from 'node:fs';
|
|
9
9
|
import { exec, spawn, execSync, execFile as execFile$1, execFileSync } from 'node:child_process';
|
|
10
|
-
import { WebSocket } from 'ws';
|
|
11
10
|
import { promisify } from 'util';
|
|
12
11
|
import { randomBytes, randomUUID, createHash } from 'node:crypto';
|
|
13
12
|
import { join as join$1 } from 'node:path';
|
|
@@ -941,239 +940,6 @@ function buildSessionDeps(rpc, opts = {}) {
|
|
|
941
940
|
};
|
|
942
941
|
}
|
|
943
942
|
|
|
944
|
-
function toolsForRole(role) {
|
|
945
|
-
if (role === "admin") return [...READ_ONLY_TOOLS, "run_bash", "send_to_session"];
|
|
946
|
-
if (role === "interact") return [...READ_ONLY_TOOLS, "send_to_session"];
|
|
947
|
-
return [...READ_ONLY_TOOLS];
|
|
948
|
-
}
|
|
949
|
-
function toRealtimeTools(tools) {
|
|
950
|
-
return tools.map((t) => ({ type: "function", name: t.name, description: t.description, parameters: t.parameters }));
|
|
951
|
-
}
|
|
952
|
-
function buildVoiceSessionUpdate(instructions, tools, opts) {
|
|
953
|
-
return {
|
|
954
|
-
type: "session.update",
|
|
955
|
-
session: {
|
|
956
|
-
type: "realtime",
|
|
957
|
-
instructions,
|
|
958
|
-
tools: toRealtimeTools(tools),
|
|
959
|
-
tool_choice: "auto",
|
|
960
|
-
...opts?.voice ? { audio: { output: { voice: opts.voice } } } : {}
|
|
961
|
-
}
|
|
962
|
-
};
|
|
963
|
-
}
|
|
964
|
-
async function handleRealtimeEvent(ev, tools, send, nameByCallId) {
|
|
965
|
-
if (ev?.type === "response.output_item.added" && ev.item?.type === "function_call") {
|
|
966
|
-
if (nameByCallId && ev.item.call_id) nameByCallId.set(ev.item.call_id, ev.item.name);
|
|
967
|
-
return null;
|
|
968
|
-
}
|
|
969
|
-
if (!ev || ev.type !== "response.function_call_arguments.done") return null;
|
|
970
|
-
const callId = ev.call_id;
|
|
971
|
-
const name = ev.name || nameByCallId && nameByCallId.get(callId) || "";
|
|
972
|
-
let args = {};
|
|
973
|
-
try {
|
|
974
|
-
args = ev.arguments ? JSON.parse(ev.arguments) : {};
|
|
975
|
-
} catch {
|
|
976
|
-
}
|
|
977
|
-
const tool = tools.find((t) => t.name === name);
|
|
978
|
-
let output;
|
|
979
|
-
if (!tool) {
|
|
980
|
-
output = `error: tool "${name}" is not available to this caller`;
|
|
981
|
-
} else {
|
|
982
|
-
try {
|
|
983
|
-
output = await tool.run(args);
|
|
984
|
-
} catch (e) {
|
|
985
|
-
output = `error: ${e?.message || e}`;
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
|
-
send({ type: "conversation.item.create", item: { type: "function_call_output", call_id: callId, output } });
|
|
989
|
-
send({ type: "response.create" });
|
|
990
|
-
return { name, output };
|
|
991
|
-
}
|
|
992
|
-
async function initVoiceSession(init) {
|
|
993
|
-
const ctx = await loadWiseAgentContext(init.deps, init.config);
|
|
994
|
-
const instructions = buildWiseAgentInstructions(ctx, init.config);
|
|
995
|
-
const granted = gateTools(buildTools(init.deps, ctx.skills), init.config, init.sender.name);
|
|
996
|
-
return { tools: granted, sessionUpdate: buildVoiceSessionUpdate(instructions, granted, { voice: init.voice }) };
|
|
997
|
-
}
|
|
998
|
-
async function initMachineVoiceSession(init) {
|
|
999
|
-
const ctx = await loadMachineContext(init.deps);
|
|
1000
|
-
const instructions = buildMachineInstructions(ctx);
|
|
1001
|
-
const allow = new Set(machineToolsForRole(init.role ?? "admin"));
|
|
1002
|
-
const granted = buildMachineTools(init.deps, ctx.skills).filter((t) => allow.has(t.name));
|
|
1003
|
-
return { tools: granted, sessionUpdate: buildVoiceSessionUpdate(instructions, granted, { voice: init.voice }) };
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
var sideband = /*#__PURE__*/Object.freeze({
|
|
1007
|
-
__proto__: null,
|
|
1008
|
-
buildVoiceSessionUpdate: buildVoiceSessionUpdate,
|
|
1009
|
-
handleRealtimeEvent: handleRealtimeEvent,
|
|
1010
|
-
initMachineVoiceSession: initMachineVoiceSession,
|
|
1011
|
-
initVoiceSession: initVoiceSession,
|
|
1012
|
-
toRealtimeTools: toRealtimeTools,
|
|
1013
|
-
toolsForRole: toolsForRole
|
|
1014
|
-
});
|
|
1015
|
-
|
|
1016
|
-
const realFactory = (url, headers) => new WebSocket(url, { headers });
|
|
1017
|
-
let _testFactory;
|
|
1018
|
-
const NOISY_EVENTS = /* @__PURE__ */ new Set([
|
|
1019
|
-
"response.audio.delta",
|
|
1020
|
-
"response.output_audio.delta",
|
|
1021
|
-
"response.audio_transcript.delta",
|
|
1022
|
-
"response.output_audio_transcript.delta",
|
|
1023
|
-
"response.text.delta",
|
|
1024
|
-
"response.output_text.delta",
|
|
1025
|
-
"response.function_call_arguments.delta",
|
|
1026
|
-
"input_audio_buffer.append",
|
|
1027
|
-
"rate_limits.updated",
|
|
1028
|
-
"conversation.item.input_audio_transcription.delta"
|
|
1029
|
-
]);
|
|
1030
|
-
const tag = (callId) => `[wise-voice callId=${callId.slice(0, 14)}\u2026]`;
|
|
1031
|
-
async function openSideband(opts) {
|
|
1032
|
-
const base = (opts.baseUrl || "https://api.openai.com").replace(/^http/, "ws").replace(/\/+$/, "");
|
|
1033
|
-
const url = `${base}/v1/realtime?call_id=${encodeURIComponent(opts.callId)}`;
|
|
1034
|
-
const { tools, sessionUpdate } = opts.prepared ?? await initVoiceSession(opts.init);
|
|
1035
|
-
const T = tag(opts.callId);
|
|
1036
|
-
const toolNames = tools.map((t) => t.name);
|
|
1037
|
-
console.log(`${T} OPENING sideband \u2192 ${base}/v1/realtime (key=${opts.apiKey ? "set:" + opts.apiKey.slice(0, 7) + "\u2026" : "MISSING"}) | ${toolNames.length} tools: [${toolNames.join(", ")}]`);
|
|
1038
|
-
const ws = (opts.wsFactory || _testFactory || realFactory)(url, { Authorization: `Bearer ${opts.apiKey}` });
|
|
1039
|
-
const send = (e) => {
|
|
1040
|
-
try {
|
|
1041
|
-
ws.send(JSON.stringify(e));
|
|
1042
|
-
} catch (err) {
|
|
1043
|
-
console.error(`${T} send failed (socket gone): ${err?.message || err}`);
|
|
1044
|
-
}
|
|
1045
|
-
};
|
|
1046
|
-
const nameByCallId = /* @__PURE__ */ new Map();
|
|
1047
|
-
let eventCount = 0;
|
|
1048
|
-
let toolCalls = 0;
|
|
1049
|
-
let closed = false;
|
|
1050
|
-
const idleMs = opts.idleTimeoutMs ?? 12e4;
|
|
1051
|
-
const lifeMs = opts.maxLifetimeMs ?? 30 * 6e4;
|
|
1052
|
-
let idleTimer;
|
|
1053
|
-
let lifeTimer;
|
|
1054
|
-
const clearTimers = () => {
|
|
1055
|
-
if (idleTimer) clearTimeout(idleTimer);
|
|
1056
|
-
if (lifeTimer) clearTimeout(lifeTimer);
|
|
1057
|
-
};
|
|
1058
|
-
const closeWith = (why) => {
|
|
1059
|
-
if (closed) return;
|
|
1060
|
-
console.log(`${T} CLOSING (${why})`);
|
|
1061
|
-
clearTimers();
|
|
1062
|
-
try {
|
|
1063
|
-
ws.close();
|
|
1064
|
-
} catch {
|
|
1065
|
-
}
|
|
1066
|
-
};
|
|
1067
|
-
const bumpIdle = () => {
|
|
1068
|
-
if (idleTimer) clearTimeout(idleTimer);
|
|
1069
|
-
idleTimer = setTimeout(() => closeWith(`idle ${idleMs / 1e3}s \u2014 no events, treating call as abandoned`), idleMs);
|
|
1070
|
-
};
|
|
1071
|
-
lifeTimer = setTimeout(() => closeWith(`max lifetime ${lifeMs / 6e4}min reached`), lifeMs);
|
|
1072
|
-
bumpIdle();
|
|
1073
|
-
let settled = false;
|
|
1074
|
-
let resolveOpen;
|
|
1075
|
-
let rejectOpen;
|
|
1076
|
-
const opened = new Promise((res, rej) => {
|
|
1077
|
-
resolveOpen = res;
|
|
1078
|
-
rejectOpen = rej;
|
|
1079
|
-
});
|
|
1080
|
-
const connectTimer = setTimeout(() => {
|
|
1081
|
-
if (!settled) {
|
|
1082
|
-
settled = true;
|
|
1083
|
-
rejectOpen(new Error("sideband connect timeout (10s)"));
|
|
1084
|
-
try {
|
|
1085
|
-
ws.close();
|
|
1086
|
-
} catch {
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
}, 1e4);
|
|
1090
|
-
ws.on("open", () => {
|
|
1091
|
-
console.log(`${T} WS OPEN \u2713 \u2014 OpenAI Realtime connected; sending session.update (instructions + ${toolNames.length} tools)`);
|
|
1092
|
-
send(sessionUpdate);
|
|
1093
|
-
if (!settled) {
|
|
1094
|
-
settled = true;
|
|
1095
|
-
clearTimeout(connectTimer);
|
|
1096
|
-
resolveOpen();
|
|
1097
|
-
}
|
|
1098
|
-
});
|
|
1099
|
-
const wantTools = new Set(toolNames);
|
|
1100
|
-
let reasserts = 0;
|
|
1101
|
-
ws.on("message", async (data) => {
|
|
1102
|
-
let ev;
|
|
1103
|
-
try {
|
|
1104
|
-
ev = JSON.parse(typeof data === "string" ? data : data?.toString?.() ?? "");
|
|
1105
|
-
} catch {
|
|
1106
|
-
return;
|
|
1107
|
-
}
|
|
1108
|
-
eventCount++;
|
|
1109
|
-
bumpIdle();
|
|
1110
|
-
if (ev?.type && !NOISY_EVENTS.has(ev.type)) {
|
|
1111
|
-
if (ev.type === "error") {
|
|
1112
|
-
console.error(`${T} \u26A0\uFE0F OpenAI error event: ${JSON.stringify(ev.error || ev).slice(0, 400)}`);
|
|
1113
|
-
} else if (ev.type === "response.output_item.added" && ev.item?.type === "function_call") {
|
|
1114
|
-
console.log(`${T} \u2190 model requested tool "${ev.item?.name}" (call_id=${ev.item?.call_id})`);
|
|
1115
|
-
try {
|
|
1116
|
-
opts.onTool?.({ callId: ev.item.call_id, name: ev.item.name || "", args: "", output: "", phase: "start" });
|
|
1117
|
-
} catch {
|
|
1118
|
-
}
|
|
1119
|
-
} else if (ev.type === "response.function_call_arguments.done") {
|
|
1120
|
-
toolCalls++;
|
|
1121
|
-
console.log(`${T} \u2190 function_call_arguments.done call_id=${ev.call_id} name=${ev.name || nameByCallId.get(ev.call_id) || "?"} args=${String(ev.arguments || "").slice(0, 200)}`);
|
|
1122
|
-
} else if (ev.type === "session.updated") {
|
|
1123
|
-
const have = (ev.session?.tools || []).map((t) => t?.name);
|
|
1124
|
-
console.log(`${T} session.updated \u2014 server now has tools: [${have.join(", ") || "NONE"}]`);
|
|
1125
|
-
} else {
|
|
1126
|
-
console.log(`${T} \u2190 ${ev.type}`);
|
|
1127
|
-
}
|
|
1128
|
-
}
|
|
1129
|
-
if (ev?.type === "session.updated" && wantTools.size) {
|
|
1130
|
-
const have = new Set((ev.session?.tools || []).map((t) => t?.name));
|
|
1131
|
-
const missing = [...wantTools].some((n) => !have.has(n));
|
|
1132
|
-
if (missing && reasserts < 10) {
|
|
1133
|
-
reasserts++;
|
|
1134
|
-
console.log(`${T} \u27F3 tools were clobbered (peer session.update) \u2014 re-asserting #${reasserts}`);
|
|
1135
|
-
send(sessionUpdate);
|
|
1136
|
-
return;
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
try {
|
|
1140
|
-
const r = await handleRealtimeEvent(ev, tools, send, nameByCallId);
|
|
1141
|
-
if (r) {
|
|
1142
|
-
console.log(`${T} \u2192 tool "${r.name}" executed \u2192 ${String(r.output).slice(0, 160).replace(/\n/g, " ")}`);
|
|
1143
|
-
try {
|
|
1144
|
-
opts.onTool?.({ callId: ev.call_id, name: r.name, args: String(ev.arguments || ""), output: r.output, phase: "done" });
|
|
1145
|
-
} catch {
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
} catch (e) {
|
|
1149
|
-
console.error(`${T} dispatch error: ${e?.message || e}`);
|
|
1150
|
-
opts.onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
1151
|
-
}
|
|
1152
|
-
});
|
|
1153
|
-
ws.on("close", (code, reason) => {
|
|
1154
|
-
closed = true;
|
|
1155
|
-
clearTimers();
|
|
1156
|
-
clearTimeout(connectTimer);
|
|
1157
|
-
console.log(`${T} WS CLOSED (code=${code ?? "?"} reason=${reason ? String(reason).slice(0, 120) : ""}) \u2014 ${eventCount} events, ${toolCalls} tool calls`);
|
|
1158
|
-
if (!settled) {
|
|
1159
|
-
settled = true;
|
|
1160
|
-
rejectOpen(new Error(`sideband closed before open (code ${code ?? "?"})`));
|
|
1161
|
-
}
|
|
1162
|
-
opts.onClose?.();
|
|
1163
|
-
});
|
|
1164
|
-
ws.on("error", (e) => {
|
|
1165
|
-
console.error(`${T} WS ERROR: ${e?.message || e}`);
|
|
1166
|
-
clearTimeout(connectTimer);
|
|
1167
|
-
if (!settled) {
|
|
1168
|
-
settled = true;
|
|
1169
|
-
rejectOpen(e instanceof Error ? e : new Error(String(e)));
|
|
1170
|
-
}
|
|
1171
|
-
opts.onError?.(e instanceof Error ? e : new Error(String(e)));
|
|
1172
|
-
});
|
|
1173
|
-
await opened;
|
|
1174
|
-
return { callId: opts.callId, close: () => closeWith("explicit close (release / new attach / shutdown)") };
|
|
1175
|
-
}
|
|
1176
|
-
|
|
1177
943
|
const execFileAsync$1 = promisify(execFile);
|
|
1178
944
|
function parseEtime(s) {
|
|
1179
945
|
if (!s) return 0;
|
|
@@ -1352,51 +1118,6 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
1352
1118
|
lastInboundRpcAt = Date.now();
|
|
1353
1119
|
};
|
|
1354
1120
|
const listeners = [];
|
|
1355
|
-
const voiceSidebands = /* @__PURE__ */ new Map();
|
|
1356
|
-
const prepareVoiceSession = async (params, context) => {
|
|
1357
|
-
const senderName = context?.user?.email || context?.user?.id || "user";
|
|
1358
|
-
if (params.sessionId) {
|
|
1359
|
-
const rpc = handlers.getSessionRPCHandlers?.(params.sessionId);
|
|
1360
|
-
if (!rpc) return { ok: false, error: "Session not found on this machine" };
|
|
1361
|
-
let cwd;
|
|
1362
|
-
let owner;
|
|
1363
|
-
let role2;
|
|
1364
|
-
try {
|
|
1365
|
-
role2 = (await rpc.getEffectiveRole(context))?.role ?? null;
|
|
1366
|
-
if (!role2) return { ok: false, error: "Not authorized for this session" };
|
|
1367
|
-
const metaRes = await rpc.getMetadata(context);
|
|
1368
|
-
cwd = metaRes?.metadata?.path;
|
|
1369
|
-
owner = metaRes?.metadata?.sharing?.owner;
|
|
1370
|
-
} catch {
|
|
1371
|
-
return { ok: false, error: "Not authorized for this session" };
|
|
1372
|
-
}
|
|
1373
|
-
try {
|
|
1374
|
-
const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
|
|
1375
|
-
const prepared = await initVoiceSession({ deps, config: { tools: toolsForRole(role2) }, sender: { name: senderName, kind: "user", verified: true }, voice: params.voice });
|
|
1376
|
-
return { ok: true, prepared, role: role2, scope: "session" };
|
|
1377
|
-
} catch (e) {
|
|
1378
|
-
return { ok: false, error: e?.message || "Failed to prepare voice session" };
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
const role = getEffectiveRole(context, currentMetadata.sharing);
|
|
1382
|
-
if (!role) return { ok: false, error: "Not authorized on this machine" };
|
|
1383
|
-
try {
|
|
1384
|
-
const machineDeps = buildMachineDeps(
|
|
1385
|
-
{
|
|
1386
|
-
getSessionIds: handlers.getSessionIds,
|
|
1387
|
-
getSessionRPCHandlers: handlers.getSessionRPCHandlers,
|
|
1388
|
-
getTrackedSessions: handlers.getTrackedSessions,
|
|
1389
|
-
spawnSession: handlers.spawnSession,
|
|
1390
|
-
getDaemonStatus: () => ({ status: currentDaemonState.status, machineId, pid: currentDaemonState.pid })
|
|
1391
|
-
},
|
|
1392
|
-
{ cwd: process.env.HOME, ownerEmail: currentMetadata.sharing?.owner }
|
|
1393
|
-
);
|
|
1394
|
-
const prepared = await initMachineVoiceSession({ deps: machineDeps, role, voice: params.voice });
|
|
1395
|
-
return { ok: true, prepared, role, scope: "machine" };
|
|
1396
|
-
} catch (e) {
|
|
1397
|
-
return { ok: false, error: e?.message || "Failed to prepare voice session" };
|
|
1398
|
-
}
|
|
1399
|
-
};
|
|
1400
1121
|
const removeListener = (listener, reason) => {
|
|
1401
1122
|
const idx = listeners.indexOf(listener);
|
|
1402
1123
|
if (idx >= 0) {
|
|
@@ -2332,7 +2053,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2332
2053
|
const tunnels = handlers.tunnels;
|
|
2333
2054
|
if (!tunnels) throw new Error("Tunnel management not available");
|
|
2334
2055
|
if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
|
|
2335
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
2056
|
+
const { FrpcTunnel } = await import('./frpc-glQKHUBu.mjs');
|
|
2336
2057
|
const tunnel = new FrpcTunnel({
|
|
2337
2058
|
name: params.name,
|
|
2338
2059
|
ports: params.ports,
|
|
@@ -2489,159 +2210,6 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2489
2210
|
return { success: false, error: error instanceof Error ? error.message : "Failed to create token" };
|
|
2490
2211
|
}
|
|
2491
2212
|
},
|
|
2492
|
-
// Attach the daemon's server-side control to an in-flight Realtime voice
|
|
2493
|
-
// session (browser holds the WebRTC audio leg). The browser passes the
|
|
2494
|
-
// `call_id` it got from OpenAI's SDP answer; the daemon opens the sideband
|
|
2495
|
-
// WS and runs WISE tools server-side, gated by the caller's session role.
|
|
2496
|
-
wiseAttachSideband: async (params, context) => {
|
|
2497
|
-
trackInbound();
|
|
2498
|
-
if (context && (!context.user || context.user.is_anonymous)) {
|
|
2499
|
-
return { success: false, error: "Sign in to use WISE voice." };
|
|
2500
|
-
}
|
|
2501
|
-
if (!params?.callId) return { success: false, error: "callId is required" };
|
|
2502
|
-
const resolved = resolveModel({ provider: "openai" }, process.env);
|
|
2503
|
-
const misconfig = describeMisconfiguration(resolved);
|
|
2504
|
-
if (misconfig || !resolved.apiKey) {
|
|
2505
|
-
return { success: false, error: misconfig || "WISE voice is not configured on this machine." };
|
|
2506
|
-
}
|
|
2507
|
-
const userKey = (context?.user?.email || context?.user?.id || "anon").toLowerCase();
|
|
2508
|
-
const prep = await prepareVoiceSession(params, context);
|
|
2509
|
-
if (!prep.ok) return { success: false, error: prep.error };
|
|
2510
|
-
const { prepared, role } = prep;
|
|
2511
|
-
const scope = params.sessionId ? `session ${params.sessionId}` : "machine-global";
|
|
2512
|
-
console.log(`[wise-voice] ATTACH request user=${userKey} scope=${scope} role=${role} callId=${params.callId.slice(0, 16)}\u2026 tools=[${(prepared.tools || []).map((t) => t.name).join(", ")}]`);
|
|
2513
|
-
const prior = voiceSidebands.get(userKey);
|
|
2514
|
-
if (prior) {
|
|
2515
|
-
console.log(`[wise-voice] superseding prior sideband for user=${userKey} (callId=${prior.callId.slice(0, 16)}\u2026) \u2014 closing to avoid a double bill`);
|
|
2516
|
-
prior.close();
|
|
2517
|
-
voiceSidebands.delete(userKey);
|
|
2518
|
-
}
|
|
2519
|
-
try {
|
|
2520
|
-
const ephemeralKey = await mintRealtimeEphemeralKey(resolved.baseUrl, resolved.apiKey, { model: params.model, voice: params.voice });
|
|
2521
|
-
const handle = await openSideband({
|
|
2522
|
-
callId: params.callId,
|
|
2523
|
-
apiKey: ephemeralKey,
|
|
2524
|
-
baseUrl: resolved.baseUrl,
|
|
2525
|
-
prepared,
|
|
2526
|
-
onClose: () => {
|
|
2527
|
-
if (voiceSidebands.get(userKey)?.callId === params.callId) voiceSidebands.delete(userKey);
|
|
2528
|
-
}
|
|
2529
|
-
});
|
|
2530
|
-
voiceSidebands.set(userKey, handle);
|
|
2531
|
-
console.log(`[wise-voice] ATTACH ok user=${userKey} scope=${scope} \u2014 sideband live (active sidebands: ${voiceSidebands.size})`);
|
|
2532
|
-
return { success: true, role, scope: params.sessionId ? "session" : "machine" };
|
|
2533
|
-
} catch (e) {
|
|
2534
|
-
console.error(`[wise-voice] ATTACH FAILED user=${userKey}: ${e?.message || e}`);
|
|
2535
|
-
return { success: false, error: e?.message || "Failed to attach voice sideband" };
|
|
2536
|
-
}
|
|
2537
|
-
},
|
|
2538
|
-
// UNIFIED INTERFACE (fixes the call_id 404): the DAEMON creates the WebRTC
|
|
2539
|
-
// call so it OWNS it and can attach the sideband WS. The browser sends its SDP
|
|
2540
|
-
// offer here (via the connect-time fetch reroute); we POST it to
|
|
2541
|
-
// /v1/realtime/calls with the machine key + WISE session config (tools +
|
|
2542
|
-
// instructions baked in), open the sideband to execute tools, and return the
|
|
2543
|
-
// SDP answer + call_id for the browser to finish the WebRTC handshake.
|
|
2544
|
-
wiseCreateCall: async (params, context) => {
|
|
2545
|
-
trackInbound();
|
|
2546
|
-
if (context && (!context.user || context.user.is_anonymous)) {
|
|
2547
|
-
return { success: false, error: "Sign in to use WISE voice." };
|
|
2548
|
-
}
|
|
2549
|
-
if (!params?.sdp) return { success: false, error: "sdp offer is required" };
|
|
2550
|
-
const resolved = resolveModel({ provider: "openai" }, process.env);
|
|
2551
|
-
const misconfig = describeMisconfiguration(resolved);
|
|
2552
|
-
if (misconfig || !resolved.apiKey) {
|
|
2553
|
-
return { success: false, error: misconfig || "WISE voice is not configured on this machine." };
|
|
2554
|
-
}
|
|
2555
|
-
const userKey = (context?.user?.email || context?.user?.id || "anon").toLowerCase();
|
|
2556
|
-
const prep = await prepareVoiceSession(params, context);
|
|
2557
|
-
if (!prep.ok) return { success: false, error: prep.error };
|
|
2558
|
-
const { prepared, role } = prep;
|
|
2559
|
-
const scope = params.sessionId ? `session ${params.sessionId}` : "machine-global";
|
|
2560
|
-
const model = params.model || "gpt-realtime-mini";
|
|
2561
|
-
console.log(`[wise-voice] CREATE-CALL request user=${userKey} scope=${scope} role=${role} model=${model} tools=[${(prepared.tools || []).map((t) => t.name).join(", ")}]`);
|
|
2562
|
-
const base = resolved.baseUrl || "https://api.openai.com";
|
|
2563
|
-
let answerSdp;
|
|
2564
|
-
let callId;
|
|
2565
|
-
try {
|
|
2566
|
-
const sessionConfig = { ...prepared.sessionUpdate.session, model };
|
|
2567
|
-
const fd = new FormData();
|
|
2568
|
-
fd.set("sdp", params.sdp);
|
|
2569
|
-
fd.set("session", JSON.stringify(sessionConfig));
|
|
2570
|
-
const ctrl = new AbortController();
|
|
2571
|
-
const timer = setTimeout(() => ctrl.abort(), 2e4);
|
|
2572
|
-
let resp;
|
|
2573
|
-
try {
|
|
2574
|
-
resp = await fetch(`${base}/v1/realtime/calls`, {
|
|
2575
|
-
method: "POST",
|
|
2576
|
-
headers: { "Authorization": `Bearer ${resolved.apiKey}` },
|
|
2577
|
-
body: fd,
|
|
2578
|
-
signal: ctrl.signal
|
|
2579
|
-
});
|
|
2580
|
-
} finally {
|
|
2581
|
-
clearTimeout(timer);
|
|
2582
|
-
}
|
|
2583
|
-
if (!resp.ok) {
|
|
2584
|
-
const body = await resp.text().catch(() => "");
|
|
2585
|
-
console.error(`[wise-voice] CREATE-CALL OpenAI error ${resp.status}: ${body.slice(0, 300)}`);
|
|
2586
|
-
return { success: false, error: `OpenAI call create failed (HTTP ${resp.status})${body ? `: ${body.slice(0, 160)}` : ""}` };
|
|
2587
|
-
}
|
|
2588
|
-
answerSdp = await resp.text();
|
|
2589
|
-
const loc = resp.headers.get("Location") || "";
|
|
2590
|
-
callId = loc.split("/").filter(Boolean).pop() || "";
|
|
2591
|
-
console.log(`[wise-voice] CREATE-CALL ok callId=${callId.slice(0, 16)}\u2026 (SDP answer ${answerSdp.length}b, Location="${loc}")`);
|
|
2592
|
-
} catch (e) {
|
|
2593
|
-
console.error(`[wise-voice] CREATE-CALL failed: ${e?.message || e}`);
|
|
2594
|
-
return { success: false, error: e?.message || "Failed to create voice call" };
|
|
2595
|
-
}
|
|
2596
|
-
let sidebandOk = false;
|
|
2597
|
-
let sidebandError;
|
|
2598
|
-
if (callId) {
|
|
2599
|
-
voiceSidebands.get(userKey)?.close();
|
|
2600
|
-
voiceSidebands.delete(userKey);
|
|
2601
|
-
try {
|
|
2602
|
-
const handle = await openSideband({
|
|
2603
|
-
callId,
|
|
2604
|
-
apiKey: resolved.apiKey,
|
|
2605
|
-
baseUrl: resolved.baseUrl,
|
|
2606
|
-
prepared,
|
|
2607
|
-
onClose: () => {
|
|
2608
|
-
if (voiceSidebands.get(userKey)?.callId === callId) voiceSidebands.delete(userKey);
|
|
2609
|
-
},
|
|
2610
|
-
// Push each tool's name/args/output to the browser (fire-and-forget)
|
|
2611
|
-
// so it can render accurate tool rows the SDK can't provide.
|
|
2612
|
-
onTool: params.onToolEvent ? (ev) => {
|
|
2613
|
-
try {
|
|
2614
|
-
const p = params.onToolEvent(ev);
|
|
2615
|
-
if (p && typeof p.catch === "function") p.catch(() => {
|
|
2616
|
-
});
|
|
2617
|
-
} catch {
|
|
2618
|
-
}
|
|
2619
|
-
} : void 0
|
|
2620
|
-
});
|
|
2621
|
-
voiceSidebands.set(userKey, handle);
|
|
2622
|
-
sidebandOk = true;
|
|
2623
|
-
console.log(`[wise-voice] CREATE-CALL sideband live user=${userKey} (active: ${voiceSidebands.size})`);
|
|
2624
|
-
} catch (e) {
|
|
2625
|
-
sidebandError = e?.message || String(e);
|
|
2626
|
-
console.error(`[wise-voice] CREATE-CALL sideband FAILED: ${sidebandError}`);
|
|
2627
|
-
}
|
|
2628
|
-
} else {
|
|
2629
|
-
sidebandError = "no call_id in OpenAI response";
|
|
2630
|
-
}
|
|
2631
|
-
return { success: true, sdp: answerSdp, callId, sidebandOk, sidebandError, role, scope: params.sessionId ? "session" : "machine" };
|
|
2632
|
-
},
|
|
2633
|
-
// Explicit teardown of the caller's active voice sideband (e.g. on hang-up,
|
|
2634
|
-
// Stop, tab close/unload). The frontend calls this so we don't wait for the
|
|
2635
|
-
// idle backstop — release the billable OpenAI session immediately.
|
|
2636
|
-
wiseReleaseVoice: async (_params, context) => {
|
|
2637
|
-
trackInbound();
|
|
2638
|
-
const userKey = (context?.user?.email || context?.user?.id || "anon").toLowerCase();
|
|
2639
|
-
const had = voiceSidebands.get(userKey);
|
|
2640
|
-
if (had) console.log(`[wise-voice] RELEASE user=${userKey} callId=${had.callId.slice(0, 16)}\u2026 (explicit hang-up)`);
|
|
2641
|
-
had?.close();
|
|
2642
|
-
voiceSidebands.delete(userKey);
|
|
2643
|
-
return { success: true };
|
|
2644
|
-
},
|
|
2645
2213
|
// Text WISE turn. GLOBAL (machine-manager) by default; pass sessionId to
|
|
2646
2214
|
// scope to a session. Runs server-side and returns the reply synchronously.
|
|
2647
2215
|
wiseAsk: async (params, context) => {
|
|
@@ -2673,8 +2241,8 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2673
2241
|
}
|
|
2674
2242
|
const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
|
|
2675
2243
|
const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
|
|
2676
|
-
const { toolsForRole
|
|
2677
|
-
const r2 = await runWiseAgent({ message: params.message, sender, config: { tools:
|
|
2244
|
+
const { toolsForRole } = await import('./sideband-7glSvpTW.mjs');
|
|
2245
|
+
const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
|
|
2678
2246
|
return fmt(r2);
|
|
2679
2247
|
}
|
|
2680
2248
|
const role = getEffectiveRole(context, currentMetadata.sharing);
|
|
@@ -2782,13 +2350,6 @@ ${d?.error || "not found"}`;
|
|
|
2782
2350
|
for (const listener of toRemove) {
|
|
2783
2351
|
removeListener(listener, "disconnect");
|
|
2784
2352
|
}
|
|
2785
|
-
for (const h of voiceSidebands.values()) {
|
|
2786
|
-
try {
|
|
2787
|
-
h.close();
|
|
2788
|
-
} catch {
|
|
2789
|
-
}
|
|
2790
|
-
}
|
|
2791
|
-
voiceSidebands.clear();
|
|
2792
2353
|
await server.unregisterService(serviceInfo.id);
|
|
2793
2354
|
await server.unregisterService(channelsServiceInfo.id).catch(() => {
|
|
2794
2355
|
});
|
|
@@ -10416,7 +9977,7 @@ async function startDaemon(options) {
|
|
|
10416
9977
|
const list = loadExposedTunnels().filter((t) => t.name !== name);
|
|
10417
9978
|
saveExposedTunnels(list);
|
|
10418
9979
|
}
|
|
10419
|
-
const { ServeManager } = await import('./serveManager-
|
|
9980
|
+
const { ServeManager } = await import('./serveManager-CoVwgYS_.mjs');
|
|
10420
9981
|
const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
|
|
10421
9982
|
ensureAutoInstalledSkills(logger).catch(() => {
|
|
10422
9983
|
});
|
|
@@ -13044,7 +12605,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
13044
12605
|
const specs = loadExposedTunnels();
|
|
13045
12606
|
if (specs.length === 0) return;
|
|
13046
12607
|
logger.log(`[exposed-tunnels] Restoring ${specs.length} tunnel(s) from ${EXPOSED_TUNNELS_FILE}`);
|
|
13047
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
12608
|
+
const { FrpcTunnel } = await import('./frpc-glQKHUBu.mjs');
|
|
13048
12609
|
for (const spec of specs) {
|
|
13049
12610
|
if (tunnels.has(spec.name)) continue;
|
|
13050
12611
|
try {
|
|
@@ -13816,4 +13377,4 @@ var run = /*#__PURE__*/Object.freeze({
|
|
|
13816
13377
|
writeStopMarker: writeStopMarker
|
|
13817
13378
|
});
|
|
13818
13379
|
|
|
13819
|
-
export {
|
|
13380
|
+
export { loadMachineContext as A, buildMachineInstructions as B, machineToolsForRole as C, buildMachineTools as D, resolveModel as E, normalizeAllowedUser as F, loadSecurityContextConfig as G, resolveSecurityContext as H, buildSecurityContextFromFlags as I, mergeSecurityContexts as J, buildSessionShareUrl as K, computeOutboundHop as L, buildMachineShareUrl as M, describeMisconfiguration as N, buildMachineDeps as O, generateHookSettings as P, DefaultTransport$1 as Q, RoutineStore as R, ServeAuth as S, acpBackend as T, acpAgentConfig as U, codexMcpBackend as V, GeminiTransport$1 as W, claudeAuth as X, instanceConfig as Y, api as Z, run as _, registerSessionService as a, stopDaemon as b, connectToHypha as c, daemonStatus as d, clearStopMarker as e, stopMarkerExists as f, getHyphaServerUrl$1 as g, getFrpsSubdomainHost as h, getFrpsServerPort as i, getFrpsServerAddr as j, getHyphaServerUrl as k, hasCookieToken as l, RoutineRunner as m, getSkillsServer as n, getSkillsWorkspaceName as o, parseFrontmatter as p, getSkillsCollectionName as q, registerMachineService as r, startDaemon as s, fetchWithTimeout as t, searchSkills as u, SKILLS_DIR as v, getSkillInfo as w, downloadSkillFile as x, listSkillFiles as y, READ_ONLY_TOOLS as z };
|
|
@@ -54,7 +54,7 @@ async function handleServeCommand() {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
async function serveAdd(args, machineId) {
|
|
57
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
57
|
+
const { connectAndGetMachine } = await import('./commands-D-m22ZpN.mjs');
|
|
58
58
|
const pos = positionalArgs(args);
|
|
59
59
|
const name = pos[0];
|
|
60
60
|
if (!name) {
|
|
@@ -93,7 +93,7 @@ async function serveAdd(args, machineId) {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
async function serveApply(args, machineId) {
|
|
96
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
96
|
+
const { connectAndGetMachine } = await import('./commands-D-m22ZpN.mjs');
|
|
97
97
|
const fs = await import('fs');
|
|
98
98
|
const yaml = await import('yaml');
|
|
99
99
|
const file = positionalArgs(args)[0];
|
|
@@ -182,7 +182,7 @@ async function serveApply(args, machineId) {
|
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
async function serveRemove(args, machineId) {
|
|
185
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
185
|
+
const { connectAndGetMachine } = await import('./commands-D-m22ZpN.mjs');
|
|
186
186
|
const pos = positionalArgs(args);
|
|
187
187
|
const name = pos[0];
|
|
188
188
|
if (!name) {
|
|
@@ -202,7 +202,7 @@ async function serveRemove(args, machineId) {
|
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
async function serveList(args, machineId) {
|
|
205
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
205
|
+
const { connectAndGetMachine } = await import('./commands-D-m22ZpN.mjs');
|
|
206
206
|
const all = hasFlag(args, "--all", "-a");
|
|
207
207
|
const json = hasFlag(args, "--json");
|
|
208
208
|
const sessionId = getFlag(args, "--session");
|
|
@@ -235,7 +235,7 @@ async function serveList(args, machineId) {
|
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
async function serveInfo(machineId) {
|
|
238
|
-
const { connectAndGetMachine } = await import('./commands-
|
|
238
|
+
const { connectAndGetMachine } = await import('./commands-D-m22ZpN.mjs');
|
|
239
239
|
const { machine, server } = await connectAndGetMachine(machineId);
|
|
240
240
|
try {
|
|
241
241
|
const info = await machine.serveInfo();
|
|
@@ -4,13 +4,12 @@ import * as fs from 'fs';
|
|
|
4
4
|
import * as http from 'http';
|
|
5
5
|
import * as net from 'net';
|
|
6
6
|
import * as path from 'path';
|
|
7
|
-
import { k as getHyphaServerUrl, S as ServeAuth, l as hasCookieToken } from './run-
|
|
7
|
+
import { k as getHyphaServerUrl, S as ServeAuth, l as hasCookieToken } from './run-CvIUzIMP.mjs';
|
|
8
8
|
import 'os';
|
|
9
9
|
import 'fs/promises';
|
|
10
10
|
import 'url';
|
|
11
11
|
import 'node:fs';
|
|
12
12
|
import 'node:child_process';
|
|
13
|
-
import 'ws';
|
|
14
13
|
import 'util';
|
|
15
14
|
import 'node:crypto';
|
|
16
15
|
import 'node:path';
|
|
@@ -713,7 +712,7 @@ class ServeManager {
|
|
|
713
712
|
const mount = this.mounts.get(mountName);
|
|
714
713
|
const subdomainOverride = mount?.access === "link" && mount.linkToken ? /* @__PURE__ */ new Map([[this.port, `static-${subdomainSafe}-${mount.linkToken}`]]) : void 0;
|
|
715
714
|
try {
|
|
716
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
715
|
+
const { FrpcTunnel } = await import('./frpc-glQKHUBu.mjs');
|
|
717
716
|
let tunnel;
|
|
718
717
|
tunnel = new FrpcTunnel({
|
|
719
718
|
name: tunnelName,
|