svamp-cli 0.2.225 → 0.2.227
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-7sqKMVIC.mjs +526 -0
- package/dist/auth-BfDZbHl6.mjs +83 -0
- package/dist/caddy-CuTbE3NY.mjs +322 -0
- package/dist/cli.mjs +2338 -0
- package/dist/commands-CU8JDM2d.mjs +428 -0
- package/dist/commands-CWP-J9Cf.mjs +610 -0
- package/dist/commands-CnlQyDxb.mjs +2762 -0
- package/dist/commands-D2iQ2v7_.mjs +196 -0
- package/dist/commands-DA_Nx893.mjs +265 -0
- package/dist/commands-H9h_Mtc4.mjs +493 -0
- package/dist/commands-YML8Rs_R.mjs +643 -0
- package/dist/fleet-B_78L11T.mjs +356 -0
- package/dist/frpc-BQyt6j5A.mjs +681 -0
- package/dist/headlessCli-CYfN9XvR.mjs +333 -0
- package/dist/httpServer-B1KVQJfm.mjs +276 -0
- package/dist/index.mjs +22 -0
- package/dist/package-VYP3g593.mjs +63 -0
- package/dist/pinnedClaudeCode-VIupR1NK.mjs +58 -0
- package/dist/rpc-DMj8ZJPz.mjs +92 -0
- package/dist/rpc-a0fp9HhD.mjs +194 -0
- package/dist/run-DkjwFIFH.mjs +1086 -0
- package/dist/run-LEPH8aka.mjs +16075 -0
- package/dist/runStore-CtptN7US.mjs +168 -0
- package/dist/sandboxDetect-DNTcbgWD.mjs +12 -0
- package/dist/scheduler-Bz80Ipep.mjs +102 -0
- package/dist/serveCommands-u_GoFaBX.mjs +310 -0
- package/dist/serveManager-9vhexH1L.mjs +781 -0
- package/dist/serviceManager-hlOVxkhW.mjs +78 -0
- package/dist/sideband-RJijk2GE.mjs +80 -0
- package/dist/store-BTs0H_y0.mjs +148 -0
- package/dist/supervisorLock-DmfzJx7B.mjs +159 -0
- package/package.json +1 -1
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync, renameSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import { requireNotSandboxed } from './sandboxDetect-DNTcbgWD.mjs';
|
|
5
|
+
import { A as shortId } from './run-LEPH8aka.mjs';
|
|
6
|
+
import 'os';
|
|
7
|
+
import 'fs/promises';
|
|
8
|
+
import 'fs';
|
|
9
|
+
import 'path';
|
|
10
|
+
import 'url';
|
|
11
|
+
import 'child_process';
|
|
12
|
+
import 'crypto';
|
|
13
|
+
import 'node:crypto';
|
|
14
|
+
import 'node:child_process';
|
|
15
|
+
import 'util';
|
|
16
|
+
import 'node:events';
|
|
17
|
+
import '@agentclientprotocol/sdk';
|
|
18
|
+
import '@modelcontextprotocol/sdk/client/index.js';
|
|
19
|
+
import '@modelcontextprotocol/sdk/client/stdio.js';
|
|
20
|
+
import '@modelcontextprotocol/sdk/types.js';
|
|
21
|
+
import 'zod';
|
|
22
|
+
import 'node:fs/promises';
|
|
23
|
+
import 'node:util';
|
|
24
|
+
|
|
25
|
+
const SVAMP_HOME = process.env.SVAMP_HOME || join(os.homedir(), ".svamp");
|
|
26
|
+
function getConfigPath(sessionId) {
|
|
27
|
+
const cwd = process.cwd();
|
|
28
|
+
return join(cwd, ".svamp", sessionId, "config.json");
|
|
29
|
+
}
|
|
30
|
+
function readConfig(configPath) {
|
|
31
|
+
try {
|
|
32
|
+
if (existsSync(configPath)) return JSON.parse(readFileSync(configPath, "utf-8"));
|
|
33
|
+
} catch {
|
|
34
|
+
}
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
function writeConfig(configPath, config) {
|
|
38
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
39
|
+
const tmp = configPath + ".tmp";
|
|
40
|
+
writeFileSync(tmp, JSON.stringify(config, null, 2));
|
|
41
|
+
renameSync(tmp, configPath);
|
|
42
|
+
}
|
|
43
|
+
async function connectAndEmit(event) {
|
|
44
|
+
const ENV_FILE = join(SVAMP_HOME, ".env");
|
|
45
|
+
if (existsSync(ENV_FILE)) {
|
|
46
|
+
const lines = readFileSync(ENV_FILE, "utf-8").split("\n");
|
|
47
|
+
for (const line of lines) {
|
|
48
|
+
const m = line.match(/^([A-Z_]+)=(.*)/);
|
|
49
|
+
if (m && !process.env[m[1]]) process.env[m[1]] = m[2].replace(/^["']|["']$/g, "");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const serverUrl = process.env.HYPHA_SERVER_URL;
|
|
53
|
+
const token = process.env.HYPHA_TOKEN;
|
|
54
|
+
if (!serverUrl || !token) {
|
|
55
|
+
console.error('No Hypha credentials. Run "svamp login" first.');
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
const origLog = console.log;
|
|
59
|
+
const origWarn = console.warn;
|
|
60
|
+
const origInfo = console.info;
|
|
61
|
+
console.log = () => {
|
|
62
|
+
};
|
|
63
|
+
console.warn = () => {
|
|
64
|
+
};
|
|
65
|
+
console.info = () => {
|
|
66
|
+
};
|
|
67
|
+
let server;
|
|
68
|
+
try {
|
|
69
|
+
const mod = await import('hypha-rpc');
|
|
70
|
+
const connectToServer = mod.connectToServer || mod.default?.connectToServer;
|
|
71
|
+
server = await connectToServer({ server_url: serverUrl, token, name: "svamp-agent-emit" });
|
|
72
|
+
} catch (err) {
|
|
73
|
+
console.log = origLog;
|
|
74
|
+
console.warn = origWarn;
|
|
75
|
+
console.info = origInfo;
|
|
76
|
+
console.error(`Failed to connect to Hypha: ${err.message}`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
console.log = origLog;
|
|
80
|
+
console.warn = origWarn;
|
|
81
|
+
console.info = origInfo;
|
|
82
|
+
await server.emit({ ...event, to: "*" });
|
|
83
|
+
await server.disconnect();
|
|
84
|
+
}
|
|
85
|
+
async function sessionSetTitle(title) {
|
|
86
|
+
const sessionId = process.env.SVAMP_SESSION_ID;
|
|
87
|
+
if (!sessionId) {
|
|
88
|
+
console.error("SVAMP_SESSION_ID not set. This command must be run inside a Svamp session.");
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
const configPath = getConfigPath(sessionId);
|
|
92
|
+
const config = readConfig(configPath);
|
|
93
|
+
config.title = title.trim();
|
|
94
|
+
writeConfig(configPath, config);
|
|
95
|
+
console.log(`Session title set: "${title.trim()}"`);
|
|
96
|
+
}
|
|
97
|
+
async function sessionSetProjectDescription(description) {
|
|
98
|
+
const dir = process.cwd();
|
|
99
|
+
const { projectName, writeProjectInfo, sanitizeDescription, projectInfoPath } = await import('./run-LEPH8aka.mjs').then(function (n) { return n.a9; });
|
|
100
|
+
const desc = sanitizeDescription(description, 240);
|
|
101
|
+
if (!desc) {
|
|
102
|
+
console.error("Project description is empty.");
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
const name = projectName(dir);
|
|
106
|
+
writeProjectInfo(dir, { name, description: desc, source: "manual", updatedAt: Date.now() });
|
|
107
|
+
console.log(`Project description set for "${name}": "${desc}"`);
|
|
108
|
+
console.log(`Shared across all sessions in this project (${projectInfoPath(dir)}).`);
|
|
109
|
+
}
|
|
110
|
+
async function sessionSetLink(url, label) {
|
|
111
|
+
const sessionId = process.env.SVAMP_SESSION_ID;
|
|
112
|
+
if (!sessionId) {
|
|
113
|
+
console.error("SVAMP_SESSION_ID not set. This command must be run inside a Svamp session.");
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
if (!url || !url.trim()) {
|
|
117
|
+
console.error("Usage: svamp session set-link <url> [label]");
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
const configPath = getConfigPath(sessionId);
|
|
121
|
+
const config = readConfig(configPath);
|
|
122
|
+
const before = readSessionLinks(config);
|
|
123
|
+
const next = upsertSessionLink(before, { url, label });
|
|
124
|
+
writeSessionLinks(configPath, config, next);
|
|
125
|
+
const link = next.find((l) => linkDedupKey(l.url) === linkDedupKey(url));
|
|
126
|
+
const verb = next.length === before.length ? "updated" : "added";
|
|
127
|
+
console.log(`Launch Pad: ${verb} "${link.label}" \u2192 ${link.url} [${link.id}]`);
|
|
128
|
+
}
|
|
129
|
+
function shortLinkId() {
|
|
130
|
+
let s = "";
|
|
131
|
+
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
132
|
+
const seed = Date.now() % 1e9 + Math.floor(Math.random() * 1e6);
|
|
133
|
+
let n = seed;
|
|
134
|
+
for (let i = 0; i < 6; i++) {
|
|
135
|
+
s += chars[n % 36];
|
|
136
|
+
n = Math.floor(n / 36) + (i + 1) * 7;
|
|
137
|
+
}
|
|
138
|
+
return s;
|
|
139
|
+
}
|
|
140
|
+
function readSessionLinks(config) {
|
|
141
|
+
if (Array.isArray(config.session_links)) {
|
|
142
|
+
return config.session_links.filter((l) => l && typeof l.url === "string").map((l) => ({
|
|
143
|
+
id: String(l.id || shortLinkId()),
|
|
144
|
+
label: String(l.label || ""),
|
|
145
|
+
url: String(l.url),
|
|
146
|
+
...l.icon ? { icon: String(l.icon) } : {}
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
if (config.session_link && typeof config.session_link.url === "string") {
|
|
150
|
+
return [{ id: "primary", label: String(config.session_link.label || "View"), url: String(config.session_link.url) }];
|
|
151
|
+
}
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
function writeSessionLinks(configPath, config, links) {
|
|
155
|
+
config.session_links = links;
|
|
156
|
+
config.session_link = links[0] ? { url: links[0].url, label: links[0].label } : void 0;
|
|
157
|
+
writeConfig(configPath, config);
|
|
158
|
+
}
|
|
159
|
+
function labelFor(url, label) {
|
|
160
|
+
return label?.trim() || (() => {
|
|
161
|
+
try {
|
|
162
|
+
return new URL(url).hostname.replace(/^www\./, "");
|
|
163
|
+
} catch {
|
|
164
|
+
return "Link";
|
|
165
|
+
}
|
|
166
|
+
})();
|
|
167
|
+
}
|
|
168
|
+
function linkDedupKey(url) {
|
|
169
|
+
const u = (url || "").trim();
|
|
170
|
+
try {
|
|
171
|
+
const p = new URL(u);
|
|
172
|
+
p.hostname = p.hostname.toLowerCase();
|
|
173
|
+
let s = p.toString();
|
|
174
|
+
if (s.endsWith("/") && !p.search && !p.hash) s = s.slice(0, -1);
|
|
175
|
+
return s;
|
|
176
|
+
} catch {
|
|
177
|
+
return u.replace(/\/+$/, "");
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function upsertSessionLink(links, incoming) {
|
|
181
|
+
const url = (incoming.url || "").trim();
|
|
182
|
+
const key = linkDedupKey(url);
|
|
183
|
+
const next = links.slice();
|
|
184
|
+
const idx = next.findIndex((l) => linkDedupKey(l.url) === key);
|
|
185
|
+
const icon = incoming.icon?.trim();
|
|
186
|
+
if (idx >= 0) {
|
|
187
|
+
const existing = next[idx];
|
|
188
|
+
next[idx] = {
|
|
189
|
+
id: existing.id,
|
|
190
|
+
url,
|
|
191
|
+
label: labelFor(url, incoming.label),
|
|
192
|
+
...icon ? { icon } : existing.icon ? { icon: existing.icon } : {}
|
|
193
|
+
};
|
|
194
|
+
} else {
|
|
195
|
+
next.push({ id: shortLinkId(), url, label: labelFor(url, incoming.label), ...icon ? { icon } : {} });
|
|
196
|
+
}
|
|
197
|
+
return next;
|
|
198
|
+
}
|
|
199
|
+
function autoAddSessionLink(url, label, icon) {
|
|
200
|
+
const sessionId = process.env.SVAMP_SESSION_ID;
|
|
201
|
+
if (!sessionId || !url || !url.trim()) return false;
|
|
202
|
+
try {
|
|
203
|
+
const configPath = getConfigPath(sessionId);
|
|
204
|
+
const config = readConfig(configPath);
|
|
205
|
+
const next = upsertSessionLink(readSessionLinks(config), { url, label, icon });
|
|
206
|
+
writeSessionLinks(configPath, config, next);
|
|
207
|
+
return true;
|
|
208
|
+
} catch {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function requireSessionId() {
|
|
213
|
+
const sessionId = process.env.SVAMP_SESSION_ID;
|
|
214
|
+
if (!sessionId) {
|
|
215
|
+
console.error("SVAMP_SESSION_ID not set. This command must be run inside a Svamp session.");
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
return sessionId;
|
|
219
|
+
}
|
|
220
|
+
async function sessionLinkAdd(url, label, icon) {
|
|
221
|
+
const sessionId = requireSessionId();
|
|
222
|
+
if (!url || !url.trim()) {
|
|
223
|
+
console.error("Usage: svamp session link add <url> [--label L] [--icon I]");
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
226
|
+
const configPath = getConfigPath(sessionId);
|
|
227
|
+
const config = readConfig(configPath);
|
|
228
|
+
const before = readSessionLinks(config);
|
|
229
|
+
const next = upsertSessionLink(before, { url, label, icon });
|
|
230
|
+
writeSessionLinks(configPath, config, next);
|
|
231
|
+
const link = next.find((l) => linkDedupKey(l.url) === linkDedupKey(url));
|
|
232
|
+
const verb = next.length === before.length ? "updated" : "added";
|
|
233
|
+
console.log(`Launch Pad: ${verb} "${link.label}" \u2192 ${link.url} [${link.id}]`);
|
|
234
|
+
}
|
|
235
|
+
async function sessionLinkList() {
|
|
236
|
+
const sessionId = requireSessionId();
|
|
237
|
+
const links = readSessionLinks(readConfig(getConfigPath(sessionId)));
|
|
238
|
+
if (!links.length) {
|
|
239
|
+
console.log('No session links. Add one: svamp session link add <url> --label "Name"');
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
for (const l of links) console.log(` ${l.icon ? l.icon + " " : ""}${l.label} ${l.url} [${l.id}]`);
|
|
243
|
+
}
|
|
244
|
+
async function sessionLinkUpdate(id, opts) {
|
|
245
|
+
const sessionId = requireSessionId();
|
|
246
|
+
const configPath = getConfigPath(sessionId);
|
|
247
|
+
const config = readConfig(configPath);
|
|
248
|
+
const links = readSessionLinks(config);
|
|
249
|
+
const link = links.find((l) => l.id === id);
|
|
250
|
+
if (!link) {
|
|
251
|
+
console.error(`No link with id "${id}". Run: svamp session link list`);
|
|
252
|
+
process.exit(1);
|
|
253
|
+
}
|
|
254
|
+
if (opts.url?.trim()) link.url = opts.url.trim();
|
|
255
|
+
if (opts.label !== void 0) link.label = labelFor(link.url, opts.label);
|
|
256
|
+
if (opts.icon !== void 0) {
|
|
257
|
+
if (opts.icon.trim()) link.icon = opts.icon.trim();
|
|
258
|
+
else delete link.icon;
|
|
259
|
+
}
|
|
260
|
+
writeSessionLinks(configPath, config, links);
|
|
261
|
+
console.log(`Launch Pad: updated [${id}] \u2192 "${link.label}" ${link.url}`);
|
|
262
|
+
}
|
|
263
|
+
async function sessionLinkRemove(id) {
|
|
264
|
+
const sessionId = requireSessionId();
|
|
265
|
+
const configPath = getConfigPath(sessionId);
|
|
266
|
+
const config = readConfig(configPath);
|
|
267
|
+
const links = readSessionLinks(config);
|
|
268
|
+
const next = links.filter((l) => l.id !== id);
|
|
269
|
+
if (next.length === links.length) {
|
|
270
|
+
console.error(`No link with id "${id}". Run: svamp session link list`);
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
writeSessionLinks(configPath, config, next);
|
|
274
|
+
console.log(`Launch Pad: removed [${id}]${next.length ? "" : " (Launch Pad now empty)"}`);
|
|
275
|
+
}
|
|
276
|
+
async function sessionNotify(message, level = "info") {
|
|
277
|
+
requireNotSandboxed("session notify");
|
|
278
|
+
const sessionId = process.env.SVAMP_SESSION_ID;
|
|
279
|
+
if (!sessionId) {
|
|
280
|
+
console.error("SVAMP_SESSION_ID not set. This command must be run inside a Svamp session.");
|
|
281
|
+
process.exit(1);
|
|
282
|
+
}
|
|
283
|
+
await connectAndEmit({
|
|
284
|
+
type: "svamp:session-notify",
|
|
285
|
+
data: { sessionId, message, level, timestamp: Date.now() }
|
|
286
|
+
});
|
|
287
|
+
console.log(`Notification sent [${level}]: ${message}`);
|
|
288
|
+
}
|
|
289
|
+
async function sessionBroadcast(action, args) {
|
|
290
|
+
requireNotSandboxed("session broadcast");
|
|
291
|
+
const sessionId = process.env.SVAMP_SESSION_ID;
|
|
292
|
+
if (!sessionId) {
|
|
293
|
+
console.error("SVAMP_SESSION_ID not set. This command must be run inside a Svamp session.");
|
|
294
|
+
process.exit(1);
|
|
295
|
+
}
|
|
296
|
+
let payload = { action };
|
|
297
|
+
if (action === "open-canvas") {
|
|
298
|
+
const url = args[0];
|
|
299
|
+
if (!url) {
|
|
300
|
+
console.error("Usage: svamp session broadcast open-canvas <url> [label]");
|
|
301
|
+
process.exit(1);
|
|
302
|
+
}
|
|
303
|
+
const label = args[1] || (() => {
|
|
304
|
+
try {
|
|
305
|
+
return new URL(url).hostname;
|
|
306
|
+
} catch {
|
|
307
|
+
return "View";
|
|
308
|
+
}
|
|
309
|
+
})();
|
|
310
|
+
payload = { action, url, label };
|
|
311
|
+
} else if (action === "close-canvas") ; else if (action === "toast") {
|
|
312
|
+
const message = args[0];
|
|
313
|
+
if (!message) {
|
|
314
|
+
console.error("Usage: svamp session broadcast toast <message>");
|
|
315
|
+
process.exit(1);
|
|
316
|
+
}
|
|
317
|
+
payload = { action, message };
|
|
318
|
+
} else {
|
|
319
|
+
console.error(`Unknown broadcast action: ${action}`);
|
|
320
|
+
console.error("Available actions: open-canvas, close-canvas, toast");
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
await connectAndEmit({
|
|
324
|
+
type: "svamp:session-broadcast",
|
|
325
|
+
data: { sessionId, ...payload }
|
|
326
|
+
});
|
|
327
|
+
console.log(`Broadcast sent: ${action}`);
|
|
328
|
+
}
|
|
329
|
+
async function connectToMachineService() {
|
|
330
|
+
const { connectAndGetMachine } = await import('./commands-CnlQyDxb.mjs');
|
|
331
|
+
return connectAndGetMachine();
|
|
332
|
+
}
|
|
333
|
+
function buildInboxMessage(args) {
|
|
334
|
+
return {
|
|
335
|
+
messageId: shortId(),
|
|
336
|
+
body: args.body,
|
|
337
|
+
timestamp: Date.now(),
|
|
338
|
+
read: false,
|
|
339
|
+
from: `agent:${args.fromSessionId}`,
|
|
340
|
+
fromSession: args.fromSessionId,
|
|
341
|
+
to: args.to,
|
|
342
|
+
subject: args.subject,
|
|
343
|
+
urgency: args.urgency || "normal",
|
|
344
|
+
...args.replyTo ? { replyTo: args.replyTo } : {},
|
|
345
|
+
...args.threadId ? { threadId: args.threadId } : {}
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
async function inboxSendCore(resolve, args) {
|
|
349
|
+
const { machine, fullId } = await resolve(args.target);
|
|
350
|
+
const message = buildInboxMessage({
|
|
351
|
+
fromSessionId: args.fromSessionId,
|
|
352
|
+
to: fullId,
|
|
353
|
+
body: args.body,
|
|
354
|
+
subject: args.subject,
|
|
355
|
+
urgency: args.urgency
|
|
356
|
+
});
|
|
357
|
+
const result = await machine.sessionRPC(fullId, "sendInboxMessage", { message });
|
|
358
|
+
return { targetId: fullId, messageId: result.messageId, message };
|
|
359
|
+
}
|
|
360
|
+
async function inboxReplyCore(localMachine, resolve, args) {
|
|
361
|
+
const result = await localMachine.sessionRPC(args.sessionId, "getInbox", {});
|
|
362
|
+
const original = (result.messages || []).find(
|
|
363
|
+
(m) => m.messageId === args.messageId || m.messageId.startsWith(args.messageId)
|
|
364
|
+
);
|
|
365
|
+
if (!original) return { kind: "not-found" };
|
|
366
|
+
let channelDelivered = false;
|
|
367
|
+
if (original.channelId && original.correlationId) {
|
|
368
|
+
const rr = await localMachine.sessionRPC(args.sessionId, "channelReply", {
|
|
369
|
+
params: { channel: original.channelId, correlationId: original.correlationId, to: original.from, body: args.body }
|
|
370
|
+
});
|
|
371
|
+
if (rr?.error) {
|
|
372
|
+
if (!original.fromSession) return { kind: "channel-error", error: rr.error };
|
|
373
|
+
} else channelDelivered = true;
|
|
374
|
+
}
|
|
375
|
+
if (original.fromSession) {
|
|
376
|
+
try {
|
|
377
|
+
const { machine, fullId } = await resolve(original.fromSession);
|
|
378
|
+
const reply = buildInboxMessage({
|
|
379
|
+
fromSessionId: args.sessionId,
|
|
380
|
+
to: fullId,
|
|
381
|
+
body: args.body,
|
|
382
|
+
subject: original.subject ? `Re: ${original.subject}` : void 0,
|
|
383
|
+
replyTo: original.messageId,
|
|
384
|
+
threadId: original.threadId || original.messageId
|
|
385
|
+
});
|
|
386
|
+
const sendResult = await machine.sessionRPC(fullId, "sendInboxMessage", { message: reply });
|
|
387
|
+
return { kind: "sent", targetId: fullId, messageId: sendResult.messageId, message: reply };
|
|
388
|
+
} catch (err) {
|
|
389
|
+
if (channelDelivered) return { kind: "channel", from: original.from, channelId: original.channelId, correlationId: original.correlationId };
|
|
390
|
+
throw err;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (channelDelivered) return { kind: "channel", from: original.from, channelId: original.channelId, correlationId: original.correlationId };
|
|
394
|
+
return { kind: "no-from-session" };
|
|
395
|
+
}
|
|
396
|
+
async function inboxSend(targetSessionId, opts) {
|
|
397
|
+
requireNotSandboxed("inbox send");
|
|
398
|
+
const sessionId = process.env.SVAMP_SESSION_ID;
|
|
399
|
+
if (!sessionId) {
|
|
400
|
+
console.error("SVAMP_SESSION_ID not set. This command must be run inside a Svamp session.");
|
|
401
|
+
process.exit(1);
|
|
402
|
+
}
|
|
403
|
+
const body = opts?.body || "";
|
|
404
|
+
if (!body) {
|
|
405
|
+
console.error("Message body is required.");
|
|
406
|
+
process.exit(1);
|
|
407
|
+
}
|
|
408
|
+
const { connectAndResolveSession } = await import('./commands-CnlQyDxb.mjs');
|
|
409
|
+
let server;
|
|
410
|
+
try {
|
|
411
|
+
const { targetId, messageId } = await inboxSendCore(
|
|
412
|
+
async (target) => {
|
|
413
|
+
const r = await connectAndResolveSession(target);
|
|
414
|
+
server = r.server;
|
|
415
|
+
return { machine: r.machine, fullId: r.fullId };
|
|
416
|
+
},
|
|
417
|
+
{ fromSessionId: sessionId, target: targetSessionId, body, subject: opts?.subject, urgency: opts?.urgency }
|
|
418
|
+
);
|
|
419
|
+
console.log(`Inbox message sent to ${targetId.slice(0, 8)} (id: ${messageId.slice(0, 8)})`);
|
|
420
|
+
} finally {
|
|
421
|
+
if (server) await server.disconnect();
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
async function inboxList(opts) {
|
|
425
|
+
requireNotSandboxed("inbox list");
|
|
426
|
+
const sessionId = process.env.SVAMP_SESSION_ID;
|
|
427
|
+
if (!sessionId) {
|
|
428
|
+
console.error("SVAMP_SESSION_ID not set. This command must be run inside a Svamp session.");
|
|
429
|
+
process.exit(1);
|
|
430
|
+
}
|
|
431
|
+
const { server, machine } = await connectToMachineService();
|
|
432
|
+
try {
|
|
433
|
+
const result = await machine.sessionRPC(sessionId, "getInbox", { opts: { unread: opts?.unread, limit: opts?.limit } });
|
|
434
|
+
const messages = result.messages;
|
|
435
|
+
if (opts?.json) {
|
|
436
|
+
console.log(JSON.stringify({ messages }, null, 2));
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
if (messages.length === 0) {
|
|
440
|
+
console.log("Inbox is empty.");
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
for (const msg of messages) {
|
|
444
|
+
const status = msg.read ? " " : "\u25CF";
|
|
445
|
+
const from = msg.from ? ` from ${msg.from}` : "";
|
|
446
|
+
const subject = msg.subject ? ` \u2014 ${msg.subject}` : "";
|
|
447
|
+
const urgencyTag = msg.urgency === "urgent" ? " [URGENT]" : "";
|
|
448
|
+
const preview = msg.body.length > 100 ? msg.body.slice(0, 97) + "..." : msg.body;
|
|
449
|
+
console.log(`${status} ${msg.messageId.slice(0, 8)}${urgencyTag}${from}${subject}: ${preview}`);
|
|
450
|
+
}
|
|
451
|
+
} finally {
|
|
452
|
+
await server.disconnect();
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
async function inboxReply(messageId, body) {
|
|
456
|
+
requireNotSandboxed("inbox reply");
|
|
457
|
+
const sessionId = process.env.SVAMP_SESSION_ID;
|
|
458
|
+
if (!sessionId) {
|
|
459
|
+
console.error("SVAMP_SESSION_ID not set. This command must be run inside a Svamp session.");
|
|
460
|
+
process.exit(1);
|
|
461
|
+
}
|
|
462
|
+
const { connectAndResolveSession } = await import('./commands-CnlQyDxb.mjs');
|
|
463
|
+
const { server: localServer, machine: localMachine } = await connectToMachineService();
|
|
464
|
+
let localDisconnected = false;
|
|
465
|
+
const disconnectLocal = async () => {
|
|
466
|
+
if (!localDisconnected) {
|
|
467
|
+
localDisconnected = true;
|
|
468
|
+
try {
|
|
469
|
+
await localServer.disconnect();
|
|
470
|
+
} catch {
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
let senderServer;
|
|
475
|
+
try {
|
|
476
|
+
const res = await inboxReplyCore(
|
|
477
|
+
localMachine,
|
|
478
|
+
async (target) => {
|
|
479
|
+
await disconnectLocal();
|
|
480
|
+
const r = await connectAndResolveSession(target);
|
|
481
|
+
senderServer = r.server;
|
|
482
|
+
return { machine: r.machine, fullId: r.fullId };
|
|
483
|
+
},
|
|
484
|
+
{ sessionId, messageId, body }
|
|
485
|
+
);
|
|
486
|
+
switch (res.kind) {
|
|
487
|
+
case "not-found":
|
|
488
|
+
console.error(`Message ${messageId} not found in inbox.`);
|
|
489
|
+
process.exit(1);
|
|
490
|
+
break;
|
|
491
|
+
case "no-from-session":
|
|
492
|
+
console.error("Cannot reply: original message has no fromSession (and not a channel message).");
|
|
493
|
+
process.exit(1);
|
|
494
|
+
break;
|
|
495
|
+
case "channel-error":
|
|
496
|
+
console.error(`Channel reply failed: ${res.error}`);
|
|
497
|
+
process.exit(1);
|
|
498
|
+
break;
|
|
499
|
+
case "channel":
|
|
500
|
+
console.log(`Reply queued to "${res.from}" on channel ${res.channelId} (correlation ${res.correlationId}).`);
|
|
501
|
+
break;
|
|
502
|
+
case "sent":
|
|
503
|
+
console.log(`Reply sent to ${res.targetId.slice(0, 8)} (id: ${res.messageId.slice(0, 8)})`);
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
} finally {
|
|
507
|
+
await disconnectLocal();
|
|
508
|
+
if (senderServer) {
|
|
509
|
+
try {
|
|
510
|
+
await senderServer.disconnect();
|
|
511
|
+
} catch {
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
async function machineNotify(message, level = "info") {
|
|
517
|
+
requireNotSandboxed("machine notify");
|
|
518
|
+
const machineId = process.env.SVAMP_MACHINE_ID;
|
|
519
|
+
await connectAndEmit({
|
|
520
|
+
type: "svamp:machine-notify",
|
|
521
|
+
data: { machineId, message, level, timestamp: Date.now() }
|
|
522
|
+
});
|
|
523
|
+
console.log(`Machine notification sent [${level}]: ${message}`);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export { autoAddSessionLink, buildInboxMessage, inboxList, inboxReply, inboxReplyCore, inboxSend, inboxSendCore, linkDedupKey, machineNotify, sessionBroadcast, sessionLinkAdd, sessionLinkList, sessionLinkRemove, sessionLinkUpdate, sessionNotify, sessionSetLink, sessionSetProjectDescription, sessionSetTitle, upsertSessionLink };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { P as resolveModel } from './run-LEPH8aka.mjs';
|
|
2
|
+
import 'os';
|
|
3
|
+
import 'fs/promises';
|
|
4
|
+
import 'fs';
|
|
5
|
+
import 'path';
|
|
6
|
+
import 'url';
|
|
7
|
+
import 'child_process';
|
|
8
|
+
import 'crypto';
|
|
9
|
+
import 'node:crypto';
|
|
10
|
+
import 'node:fs';
|
|
11
|
+
import 'node:child_process';
|
|
12
|
+
import 'util';
|
|
13
|
+
import 'node:path';
|
|
14
|
+
import 'node:events';
|
|
15
|
+
import 'node:os';
|
|
16
|
+
import '@agentclientprotocol/sdk';
|
|
17
|
+
import '@modelcontextprotocol/sdk/client/index.js';
|
|
18
|
+
import '@modelcontextprotocol/sdk/client/stdio.js';
|
|
19
|
+
import '@modelcontextprotocol/sdk/types.js';
|
|
20
|
+
import 'zod';
|
|
21
|
+
import 'node:fs/promises';
|
|
22
|
+
import 'node:util';
|
|
23
|
+
|
|
24
|
+
function buildWiseAgentEnvUpdates(action) {
|
|
25
|
+
switch (action.kind) {
|
|
26
|
+
case "use-openai":
|
|
27
|
+
return { WISE_AGENT_PROVIDER: "openai", ...action.key ? { WISE_AGENT_API_KEY: action.key } : {} };
|
|
28
|
+
case "use-hypha-proxy":
|
|
29
|
+
return { WISE_AGENT_PROVIDER: "hypha-proxy", ...action.url ? { SVAMP_HYPHA_PROXY_URL: action.url } : {} };
|
|
30
|
+
case "use-claude-haiku":
|
|
31
|
+
return { WISE_AGENT_PROVIDER: "claude-haiku", WISE_AGENT_MODEL: void 0, ...action.url ? { SVAMP_HYPHA_PROXY_URL: action.url } : {} };
|
|
32
|
+
case "set":
|
|
33
|
+
return { WISE_AGENT_PROVIDER: "openai", WISE_AGENT_BASE_URL: action.url, WISE_AGENT_API_KEY: action.key };
|
|
34
|
+
case "set-key":
|
|
35
|
+
return { WISE_AGENT_API_KEY: action.key };
|
|
36
|
+
case "set-model":
|
|
37
|
+
return { WISE_AGENT_MODEL: action.model };
|
|
38
|
+
case "set-base-url":
|
|
39
|
+
return { WISE_AGENT_BASE_URL: action.url };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function redactKey(key) {
|
|
43
|
+
if (!key) return "(none)";
|
|
44
|
+
if (key.length <= 12) return "***";
|
|
45
|
+
return `${key.slice(0, 8)}\u2026${key.slice(-4)}`;
|
|
46
|
+
}
|
|
47
|
+
function describeWiseAgentAuth(env) {
|
|
48
|
+
const r = resolveModel(void 0, env);
|
|
49
|
+
return {
|
|
50
|
+
provider: r.provider,
|
|
51
|
+
model: r.model,
|
|
52
|
+
baseUrl: r.baseUrl || "(unset)",
|
|
53
|
+
keyRedacted: redactKey(r.apiKey)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function parseWiseAgentAuthArgs(args) {
|
|
57
|
+
const [sub, ...rest] = args;
|
|
58
|
+
if (!sub || sub === "status") return null;
|
|
59
|
+
switch (sub) {
|
|
60
|
+
case "use-openai":
|
|
61
|
+
return { kind: "use-openai", key: rest[0] };
|
|
62
|
+
case "use-hypha-proxy":
|
|
63
|
+
return { kind: "use-hypha-proxy", url: rest[0] };
|
|
64
|
+
case "use-claude-haiku":
|
|
65
|
+
return { kind: "use-claude-haiku", url: rest[0] };
|
|
66
|
+
case "set":
|
|
67
|
+
if (!rest[0] || !rest[1]) throw new Error("Usage: svamp wise-agent auth set <BASE_URL> <KEY>");
|
|
68
|
+
return { kind: "set", url: rest[0], key: rest[1] };
|
|
69
|
+
case "set-key":
|
|
70
|
+
if (!rest[0]) throw new Error("Usage: svamp wise-agent auth set-key <KEY>");
|
|
71
|
+
return { kind: "set-key", key: rest[0] };
|
|
72
|
+
case "set-model":
|
|
73
|
+
if (!rest[0]) throw new Error("Usage: svamp wise-agent auth set-model <model>");
|
|
74
|
+
return { kind: "set-model", model: rest[0] };
|
|
75
|
+
case "set-base-url":
|
|
76
|
+
if (!rest[0]) throw new Error("Usage: svamp wise-agent auth set-base-url <url>");
|
|
77
|
+
return { kind: "set-base-url", url: rest[0] };
|
|
78
|
+
default:
|
|
79
|
+
throw new Error(`Unknown wise-agent auth command "${sub}". Try: status | use-openai [KEY] | use-hypha-proxy [URL] | use-claude-haiku [URL] | set <URL> <KEY> | set-key <KEY> | set-model <m> | set-base-url <url>`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export { buildWiseAgentEnvUpdates, describeWiseAgentAuth, parseWiseAgentAuthArgs, redactKey };
|