shadok-ai 0.17.8 → 0.17.9
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/server.js +52 -5
- package/dist/server.js.map +1 -1
- package/dist/telegram.d.ts +1 -1
- package/dist/telegram.js +20 -8
- package/dist/telegram.js.map +1 -1
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -42,7 +42,7 @@ import { computePace, paceBlock, WINDOW_SEC } from "./pace.js";
|
|
|
42
42
|
import { getUsage } from "./usage.js";
|
|
43
43
|
import { loadChannels, loadGroups, saveGroups, normalizeGroups, ensureMainGroup, upsertChannel, removeChannel, mergeClientChannels, channelWriteAllowed, profilePatch, loadTgGroup, saveTgGroup, isHomeChannel, homeAdoptionTarget, resolveSessionTarget, resolveResumeId, resumeTarget, } from "./channels.js";
|
|
44
44
|
import { deliverWithRetry } from "./reload.js";
|
|
45
|
-
import { startTelegram, renameTelegramTopic, closeTelegramTopic, probeToken, isStalePreface, announceLoggedOut, resetLoggedOutNotice, attachmentPrompt, pasteFileName,
|
|
45
|
+
import { startTelegram, renameTelegramTopic, closeTelegramTopic, probeToken, isStalePreface, announceLoggedOut, resetLoggedOutNotice, attachmentPrompt, pasteFileName, mediaDir, } from "./telegram.js";
|
|
46
46
|
import { transcribe, audioClipExtension } from "./whisper.js";
|
|
47
47
|
import { cancelPairingCode, issuePairingCode, pairingStatus } from "./tg-pairing.js";
|
|
48
48
|
import { migrateTgBindings } from "./channels.js";
|
|
@@ -1377,8 +1377,9 @@ app.post("/paste", express.raw({ type: () => true, limit: PASTE_LIMIT }), (req,
|
|
|
1377
1377
|
const name = decodeURIComponent(String(req.headers["x-filename"] ?? ""));
|
|
1378
1378
|
const kind = contentType.trim().toLowerCase().startsWith("image/") ? "image" : "file";
|
|
1379
1379
|
try {
|
|
1380
|
-
|
|
1381
|
-
|
|
1380
|
+
const dir = mediaDir();
|
|
1381
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
1382
|
+
const file = path.join(dir, pasteFileName(randomUUID(), name, contentType));
|
|
1382
1383
|
fs.writeFileSync(file, body);
|
|
1383
1384
|
// The very wording Telegram already uses, so an agent sees one format.
|
|
1384
1385
|
res.json({ path: file, line: attachmentPrompt([{ path: file, kind }]) });
|
|
@@ -2246,6 +2247,51 @@ function channelStateHome(chan) {
|
|
|
2246
2247
|
}
|
|
2247
2248
|
/** Task 12: the one shared pot every group HOME links as `~/shared`. */
|
|
2248
2249
|
const SHARED_POT_PATH = (process.env.SHADOK_SHARED_POT_PATH ?? "").trim() || DEFAULT_SHARED_POT_PATH;
|
|
2250
|
+
// Multi-tenant: Telegram/paste attachments must be readable by a privilege-
|
|
2251
|
+
// dropped agent, but the default `~/.shadok-ai/media` is `/root/.shadok-ai/media`
|
|
2252
|
+
// — and `/root` (mode 700) is un-traversable by the `main`/`sk-*` drop users, so
|
|
2253
|
+
// an agent handed `[Attached image: /root/.shadok-ai/media/…]` cannot open it
|
|
2254
|
+
// (invariant 54's family). Relocate media under the shared pot (which every drop
|
|
2255
|
+
// user is in `shadok-shared` to traverse), carry existing attachments over so
|
|
2256
|
+
// already-sent images keep resolving, and point `mediaDir()` there via the env.
|
|
2257
|
+
// Best-effort: a failure just leaves media at `~/.shadok-ai/media`, the old
|
|
2258
|
+
// (broken-for-agents) behaviour, never a boot failure. Mono-tenant is untouched.
|
|
2259
|
+
if (DEPLOYMENT_MODE === "multi-tenant") {
|
|
2260
|
+
try {
|
|
2261
|
+
const shared = path.join(SHARED_POT_PATH, "media");
|
|
2262
|
+
fs.mkdirSync(shared, { recursive: true });
|
|
2263
|
+
// Own it to the pot's group (`shadok-shared`) with setgid, so files the
|
|
2264
|
+
// server writes inherit that group and the drop users can read them.
|
|
2265
|
+
try {
|
|
2266
|
+
const potGid = fs.statSync(SHARED_POT_PATH).gid;
|
|
2267
|
+
fs.chownSync(shared, 0, potGid);
|
|
2268
|
+
fs.chmodSync(shared, 0o2775);
|
|
2269
|
+
}
|
|
2270
|
+
catch {
|
|
2271
|
+
/* pot not provisioned yet (pre-migration) — the next boot fixes it */
|
|
2272
|
+
}
|
|
2273
|
+
const legacy = path.join(os.homedir(), ".shadok-ai", "media");
|
|
2274
|
+
if (legacy !== shared && fs.existsSync(legacy)) {
|
|
2275
|
+
for (const name of fs.readdirSync(legacy)) {
|
|
2276
|
+
const dst = path.join(shared, name);
|
|
2277
|
+
if (!fs.existsSync(dst)) {
|
|
2278
|
+
try {
|
|
2279
|
+
fs.copyFileSync(path.join(legacy, name), dst);
|
|
2280
|
+
}
|
|
2281
|
+
catch {
|
|
2282
|
+
/* skip an unreadable/odd entry */
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
process.env.SHADOK_MEDIA_DIR = shared;
|
|
2288
|
+
console.log(`media: using ${shared} (reachable by dropped agents)`);
|
|
2289
|
+
}
|
|
2290
|
+
catch (e) {
|
|
2291
|
+
console.warn("media: could not set up the shared media dir, keeping ~/.shadok-ai/media: " +
|
|
2292
|
+
(e instanceof Error ? e.message : String(e)));
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2249
2295
|
/** Run a privileged account command. Root runs it directly; otherwise go through
|
|
2250
2296
|
* NON-interactive sudo (`-n`) so a MISSING right fails fast with a non-zero exit
|
|
2251
2297
|
* instead of prompting — the T3 false positive (sudoers allows `true` but not
|
|
@@ -4289,8 +4335,9 @@ app.get("/download", (req, res) => {
|
|
|
4289
4335
|
// same `nosniff` + `sandbox` CSP as /download neutralises an SVG opened directly.
|
|
4290
4336
|
app.get("/media/:name", (req, res) => {
|
|
4291
4337
|
const base = path.basename(String(req.params.name ?? ""));
|
|
4292
|
-
const
|
|
4293
|
-
|
|
4338
|
+
const dir = mediaDir();
|
|
4339
|
+
const file = path.join(dir, base);
|
|
4340
|
+
if (!file.startsWith(path.join(dir, path.sep)) && path.dirname(file) !== dir) {
|
|
4294
4341
|
return res.status(400).type("text").send("bad name");
|
|
4295
4342
|
}
|
|
4296
4343
|
let stat;
|