verybot 0.1.8
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 +167 -0
- package/dist/aliases/store.d.ts +21 -0
- package/dist/aliases/store.js +148 -0
- package/dist/aliases/types.d.ts +6 -0
- package/dist/aliases/types.js +1 -0
- package/dist/brain/agent-registry.d.ts +96 -0
- package/dist/brain/agent-registry.js +141 -0
- package/dist/brain/agent.d.ts +167 -0
- package/dist/brain/agent.js +932 -0
- package/dist/brain/channel-store.d.ts +27 -0
- package/dist/brain/channel-store.js +78 -0
- package/dist/brain/compaction.d.ts +37 -0
- package/dist/brain/compaction.js +214 -0
- package/dist/brain/context.d.ts +43 -0
- package/dist/brain/context.js +139 -0
- package/dist/brain/delegation-store.d.ts +33 -0
- package/dist/brain/delegation-store.js +106 -0
- package/dist/brain/loop.d.ts +24 -0
- package/dist/brain/loop.js +318 -0
- package/dist/brain/mcp-adapter.d.ts +43 -0
- package/dist/brain/mcp-adapter.js +244 -0
- package/dist/brain/memory-extractor.d.ts +26 -0
- package/dist/brain/memory-extractor.js +82 -0
- package/dist/brain/providers.d.ts +14 -0
- package/dist/brain/providers.js +85 -0
- package/dist/brain/queue.d.ts +18 -0
- package/dist/brain/queue.js +111 -0
- package/dist/brain/run-tools.d.ts +50 -0
- package/dist/brain/run-tools.js +136 -0
- package/dist/brain/session-key.d.ts +23 -0
- package/dist/brain/session-key.js +41 -0
- package/dist/brain/session-state.d.ts +36 -0
- package/dist/brain/session-state.js +51 -0
- package/dist/brain/session-store.d.ts +50 -0
- package/dist/brain/session-store.js +207 -0
- package/dist/brain/session.d.ts +32 -0
- package/dist/brain/session.js +75 -0
- package/dist/brain/task-subscriber.d.ts +56 -0
- package/dist/brain/task-subscriber.js +317 -0
- package/dist/brain/user-content.d.ts +16 -0
- package/dist/brain/user-content.js +32 -0
- package/dist/brain/utils.d.ts +4 -0
- package/dist/brain/utils.js +26 -0
- package/dist/brain/worker-coordinator.d.ts +25 -0
- package/dist/brain/worker-coordinator.js +83 -0
- package/dist/channels/commands.d.ts +50 -0
- package/dist/channels/commands.js +132 -0
- package/dist/channels/discord/channel.d.ts +29 -0
- package/dist/channels/discord/channel.js +159 -0
- package/dist/channels/discord/markdown.d.ts +19 -0
- package/dist/channels/discord/markdown.js +62 -0
- package/dist/channels/manager.d.ts +29 -0
- package/dist/channels/manager.js +100 -0
- package/dist/channels/slack/channel.d.ts +37 -0
- package/dist/channels/slack/channel.js +227 -0
- package/dist/channels/slack/markdown.d.ts +19 -0
- package/dist/channels/slack/markdown.js +62 -0
- package/dist/channels/specs.d.ts +32 -0
- package/dist/channels/specs.js +99 -0
- package/dist/channels/telegram/channel.d.ts +29 -0
- package/dist/channels/telegram/channel.js +182 -0
- package/dist/channels/telegram/markdown.d.ts +17 -0
- package/dist/channels/telegram/markdown.js +66 -0
- package/dist/channels/types.d.ts +26 -0
- package/dist/channels/types.js +1 -0
- package/dist/channels/whatsapp/channel.d.ts +34 -0
- package/dist/channels/whatsapp/channel.js +276 -0
- package/dist/channels/whatsapp/markdown.d.ts +20 -0
- package/dist/channels/whatsapp/markdown.js +51 -0
- package/dist/cli/claude-login.d.ts +5 -0
- package/dist/cli/claude-login.js +47 -0
- package/dist/cli/config.d.ts +5 -0
- package/dist/cli/config.js +78 -0
- package/dist/cli/index.d.ts +11 -0
- package/dist/cli/index.js +96 -0
- package/dist/computer/browser/actions.d.ts +31 -0
- package/dist/computer/browser/actions.js +148 -0
- package/dist/computer/browser/context-manager.d.ts +28 -0
- package/dist/computer/browser/context-manager.js +78 -0
- package/dist/computer/browser/manager.d.ts +91 -0
- package/dist/computer/browser/manager.js +344 -0
- package/dist/computer/browser/profile-badge.d.ts +13 -0
- package/dist/computer/browser/profile-badge.js +67 -0
- package/dist/computer/browser/screenshot.d.ts +5 -0
- package/dist/computer/browser/screenshot.js +21 -0
- package/dist/computer/browser/snapshot.d.ts +30 -0
- package/dist/computer/browser/snapshot.js +242 -0
- package/dist/computer/browser/tools.d.ts +5 -0
- package/dist/computer/browser/tools.js +167 -0
- package/dist/computer/browser/types.d.ts +26 -0
- package/dist/computer/browser/types.js +1 -0
- package/dist/computer/desktop/adapter.d.ts +25 -0
- package/dist/computer/desktop/adapter.js +11 -0
- package/dist/computer/desktop/macos.d.ts +24 -0
- package/dist/computer/desktop/macos.js +223 -0
- package/dist/computer/desktop/tools.d.ts +25 -0
- package/dist/computer/desktop/tools.js +114 -0
- package/dist/config/agent-config.d.ts +55 -0
- package/dist/config/agent-config.js +16 -0
- package/dist/config/model-catalog.d.ts +22 -0
- package/dist/config/model-catalog.js +112 -0
- package/dist/config/model-spec.d.ts +8 -0
- package/dist/config/model-spec.js +66 -0
- package/dist/config/store.d.ts +25 -0
- package/dist/config/store.js +143 -0
- package/dist/config.d.ts +110 -0
- package/dist/config.js +259 -0
- package/dist/control-ui/assets/index-Cbl7G5Sc.css +1 -0
- package/dist/control-ui/assets/index-Cu1P4C62.js +266 -0
- package/dist/control-ui/assets/noto-sans-cyrillic-ext-wght-normal-DSNfmdVt.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-cyrillic-wght-normal-B2hlT84T.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-devanagari-wght-normal-Cv-Vwajv.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-greek-ext-wght-normal-12T8GTDR.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-greek-wght-normal-Ymb6dZNd.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-latin-ext-wght-normal-W1qJv59z.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-latin-wght-normal-BYSzYMf3.woff2 +0 -0
- package/dist/control-ui/assets/noto-sans-vietnamese-wght-normal-DLTJy58D.woff2 +0 -0
- package/dist/control-ui/index.html +14 -0
- package/dist/control-ui/vite.svg +1 -0
- package/dist/events.d.ts +2 -0
- package/dist/events.js +11 -0
- package/dist/gateway/broadcast.d.ts +5 -0
- package/dist/gateway/broadcast.js +33 -0
- package/dist/gateway/methods/aliases.d.ts +17 -0
- package/dist/gateway/methods/aliases.js +22 -0
- package/dist/gateway/methods/chat.d.ts +33 -0
- package/dist/gateway/methods/chat.js +37 -0
- package/dist/gateway/methods/config.d.ts +14 -0
- package/dist/gateway/methods/config.js +24 -0
- package/dist/gateway/methods/models.d.ts +10 -0
- package/dist/gateway/methods/models.js +14 -0
- package/dist/gateway/methods/playbooks.d.ts +45 -0
- package/dist/gateway/methods/playbooks.js +488 -0
- package/dist/gateway/methods/prompt-templates.d.ts +27 -0
- package/dist/gateway/methods/prompt-templates.js +106 -0
- package/dist/gateway/methods/scheduler.d.ts +62 -0
- package/dist/gateway/methods/scheduler.js +129 -0
- package/dist/gateway/methods/sessions.d.ts +44 -0
- package/dist/gateway/methods/sessions.js +111 -0
- package/dist/gateway/methods/system.d.ts +12 -0
- package/dist/gateway/methods/system.js +39 -0
- package/dist/gateway/methods/tasks.d.ts +40 -0
- package/dist/gateway/methods/tasks.js +151 -0
- package/dist/gateway/methods/teams.d.ts +69 -0
- package/dist/gateway/methods/teams.js +376 -0
- package/dist/gateway/methods/tools.d.ts +6 -0
- package/dist/gateway/methods/tools.js +7 -0
- package/dist/gateway/methods/whatsapp.d.ts +19 -0
- package/dist/gateway/methods/whatsapp.js +35 -0
- package/dist/gateway/rpc.d.ts +38 -0
- package/dist/gateway/rpc.js +79 -0
- package/dist/gateway/server.d.ts +9 -0
- package/dist/gateway/server.js +137 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +254 -0
- package/dist/integrations/github.d.ts +7 -0
- package/dist/integrations/github.js +133 -0
- package/dist/integrations/mcp.d.ts +7 -0
- package/dist/integrations/mcp.js +106 -0
- package/dist/integrations/registry.d.ts +47 -0
- package/dist/integrations/registry.js +332 -0
- package/dist/integrations/scanner.d.ts +10 -0
- package/dist/integrations/scanner.js +122 -0
- package/dist/integrations/twitter.d.ts +10 -0
- package/dist/integrations/twitter.js +120 -0
- package/dist/integrations/types.d.ts +72 -0
- package/dist/integrations/types.js +1 -0
- package/dist/logger.d.ts +16 -0
- package/dist/logger.js +104 -0
- package/dist/markdown/chunk.d.ts +9 -0
- package/dist/markdown/chunk.js +52 -0
- package/dist/markdown/ir.d.ts +37 -0
- package/dist/markdown/ir.js +529 -0
- package/dist/markdown/render.d.ts +22 -0
- package/dist/markdown/render.js +148 -0
- package/dist/markdown/table-render.d.ts +43 -0
- package/dist/markdown/table-render.js +219 -0
- package/dist/markdown/tables.d.ts +17 -0
- package/dist/markdown/tables.js +27 -0
- package/dist/memory/embedding.d.ts +16 -0
- package/dist/memory/embedding.js +66 -0
- package/dist/memory/explicit.d.ts +16 -0
- package/dist/memory/explicit.js +29 -0
- package/dist/memory/extractor.d.ts +13 -0
- package/dist/memory/extractor.js +82 -0
- package/dist/memory/search.d.ts +15 -0
- package/dist/memory/search.js +57 -0
- package/dist/memory/session-learning.d.ts +23 -0
- package/dist/memory/session-learning.js +55 -0
- package/dist/memory/store.d.ts +36 -0
- package/dist/memory/store.js +334 -0
- package/dist/memory/types.d.ts +9 -0
- package/dist/memory/types.js +2 -0
- package/dist/paths.d.ts +28 -0
- package/dist/paths.js +48 -0
- package/dist/prompt-templates/builtins/index.d.ts +4 -0
- package/dist/prompt-templates/builtins/index.js +5 -0
- package/dist/prompt-templates/builtins/planner.d.ts +4 -0
- package/dist/prompt-templates/builtins/planner.js +77 -0
- package/dist/prompt-templates/store.d.ts +45 -0
- package/dist/prompt-templates/store.js +224 -0
- package/dist/prompt-templates/types.d.ts +10 -0
- package/dist/prompt-templates/types.js +1 -0
- package/dist/scheduler/connected-channels.d.ts +24 -0
- package/dist/scheduler/connected-channels.js +57 -0
- package/dist/scheduler/scheduler.d.ts +22 -0
- package/dist/scheduler/scheduler.js +132 -0
- package/dist/scheduler/store.d.ts +27 -0
- package/dist/scheduler/store.js +205 -0
- package/dist/scheduler/types.d.ts +29 -0
- package/dist/scheduler/types.js +1 -0
- package/dist/security/command-validator.d.ts +22 -0
- package/dist/security/command-validator.js +160 -0
- package/dist/security/docker-sandbox.d.ts +48 -0
- package/dist/security/docker-sandbox.js +218 -0
- package/dist/security/env-filter.d.ts +8 -0
- package/dist/security/env-filter.js +41 -0
- package/dist/skills/loader.d.ts +33 -0
- package/dist/skills/loader.js +132 -0
- package/dist/skills/prompt.d.ts +6 -0
- package/dist/skills/prompt.js +17 -0
- package/dist/skills/read-tool.d.ts +7 -0
- package/dist/skills/read-tool.js +24 -0
- package/dist/skills/scanner.d.ts +6 -0
- package/dist/skills/scanner.js +73 -0
- package/dist/skills/types.d.ts +15 -0
- package/dist/skills/types.js +1 -0
- package/dist/tasks/inline-attachment-content.d.ts +9 -0
- package/dist/tasks/inline-attachment-content.js +64 -0
- package/dist/tasks/store.d.ts +112 -0
- package/dist/tasks/store.js +519 -0
- package/dist/tasks/types.d.ts +129 -0
- package/dist/tasks/types.js +80 -0
- package/dist/teams/status-config.d.ts +8 -0
- package/dist/teams/status-config.js +40 -0
- package/dist/teams/store.d.ts +111 -0
- package/dist/teams/store.js +671 -0
- package/dist/teams/types.d.ts +30 -0
- package/dist/teams/types.js +1 -0
- package/dist/tools/bash.d.ts +18 -0
- package/dist/tools/bash.js +64 -0
- package/dist/tools/channel-history.d.ts +10 -0
- package/dist/tools/channel-history.js +43 -0
- package/dist/tools/delegate.d.ts +20 -0
- package/dist/tools/delegate.js +299 -0
- package/dist/tools/fs.d.ts +4 -0
- package/dist/tools/fs.js +335 -0
- package/dist/tools/integration-toggle.d.ts +14 -0
- package/dist/tools/integration-toggle.js +47 -0
- package/dist/tools/memory.d.ts +13 -0
- package/dist/tools/memory.js +59 -0
- package/dist/tools/prompt-templates.d.ts +7 -0
- package/dist/tools/prompt-templates.js +133 -0
- package/dist/tools/registry.d.ts +6 -0
- package/dist/tools/registry.js +9 -0
- package/dist/tools/schedule.d.ts +8 -0
- package/dist/tools/schedule.js +219 -0
- package/dist/tools/speak.d.ts +10 -0
- package/dist/tools/speak.js +56 -0
- package/dist/tools/tasks.d.ts +67 -0
- package/dist/tools/tasks.js +288 -0
- package/dist/tools/teams.d.ts +22 -0
- package/dist/tools/teams.js +470 -0
- package/dist/tools/web-fetch.d.ts +3 -0
- package/dist/tools/web-fetch.js +22 -0
- package/dist/tts/edge.d.ts +10 -0
- package/dist/tts/edge.js +60 -0
- package/dist/tts/speak.d.ts +12 -0
- package/dist/tts/speak.js +81 -0
- package/dist/tts/transcribe.d.ts +5 -0
- package/dist/tts/transcribe.js +40 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +22 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +13 -0
- package/package.json +102 -0
- package/verybot.js +2 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown → Telegram HTML conversion.
|
|
3
|
+
* Markdown to Telegram HTML conversion.
|
|
4
|
+
*/
|
|
5
|
+
import { chunkMarkdownIR, markdownToIR, } from "../../markdown/ir.js";
|
|
6
|
+
import { renderMarkdownWithMarkers } from "../../markdown/render.js";
|
|
7
|
+
export function escapeHtml(text) {
|
|
8
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
9
|
+
}
|
|
10
|
+
function escapeHtmlAttr(text) {
|
|
11
|
+
return escapeHtml(text).replace(/"/g, """);
|
|
12
|
+
}
|
|
13
|
+
const SAFE_LINK_PROTOCOL = /^https?:\/\/|^mailto:|^tel:/i;
|
|
14
|
+
function buildTelegramLink(link, _text) {
|
|
15
|
+
const href = link.href.trim();
|
|
16
|
+
if (!href || link.start === link.end) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
if (!SAFE_LINK_PROTOCOL.test(href)) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const safeHref = escapeHtmlAttr(href);
|
|
23
|
+
return {
|
|
24
|
+
start: link.start,
|
|
25
|
+
end: link.end,
|
|
26
|
+
open: `<a href="${safeHref}">`,
|
|
27
|
+
close: "</a>",
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function renderTelegramHtml(ir) {
|
|
31
|
+
return renderMarkdownWithMarkers(ir, {
|
|
32
|
+
styleMarkers: {
|
|
33
|
+
bold: { open: "<b>", close: "</b>" },
|
|
34
|
+
italic: { open: "<i>", close: "</i>" },
|
|
35
|
+
strikethrough: { open: "<s>", close: "</s>" },
|
|
36
|
+
code: { open: "<code>", close: "</code>" },
|
|
37
|
+
code_block: { open: "<pre><code>", close: "</code></pre>" },
|
|
38
|
+
},
|
|
39
|
+
escapeText: escapeHtml,
|
|
40
|
+
buildLink: buildTelegramLink,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
export function markdownToTelegramHtml(markdown, options = {}) {
|
|
44
|
+
const ir = markdownToIR(markdown ?? "", {
|
|
45
|
+
linkify: true,
|
|
46
|
+
headingStyle: "none",
|
|
47
|
+
blockquotePrefix: "",
|
|
48
|
+
tableMode: options.tableMode,
|
|
49
|
+
});
|
|
50
|
+
return renderTelegramHtml(ir);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Convert markdown to chunked Telegram HTML strings.
|
|
54
|
+
* Each chunk is ≤ `limit` characters of the plain-text IR (the HTML tags
|
|
55
|
+
* add overhead but Telegram counts the visible text).
|
|
56
|
+
*/
|
|
57
|
+
export function markdownToTelegramHtmlChunks(markdown, limit, options = {}) {
|
|
58
|
+
const ir = markdownToIR(markdown ?? "", {
|
|
59
|
+
linkify: true,
|
|
60
|
+
headingStyle: "none",
|
|
61
|
+
blockquotePrefix: "",
|
|
62
|
+
tableMode: options.tableMode,
|
|
63
|
+
});
|
|
64
|
+
const chunks = chunkMarkdownIR(ir, limit);
|
|
65
|
+
return chunks.map((chunk) => renderTelegramHtml(chunk));
|
|
66
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface IncomingMessage {
|
|
2
|
+
channelType: string;
|
|
3
|
+
channelId: string;
|
|
4
|
+
userId: string;
|
|
5
|
+
text?: string;
|
|
6
|
+
/** Whether this message originated from a voice input. */
|
|
7
|
+
isVoice?: boolean;
|
|
8
|
+
/** Team ID resolved by the channel (e.g. via /team command in Telegram). */
|
|
9
|
+
teamId?: string;
|
|
10
|
+
}
|
|
11
|
+
export type MessageHandler = (msg: IncomingMessage, channel: Channel) => Promise<void>;
|
|
12
|
+
export interface ChannelMessage {
|
|
13
|
+
user: string;
|
|
14
|
+
text: string;
|
|
15
|
+
ts: string;
|
|
16
|
+
}
|
|
17
|
+
export interface Channel {
|
|
18
|
+
name: string;
|
|
19
|
+
start(): Promise<void>;
|
|
20
|
+
stop(): Promise<void>;
|
|
21
|
+
send(channelId: string, text: string): Promise<void>;
|
|
22
|
+
/** Send a voice message. Falls back to text if not supported by the channel. */
|
|
23
|
+
sendVoice?(channelId: string, audioPath: string): Promise<void>;
|
|
24
|
+
/** Read recent messages from a channel/thread. */
|
|
25
|
+
readHistory?(channelId: string, limit?: number, threadTs?: string): Promise<ChannelMessage[]>;
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Channel, MessageHandler } from "../types.js";
|
|
2
|
+
import type { MarkdownTableMode } from "../../markdown/ir.js";
|
|
3
|
+
/** Callback that resolves the current table mode from config at send time. */
|
|
4
|
+
export type ResolveTableMode = () => MarkdownTableMode;
|
|
5
|
+
export interface CreateWhatsAppChannelOpts {
|
|
6
|
+
/**
|
|
7
|
+
* Opaque identifier for config gating and channel fingerprinting.
|
|
8
|
+
* Baileys authenticates via QR code; this value is not sent to WhatsApp.
|
|
9
|
+
* Set any non-empty string (e.g. your phone number) to enable the channel.
|
|
10
|
+
*/
|
|
11
|
+
phoneId: string;
|
|
12
|
+
/** When true, only process messages sent by myself (fromMe). */
|
|
13
|
+
selfOnly?: boolean;
|
|
14
|
+
onMessage: MessageHandler;
|
|
15
|
+
onClear?: (channelType: string, channelId: string, teamId?: string) => Promise<void>;
|
|
16
|
+
onLearn?: (channelType: string, channelId: string, topic?: string, teamId?: string) => Promise<{
|
|
17
|
+
topic?: string;
|
|
18
|
+
extracted: number;
|
|
19
|
+
saved: number;
|
|
20
|
+
skipped: number;
|
|
21
|
+
savedFacts: string[];
|
|
22
|
+
}>;
|
|
23
|
+
onRemember?: (channelType: string, channelId: string, fact: string, teamId?: string) => Promise<{
|
|
24
|
+
saved: boolean;
|
|
25
|
+
fact: string;
|
|
26
|
+
}>;
|
|
27
|
+
listTeams?: () => {
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
}[];
|
|
31
|
+
defaultTeamId?: string;
|
|
32
|
+
resolveTableMode?: ResolveTableMode;
|
|
33
|
+
}
|
|
34
|
+
export declare function createWhatsAppChannel(opts: CreateWhatsAppChannelOpts): Channel;
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import makeWASocket, { useMultiFileAuthState, DisconnectReason, downloadMediaMessage, generateMessageIDV2, } from "@whiskeysockets/baileys";
|
|
2
|
+
import { transcribe } from "../../tts/transcribe.js";
|
|
3
|
+
import { logger } from "../../logger.js";
|
|
4
|
+
import { markdownToWhatsAppChunks, escapeWhatsApp, WHATSAPP_TEXT_LIMIT } from "./markdown.js";
|
|
5
|
+
import { CommandRouter } from "../commands.js";
|
|
6
|
+
import { WHATSAPP_AUTH_DIR } from "../../paths.js";
|
|
7
|
+
import { emit } from "../../events.js";
|
|
8
|
+
import QRCode from "qrcode";
|
|
9
|
+
/** Typing indicator refresh interval (WhatsApp presence expires ~10s). */
|
|
10
|
+
const TYPING_INTERVAL_MS = 5000;
|
|
11
|
+
/** Initial reconnect delay (doubles on each attempt). */
|
|
12
|
+
const BASE_RECONNECT_DELAY_MS = 1000;
|
|
13
|
+
/** Cap reconnect backoff at 60s. */
|
|
14
|
+
const MAX_RECONNECT_DELAY_MS = 60_000;
|
|
15
|
+
/** Max number of sent message IDs to track for loop prevention. */
|
|
16
|
+
const SENT_IDS_LIMIT = 500;
|
|
17
|
+
/**
|
|
18
|
+
* Thin pino-compatible logger adapter that forwards to Winston.
|
|
19
|
+
* Baileys expects a pino-shaped logger with child(), level, and log methods.
|
|
20
|
+
*/
|
|
21
|
+
function createBaileysLogger() {
|
|
22
|
+
const noop = () => { };
|
|
23
|
+
const adapter = {
|
|
24
|
+
level: "silent",
|
|
25
|
+
trace: noop,
|
|
26
|
+
debug: noop,
|
|
27
|
+
info: (msg) => logger.debug(`[baileys] ${msg}`),
|
|
28
|
+
warn: (msg) => logger.warn(`[baileys] ${msg}`),
|
|
29
|
+
error: (msg) => logger.error(`[baileys] ${msg}`),
|
|
30
|
+
fatal: (msg) => logger.error(`[baileys:fatal] ${msg}`),
|
|
31
|
+
child: () => adapter,
|
|
32
|
+
};
|
|
33
|
+
return adapter;
|
|
34
|
+
}
|
|
35
|
+
export function createWhatsAppChannel(opts) {
|
|
36
|
+
const { onMessage, onClear, onLearn, onRemember, listTeams, defaultTeamId, selfOnly = false, resolveTableMode = () => "bullets", } = opts;
|
|
37
|
+
let sock = null;
|
|
38
|
+
let stopped = false;
|
|
39
|
+
let reconnectAttempts = 0;
|
|
40
|
+
let reconnectTimer = null;
|
|
41
|
+
const typingIntervals = new Map();
|
|
42
|
+
const commands = new CommandRouter({ onClear, onLearn, onRemember, listTeams, defaultTeamId });
|
|
43
|
+
/** IDs of messages sent by the bot — used to avoid processing our own replies. */
|
|
44
|
+
const sentIds = new Set();
|
|
45
|
+
function trackSentId(id) {
|
|
46
|
+
if (!id)
|
|
47
|
+
return;
|
|
48
|
+
sentIds.add(id);
|
|
49
|
+
if (sentIds.size > SENT_IDS_LIMIT) {
|
|
50
|
+
const first = sentIds.values().next().value;
|
|
51
|
+
sentIds.delete(first);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Send an outbound WhatsApp message with a pre-generated ID so we can
|
|
56
|
+
* suppress self-echoes even if upsert arrives before sendMessage resolves.
|
|
57
|
+
*/
|
|
58
|
+
async function sendTrackedMessage(jid, content) {
|
|
59
|
+
const activeSock = sock;
|
|
60
|
+
if (!activeSock)
|
|
61
|
+
return;
|
|
62
|
+
const messageId = generateMessageIDV2(activeSock.user?.id);
|
|
63
|
+
trackSentId(messageId);
|
|
64
|
+
const sent = await activeSock.sendMessage(jid, content, { messageId });
|
|
65
|
+
trackSentId(sent?.key?.id);
|
|
66
|
+
}
|
|
67
|
+
/** Format a CommandResult using WhatsApp formatting. */
|
|
68
|
+
function formatResult(result) {
|
|
69
|
+
return result.parts
|
|
70
|
+
.map((p) => {
|
|
71
|
+
if (typeof p === "string")
|
|
72
|
+
return escapeWhatsApp(p);
|
|
73
|
+
if ("bold" in p)
|
|
74
|
+
return `*${escapeWhatsApp(p.bold)}*`;
|
|
75
|
+
return `\`${p.code}\``;
|
|
76
|
+
})
|
|
77
|
+
.join("");
|
|
78
|
+
}
|
|
79
|
+
function startTyping(jid) {
|
|
80
|
+
stopTyping(jid);
|
|
81
|
+
sock?.sendPresenceUpdate("composing", jid).catch((err) => logger.error(`[whatsapp] typing error: ${err}`));
|
|
82
|
+
const interval = setInterval(() => {
|
|
83
|
+
sock?.sendPresenceUpdate("composing", jid).catch((err) => logger.error(`[whatsapp] typing interval error: ${err}`));
|
|
84
|
+
}, TYPING_INTERVAL_MS);
|
|
85
|
+
typingIntervals.set(jid, interval);
|
|
86
|
+
}
|
|
87
|
+
function stopTyping(jid) {
|
|
88
|
+
const interval = typingIntervals.get(jid);
|
|
89
|
+
const hadActiveTyping = Boolean(interval);
|
|
90
|
+
if (interval) {
|
|
91
|
+
clearInterval(interval);
|
|
92
|
+
typingIntervals.delete(jid);
|
|
93
|
+
}
|
|
94
|
+
if (hadActiveTyping) {
|
|
95
|
+
sock?.sendPresenceUpdate("paused", jid).catch(() => { });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function clearAllTyping() {
|
|
99
|
+
for (const jid of typingIntervals.keys())
|
|
100
|
+
stopTyping(jid);
|
|
101
|
+
}
|
|
102
|
+
async function connectSocket() {
|
|
103
|
+
if (stopped)
|
|
104
|
+
return;
|
|
105
|
+
// Clear stale typing intervals from previous connection
|
|
106
|
+
clearAllTyping();
|
|
107
|
+
const { state, saveCreds } = await useMultiFileAuthState(WHATSAPP_AUTH_DIR);
|
|
108
|
+
sock = makeWASocket({
|
|
109
|
+
auth: state,
|
|
110
|
+
logger: createBaileysLogger(),
|
|
111
|
+
printQRInTerminal: false, // QR is sent to UI via event bus
|
|
112
|
+
});
|
|
113
|
+
sock.ev.on("creds.update", saveCreds);
|
|
114
|
+
sock.ev.on("connection.update", async (update) => {
|
|
115
|
+
const { connection, lastDisconnect, qr } = update;
|
|
116
|
+
// Broadcast QR code to UI clients
|
|
117
|
+
if (qr) {
|
|
118
|
+
try {
|
|
119
|
+
const dataUrl = await QRCode.toDataURL(qr, { width: 256 });
|
|
120
|
+
emit("whatsapp", { type: "qr", dataUrl });
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
logger.error(`[whatsapp] QR generation failed: ${err}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (connection === "close") {
|
|
127
|
+
emit("whatsapp", { type: "disconnected" });
|
|
128
|
+
const statusCode = lastDisconnect?.error?.output?.statusCode;
|
|
129
|
+
const isLoggedOut = statusCode === DisconnectReason.loggedOut;
|
|
130
|
+
if (isLoggedOut) {
|
|
131
|
+
logger.warn("[whatsapp] logged out — will not reconnect");
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (!stopped) {
|
|
135
|
+
const delay = Math.min(BASE_RECONNECT_DELAY_MS * 2 ** reconnectAttempts, MAX_RECONNECT_DELAY_MS);
|
|
136
|
+
reconnectAttempts++;
|
|
137
|
+
logger.info(`[whatsapp] disconnected (code ${statusCode}), reconnecting in ${delay}ms...`);
|
|
138
|
+
if (reconnectTimer)
|
|
139
|
+
clearTimeout(reconnectTimer);
|
|
140
|
+
reconnectTimer = setTimeout(() => {
|
|
141
|
+
reconnectTimer = null;
|
|
142
|
+
void connectSocket();
|
|
143
|
+
}, delay);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (connection === "open") {
|
|
147
|
+
reconnectAttempts = 0;
|
|
148
|
+
emit("whatsapp", { type: "connected" });
|
|
149
|
+
logger.info("[whatsapp] connected");
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
sock.ev.on("messages.upsert", async ({ messages, type }) => {
|
|
153
|
+
logger.info(`[whatsapp] messages.upsert: type=${type}, count=${messages.length}`);
|
|
154
|
+
for (const msg of messages) {
|
|
155
|
+
try {
|
|
156
|
+
logger.info(`[whatsapp] msg: fromMe=${msg.key.fromMe}, jid=${msg.key.remoteJid}, id=${msg.key.id}, hasMessage=${!!msg.message}`);
|
|
157
|
+
if (!msg.message)
|
|
158
|
+
continue;
|
|
159
|
+
// Skip messages sent by the bot to avoid loops.
|
|
160
|
+
if (msg.key.id && sentIds.has(msg.key.id))
|
|
161
|
+
continue;
|
|
162
|
+
// In self-only mode, ignore messages from others
|
|
163
|
+
if (selfOnly && !msg.key.fromMe)
|
|
164
|
+
continue;
|
|
165
|
+
const jid = msg.key.remoteJid;
|
|
166
|
+
if (!jid)
|
|
167
|
+
continue;
|
|
168
|
+
const userId = msg.key.participant ?? jid;
|
|
169
|
+
// Voice message handling
|
|
170
|
+
const audioMsg = msg.message.audioMessage;
|
|
171
|
+
if (audioMsg?.ptt) {
|
|
172
|
+
logger.info(`[whatsapp] voice message received`);
|
|
173
|
+
startTyping(jid);
|
|
174
|
+
try {
|
|
175
|
+
const buffer = await downloadMediaMessage(msg, "buffer", {});
|
|
176
|
+
const text = await transcribe(buffer, "voice.ogg");
|
|
177
|
+
logger.info(`[whatsapp] transcribed voice (${text.length} chars)`);
|
|
178
|
+
await onMessage({
|
|
179
|
+
channelType: "whatsapp",
|
|
180
|
+
channelId: jid,
|
|
181
|
+
userId,
|
|
182
|
+
text,
|
|
183
|
+
isVoice: true,
|
|
184
|
+
teamId: commands.resolveTeamId(jid),
|
|
185
|
+
}, channel);
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
189
|
+
logger.error(`[whatsapp] voice transcription failed: ${errMsg}`);
|
|
190
|
+
await sendTrackedMessage(jid, { text: "Sorry, I couldn't process your voice message." });
|
|
191
|
+
}
|
|
192
|
+
finally {
|
|
193
|
+
stopTyping(jid);
|
|
194
|
+
}
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
// Text message handling
|
|
198
|
+
const text = msg.message.conversation ??
|
|
199
|
+
msg.message.extendedTextMessage?.text;
|
|
200
|
+
if (!text)
|
|
201
|
+
continue;
|
|
202
|
+
// Try command handling first
|
|
203
|
+
const cmdResult = await commands.handle("whatsapp", jid, text);
|
|
204
|
+
if (cmdResult) {
|
|
205
|
+
await sendTrackedMessage(jid, { text: formatResult(cmdResult) });
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
logger.info(`[whatsapp] message received`);
|
|
209
|
+
startTyping(jid);
|
|
210
|
+
try {
|
|
211
|
+
await onMessage({
|
|
212
|
+
channelType: "whatsapp",
|
|
213
|
+
channelId: jid,
|
|
214
|
+
userId,
|
|
215
|
+
text,
|
|
216
|
+
teamId: commands.resolveTeamId(jid),
|
|
217
|
+
}, channel);
|
|
218
|
+
}
|
|
219
|
+
finally {
|
|
220
|
+
stopTyping(jid);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
catch (err) {
|
|
224
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
225
|
+
logger.error(`[whatsapp] error handling message: ${errMsg}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
const channel = {
|
|
231
|
+
name: "whatsapp",
|
|
232
|
+
start: async () => {
|
|
233
|
+
stopped = false;
|
|
234
|
+
reconnectAttempts = 0;
|
|
235
|
+
if (reconnectTimer) {
|
|
236
|
+
clearTimeout(reconnectTimer);
|
|
237
|
+
reconnectTimer = null;
|
|
238
|
+
}
|
|
239
|
+
await connectSocket();
|
|
240
|
+
},
|
|
241
|
+
stop: async () => {
|
|
242
|
+
stopped = true;
|
|
243
|
+
if (reconnectTimer) {
|
|
244
|
+
clearTimeout(reconnectTimer);
|
|
245
|
+
reconnectTimer = null;
|
|
246
|
+
}
|
|
247
|
+
clearAllTyping();
|
|
248
|
+
sock?.end(undefined);
|
|
249
|
+
sock = null;
|
|
250
|
+
},
|
|
251
|
+
send: async (channelId, text) => {
|
|
252
|
+
stopTyping(channelId);
|
|
253
|
+
const tableMode = resolveTableMode();
|
|
254
|
+
const chunks = markdownToWhatsAppChunks(text, WHATSAPP_TEXT_LIMIT, { tableMode });
|
|
255
|
+
if (chunks.length === 0 && text) {
|
|
256
|
+
await sendTrackedMessage(channelId, { text });
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
for (const chunk of chunks) {
|
|
260
|
+
await sendTrackedMessage(channelId, { text: chunk });
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
sendVoice: async (channelId, audioPath) => {
|
|
264
|
+
stopTyping(channelId);
|
|
265
|
+
const { readFile } = await import("fs/promises");
|
|
266
|
+
const audio = await readFile(audioPath);
|
|
267
|
+
await sendTrackedMessage(channelId, {
|
|
268
|
+
audio,
|
|
269
|
+
mimetype: "audio/ogg; codecs=opus",
|
|
270
|
+
ptt: true,
|
|
271
|
+
});
|
|
272
|
+
logger.info(`[whatsapp] voice message sent`);
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
return channel;
|
|
276
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown → WhatsApp text conversion.
|
|
3
|
+
* WhatsApp formatting: *bold*, _italic_, ~strikethrough~, `code`, ```code block```
|
|
4
|
+
* No link syntax — WhatsApp auto-linkifies URLs.
|
|
5
|
+
*/
|
|
6
|
+
import { type MarkdownTableMode } from "../../markdown/ir.js";
|
|
7
|
+
export declare function escapeWhatsApp(text: string): string;
|
|
8
|
+
/** Single-shot conversion (no chunking). */
|
|
9
|
+
export declare function markdownToWhatsApp(markdown: string, options?: {
|
|
10
|
+
tableMode?: MarkdownTableMode;
|
|
11
|
+
}): string;
|
|
12
|
+
/** WhatsApp message size limit. */
|
|
13
|
+
export declare const WHATSAPP_TEXT_LIMIT = 4096;
|
|
14
|
+
/**
|
|
15
|
+
* Convert markdown to chunked WhatsApp text strings.
|
|
16
|
+
* Each chunk is ≤ `limit` characters.
|
|
17
|
+
*/
|
|
18
|
+
export declare function markdownToWhatsAppChunks(markdown: string, limit: number, options?: {
|
|
19
|
+
tableMode?: MarkdownTableMode;
|
|
20
|
+
}): string[];
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown → WhatsApp text conversion.
|
|
3
|
+
* WhatsApp formatting: *bold*, _italic_, ~strikethrough~, `code`, ```code block```
|
|
4
|
+
* No link syntax — WhatsApp auto-linkifies URLs.
|
|
5
|
+
*/
|
|
6
|
+
import { chunkMarkdownIR, markdownToIR, } from "../../markdown/ir.js";
|
|
7
|
+
import { renderMarkdownWithMarkers } from "../../markdown/render.js";
|
|
8
|
+
/** Characters that have formatting meaning in WhatsApp. */
|
|
9
|
+
const WA_SPECIAL = /[*_~`]/g;
|
|
10
|
+
export function escapeWhatsApp(text) {
|
|
11
|
+
return text.replace(WA_SPECIAL, "\\$&");
|
|
12
|
+
}
|
|
13
|
+
function renderWhatsApp(ir) {
|
|
14
|
+
return renderMarkdownWithMarkers(ir, {
|
|
15
|
+
styleMarkers: {
|
|
16
|
+
bold: { open: "*", close: "*" },
|
|
17
|
+
italic: { open: "_", close: "_" },
|
|
18
|
+
strikethrough: { open: "~", close: "~" },
|
|
19
|
+
code: { open: "`", close: "`" },
|
|
20
|
+
code_block: { open: "```\n", close: "\n```" },
|
|
21
|
+
},
|
|
22
|
+
escapeText: escapeWhatsApp,
|
|
23
|
+
// No buildLink — WhatsApp auto-linkifies URLs
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/** Single-shot conversion (no chunking). */
|
|
27
|
+
export function markdownToWhatsApp(markdown, options = {}) {
|
|
28
|
+
const ir = markdownToIR(markdown ?? "", {
|
|
29
|
+
linkify: true,
|
|
30
|
+
headingStyle: "none",
|
|
31
|
+
blockquotePrefix: "",
|
|
32
|
+
tableMode: options.tableMode,
|
|
33
|
+
});
|
|
34
|
+
return renderWhatsApp(ir);
|
|
35
|
+
}
|
|
36
|
+
/** WhatsApp message size limit. */
|
|
37
|
+
export const WHATSAPP_TEXT_LIMIT = 4096;
|
|
38
|
+
/**
|
|
39
|
+
* Convert markdown to chunked WhatsApp text strings.
|
|
40
|
+
* Each chunk is ≤ `limit` characters.
|
|
41
|
+
*/
|
|
42
|
+
export function markdownToWhatsAppChunks(markdown, limit, options = {}) {
|
|
43
|
+
const ir = markdownToIR(markdown ?? "", {
|
|
44
|
+
linkify: true,
|
|
45
|
+
headingStyle: "none",
|
|
46
|
+
blockquotePrefix: "",
|
|
47
|
+
tableMode: options.tableMode,
|
|
48
|
+
});
|
|
49
|
+
const chunks = chunkMarkdownIR(ir, limit);
|
|
50
|
+
return chunks.map((chunk) => renderWhatsApp(chunk));
|
|
51
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { createInterface } from "node:readline";
|
|
3
|
+
import { ConfigStore } from "../config/store.js";
|
|
4
|
+
import { BASE_DIR } from "../paths.js";
|
|
5
|
+
/** Prompt the user for a single line of input. */
|
|
6
|
+
function prompt(question) {
|
|
7
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
rl.question(question, (answer) => {
|
|
10
|
+
rl.close();
|
|
11
|
+
resolve(answer.trim());
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Run `claude setup-token` interactively, then ask the user to paste
|
|
17
|
+
* the resulting token and save it to config.json.
|
|
18
|
+
*/
|
|
19
|
+
export async function handleClaudeLogin() {
|
|
20
|
+
console.log("Running `claude setup-token`...\n");
|
|
21
|
+
const result = spawnSync("claude", ["setup-token"], {
|
|
22
|
+
stdio: "inherit",
|
|
23
|
+
encoding: "utf-8",
|
|
24
|
+
});
|
|
25
|
+
if (result.error) {
|
|
26
|
+
console.error("Failed to run `claude setup-token`. Is the Claude Code CLI installed?\n" +
|
|
27
|
+
"Install it with: npm install -g @anthropic-ai/claude-code");
|
|
28
|
+
process.exitCode = 1;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (result.status !== 0) {
|
|
32
|
+
console.error(`\nclaude setup-token exited with code ${result.status}`);
|
|
33
|
+
process.exitCode = 1;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
console.log("");
|
|
37
|
+
const token = await prompt("Paste the token (sk-ant-oat01-...): ");
|
|
38
|
+
if (!token.startsWith("sk-ant-")) {
|
|
39
|
+
console.error("Invalid token — expected it to start with sk-ant-");
|
|
40
|
+
process.exitCode = 1;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const store = new ConfigStore(BASE_DIR);
|
|
44
|
+
store.patch({ CLAUDE_CODE_OAUTH_TOKEN: token });
|
|
45
|
+
console.log("\n✓ Token saved to config.json");
|
|
46
|
+
console.log(" Restart the gateway to use it.");
|
|
47
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { ConfigStore } from "../config/store.js";
|
|
2
|
+
import { seedConfigStore, generateGatewayToken } from "../config.js";
|
|
3
|
+
import { BASE_DIR } from "../paths.js";
|
|
4
|
+
/** Resolve a dot-separated path on a nested object. */
|
|
5
|
+
function getByPath(obj, path) {
|
|
6
|
+
return path.split(".").reduce((o, k) => (o && typeof o === "object" ? o[k] : undefined), obj);
|
|
7
|
+
}
|
|
8
|
+
/** Build a nested partial object from a dot-separated path and a value. */
|
|
9
|
+
function setByPath(path, value) {
|
|
10
|
+
const keys = path.split(".");
|
|
11
|
+
if (keys.length === 1)
|
|
12
|
+
return { [path]: value };
|
|
13
|
+
const root = {};
|
|
14
|
+
let current = root;
|
|
15
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
16
|
+
current[keys[i]] = {};
|
|
17
|
+
current = current[keys[i]];
|
|
18
|
+
}
|
|
19
|
+
current[keys[keys.length - 1]] = value;
|
|
20
|
+
return root;
|
|
21
|
+
}
|
|
22
|
+
/** Auto-coerce CLI string values to native types. */
|
|
23
|
+
function coerce(raw) {
|
|
24
|
+
if (raw === "true")
|
|
25
|
+
return true;
|
|
26
|
+
if (raw === "false")
|
|
27
|
+
return false;
|
|
28
|
+
if (raw === "null")
|
|
29
|
+
return null;
|
|
30
|
+
const num = Number(raw);
|
|
31
|
+
if (!Number.isNaN(num) && raw.trim() !== "")
|
|
32
|
+
return num;
|
|
33
|
+
return raw;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Handle `config get [KEY]` and `config set KEY VALUE` CLI subcommands.
|
|
37
|
+
* Operates directly on config.json — no gateway boot needed.
|
|
38
|
+
*/
|
|
39
|
+
export function handleConfigCommand(sub, rest) {
|
|
40
|
+
const store = new ConfigStore(BASE_DIR);
|
|
41
|
+
seedConfigStore(store);
|
|
42
|
+
if (sub === "get") {
|
|
43
|
+
const key = rest[0];
|
|
44
|
+
if (!key) {
|
|
45
|
+
console.log(JSON.stringify(store.getRedacted(), null, 2));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const data = store.load();
|
|
49
|
+
const value = getByPath(data, key);
|
|
50
|
+
if (value === undefined) {
|
|
51
|
+
console.error(`Key "${key}" not found in config`);
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
console.log(typeof value === "string" ? value : JSON.stringify(value, null, 2));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (sub === "set") {
|
|
59
|
+
const [key, ...valueParts] = rest;
|
|
60
|
+
if (!key || valueParts.length === 0) {
|
|
61
|
+
console.error("Usage: config set KEY VALUE");
|
|
62
|
+
process.exitCode = 1;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const value = coerce(valueParts.join(" "));
|
|
66
|
+
store.patch(setByPath(key, value));
|
|
67
|
+
console.log(`Set ${key}`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (sub === "regenerate_gateway_token") {
|
|
71
|
+
const token = generateGatewayToken();
|
|
72
|
+
store.patch({ GATEWAY_TOKEN: token });
|
|
73
|
+
console.log(token);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
console.error("Usage: config get [KEY] | config set KEY VALUE | config regenerate_gateway_token");
|
|
77
|
+
process.exitCode = 1;
|
|
78
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface RuntimeCliOptions {
|
|
2
|
+
gatewayHost?: string;
|
|
3
|
+
gatewayPort?: number;
|
|
4
|
+
}
|
|
5
|
+
/** Parse runtime flags used when launching the main gateway process. */
|
|
6
|
+
export declare function parseRuntimeCliOptions(argv?: string[]): RuntimeCliOptions;
|
|
7
|
+
/**
|
|
8
|
+
* Route lightweight CLI subcommands without booting the gateway.
|
|
9
|
+
* Returns a promise (or null) so the caller can await async commands.
|
|
10
|
+
*/
|
|
11
|
+
export declare function handleCli(): Promise<void> | null;
|