svamp-cli 0.2.194 → 0.2.196
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/bin/skills/crew/SKILL.md +24 -1
- package/dist/{agentCommands-u3lACoAL.mjs → agentCommands-Epy2LP4k.mjs} +5 -5
- package/dist/{auth-CA8v11FJ.mjs → auth-BVAW-c8L.mjs} +1 -1
- package/dist/cli.mjs +60 -60
- package/dist/{commands-DRYKMdYK.mjs → commands-D3KJdH3r.mjs} +5 -3
- package/dist/{commands-CZD9sTwX.mjs → commands-D8ad_1Aa.mjs} +1 -1
- package/dist/{commands-Cj1ipwaC.mjs → commands-DYxBgrYh.mjs} +2 -2
- package/dist/{commands-DlKKUqVK.mjs → commands-DsYzp1Pc.mjs} +1 -1
- package/dist/{commands-CnOAurhc.mjs → commands-Dx6gz9Dr.mjs} +5 -5
- package/dist/{commands-CqnF0mFt.mjs → commands-i3J0d8xS.mjs} +137 -49
- package/dist/{commands-UDDf7PQt.mjs → commands-yNPBtne1.mjs} +1 -1
- package/dist/{fleet-CPdmiNDX.mjs → fleet-DwAy8i7Z.mjs} +1 -1
- package/dist/{frpc-CBDwOpZZ.mjs → frpc-DeD80HI1.mjs} +1 -1
- package/dist/{headlessCli-CFj2Gxe0.mjs → headlessCli-Cw8ZR1H7.mjs} +2 -2
- package/dist/{httpServer-Cf54pQ77.mjs → httpServer-B1KVQJfm.mjs} +38 -0
- package/dist/index.mjs +1 -1
- package/dist/{package-C9H0l19h.mjs → package-CpNcSvfm.mjs} +2 -2
- package/dist/{rpc-6ftZ30co.mjs → rpc-Blk-caU2.mjs} +1 -1
- package/dist/{rpc-BHrZL52o.mjs → rpc-CRpXmlRy.mjs} +6 -2
- package/dist/{run-6hsamvmy.mjs → run-CBWkZyBD.mjs} +1 -1
- package/dist/{run-Dn-FEr9q.mjs → run-Cxq7C5mA.mjs} +329 -39
- package/dist/{scheduler-BtltL3kB.mjs → scheduler-ro49WXPg.mjs} +1 -1
- package/dist/{serveCommands-DEGssQo6.mjs → serveCommands-5tBepki5.mjs} +5 -5
- package/dist/{serveManager-DwO36v9t.mjs → serveManager-D0edqFb9.mjs} +2 -2
- package/dist/{sideband-CjLQLnk1.mjs → sideband-p3YKDEQ0.mjs} +1 -1
- package/package.json +2 -2
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import os$1, { homedir as homedir$1 } from 'os';
|
|
2
2
|
import fs, { mkdir as mkdir$1, readdir as readdir$1, readFile, writeFile as writeFile$1, rename, unlink } from 'fs/promises';
|
|
3
|
-
import { readFileSync as readFileSync$1, mkdirSync as mkdirSync$1, writeFileSync as writeFileSync$1, renameSync as renameSync$1, existsSync as existsSync$1, realpathSync, rmSync as rmSync$1, copyFileSync, unlinkSync as unlinkSync$1, readdirSync as readdirSync$1, watch, rmdirSync } from 'fs';
|
|
4
|
-
import path__default, { join as join$1, resolve, dirname as dirname$1, basename as basename$1 } from 'path';
|
|
3
|
+
import { readFileSync as readFileSync$1, mkdirSync as mkdirSync$1, writeFileSync as writeFileSync$1, renameSync as renameSync$1, existsSync as existsSync$1, realpathSync as realpathSync$1, rmSync as rmSync$1, copyFileSync, unlinkSync as unlinkSync$1, readdirSync as readdirSync$1, watch, rmdirSync } from 'fs';
|
|
4
|
+
import path__default, { join as join$1, resolve as resolve$1, dirname as dirname$1, basename as basename$1 } from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import { execFile, spawn as spawn$1, execSync as execSync$1, spawnSync } from 'child_process';
|
|
7
7
|
import { randomUUID as randomUUID$1 } from 'crypto';
|
|
8
8
|
import { randomBytes, createHash, randomUUID } from 'node:crypto';
|
|
9
|
-
import { existsSync, readFileSync,
|
|
9
|
+
import { existsSync, readFileSync, realpathSync, readdirSync, statSync, mkdirSync, writeFileSync, renameSync, rmSync, appendFileSync, unlinkSync } from 'node:fs';
|
|
10
10
|
import { exec, execSync, spawn, execFile as execFile$1, execFileSync } from 'node:child_process';
|
|
11
11
|
import { promisify } from 'util';
|
|
12
|
-
import { join, basename, dirname } from 'node:path';
|
|
12
|
+
import { extname, resolve, join, sep, basename, dirname } from 'node:path';
|
|
13
13
|
import { EventEmitter } from 'node:events';
|
|
14
14
|
import os, { homedir, platform } from 'node:os';
|
|
15
15
|
import { ndJsonStream, ClientSideConnection } from '@agentclientprotocol/sdk';
|
|
@@ -1233,6 +1233,155 @@ function renderTemplate(template, ctx) {
|
|
|
1233
1233
|
});
|
|
1234
1234
|
}
|
|
1235
1235
|
|
|
1236
|
+
const HARD_MAX_BYTES = 25 * 1024 * 1024;
|
|
1237
|
+
const DEFAULT_MAX_BYTES = 5 * 1024 * 1024;
|
|
1238
|
+
const DEFAULT_MAX_COUNT = 100;
|
|
1239
|
+
const DEFAULT_ALLOWED_EXT = [
|
|
1240
|
+
".png",
|
|
1241
|
+
".jpg",
|
|
1242
|
+
".jpeg",
|
|
1243
|
+
".gif",
|
|
1244
|
+
".webp",
|
|
1245
|
+
".bmp",
|
|
1246
|
+
".svg",
|
|
1247
|
+
".pdf",
|
|
1248
|
+
".txt",
|
|
1249
|
+
".md",
|
|
1250
|
+
".csv",
|
|
1251
|
+
".json",
|
|
1252
|
+
".log",
|
|
1253
|
+
".yaml",
|
|
1254
|
+
".yml"
|
|
1255
|
+
];
|
|
1256
|
+
function uploadEnabled(c) {
|
|
1257
|
+
return !!(c && c.upload && c.upload.enabled === true);
|
|
1258
|
+
}
|
|
1259
|
+
function effectiveUploadConfig(c) {
|
|
1260
|
+
const u = c.upload || {};
|
|
1261
|
+
const maxBytes = Math.min(
|
|
1262
|
+
typeof u.maxBytes === "number" && u.maxBytes > 0 ? u.maxBytes : DEFAULT_MAX_BYTES,
|
|
1263
|
+
HARD_MAX_BYTES
|
|
1264
|
+
);
|
|
1265
|
+
const maxCount = typeof u.maxCount === "number" && u.maxCount > 0 ? Math.floor(u.maxCount) : DEFAULT_MAX_COUNT;
|
|
1266
|
+
const allowedExt = Array.isArray(u.allowedExt) ? u.allowedExt.map((e) => String(e).toLowerCase().trim()).filter(Boolean) : DEFAULT_ALLOWED_EXT;
|
|
1267
|
+
return { ...u, maxBytes, maxCount, allowedExt };
|
|
1268
|
+
}
|
|
1269
|
+
function validateUploadConfig(c) {
|
|
1270
|
+
const u = c.upload;
|
|
1271
|
+
if (u == null) return [];
|
|
1272
|
+
if (typeof u !== "object") return ["upload must be an object"];
|
|
1273
|
+
const errs = [];
|
|
1274
|
+
if (u.enabled != null && typeof u.enabled !== "boolean") errs.push("upload.enabled must be a boolean");
|
|
1275
|
+
if (u.maxBytes != null && (typeof u.maxBytes !== "number" || u.maxBytes <= 0)) errs.push("upload.maxBytes must be a positive number");
|
|
1276
|
+
if (u.maxCount != null && (typeof u.maxCount !== "number" || u.maxCount <= 0)) errs.push("upload.maxCount must be a positive number");
|
|
1277
|
+
if (u.allowedExt != null && !Array.isArray(u.allowedExt)) errs.push("upload.allowedExt must be an array of extensions");
|
|
1278
|
+
if (u.allowedMime != null && !Array.isArray(u.allowedMime)) errs.push("upload.allowedMime must be an array of mime types");
|
|
1279
|
+
if (u.dir != null) {
|
|
1280
|
+
if (typeof u.dir !== "string") errs.push("upload.dir must be a string");
|
|
1281
|
+
else if (u.dir.startsWith("/") || u.dir.startsWith("\\") || /(^|[\\/])\.\.([\\/]|$)/.test(u.dir) || u.dir.includes("\0"))
|
|
1282
|
+
errs.push('upload.dir must be a project-relative path without ".." traversal');
|
|
1283
|
+
}
|
|
1284
|
+
return errs;
|
|
1285
|
+
}
|
|
1286
|
+
const CONTROL_CHARS = /[\x00-\x1f\x7f]/;
|
|
1287
|
+
function sanitizeFilename(raw) {
|
|
1288
|
+
const input = String(raw ?? "").trim();
|
|
1289
|
+
if (!input) return { error: "filename required" };
|
|
1290
|
+
if (input.length > 200) return { error: "filename too long (max 200 chars)" };
|
|
1291
|
+
if (CONTROL_CHARS.test(input)) return { error: "filename contains control characters" };
|
|
1292
|
+
if (input.includes("\0")) return { error: "filename contains null byte" };
|
|
1293
|
+
if (input.includes("/") || input.includes("\\")) return { error: "filename must not contain path separators" };
|
|
1294
|
+
if (input.includes("..")) return { error: 'filename must not contain ".."' };
|
|
1295
|
+
const name = basename(input);
|
|
1296
|
+
if (!name || name === "." || name === "..") return { error: "invalid filename" };
|
|
1297
|
+
if (name.startsWith(".")) return { error: "filename must not start with a dot" };
|
|
1298
|
+
if (!/^[A-Za-z0-9 ._-]+$/.test(name)) return { error: "filename has disallowed characters (use letters, digits, space, . _ -)" };
|
|
1299
|
+
return { name };
|
|
1300
|
+
}
|
|
1301
|
+
function validateUpload(input) {
|
|
1302
|
+
const { projectDir, channel, bytes, contentType } = input;
|
|
1303
|
+
if (!uploadEnabled(channel)) return { error: "uploads are not enabled on this channel" };
|
|
1304
|
+
const cfg = effectiveUploadConfig(channel);
|
|
1305
|
+
const fn = sanitizeFilename(input.filename);
|
|
1306
|
+
if (fn.error || !fn.name) return { error: fn.error || "invalid filename" };
|
|
1307
|
+
const name = fn.name;
|
|
1308
|
+
if (!Number.isFinite(bytes) || bytes < 0) return { error: "invalid file size" };
|
|
1309
|
+
if (bytes === 0) return { error: "empty file" };
|
|
1310
|
+
if (bytes > cfg.maxBytes) return { error: `file too large (${bytes} bytes > ${cfg.maxBytes} limit)` };
|
|
1311
|
+
const ext = extname(name).toLowerCase();
|
|
1312
|
+
if (!ext) return { error: "file has no extension" };
|
|
1313
|
+
if (!cfg.allowedExt.includes(ext)) return { error: `file type not allowed: ${ext} (allowed: ${cfg.allowedExt.join(", ") || "none"})` };
|
|
1314
|
+
if (Array.isArray(cfg.allowedMime) && cfg.allowedMime.length) {
|
|
1315
|
+
const ct = String(contentType || "").toLowerCase().split(";")[0].trim();
|
|
1316
|
+
if (!ct) return { error: "content-type required (channel restricts MIME types)" };
|
|
1317
|
+
if (!cfg.allowedMime.map((m) => String(m).toLowerCase().trim()).includes(ct))
|
|
1318
|
+
return { error: `mime type not allowed: ${ct}` };
|
|
1319
|
+
}
|
|
1320
|
+
let projectReal;
|
|
1321
|
+
try {
|
|
1322
|
+
projectReal = realpathSync(projectDir);
|
|
1323
|
+
} catch {
|
|
1324
|
+
projectReal = resolve(projectDir);
|
|
1325
|
+
}
|
|
1326
|
+
const relDir = cfg.dir && String(cfg.dir).trim() ? String(cfg.dir).trim() : join(".svamp", "channels", "uploads", String(channel.id || "unknown"));
|
|
1327
|
+
const destDir = resolve(projectReal, relDir);
|
|
1328
|
+
if (destDir !== projectReal && !destDir.startsWith(projectReal + sep))
|
|
1329
|
+
return { error: "upload destination escapes the project folder" };
|
|
1330
|
+
const fullPath = join(destDir, name);
|
|
1331
|
+
if (fullPath !== destDir && !fullPath.startsWith(destDir + sep))
|
|
1332
|
+
return { error: "upload path escapes the destination directory" };
|
|
1333
|
+
try {
|
|
1334
|
+
if (existsSync(destDir)) {
|
|
1335
|
+
const count = readdirSync(destDir).filter((f) => {
|
|
1336
|
+
try {
|
|
1337
|
+
return statSync(join(destDir, f)).isFile();
|
|
1338
|
+
} catch {
|
|
1339
|
+
return false;
|
|
1340
|
+
}
|
|
1341
|
+
}).length;
|
|
1342
|
+
if (count >= cfg.maxCount) return { error: `upload limit reached (${cfg.maxCount} files)` };
|
|
1343
|
+
}
|
|
1344
|
+
} catch {
|
|
1345
|
+
}
|
|
1346
|
+
return { target: { destDir, name, fullPath } };
|
|
1347
|
+
}
|
|
1348
|
+
function writeUpload(projectDir, target, data) {
|
|
1349
|
+
mkdirSync(target.destDir, { recursive: true });
|
|
1350
|
+
let finalName = target.name;
|
|
1351
|
+
let finalPath = target.fullPath;
|
|
1352
|
+
if (existsSync(finalPath)) {
|
|
1353
|
+
const ext = extname(target.name);
|
|
1354
|
+
const stem = target.name.slice(0, target.name.length - ext.length);
|
|
1355
|
+
finalName = `${stem}-${randomBytes(3).toString("hex")}${ext}`;
|
|
1356
|
+
finalPath = join(target.destDir, finalName);
|
|
1357
|
+
}
|
|
1358
|
+
const tmp = finalPath + ".tmp-" + randomBytes(4).toString("hex");
|
|
1359
|
+
writeFileSync(tmp, data);
|
|
1360
|
+
renameSync(tmp, finalPath);
|
|
1361
|
+
let projectReal;
|
|
1362
|
+
try {
|
|
1363
|
+
projectReal = realpathSync(projectDir);
|
|
1364
|
+
} catch {
|
|
1365
|
+
projectReal = resolve(projectDir);
|
|
1366
|
+
}
|
|
1367
|
+
const relPath = finalPath.startsWith(projectReal + sep) ? finalPath.slice(projectReal.length + 1) : finalPath;
|
|
1368
|
+
return { name: finalName, relPath, fullPath: finalPath, bytes: data.length };
|
|
1369
|
+
}
|
|
1370
|
+
function decodeBase64Capped(b64, maxBytes) {
|
|
1371
|
+
const s = String(b64 ?? "");
|
|
1372
|
+
if (!s) return { error: "empty file content" };
|
|
1373
|
+
if (s.length > Math.ceil(maxBytes / 3) * 4 + 16) return { error: `file too large (exceeds ${maxBytes} byte limit)` };
|
|
1374
|
+
let data;
|
|
1375
|
+
try {
|
|
1376
|
+
data = Buffer.from(s, "base64");
|
|
1377
|
+
} catch {
|
|
1378
|
+
return { error: "invalid base64 content" };
|
|
1379
|
+
}
|
|
1380
|
+
if (!data.length) return { error: "invalid or empty base64 content" };
|
|
1381
|
+
if (data.length > maxBytes) return { error: `file too large (${data.length} bytes > ${maxBytes} limit)` };
|
|
1382
|
+
return { data };
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1236
1385
|
const genId = () => "c_" + randomBytes(5).toString("hex");
|
|
1237
1386
|
const genKey = () => "ck_" + randomBytes(18).toString("base64url");
|
|
1238
1387
|
const DEFAULT_TEMPLATE = `<inbound-message from="\${sender.name}" sender-type="\${sender.kind}" verified="\${sender.verified}" channel="\${channel.name}" call-id="\${call.id}" at="\${now}">
|
|
@@ -1268,6 +1417,7 @@ function validateChannel(c) {
|
|
|
1268
1417
|
if (c.skill?.description && unsafe.test(c.skill.description)) errs.push(`skill.description must be single-line and not contain < > " ' &`);
|
|
1269
1418
|
if (m === "fixed" && c.identity.fixed?.name && unsafe.test(c.identity.fixed.name)) errs.push(`identity.fixed.name must not contain < > " ' & or newlines`);
|
|
1270
1419
|
for (const cl of c.identity?.callers || []) if (unsafe.test(cl.name || "")) errs.push(`caller name "${cl.name}" must not contain < > " ' & or newlines`);
|
|
1420
|
+
errs.push(...validateUploadConfig(c));
|
|
1271
1421
|
return errs;
|
|
1272
1422
|
}
|
|
1273
1423
|
function normalizeBind(c) {
|
|
@@ -1377,6 +1527,7 @@ function generateSkillBody(channel, ctx) {
|
|
|
1377
1527
|
const gw = ctx?.channelsServiceId ? gatewayBase(svc, base) : `${base}/<workspace>/services/<machine>:channels`;
|
|
1378
1528
|
const skillUrl = `${gw}/skill?channel=${channel.id}`;
|
|
1379
1529
|
const sendUrl = `${gw}/send`;
|
|
1530
|
+
const uploadUrl = `${gw}/upload`;
|
|
1380
1531
|
const key = ctx?.key || "<your-key>";
|
|
1381
1532
|
const isAgent = channel.action?.kind === "agent";
|
|
1382
1533
|
const isQueue = channel.reply?.mode === "queue";
|
|
@@ -1422,6 +1573,47 @@ to \`send()\`. The reply is then delivered straight to **your session's inbox**
|
|
|
1422
1573
|
channel's outbox and you must poll \`receive()\` (above) for it \u2014 it will **not** appear
|
|
1423
1574
|
in your inbox.` : "";
|
|
1424
1575
|
const rpcAuthNote = hyphaOpen ? `Your verified Hypha identity is accepted \u2014 no key needed.` : `Uses your verified Hypha identity.`;
|
|
1576
|
+
const uploadSection = uploadEnabled(channel) ? (() => {
|
|
1577
|
+
const u = effectiveUploadConfig(channel);
|
|
1578
|
+
const mb = (u.maxBytes / (1024 * 1024)).toFixed(u.maxBytes % (1024 * 1024) === 0 ? 0 : 1);
|
|
1579
|
+
const exts = u.allowedExt.join(", ") || "(none)";
|
|
1580
|
+
const mimeLine = Array.isArray(u.allowedMime) && u.allowedMime.length ? `
|
|
1581
|
+
- **Allowed MIME types:** ${u.allowedMime.join(", ")}` : "";
|
|
1582
|
+
return `
|
|
1583
|
+
|
|
1584
|
+
## Uploading a file
|
|
1585
|
+
This channel **accepts file uploads** (server-validated, deny-by-default elsewhere).
|
|
1586
|
+
Send the file base64-encoded; the server validates it and saves it into the session's
|
|
1587
|
+
project folder, returning the saved relative path. Reference that path in a follow-up
|
|
1588
|
+
\`send()\` so the agent knows what you uploaded.
|
|
1589
|
+
|
|
1590
|
+
**Limits (enforced server-side):**
|
|
1591
|
+
- **Max size:** ${mb} MiB per file
|
|
1592
|
+
- **Max files retained:** ${u.maxCount}
|
|
1593
|
+
- **Allowed extensions:** ${exts}${mimeLine}
|
|
1594
|
+
- Filenames are sanitized to a safe basename \u2014 no paths, no \`..\` traversal.
|
|
1595
|
+
|
|
1596
|
+
### Hypha RPC
|
|
1597
|
+
\`\`\`js
|
|
1598
|
+
const res = await get_service("${svc}").upload({
|
|
1599
|
+
channel: "${channel.id}",
|
|
1600
|
+
filename: "report.pdf",
|
|
1601
|
+
content_base64: "<base64 of the file bytes>",
|
|
1602
|
+
content_type: "application/pdf",
|
|
1603
|
+
from: "your-name"${rSession ? `,
|
|
1604
|
+
session: "${rSession}"` : ""}${hyphaOpen ? "" : `,
|
|
1605
|
+
key: "${key}"`}
|
|
1606
|
+
});
|
|
1607
|
+
// \u2192 { ok: true, name, path, bytes }
|
|
1608
|
+
\`\`\`
|
|
1609
|
+
|
|
1610
|
+
### HTTP
|
|
1611
|
+
\`\`\`bash
|
|
1612
|
+
curl -X POST "${uploadUrl}" \\
|
|
1613
|
+
-H 'Content-Type: application/json' \\
|
|
1614
|
+
-d '{"kwargs": {"channel": "${channel.id}", "filename": "report.pdf", "content_base64": "<base64>", "content_type": "application/pdf", "from": "your-name", "key": "${key}"}}'
|
|
1615
|
+
\`\`\``;
|
|
1616
|
+
})() : "";
|
|
1425
1617
|
return `---
|
|
1426
1618
|
name: ${name}
|
|
1427
1619
|
description: ${desc}
|
|
@@ -1454,7 +1646,7 @@ curl -X POST "${sendUrl}" \\
|
|
|
1454
1646
|
-d '{"kwargs": {"channel": "${channel.id}", "message": "your message here", "from": "your-name", "key": "${key}"${sessionKv}}}'
|
|
1455
1647
|
\`\`\`
|
|
1456
1648
|
|
|
1457
|
-
Always-current copy of this skill (all ids filled in): ${skillUrl}${queueSection}`;
|
|
1649
|
+
Always-current copy of this skill (all ids filled in): ${skillUrl}${uploadSection}${queueSection}`;
|
|
1458
1650
|
}
|
|
1459
1651
|
|
|
1460
1652
|
function resolveSender(channel, input = {}) {
|
|
@@ -2720,7 +2912,7 @@ async function registerMachineService(server, machineId, metadata, daemonState,
|
|
|
2720
2912
|
const tunnels = handlers.tunnels;
|
|
2721
2913
|
if (!tunnels) throw new Error("Tunnel management not available");
|
|
2722
2914
|
if (tunnels.has(params.name)) throw new Error(`Tunnel '${params.name}' already running`);
|
|
2723
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
2915
|
+
const { FrpcTunnel } = await import('./frpc-DeD80HI1.mjs');
|
|
2724
2916
|
const tunnel = new FrpcTunnel({
|
|
2725
2917
|
name: params.name,
|
|
2726
2918
|
ports: params.ports,
|
|
@@ -3167,7 +3359,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
3167
3359
|
}
|
|
3168
3360
|
const deps = buildSessionDeps(rpc, { cwd, ownerEmail: owner });
|
|
3169
3361
|
const sender = { name: context?.user?.email || context?.user?.id || "user", kind: "user", verified: true };
|
|
3170
|
-
const { toolsForRole } = await import('./sideband-
|
|
3362
|
+
const { toolsForRole } = await import('./sideband-p3YKDEQ0.mjs');
|
|
3171
3363
|
const r2 = await runWiseAgent({ message: params.message, sender, config: { tools: toolsForRole(role2) }, deps, transport, model: resolved.model });
|
|
3172
3364
|
return fmt(r2);
|
|
3173
3365
|
}
|
|
@@ -3266,7 +3458,7 @@ QUESTION: ${params.question || "Summarize this concisely."}` }
|
|
|
3266
3458
|
if (r.error || !r.sender) return { error: r.error || "unauthorized" };
|
|
3267
3459
|
const callId = "call_" + Math.random().toString(16).slice(2, 12);
|
|
3268
3460
|
const rendered = renderMessage(c, { sender: r.sender, body: { message: kwargs.message }, callId });
|
|
3269
|
-
const { queryCore } = await import('./commands-
|
|
3461
|
+
const { queryCore } = await import('./commands-DsYzp1Pc.mjs');
|
|
3270
3462
|
const timeout = c.reply?.timeout_sec || 120;
|
|
3271
3463
|
let result;
|
|
3272
3464
|
try {
|
|
@@ -3371,6 +3563,44 @@ ${d?.error || "not found"}`;
|
|
|
3371
3563
|
}
|
|
3372
3564
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
3373
3565
|
}
|
|
3566
|
+
},
|
|
3567
|
+
// Safety-restricted file upload (#0093). Deny-by-default: only channels whose
|
|
3568
|
+
// config sets upload.enabled accept files. The upload is a pure, validated file
|
|
3569
|
+
// write into the channel's project folder — no agent run — so we do NOT spawn a
|
|
3570
|
+
// stateless one-shot; we resolve the channel + dir from disk and write directly,
|
|
3571
|
+
// which works whether or not a live session hosts the channel. All safety checks
|
|
3572
|
+
// (filename/size/extension/MIME/count/path-confinement) live in channel/upload.ts.
|
|
3573
|
+
upload: async (kwargs = {}, context) => {
|
|
3574
|
+
trackInbound();
|
|
3575
|
+
if (!kwargs.channel) return { error: MISSING_CHANNEL_HINT };
|
|
3576
|
+
const found = readChannelFromDisk(kwargs.channel);
|
|
3577
|
+
if (!found) return { error: "channel not found" };
|
|
3578
|
+
const { c, dir } = found;
|
|
3579
|
+
if (!uploadEnabled(c)) return { error: "uploads are not enabled on this channel" };
|
|
3580
|
+
const u = context?.user;
|
|
3581
|
+
const r = resolveSender(c, {
|
|
3582
|
+
key: kwargs.key,
|
|
3583
|
+
from: kwargs.from,
|
|
3584
|
+
hyphaUser: u && u.is_anonymous !== true ? u.email || u.id : void 0,
|
|
3585
|
+
hyphaAnonymous: u?.is_anonymous === true,
|
|
3586
|
+
hyphaWorkspace: u?.scope?.current_workspace
|
|
3587
|
+
});
|
|
3588
|
+
if (r.error || !r.sender) return { error: r.error || "unauthorized" };
|
|
3589
|
+
const cfg = effectiveUploadConfig(c);
|
|
3590
|
+
const dec = decodeBase64Capped(kwargs.content_base64, cfg.maxBytes);
|
|
3591
|
+
if (dec.error || !dec.data) return { error: dec.error || "invalid content" };
|
|
3592
|
+
const v = validateUpload({ projectDir: dir, channel: c, filename: kwargs.filename, bytes: dec.data.length, contentType: kwargs.content_type });
|
|
3593
|
+
if (v.error || !v.target) return { error: v.error || "upload rejected" };
|
|
3594
|
+
try {
|
|
3595
|
+
const saved = writeUpload(dir, v.target, dec.data);
|
|
3596
|
+
try {
|
|
3597
|
+
new ChannelStore(dir).recordCall(c.id, { sender: r.sender.name, verified: r.sender.verified, callId: "up_" + Math.random().toString(16).slice(2, 10), outcome: "uploaded" });
|
|
3598
|
+
} catch {
|
|
3599
|
+
}
|
|
3600
|
+
return { ok: true, name: saved.name, path: saved.relPath, bytes: saved.bytes };
|
|
3601
|
+
} catch (e) {
|
|
3602
|
+
return { ok: false, error: e?.message || String(e) };
|
|
3603
|
+
}
|
|
3374
3604
|
}
|
|
3375
3605
|
},
|
|
3376
3606
|
{ overwrite: true }
|
|
@@ -3760,7 +3990,9 @@ var claudeAuth = /*#__PURE__*/Object.freeze({
|
|
|
3760
3990
|
|
|
3761
3991
|
const PARTICIPANTS_CHANNEL_ID = "sys-participants";
|
|
3762
3992
|
function channelPublicView(c) {
|
|
3763
|
-
|
|
3993
|
+
const u = c.upload;
|
|
3994
|
+
const upload = u && u.enabled === true ? { enabled: true, ...typeof u.maxBytes === "number" ? { maxBytes: u.maxBytes } : {}, ...Array.isArray(u.allowedExt) ? { allowedExt: u.allowedExt } : {} } : void 0;
|
|
3995
|
+
return { id: c.id, name: c.name, description: c.description, identity: { mode: c.identity?.mode }, action: c.action?.kind, bind: c.bind, ...upload ? { upload } : {} };
|
|
3764
3996
|
}
|
|
3765
3997
|
function isStructuredMessage(msg) {
|
|
3766
3998
|
return !!(msg.from || msg.fromSession || msg.subject || msg.replyTo || msg.threadId || msg.channel);
|
|
@@ -3782,7 +4014,6 @@ function formatInboxMessageXml(msg) {
|
|
|
3782
4014
|
if (msg.subject) attrs.push(`subject="${escapeXml(msg.subject)}"`);
|
|
3783
4015
|
if (msg.urgency) attrs.push(`urgency="${msg.urgency}"`);
|
|
3784
4016
|
if (msg.replyTo) attrs.push(`reply-to="${escapeXml(msg.replyTo)}"`);
|
|
3785
|
-
if (msg.cc && msg.cc.length > 0) attrs.push(`cc="${msg.cc.map(escapeXml).join(",")}"`);
|
|
3786
4017
|
if (msg.threadId) attrs.push(`thread-id="${escapeXml(msg.threadId)}"`);
|
|
3787
4018
|
attrs.push(`timestamp="${msg.timestamp}"`);
|
|
3788
4019
|
return `<svamp-message ${attrs.join(" ")}>
|
|
@@ -3942,13 +4173,15 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
3942
4173
|
body: m.body,
|
|
3943
4174
|
timestamp: m.timestamp,
|
|
3944
4175
|
read: m.read,
|
|
4176
|
+
// `handled` = agent consumed it; drives the "pending" (unread) badge
|
|
4177
|
+
// together with `read` so handled messages don't linger as phantom-unread.
|
|
4178
|
+
...m.handled ? { handled: true } : {},
|
|
3945
4179
|
from: m.from,
|
|
3946
4180
|
fromSession: m.fromSession,
|
|
3947
4181
|
to: m.to,
|
|
3948
4182
|
subject: m.subject,
|
|
3949
4183
|
urgency: m.urgency,
|
|
3950
4184
|
replyTo: m.replyTo,
|
|
3951
|
-
cc: m.cc,
|
|
3952
4185
|
threadId: m.threadId,
|
|
3953
4186
|
// Channel provenance so the inbox UI can show who/verified.
|
|
3954
4187
|
...m.verified !== void 0 ? { verified: m.verified } : {},
|
|
@@ -4553,6 +4786,40 @@ function createSessionStore(server, sessionId, initialMetadata, initialAgentStat
|
|
|
4553
4786
|
return { ok: false, call_id: callId, status: "error", error: e?.message || String(e) };
|
|
4554
4787
|
}
|
|
4555
4788
|
},
|
|
4789
|
+
// Safety-restricted file upload (#0093). Deny-by-default: a channel accepts
|
|
4790
|
+
// uploads ONLY when its config sets upload.enabled. Auth = the channel's OWN
|
|
4791
|
+
// identity policy (resolveSender), same as channelSend. All validation is
|
|
4792
|
+
// server-side (channel/upload.ts): filename sanitization (no traversal),
|
|
4793
|
+
// size cap, extension/MIME allowlist, file-count cap, path confinement.
|
|
4794
|
+
channelUpload: async (params, context) => {
|
|
4795
|
+
const c = channelStore.get(params.channel);
|
|
4796
|
+
if (!c || c.enabled === false) return { error: "channel not found" };
|
|
4797
|
+
if (!uploadEnabled(c)) return { error: "uploads are not enabled on this channel" };
|
|
4798
|
+
const u = context?.user;
|
|
4799
|
+
const r = resolveSender(c, {
|
|
4800
|
+
key: params.key,
|
|
4801
|
+
from: params.from,
|
|
4802
|
+
hyphaUser: u && u.is_anonymous !== true ? u.email || u.id : void 0,
|
|
4803
|
+
hyphaAnonymous: u?.is_anonymous === true,
|
|
4804
|
+
hyphaWorkspace: u?.scope?.current_workspace
|
|
4805
|
+
});
|
|
4806
|
+
if (r.error || !r.sender) return { error: r.error || "unauthorized" };
|
|
4807
|
+
const cfg2 = effectiveUploadConfig(c);
|
|
4808
|
+
const dec = decodeBase64Capped(params.content_base64, cfg2.maxBytes);
|
|
4809
|
+
if (dec.error || !dec.data) return { error: dec.error || "invalid content" };
|
|
4810
|
+
const v = validateUpload({ projectDir: metadata.path, channel: c, filename: params.filename, bytes: dec.data.length, contentType: params.content_type });
|
|
4811
|
+
if (v.error || !v.target) return { error: v.error || "upload rejected" };
|
|
4812
|
+
const callId = "call_" + randomUUID().slice(0, 10);
|
|
4813
|
+
try {
|
|
4814
|
+
const saved = writeUpload(metadata.path, v.target, dec.data);
|
|
4815
|
+
channelStore.recordCall(c.id, { sender: r.sender.name, verified: r.sender.verified, callId, outcome: "uploaded" });
|
|
4816
|
+
syncChannelsToMetadata();
|
|
4817
|
+
return { ok: true, name: saved.name, path: saved.relPath, bytes: saved.bytes };
|
|
4818
|
+
} catch (e) {
|
|
4819
|
+
channelStore.recordCall(c.id, { sender: r.sender.name, verified: r.sender.verified, callId, outcome: "error" });
|
|
4820
|
+
return { ok: false, error: e?.message || String(e) };
|
|
4821
|
+
}
|
|
4822
|
+
},
|
|
4556
4823
|
// Agent/owner answers a queued (async) channel message → channel outbox, addressed
|
|
4557
4824
|
// to the original external caller. Routed here by `inbox reply` for channel-origin
|
|
4558
4825
|
// inbox messages (which carry channelId + correlationId + from). Owner-gated since
|
|
@@ -5063,6 +5330,13 @@ Output ONLY the full revised reply text \u2014 no preamble, no commentary, no su
|
|
|
5063
5330
|
syncInboxToMetadata();
|
|
5064
5331
|
}
|
|
5065
5332
|
},
|
|
5333
|
+
markInboxHandled: (messageId) => {
|
|
5334
|
+
const msg = inbox.find((m) => m.messageId === messageId);
|
|
5335
|
+
if (msg && !msg.handled) {
|
|
5336
|
+
msg.handled = true;
|
|
5337
|
+
syncInboxToMetadata();
|
|
5338
|
+
}
|
|
5339
|
+
},
|
|
5066
5340
|
disconnect: async () => {
|
|
5067
5341
|
const toRemove = [...listeners];
|
|
5068
5342
|
for (const listener of toRemove) {
|
|
@@ -8039,9 +8313,9 @@ var GeminiTransport$1 = /*#__PURE__*/Object.freeze({
|
|
|
8039
8313
|
function resolveTranscriptPath(cwd, claudeSessionId) {
|
|
8040
8314
|
let real;
|
|
8041
8315
|
try {
|
|
8042
|
-
real = realpathSync(cwd);
|
|
8316
|
+
real = realpathSync$1(cwd);
|
|
8043
8317
|
} catch {
|
|
8044
|
-
real = resolve(cwd);
|
|
8318
|
+
real = resolve$1(cwd);
|
|
8045
8319
|
}
|
|
8046
8320
|
const projectId = real.replace(/[^a-zA-Z0-9-]/g, "-");
|
|
8047
8321
|
const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR || join$1(os$1.homedir(), ".claude");
|
|
@@ -9913,7 +10187,7 @@ function normalizeStatus(s) {
|
|
|
9913
10187
|
if (s === "done") return "archived";
|
|
9914
10188
|
return s === "ready" || s === "in_progress" || s === "archived" ? s : "ready";
|
|
9915
10189
|
}
|
|
9916
|
-
const FIELD_ORDER = ["id", "title", "status", "scope", "labels", "verify", "disposition", "triaged", "branch", "session", "original", "created", "closed"];
|
|
10190
|
+
const FIELD_ORDER = ["id", "title", "status", "scope", "labels", "verify", "disposition", "triaged", "branch", "session", "owner", "original", "created", "closed"];
|
|
9917
10191
|
function resolveProjectRoot(start = process.cwd()) {
|
|
9918
10192
|
let dir = start;
|
|
9919
10193
|
for (let i = 0; i < 40; i++) {
|
|
@@ -9970,6 +10244,7 @@ function parseIssue(content) {
|
|
|
9970
10244
|
triaged: fm.triaged === true,
|
|
9971
10245
|
branch: fm.branch ?? null,
|
|
9972
10246
|
session: fm.session ?? null,
|
|
10247
|
+
owner: fm.owner ?? null,
|
|
9973
10248
|
original: fm.original ?? null,
|
|
9974
10249
|
created: String(fm.created ?? (/* @__PURE__ */ new Date()).toISOString()),
|
|
9975
10250
|
closed: fm.closed ?? null,
|
|
@@ -9997,6 +10272,13 @@ function listIssues(projectRoot, opts = {}) {
|
|
|
9997
10272
|
if (opts.scope) items = items.filter((i) => i.scope === opts.scope);
|
|
9998
10273
|
return items.sort((a, b) => Number(a.id) - Number(b.id));
|
|
9999
10274
|
}
|
|
10275
|
+
function isVisibleTo(issue, session) {
|
|
10276
|
+
if (issue.scope === "session") return issue.session === session || issue.owner === session;
|
|
10277
|
+
if (issue.owner === session) return true;
|
|
10278
|
+
if (issue.session === session) return true;
|
|
10279
|
+
if (!issue.owner && !issue.session) return true;
|
|
10280
|
+
return false;
|
|
10281
|
+
}
|
|
10000
10282
|
function getIssue(projectRoot, id) {
|
|
10001
10283
|
const padded = /^\d+$/.test(id) ? id.padStart(4, "0") : id;
|
|
10002
10284
|
for (const p of [issuePath(projectRoot, padded), issuePath(projectRoot, padded, true)]) {
|
|
@@ -10033,6 +10315,7 @@ function addIssue(projectRoot, fields) {
|
|
|
10033
10315
|
triaged: fields.triaged ?? false,
|
|
10034
10316
|
branch: null,
|
|
10035
10317
|
session: fields.session ?? null,
|
|
10318
|
+
owner: fields.owner ?? null,
|
|
10036
10319
|
original: fields.original ?? null,
|
|
10037
10320
|
created: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10038
10321
|
closed: null,
|
|
@@ -11614,7 +11897,7 @@ async function startDaemon(options) {
|
|
|
11614
11897
|
saveExposedTunnels(list);
|
|
11615
11898
|
}
|
|
11616
11899
|
async function createExposedTunnel(spec) {
|
|
11617
|
-
const { FrpcTunnel } = await import('./frpc-
|
|
11900
|
+
const { FrpcTunnel } = await import('./frpc-DeD80HI1.mjs');
|
|
11618
11901
|
const tunnel = new FrpcTunnel({
|
|
11619
11902
|
name: spec.name,
|
|
11620
11903
|
ports: spec.ports,
|
|
@@ -11634,7 +11917,7 @@ async function startDaemon(options) {
|
|
|
11634
11917
|
return tunnel;
|
|
11635
11918
|
}
|
|
11636
11919
|
const tunnelRecreateState = /* @__PURE__ */ new Map();
|
|
11637
|
-
const { ServeManager } = await import('./serveManager-
|
|
11920
|
+
const { ServeManager } = await import('./serveManager-D0edqFb9.mjs');
|
|
11638
11921
|
const serveManager = new ServeManager(SVAMP_HOME, (msg) => logger.log(`[SERVE] ${msg}`), hyphaServerUrl);
|
|
11639
11922
|
ensureAutoInstalledSkills(logger).catch(() => {
|
|
11640
11923
|
});
|
|
@@ -13416,6 +13699,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
13416
13699
|
}
|
|
13417
13700
|
if (decision.action === "wake") {
|
|
13418
13701
|
logger.log(`[Session ${sessionId}] Delivering inbox message to agent`);
|
|
13702
|
+
sessionService.markInboxHandled(message.messageId);
|
|
13419
13703
|
writeInboundContext(sessionId, { hopCount: message.hopCount ?? 0, threadId: message.threadId || message.messageId });
|
|
13420
13704
|
sessionService.pushMessage(formatted, "user");
|
|
13421
13705
|
if (!claudeProcess || claudeProcess.exitCode !== null) {
|
|
@@ -13498,11 +13782,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
13498
13782
|
});
|
|
13499
13783
|
},
|
|
13500
13784
|
onIssue: async (params) => {
|
|
13501
|
-
const { issueRpc } = await import('./rpc-
|
|
13785
|
+
const { issueRpc } = await import('./rpc-CRpXmlRy.mjs');
|
|
13502
13786
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner });
|
|
13503
13787
|
},
|
|
13504
13788
|
onWorkflow: async (params) => {
|
|
13505
|
-
const { workflowRpc } = await import('./rpc-
|
|
13789
|
+
const { workflowRpc } = await import('./rpc-Blk-caU2.mjs');
|
|
13506
13790
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
13507
13791
|
},
|
|
13508
13792
|
onRipgrep: async (args, cwd) => {
|
|
@@ -13519,23 +13803,23 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
13519
13803
|
});
|
|
13520
13804
|
},
|
|
13521
13805
|
onReadFile: async (path) => {
|
|
13522
|
-
const resolvedPath = resolve(directory, path);
|
|
13523
|
-
if (sessionMetadata.securityContext && resolvedPath !== resolve(directory) && !resolvedPath.startsWith(resolve(directory) + "/")) {
|
|
13806
|
+
const resolvedPath = resolve$1(directory, path);
|
|
13807
|
+
if (sessionMetadata.securityContext && resolvedPath !== resolve$1(directory) && !resolvedPath.startsWith(resolve$1(directory) + "/")) {
|
|
13524
13808
|
throw new Error("Path outside working directory");
|
|
13525
13809
|
}
|
|
13526
13810
|
return await readSessionFileBase64(resolvedPath);
|
|
13527
13811
|
},
|
|
13528
13812
|
onWriteFile: async (path, content) => {
|
|
13529
|
-
const resolvedPath = resolve(directory, path);
|
|
13530
|
-
if (sessionMetadata.securityContext && resolvedPath !== resolve(directory) && !resolvedPath.startsWith(resolve(directory) + "/")) {
|
|
13813
|
+
const resolvedPath = resolve$1(directory, path);
|
|
13814
|
+
if (sessionMetadata.securityContext && resolvedPath !== resolve$1(directory) && !resolvedPath.startsWith(resolve$1(directory) + "/")) {
|
|
13531
13815
|
throw new Error("Path outside working directory");
|
|
13532
13816
|
}
|
|
13533
13817
|
await fs.mkdir(dirname$1(resolvedPath), { recursive: true });
|
|
13534
13818
|
await fs.writeFile(resolvedPath, Buffer.from(content, "base64"));
|
|
13535
13819
|
},
|
|
13536
13820
|
onListDirectory: async (path) => {
|
|
13537
|
-
const resolvedDir = resolve(directory, path || ".");
|
|
13538
|
-
if (sessionMetadata.securityContext && resolvedDir !== resolve(directory) && !resolvedDir.startsWith(resolve(directory) + "/")) {
|
|
13821
|
+
const resolvedDir = resolve$1(directory, path || ".");
|
|
13822
|
+
if (sessionMetadata.securityContext && resolvedDir !== resolve$1(directory) && !resolvedDir.startsWith(resolve$1(directory) + "/")) {
|
|
13539
13823
|
throw new Error("Path outside working directory");
|
|
13540
13824
|
}
|
|
13541
13825
|
const entries = await fs.readdir(resolvedDir, { withFileTypes: true });
|
|
@@ -13573,8 +13857,8 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
13573
13857
|
return null;
|
|
13574
13858
|
}
|
|
13575
13859
|
}
|
|
13576
|
-
const resolvedPath = resolve(directory, treePath);
|
|
13577
|
-
if (sessionMetadata.securityContext && resolvedPath !== resolve(directory) && !resolvedPath.startsWith(resolve(directory) + "/")) {
|
|
13860
|
+
const resolvedPath = resolve$1(directory, treePath);
|
|
13861
|
+
if (sessionMetadata.securityContext && resolvedPath !== resolve$1(directory) && !resolvedPath.startsWith(resolve$1(directory) + "/")) {
|
|
13578
13862
|
throw new Error("Path outside working directory");
|
|
13579
13863
|
}
|
|
13580
13864
|
const tree = await buildTree(resolvedPath, basename$1(resolvedPath), 0);
|
|
@@ -13623,6 +13907,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
13623
13907
|
sessionService.updateMetadata(sessionMetadata);
|
|
13624
13908
|
if (typeof next.inboxHop === "number") {
|
|
13625
13909
|
writeInboundContext(sessionId, { hopCount: next.inboxHop, threadId: next.inboxThreadId });
|
|
13910
|
+
sessionService.markInboxHandled(next.id);
|
|
13626
13911
|
}
|
|
13627
13912
|
logger.log(`[Session ${sessionId}] Processing queued message: "${next.text.slice(0, 50)}..."`);
|
|
13628
13913
|
sessionWasProcessing = true;
|
|
@@ -13898,6 +14183,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
13898
14183
|
const wake = decision.action === "wake" && acpBackendReady;
|
|
13899
14184
|
if (wake) {
|
|
13900
14185
|
logger.log(`[${agentName} Session ${sessionId}] Delivering inbox message to agent`);
|
|
14186
|
+
sessionService.markInboxHandled(message.messageId);
|
|
13901
14187
|
writeInboundContext(sessionId, { hopCount: message.hopCount ?? 0, threadId: message.threadId || message.messageId });
|
|
13902
14188
|
sessionService.pushMessage(formatted, "user");
|
|
13903
14189
|
sessionMetadata = { ...sessionMetadata, lifecycleState: "running" };
|
|
@@ -13928,6 +14214,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
13928
14214
|
sessionService.updateMetadata(sessionMetadata);
|
|
13929
14215
|
if (typeof next.inboxHop === "number") {
|
|
13930
14216
|
writeInboundContext(sessionId, { hopCount: next.inboxHop, threadId: next.inboxThreadId });
|
|
14217
|
+
sessionService.markInboxHandled(next.id);
|
|
13931
14218
|
}
|
|
13932
14219
|
sessionService.pushMessage(next.text, "user");
|
|
13933
14220
|
sessionService.sendKeepAlive(true);
|
|
@@ -13979,6 +14266,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
13979
14266
|
const remaining = queue.slice(1);
|
|
13980
14267
|
sessionMetadata = { ...sessionMetadata, messageQueue: remaining.length > 0 ? remaining : void 0, lifecycleState: "running" };
|
|
13981
14268
|
sessionService.updateMetadata(sessionMetadata);
|
|
14269
|
+
sessionService.markInboxHandled(next.id);
|
|
13982
14270
|
logger.log(`[Session ${sessionId}] Processing queued message from metadata update: "${next.text.slice(0, 50)}..."`);
|
|
13983
14271
|
sessionService.sendKeepAlive(true);
|
|
13984
14272
|
agentBackend.sendPrompt(sessionId, next.text).catch((err) => {
|
|
@@ -14008,11 +14296,11 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14008
14296
|
});
|
|
14009
14297
|
},
|
|
14010
14298
|
onIssue: async (params) => {
|
|
14011
|
-
const { issueRpc } = await import('./rpc-
|
|
14299
|
+
const { issueRpc } = await import('./rpc-CRpXmlRy.mjs');
|
|
14012
14300
|
return issueRpc(params?.cwd || directory, params || {}, { notifySession: notifyIssueOwner });
|
|
14013
14301
|
},
|
|
14014
14302
|
onWorkflow: async (params) => {
|
|
14015
|
-
const { workflowRpc } = await import('./rpc-
|
|
14303
|
+
const { workflowRpc } = await import('./rpc-Blk-caU2.mjs');
|
|
14016
14304
|
return workflowRpc(params?.cwd || directory, params || {});
|
|
14017
14305
|
},
|
|
14018
14306
|
onRipgrep: async (args, cwd) => {
|
|
@@ -14029,23 +14317,23 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14029
14317
|
});
|
|
14030
14318
|
},
|
|
14031
14319
|
onReadFile: async (path) => {
|
|
14032
|
-
const resolvedPath = resolve(directory, path);
|
|
14033
|
-
if (sessionMetadata.securityContext && resolvedPath !== resolve(directory) && !resolvedPath.startsWith(resolve(directory) + "/")) {
|
|
14320
|
+
const resolvedPath = resolve$1(directory, path);
|
|
14321
|
+
if (sessionMetadata.securityContext && resolvedPath !== resolve$1(directory) && !resolvedPath.startsWith(resolve$1(directory) + "/")) {
|
|
14034
14322
|
throw new Error("Path outside working directory");
|
|
14035
14323
|
}
|
|
14036
14324
|
return await readSessionFileBase64(resolvedPath);
|
|
14037
14325
|
},
|
|
14038
14326
|
onWriteFile: async (path, content) => {
|
|
14039
|
-
const resolvedPath = resolve(directory, path);
|
|
14040
|
-
if (sessionMetadata.securityContext && resolvedPath !== resolve(directory) && !resolvedPath.startsWith(resolve(directory) + "/")) {
|
|
14327
|
+
const resolvedPath = resolve$1(directory, path);
|
|
14328
|
+
if (sessionMetadata.securityContext && resolvedPath !== resolve$1(directory) && !resolvedPath.startsWith(resolve$1(directory) + "/")) {
|
|
14041
14329
|
throw new Error("Path outside working directory");
|
|
14042
14330
|
}
|
|
14043
14331
|
await fs.mkdir(dirname$1(resolvedPath), { recursive: true });
|
|
14044
14332
|
await fs.writeFile(resolvedPath, Buffer.from(content, "base64"));
|
|
14045
14333
|
},
|
|
14046
14334
|
onListDirectory: async (path) => {
|
|
14047
|
-
const resolvedDir = resolve(directory, path || ".");
|
|
14048
|
-
if (sessionMetadata.securityContext && resolvedDir !== resolve(directory) && !resolvedDir.startsWith(resolve(directory) + "/")) {
|
|
14335
|
+
const resolvedDir = resolve$1(directory, path || ".");
|
|
14336
|
+
if (sessionMetadata.securityContext && resolvedDir !== resolve$1(directory) && !resolvedDir.startsWith(resolve$1(directory) + "/")) {
|
|
14049
14337
|
throw new Error("Path outside working directory");
|
|
14050
14338
|
}
|
|
14051
14339
|
const entries = await fs.readdir(resolvedDir, { withFileTypes: true });
|
|
@@ -14083,8 +14371,8 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14083
14371
|
return null;
|
|
14084
14372
|
}
|
|
14085
14373
|
}
|
|
14086
|
-
const resolvedPath = resolve(directory, treePath);
|
|
14087
|
-
if (sessionMetadata.securityContext && resolvedPath !== resolve(directory) && !resolvedPath.startsWith(resolve(directory) + "/")) {
|
|
14374
|
+
const resolvedPath = resolve$1(directory, treePath);
|
|
14375
|
+
if (sessionMetadata.securityContext && resolvedPath !== resolve$1(directory) && !resolvedPath.startsWith(resolve$1(directory) + "/")) {
|
|
14088
14376
|
throw new Error("Path outside working directory");
|
|
14089
14377
|
}
|
|
14090
14378
|
const tree = await buildTree(resolvedPath, basename$1(resolvedPath), 0);
|
|
@@ -14113,6 +14401,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14113
14401
|
const remaining = queue.slice(1);
|
|
14114
14402
|
sessionMetadata = { ...sessionMetadata, messageQueue: remaining.length > 0 ? remaining : void 0, lifecycleState: "running" };
|
|
14115
14403
|
sessionService.updateMetadata(sessionMetadata);
|
|
14404
|
+
sessionService.markInboxHandled(next.id);
|
|
14116
14405
|
logger.log(`[Session ${sessionId}] Processing queued message (ACP loop activation): "${next.text.slice(0, 50)}..."`);
|
|
14117
14406
|
sessionService.sendKeepAlive(true);
|
|
14118
14407
|
agentBackend.sendPrompt(sessionId, next.text).catch((err) => {
|
|
@@ -14203,6 +14492,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14203
14492
|
sessionMetadata = { ...sessionMetadata, messageQueue: remaining.length > 0 ? remaining : void 0, lifecycleState: "running" };
|
|
14204
14493
|
sessionService.updateMetadata(sessionMetadata);
|
|
14205
14494
|
sessionService.sendKeepAlive(true);
|
|
14495
|
+
sessionService.markInboxHandled(next.id);
|
|
14206
14496
|
logger.log(`[Session ${sessionId}] Processing queued message: "${next.text.slice(0, 50)}..."`);
|
|
14207
14497
|
sessionService.pushMessage(next.displayText || next.text, "user");
|
|
14208
14498
|
agentBackend.sendPrompt(sessionId, next.text).catch((err) => {
|
|
@@ -14500,7 +14790,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14500
14790
|
const channelHttpPort = Number(process.env.SVAMP_CHANNEL_HTTP_PORT) || 0;
|
|
14501
14791
|
if (channelHttpPort > 0) {
|
|
14502
14792
|
try {
|
|
14503
|
-
const { createChannelHttpServer } = await import('./httpServer-
|
|
14793
|
+
const { createChannelHttpServer } = await import('./httpServer-B1KVQJfm.mjs');
|
|
14504
14794
|
const channelHttpServer = createChannelHttpServer({
|
|
14505
14795
|
getSessionIds: () => {
|
|
14506
14796
|
const ids = [];
|
|
@@ -14765,7 +15055,7 @@ ${capturedError}${buildClaudeErrorHint(capturedError)}`;
|
|
|
14765
15055
|
const PING_TIMEOUT_MS = 15e3;
|
|
14766
15056
|
const POST_RECONNECT_GRACE_MS = 2e4;
|
|
14767
15057
|
const RECONNECT_JITTER_MS = 2500;
|
|
14768
|
-
const { WorkflowScheduler } = await import('./scheduler-
|
|
15058
|
+
const { WorkflowScheduler } = await import('./scheduler-ro49WXPg.mjs');
|
|
14769
15059
|
const workflowScheduler = new WorkflowScheduler({
|
|
14770
15060
|
projectRoots: () => {
|
|
14771
15061
|
const dirs = /* @__PURE__ */ new Set();
|
|
@@ -15344,4 +15634,4 @@ var run = /*#__PURE__*/Object.freeze({
|
|
|
15344
15634
|
writeStopMarker: writeStopMarker
|
|
15345
15635
|
});
|
|
15346
15636
|
|
|
15347
|
-
export {
|
|
15637
|
+
export { describeMisconfiguration as $, buildMachineInstructions as A, machineToolsForRole as B, buildMachineTools as C, parseFrontmatter as D, getSkillsServer as E, getSkillsWorkspaceName as F, getSkillsCollectionName as G, fetchWithTimeout as H, searchSkills as I, SKILLS_DIR as J, getSkillInfo as K, downloadSkillFile as L, listSkillFiles as M, resolveModel as N, formatHandle as O, normalizeAllowedUser as P, loadSecurityContextConfig as Q, READ_ONLY_TOOLS as R, ServeAuth as S, resolveSecurityContext as T, buildSecurityContextFromFlags as U, mergeSecurityContexts as V, buildSessionShareUrl as W, computeOutboundHop as X, buildMachineShareUrl as Y, parseHandle as Z, handleMatchesMetadata as _, createSessionStore as a, buildMachineDeps as a0, applyClaudeProxyEnv as a1, composeSessionId as a2, generateFriendlyName as a3, generateHookSettings as a4, claudeAuth as a5, projectInfo as a6, DefaultTransport$1 as a7, acpBackend as a8, acpAgentConfig as a9, codexMcpBackend as aa, GeminiTransport$1 as ab, instanceConfig as ac, api as ad, run as ae, 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, resolveProjectRoot as m, getIssue as n, addComment as o, addIssue as p, listIssues as q, registerMachineService as r, startDaemon as s, searchIssues as t, updateIssue as u, isVisibleTo as v, cronMatches as w, summarize as x, shortId as y, loadMachineContext as z };
|