niahere 0.2.78 → 0.2.80
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/package.json +1 -1
- package/src/channels/slack.ts +16 -1
- package/src/channels/telegram.ts +8 -3
- package/src/cli/index.ts +2 -2
- package/src/core/daemon.ts +4 -1
- package/src/core/engine-guard.ts +5 -0
- package/src/mcp/tools.ts +3 -1
- package/src/prompts/environment.md +1 -0
- package/src/types/channel.ts +2 -0
package/package.json
CHANGED
package/src/channels/slack.ts
CHANGED
|
@@ -53,6 +53,16 @@ class SlackChannel implements Channel {
|
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
async sendMediaToThread(channelId: string, data: Buffer, mimeType: string, filename?: string, threadTs?: string): Promise<void> {
|
|
57
|
+
if (!this.app) throw new Error("Slack not started");
|
|
58
|
+
await this.app.client.filesUploadV2({
|
|
59
|
+
channel_id: channelId,
|
|
60
|
+
file: data,
|
|
61
|
+
filename: filename || `file.${mimeType.split("/")[1] || "bin"}`,
|
|
62
|
+
...(threadTs ? { thread_ts: threadTs } : {}),
|
|
63
|
+
} as any);
|
|
64
|
+
}
|
|
65
|
+
|
|
56
66
|
async start(): Promise<void> {
|
|
57
67
|
const config = getConfig();
|
|
58
68
|
const botToken = config.channels.slack.bot_token!;
|
|
@@ -308,6 +318,11 @@ class SlackChannel implements Channel {
|
|
|
308
318
|
return ext && /^[a-zA-Z0-9]{1,16}$/.test(ext) ? ext : "bin";
|
|
309
319
|
}
|
|
310
320
|
|
|
321
|
+
function cacheExtension(filename: string | undefined, mime: string, attType: AttachmentType): string {
|
|
322
|
+
if (attType === "image" && mime !== "image/gif") return "jpg";
|
|
323
|
+
return safeExtension(filename);
|
|
324
|
+
}
|
|
325
|
+
|
|
311
326
|
async function extractSlackAttachments(files: any[], scope: string): Promise<Attachment[]> {
|
|
312
327
|
const attachments: Attachment[] = [];
|
|
313
328
|
const scopedAttachDir = cacheDirForScope(scope);
|
|
@@ -327,7 +342,7 @@ class SlackChannel implements Channel {
|
|
|
327
342
|
|
|
328
343
|
// Check disk (survives daemon restarts) — scoped by Slack room/thread.
|
|
329
344
|
const hash = urlHash(file.url_private_download);
|
|
330
|
-
const ext =
|
|
345
|
+
const ext = cacheExtension(file.name, mime, attType);
|
|
331
346
|
const diskPath = join(scopedAttachDir, `${hash}.${ext}`);
|
|
332
347
|
const metaPath = join(scopedAttachDir, `${hash}.meta.json`);
|
|
333
348
|
if (existsSync(diskPath) && existsSync(metaPath)) {
|
package/src/channels/telegram.ts
CHANGED
|
@@ -17,6 +17,11 @@ function safeExtension(filename?: string): string {
|
|
|
17
17
|
return ext && /^[a-zA-Z0-9]{1,16}$/.test(ext) ? ext : "bin";
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
function cacheExtension(filename: string | undefined, mimeType: string): string {
|
|
21
|
+
if (mimeType === "image/jpeg") return "jpg";
|
|
22
|
+
return safeExtension(filename);
|
|
23
|
+
}
|
|
24
|
+
|
|
20
25
|
class TelegramChannel implements Channel {
|
|
21
26
|
name = "telegram";
|
|
22
27
|
private bot: Bot | null = null;
|
|
@@ -52,11 +57,11 @@ class TelegramChannel implements Channel {
|
|
|
52
57
|
return Buffer.from(await resp.arrayBuffer());
|
|
53
58
|
}
|
|
54
59
|
|
|
55
|
-
private cacheAttachment(chatId: number, roomIndex: number, data: Buffer, filename?: string): string {
|
|
60
|
+
private cacheAttachment(chatId: number, roomIndex: number, data: Buffer, mimeType: string, filename?: string): string {
|
|
56
61
|
const scope = `telegram-${chatId}-${roomIndex}`;
|
|
57
62
|
const dir = join(getNiaHome(), "tmp", "attachments", scope);
|
|
58
63
|
mkdirSync(dir, { recursive: true });
|
|
59
|
-
const ext =
|
|
64
|
+
const ext = cacheExtension(filename, mimeType);
|
|
60
65
|
const hash = createHash("sha256").update(data).digest("hex").slice(0, 16);
|
|
61
66
|
const path = join(dir, `${hash}.${ext}`);
|
|
62
67
|
writeFileSync(path, data);
|
|
@@ -257,7 +262,7 @@ class TelegramChannel implements Channel {
|
|
|
257
262
|
data = prepared.data;
|
|
258
263
|
finalMime = prepared.mimeType;
|
|
259
264
|
}
|
|
260
|
-
const sourcePath = self.cacheAttachment(ctx.chatId, state.roomIndex, data, doc.file_name);
|
|
265
|
+
const sourcePath = self.cacheAttachment(ctx.chatId, state.roomIndex, data, finalMime, doc.file_name);
|
|
261
266
|
const attachment: Attachment = { type: attType, data, mimeType: finalMime, filename: doc.file_name, sourcePath };
|
|
262
267
|
const caption = ctx.message.caption || (attType === "image" ? "What's in this image?" : "Here's a file.");
|
|
263
268
|
await processMessage(ctx, state, caption, [attachment]);
|
package/src/cli/index.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { rulesCommand, memoryCommand } from "./self";
|
|
|
16
16
|
import { watchCommand } from "./watch";
|
|
17
17
|
import { agentCommand } from "./agent";
|
|
18
18
|
import { employeeCommand } from "./employee";
|
|
19
|
-
import { guardActiveEngines, parseGuardFlags } from "../core/engine-guard";
|
|
19
|
+
import { guardActiveEngines, parseGuardFlags, withDefaultWait } from "../core/engine-guard";
|
|
20
20
|
|
|
21
21
|
// Set LOG_LEVEL from config before anything else logs
|
|
22
22
|
try {
|
|
@@ -497,7 +497,7 @@ switch (command) {
|
|
|
497
497
|
}
|
|
498
498
|
|
|
499
499
|
case "update": {
|
|
500
|
-
const updateGuard = parseGuardFlags(process.argv.slice(3));
|
|
500
|
+
const updateGuard = withDefaultWait(parseGuardFlags(process.argv.slice(3)), 1);
|
|
501
501
|
const { version: currentVersion } = await import("../../package.json");
|
|
502
502
|
console.log(`Current: v${currentVersion}`);
|
|
503
503
|
|
package/src/core/daemon.ts
CHANGED
|
@@ -57,7 +57,10 @@ export function stopDaemon(opts: { force?: boolean } = {}): boolean {
|
|
|
57
57
|
|
|
58
58
|
// Kill all daemon processes — pidfile PID plus any orphans.
|
|
59
59
|
const killed = killAllDaemons(pidfilePid);
|
|
60
|
-
if (killed === 0 && pidfilePid === null)
|
|
60
|
+
if (killed === 0 && pidfilePid === null) {
|
|
61
|
+
if (opts.force) clearForceShutdownRequest();
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
61
64
|
|
|
62
65
|
// Wait for processes to finish (up to 5 min for active engines, then SIGKILL)
|
|
63
66
|
waitForExit(opts.force ? 30_000 : 310_000);
|
package/src/core/engine-guard.ts
CHANGED
|
@@ -30,6 +30,11 @@ export function parseGuardFlags(args: string[]): GuardOptions {
|
|
|
30
30
|
return { waitMinutes, force };
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
export function withDefaultWait(opts: GuardOptions, defaultWaitMinutes: number): GuardOptions {
|
|
34
|
+
if (opts.force || opts.waitMinutes > 0) return opts;
|
|
35
|
+
return { ...opts, waitMinutes: defaultWaitMinutes };
|
|
36
|
+
}
|
|
37
|
+
|
|
33
38
|
interface ActiveSummary {
|
|
34
39
|
count: number;
|
|
35
40
|
rooms: string[];
|
package/src/mcp/tools.ts
CHANGED
|
@@ -296,7 +296,9 @@ export async function sendMessage(text: string, channelName?: string, mediaPath?
|
|
|
296
296
|
const mimeType = guessMime(mediaPath);
|
|
297
297
|
const filename = basename(mediaPath);
|
|
298
298
|
|
|
299
|
-
if (channel?.
|
|
299
|
+
if (useThread && channel?.sendMediaToThread) {
|
|
300
|
+
await channel.sendMediaToThread(sourceCtx!.slackChannelId!, data, mimeType, filename, sourceCtx!.slackThreadTs);
|
|
301
|
+
} else if (channel?.sendMedia) {
|
|
300
302
|
await channel.sendMedia(data, mimeType, filename);
|
|
301
303
|
} else {
|
|
302
304
|
await sendMediaDirect(channelTarget, data, mimeType, filename);
|
|
@@ -90,6 +90,7 @@ After config changes, run `nia restart` to apply.
|
|
|
90
90
|
`nia stop`, `nia restart`, and `nia update` guard against active engines by default.
|
|
91
91
|
|
|
92
92
|
- `--wait <minutes>` — poll every 5s, proceed when engines clear or timeout
|
|
93
|
+
- `nia update` waits up to 1 minute by default when engines are active
|
|
93
94
|
- `--force` — skip the engine check, proceed immediately
|
|
94
95
|
|
|
95
96
|
Config reference:
|
package/src/types/channel.ts
CHANGED
|
@@ -4,6 +4,8 @@ export interface Channel {
|
|
|
4
4
|
stop(): Promise<void>;
|
|
5
5
|
sendMessage?(text: string): Promise<void>;
|
|
6
6
|
sendMedia?(data: Buffer, mimeType: string, filename?: string): Promise<void>;
|
|
7
|
+
/** Send media to a specific channel/thread when the channel supports it. */
|
|
8
|
+
sendMediaToThread?(channelId: string, data: Buffer, mimeType: string, filename?: string, threadTs?: string): Promise<void>;
|
|
7
9
|
/** Send a message to a specific channel/thread (e.g. reply back to a Slack thread). */
|
|
8
10
|
sendToThread?(channelId: string, text: string, threadTs?: string): Promise<void>;
|
|
9
11
|
}
|