niahere 0.3.10 → 0.3.11
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/README.md +2 -1
- package/package.json +1 -1
- package/src/channels/index.ts +19 -0
- package/src/channels/slack.ts +3 -1
- package/src/channels/telegram.ts +2 -1
- package/src/chat/engine.ts +1 -2
- package/src/cli/index.ts +21 -6
- package/src/cli/status.ts +2 -2
- package/src/commands/validate.ts +2 -0
- package/src/core/daemon.ts +13 -3
- package/src/types/config.ts +2 -0
- package/src/utils/config.ts +8 -2
package/README.md
CHANGED
|
@@ -86,7 +86,8 @@ nia agent show <name> — show agent details and prompt
|
|
|
86
86
|
nia skills [source] — list available skills
|
|
87
87
|
|
|
88
88
|
nia channels — show channel status (on/off)
|
|
89
|
-
nia channels on / off — enable/disable channels (applied via SIGHUP, no restart)
|
|
89
|
+
nia channels on / off — enable/disable all channels (applied via SIGHUP, no restart)
|
|
90
|
+
nia channels off telegram — disable one channel without removing its token
|
|
90
91
|
nia watch list — list Slack watch channels
|
|
91
92
|
nia watch add/remove/enable/disable — manage watch channels
|
|
92
93
|
|
package/package.json
CHANGED
package/src/channels/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Channel } from "../types";
|
|
2
2
|
import { registerChannel, getFactories, trackStarted, clearStarted } from "./registry";
|
|
3
3
|
import { log } from "../utils/log";
|
|
4
|
+
import { getConfig } from "../utils/config";
|
|
4
5
|
import { createTelegramChannel } from "./telegram";
|
|
5
6
|
import { createSlackChannel } from "./slack";
|
|
6
7
|
import { createPhoneChannel } from "./phone";
|
|
@@ -59,6 +60,24 @@ export async function startChannels(): Promise<StartResult> {
|
|
|
59
60
|
return { started, failed };
|
|
60
61
|
}
|
|
61
62
|
|
|
63
|
+
export function getConfiguredChannelNames(): string[] {
|
|
64
|
+
const { channels } = getConfig();
|
|
65
|
+
if (!channels.enabled) return [];
|
|
66
|
+
|
|
67
|
+
const names: string[] = [];
|
|
68
|
+
if (channels.telegram.enabled && channels.telegram.bot_token) names.push("telegram");
|
|
69
|
+
if (channels.slack.enabled && channels.slack.bot_token && channels.slack.app_token) names.push("slack");
|
|
70
|
+
if (channels.phone.enabled && channels.twilio.sid && channels.twilio.secret && channels.phone.from_number) {
|
|
71
|
+
names.push("phone");
|
|
72
|
+
}
|
|
73
|
+
const smsFromNumber = channels.sms.from_number ?? channels.phone.from_number;
|
|
74
|
+
if (channels.sms.enabled && channels.twilio.sid && channels.twilio.secret && smsFromNumber) names.push("sms");
|
|
75
|
+
if (channels.whatsapp.enabled && channels.twilio.sid && channels.twilio.secret && channels.whatsapp.from_number) {
|
|
76
|
+
names.push("whatsapp");
|
|
77
|
+
}
|
|
78
|
+
return names;
|
|
79
|
+
}
|
|
80
|
+
|
|
62
81
|
export async function stopChannels(channels: Channel[]): Promise<void> {
|
|
63
82
|
const results = await Promise.allSettled(
|
|
64
83
|
channels.map(async (channel) => {
|
package/src/channels/slack.ts
CHANGED
|
@@ -517,6 +517,8 @@ class SlackChannel implements Channel {
|
|
|
517
517
|
|
|
518
518
|
export function createSlackChannel(): SlackChannel | null {
|
|
519
519
|
const config = getConfig();
|
|
520
|
-
if (!config.channels.slack.bot_token || !config.channels.slack.app_token)
|
|
520
|
+
if (!config.channels.slack.enabled || !config.channels.slack.bot_token || !config.channels.slack.app_token) {
|
|
521
|
+
return null;
|
|
522
|
+
}
|
|
521
523
|
return new SlackChannel();
|
|
522
524
|
}
|
package/src/channels/telegram.ts
CHANGED
|
@@ -286,6 +286,7 @@ class TelegramChannel implements Channel {
|
|
|
286
286
|
}
|
|
287
287
|
|
|
288
288
|
export function createTelegramChannel(): TelegramChannel | null {
|
|
289
|
-
|
|
289
|
+
const telegram = getConfig().channels.telegram;
|
|
290
|
+
if (!telegram.enabled || !telegram.bot_token) return null;
|
|
290
291
|
return new TelegramChannel();
|
|
291
292
|
}
|
package/src/chat/engine.ts
CHANGED
|
@@ -33,8 +33,7 @@ const IDLE_TIMEOUT = 10 * 60 * 1000; // 10 minutes
|
|
|
33
33
|
const LONG_RUNNING_WARN = 30 * 60 * 1000; // 30 minutes
|
|
34
34
|
const MAX_SEND_RETRIES = 2;
|
|
35
35
|
const SEND_RETRY_DELAYS = [3_000, 8_000];
|
|
36
|
-
const GENERIC_CHAT_ERROR =
|
|
37
|
-
"Claude/Anthropic returned an error without details. This is usually temporary; please try again shortly.";
|
|
36
|
+
const GENERIC_CHAT_ERROR = "💀";
|
|
38
37
|
|
|
39
38
|
interface SDKUserMessage {
|
|
40
39
|
type: "user";
|
package/src/cli/index.ts
CHANGED
|
@@ -49,8 +49,10 @@ async function awaitStartup(timeout = 60_000): Promise<void> {
|
|
|
49
49
|
const config = getConfig();
|
|
50
50
|
const expecting = new Set<string>();
|
|
51
51
|
if (config.channels.enabled) {
|
|
52
|
-
if (config.channels.telegram.bot_token) expecting.add("telegram");
|
|
53
|
-
if (config.channels.slack.bot_token && config.channels.slack.app_token)
|
|
52
|
+
if (config.channels.telegram.enabled && config.channels.telegram.bot_token) expecting.add("telegram");
|
|
53
|
+
if (config.channels.slack.enabled && config.channels.slack.bot_token && config.channels.slack.app_token) {
|
|
54
|
+
expecting.add("slack");
|
|
55
|
+
}
|
|
54
56
|
}
|
|
55
57
|
expecting.add("scheduler");
|
|
56
58
|
|
|
@@ -445,16 +447,29 @@ switch (command) {
|
|
|
445
447
|
|
|
446
448
|
case "channels": {
|
|
447
449
|
const sub = process.argv[3];
|
|
450
|
+
const target = process.argv[4];
|
|
448
451
|
const { updateRawConfig } = await import("../utils/config");
|
|
449
452
|
if (sub === "on" || sub === "off") {
|
|
450
453
|
const enabled = sub === "on";
|
|
451
|
-
|
|
454
|
+
if (target) {
|
|
455
|
+
const supported = new Set(["telegram", "slack", "phone", "sms", "whatsapp"]);
|
|
456
|
+
if (!supported.has(target)) fail("Usage: nia channels <on|off> [telegram|slack|phone|sms|whatsapp]");
|
|
457
|
+
updateRawConfig({ channels: { ...(enabled ? { enabled: true } : {}), [target]: { enabled } } });
|
|
458
|
+
} else {
|
|
459
|
+
updateRawConfig({ channels: { enabled } });
|
|
460
|
+
}
|
|
452
461
|
const pid = readPid();
|
|
453
462
|
if (pid && isRunning()) {
|
|
454
463
|
process.kill(pid, "SIGHUP");
|
|
455
|
-
console.log(
|
|
464
|
+
console.log(
|
|
465
|
+
target ? `${target} ${enabled ? "enabled" : "disabled"}` : `channels ${enabled ? "enabled" : "disabled"}`,
|
|
466
|
+
);
|
|
456
467
|
} else {
|
|
457
|
-
console.log(
|
|
468
|
+
console.log(
|
|
469
|
+
target
|
|
470
|
+
? `${target} ${enabled ? "enabled" : "disabled"} — start nia to apply`
|
|
471
|
+
: `channels ${enabled ? "enabled" : "disabled"} — start nia to apply`,
|
|
472
|
+
);
|
|
458
473
|
}
|
|
459
474
|
} else {
|
|
460
475
|
console.log(`channels: ${getConfig().channels.enabled ? "on" : "off"}`);
|
|
@@ -605,7 +620,7 @@ Persona:
|
|
|
605
620
|
skills [source] List available skills
|
|
606
621
|
|
|
607
622
|
Channels:
|
|
608
|
-
channels [on|off]
|
|
623
|
+
channels [on|off] [name] Toggle all channels or one channel
|
|
609
624
|
watch <sub> Manage Slack watch channels
|
|
610
625
|
telegram [setup] Configure telegram
|
|
611
626
|
slack [setup] Configure slack
|
package/src/cli/status.ts
CHANGED
|
@@ -84,7 +84,7 @@ export async function statusCommand(argv: string[] = []): Promise<void> {
|
|
|
84
84
|
configured: Boolean(config.channels.telegram.bot_token),
|
|
85
85
|
status: !config.channels.telegram.bot_token
|
|
86
86
|
? "not configured"
|
|
87
|
-
: !config.channels.enabled
|
|
87
|
+
: !config.channels.enabled || !config.channels.telegram.enabled
|
|
88
88
|
? "disabled"
|
|
89
89
|
: running
|
|
90
90
|
? "active"
|
|
@@ -96,7 +96,7 @@ export async function statusCommand(argv: string[] = []): Promise<void> {
|
|
|
96
96
|
appTokenConfigured: Boolean(config.channels.slack.app_token),
|
|
97
97
|
status: !config.channels.slack.bot_token
|
|
98
98
|
? "not configured"
|
|
99
|
-
: !config.channels.enabled
|
|
99
|
+
: !config.channels.enabled || !config.channels.slack.enabled
|
|
100
100
|
? "disabled"
|
|
101
101
|
: running
|
|
102
102
|
? config.channels.slack.app_token
|
package/src/commands/validate.ts
CHANGED
|
@@ -114,6 +114,7 @@ export function validateConfig(): Result {
|
|
|
114
114
|
// Telegram
|
|
115
115
|
const tg = ch.telegram as Record<string, unknown> | undefined;
|
|
116
116
|
if (tg) {
|
|
117
|
+
if (tg.enabled === false) messages.push(`${WARN} telegram disabled`);
|
|
117
118
|
if (tg.bot_token) {
|
|
118
119
|
messages.push(`${PASS} telegram.bot_token set`);
|
|
119
120
|
} else {
|
|
@@ -124,6 +125,7 @@ export function validateConfig(): Result {
|
|
|
124
125
|
// Slack
|
|
125
126
|
const sl = ch.slack as Record<string, unknown> | undefined;
|
|
126
127
|
if (sl) {
|
|
128
|
+
if (sl.enabled === false) messages.push(`${WARN} slack disabled`);
|
|
127
129
|
if (!sl.bot_token) {
|
|
128
130
|
messages.push(`${WARN} slack.bot_token missing — slack won't start`);
|
|
129
131
|
} else {
|
package/src/core/daemon.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { isRunning, readPid, removePid, writePid } from "../utils/pid";
|
|
|
8
8
|
import { ActiveEngine, Job } from "../db/models";
|
|
9
9
|
import { runMigrations } from "../db/migrate";
|
|
10
10
|
import { closeDb, getSql } from "../db/connection";
|
|
11
|
-
import { registerAllChannels, startChannels, stopChannels, getStarted } from "../channels";
|
|
11
|
+
import { registerAllChannels, startChannels, stopChannels, getStarted, getConfiguredChannelNames } from "../channels";
|
|
12
12
|
import type { Channel } from "../types";
|
|
13
13
|
import { startScheduler, stopScheduler, recomputeAllNextRuns } from "./scheduler";
|
|
14
14
|
import { startAlive, stopAlive } from "./alive";
|
|
@@ -346,11 +346,16 @@ export async function runDaemon(): Promise<void> {
|
|
|
346
346
|
process.on("SIGHUP", async () => {
|
|
347
347
|
log.info("received SIGHUP, reloading config");
|
|
348
348
|
resetConfig();
|
|
349
|
-
const fresh = getConfig();
|
|
350
349
|
|
|
351
350
|
const running = getStarted();
|
|
352
|
-
const
|
|
351
|
+
const wantedNames = getConfiguredChannelNames();
|
|
352
|
+
const runningNames = running.map((channel) => channel.name).sort();
|
|
353
|
+
const wantChannels = wantedNames.length > 0;
|
|
353
354
|
const haveChannels = running.length > 0;
|
|
355
|
+
const needsReconcile =
|
|
356
|
+
wantChannels &&
|
|
357
|
+
haveChannels &&
|
|
358
|
+
(wantedNames.length !== runningNames.length || wantedNames.sort().some((name, i) => name !== runningNames[i]));
|
|
354
359
|
|
|
355
360
|
if (wantChannels && !haveChannels) {
|
|
356
361
|
log.info("SIGHUP: starting channels");
|
|
@@ -360,6 +365,11 @@ export async function runDaemon(): Promise<void> {
|
|
|
360
365
|
log.info("SIGHUP: stopping channels");
|
|
361
366
|
await stopChannels(running);
|
|
362
367
|
channels = [];
|
|
368
|
+
} else if (needsReconcile) {
|
|
369
|
+
log.info({ wantedNames, runningNames }, "SIGHUP: reconciling channels");
|
|
370
|
+
await stopChannels(running);
|
|
371
|
+
const result = await startChannels();
|
|
372
|
+
channels = result.started;
|
|
363
373
|
}
|
|
364
374
|
|
|
365
375
|
await recomputeAllNextRuns().catch(() => {});
|
package/src/types/config.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export interface TelegramConfig {
|
|
2
|
+
enabled: boolean;
|
|
2
3
|
bot_token: string | null;
|
|
3
4
|
chat_id: number | null;
|
|
4
5
|
open: boolean;
|
|
@@ -17,6 +18,7 @@ export interface SlackWatchChannel {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export interface SlackConfig {
|
|
21
|
+
enabled: boolean;
|
|
20
22
|
bot_token: string | null;
|
|
21
23
|
app_token: string | null;
|
|
22
24
|
dm_user_id: string | null;
|
package/src/utils/config.ts
CHANGED
|
@@ -24,8 +24,9 @@ const DEFAULTS: Config = {
|
|
|
24
24
|
channels: {
|
|
25
25
|
enabled: true,
|
|
26
26
|
default: "telegram",
|
|
27
|
-
telegram: { bot_token: null, chat_id: null, open: false },
|
|
27
|
+
telegram: { enabled: true, bot_token: null, chat_id: null, open: false },
|
|
28
28
|
slack: {
|
|
29
|
+
enabled: true,
|
|
29
30
|
bot_token: null,
|
|
30
31
|
app_token: null,
|
|
31
32
|
dm_user_id: null,
|
|
@@ -157,6 +158,8 @@ export function loadConfig(): Config {
|
|
|
157
158
|
const defaultChannel = typeof ch.default === "string" ? ch.default : DEFAULTS.channels.default;
|
|
158
159
|
|
|
159
160
|
// Telegram — env vars override config
|
|
161
|
+
const tgEnabled = chTg.enabled !== false;
|
|
162
|
+
|
|
160
163
|
const tgBotToken = process.env.TELEGRAM_BOT_TOKEN || (typeof chTg.bot_token === "string" ? chTg.bot_token : null);
|
|
161
164
|
|
|
162
165
|
const tgChatId =
|
|
@@ -166,6 +169,8 @@ export function loadConfig(): Config {
|
|
|
166
169
|
const tgOpen = chTg.open === true;
|
|
167
170
|
|
|
168
171
|
// Slack — env vars override config
|
|
172
|
+
const slEnabled = chSl.enabled !== false;
|
|
173
|
+
|
|
169
174
|
const slBotToken = process.env.SLACK_BOT_TOKEN || (typeof chSl.bot_token === "string" ? chSl.bot_token : null);
|
|
170
175
|
|
|
171
176
|
const slAppToken = process.env.SLACK_APP_TOKEN || (typeof chSl.app_token === "string" ? chSl.app_token : null);
|
|
@@ -254,8 +259,9 @@ export function loadConfig(): Config {
|
|
|
254
259
|
channels: {
|
|
255
260
|
enabled: channelsEnabled,
|
|
256
261
|
default: defaultChannel,
|
|
257
|
-
telegram: { bot_token: tgBotToken, chat_id: tgChatId, open: tgOpen },
|
|
262
|
+
telegram: { enabled: tgEnabled, bot_token: tgBotToken, chat_id: tgChatId, open: tgOpen },
|
|
258
263
|
slack: {
|
|
264
|
+
enabled: slEnabled,
|
|
259
265
|
bot_token: slBotToken,
|
|
260
266
|
app_token: slAppToken,
|
|
261
267
|
dm_user_id: slDmUserId,
|