whale-code 6.4.0 → 6.5.0
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/swagmanager-mcp.js +7 -0
- package/dist/cli/app.js +30 -2
- package/dist/cli/chat/ChatApp.d.ts +4 -4
- package/dist/cli/chat/ChatApp.js +114 -44
- package/dist/cli/chat/ChatInput.d.ts +13 -6
- package/dist/cli/chat/ChatInput.js +433 -89
- package/dist/cli/chat/MemoryManager.d.ts +15 -0
- package/dist/cli/chat/MemoryManager.js +61 -0
- package/dist/cli/chat/MessageList.d.ts +8 -0
- package/dist/cli/chat/MessageList.js +1 -1
- package/dist/cli/chat/NodeManager.d.ts +30 -0
- package/dist/cli/chat/NodeManager.js +89 -0
- package/dist/cli/chat/NodeSelector.d.ts +19 -0
- package/dist/cli/chat/NodeSelector.js +37 -0
- package/dist/cli/chat/PlanApproval.d.ts +17 -0
- package/dist/cli/chat/PlanApproval.js +82 -0
- package/dist/cli/chat/SessionManager.d.ts +16 -0
- package/dist/cli/chat/SessionManager.js +43 -0
- package/dist/cli/chat/SlashMenu.d.ts +38 -0
- package/dist/cli/chat/SlashMenu.js +208 -0
- package/dist/cli/chat/StatusBar.d.ts +16 -0
- package/dist/cli/chat/StatusBar.js +22 -0
- package/dist/cli/chat/ThemeSelector.d.ts +14 -0
- package/dist/cli/chat/ThemeSelector.js +29 -0
- package/dist/cli/chat/ToolIndicator.d.ts +8 -0
- package/dist/cli/chat/ToolIndicator.js +33 -9
- package/dist/cli/chat/hooks/useAgentLoop.d.ts +2 -1
- package/dist/cli/chat/hooks/useAgentLoop.js +22 -17
- package/dist/cli/chat/hooks/useSlashCommands.d.ts +19 -0
- package/dist/cli/chat/hooks/useSlashCommands.js +254 -15
- package/dist/cli/commands/config-cmd.js +4 -25
- package/dist/cli/commands/db.d.ts +13 -0
- package/dist/cli/commands/db.js +243 -0
- package/dist/cli/commands/doctor.js +6 -9
- package/dist/cli/commands/mcp.js +1 -20
- package/dist/cli/services/agent-events.d.ts +22 -1
- package/dist/cli/services/agent-events.js +9 -0
- package/dist/cli/services/agent-loop.js +66 -2
- package/dist/cli/services/agent-worker-base.js +21 -6
- package/dist/cli/services/api-retry.d.ts +25 -0
- package/dist/cli/services/api-retry.js +91 -0
- package/dist/cli/services/auth-service.d.ts +1 -1
- package/dist/cli/services/auth-service.js +40 -19
- package/dist/cli/services/background-processes.js +26 -2
- package/dist/cli/services/config-store.d.ts +13 -1
- package/dist/cli/services/config-store.js +116 -13
- package/dist/cli/services/format-server-response.js +12 -6
- package/dist/cli/services/ink-resize-fix.d.ts +18 -0
- package/dist/cli/services/ink-resize-fix.js +66 -0
- package/dist/cli/services/interactive-tools.d.ts +14 -0
- package/dist/cli/services/interactive-tools.js +47 -2
- package/dist/cli/services/keybinding-manager.js +1 -1
- package/dist/cli/services/local-tools.js +35 -2
- package/dist/cli/services/server-tools.js +175 -3
- package/dist/cli/services/subagent.js +15 -3
- package/dist/cli/services/system-prompt.js +5 -3
- package/dist/cli/services/task-decomposer.d.ts +35 -0
- package/dist/cli/services/task-decomposer.js +199 -0
- package/dist/cli/services/team-lead.d.ts +18 -0
- package/dist/cli/services/team-lead.js +80 -0
- package/dist/cli/services/teammate.js +5 -5
- package/dist/cli/services/telemetry.d.ts +8 -2
- package/dist/cli/services/telemetry.js +116 -92
- package/dist/cli/services/tools/agent-tools.d.ts +1 -0
- package/dist/cli/services/tools/agent-tools.js +50 -4
- package/dist/cli/services/tools/file-ops.d.ts +2 -0
- package/dist/cli/services/tools/file-ops.js +71 -19
- package/dist/cli/services/tools/shell-exec.js +22 -12
- package/dist/cli/shared/Theme.d.ts +1 -2
- package/dist/cli/shared/Theme.js +1 -1
- package/dist/cli/shared/WhaleBanner.d.ts +4 -1
- package/dist/cli/shared/WhaleBanner.js +12 -8
- package/dist/cli/shared/markdown.d.ts +5 -4
- package/dist/cli/shared/markdown.js +376 -334
- package/dist/cli/shared/theme-manager.d.ts +27 -0
- package/dist/cli/shared/theme-manager.js +178 -0
- package/dist/cli/shared/theme-presets.d.ts +16 -0
- package/dist/cli/shared/theme-presets.js +265 -0
- package/dist/index.js +0 -51
- package/dist/node/adapters/imessage.d.ts +10 -0
- package/dist/node/adapters/imessage.js +45 -6
- package/dist/node/cli.js +459 -8
- package/dist/node/config.d.ts +17 -0
- package/dist/node/gateway-client.d.ts +55 -0
- package/dist/node/gateway-client.js +201 -0
- package/dist/node/portal/clipboard.d.ts +28 -0
- package/dist/node/portal/clipboard.js +183 -0
- package/dist/node/portal/discovery.d.ts +29 -0
- package/dist/node/portal/discovery.js +61 -0
- package/dist/node/portal/forward.d.ts +30 -0
- package/dist/node/portal/forward.js +90 -0
- package/dist/node/portal/index.d.ts +47 -0
- package/dist/node/portal/index.js +250 -0
- package/dist/node/portal/multiplexer.d.ts +48 -0
- package/dist/node/portal/multiplexer.js +207 -0
- package/dist/node/portal/permissions.d.ts +36 -0
- package/dist/node/portal/permissions.js +131 -0
- package/dist/node/portal/protocol.d.ts +140 -0
- package/dist/node/portal/protocol.js +193 -0
- package/dist/node/portal/screen.d.ts +18 -0
- package/dist/node/portal/screen.js +93 -0
- package/dist/node/portal/session.d.ts +68 -0
- package/dist/node/portal/session.js +127 -0
- package/dist/node/portal/shell.d.ts +26 -0
- package/dist/node/portal/shell.js +142 -0
- package/dist/node/portal/stream.d.ts +43 -0
- package/dist/node/portal/stream.js +90 -0
- package/dist/node/portal/transfer.d.ts +33 -0
- package/dist/node/portal/transfer.js +231 -0
- package/dist/node/portal/ui.d.ts +16 -0
- package/dist/node/portal/ui.js +148 -0
- package/dist/node/remote-desktop/compile-helper.d.ts +13 -0
- package/dist/node/remote-desktop/compile-helper.js +73 -0
- package/dist/node/remote-desktop/index.d.ts +67 -0
- package/dist/node/remote-desktop/index.js +220 -0
- package/dist/node/remote-desktop/protocol.d.ts +96 -0
- package/dist/node/remote-desktop/protocol.js +67 -0
- package/dist/node/runtime.d.ts +8 -1
- package/dist/node/runtime.js +117 -9
- package/dist/server/handlers/__test-utils__/test-db.d.ts +25 -0
- package/dist/server/handlers/__test-utils__/test-db.js +128 -0
- package/dist/server/handlers/api-keys.js +26 -2
- package/dist/server/handlers/browser.d.ts +0 -4
- package/dist/server/handlers/browser.js +0 -46
- package/dist/server/handlers/catalog.js +37 -14
- package/dist/server/handlers/clickhouse.d.ts +10 -0
- package/dist/server/handlers/clickhouse.js +215 -0
- package/dist/server/handlers/comms.d.ts +308 -4
- package/dist/server/handlers/comms.js +444 -11
- package/dist/server/handlers/creations.js +1 -1
- package/dist/server/handlers/crm.d.ts +54 -8
- package/dist/server/handlers/crm.js +353 -68
- package/dist/server/handlers/embeddings.js +3 -3
- package/dist/server/handlers/enrichment.js +39 -55
- package/dist/server/handlers/inventory.js +1 -1
- package/dist/server/handlers/kali.d.ts +9 -1
- package/dist/server/handlers/kali.js +50 -1
- package/dist/server/handlers/media.d.ts +8 -0
- package/dist/server/handlers/media.js +902 -0
- package/dist/server/handlers/meta-ads.js +6 -3
- package/dist/server/handlers/nodes.d.ts +2 -0
- package/dist/server/handlers/nodes.js +331 -40
- package/dist/server/handlers/operations.d.ts +4 -6
- package/dist/server/handlers/operations.js +99 -38
- package/dist/server/handlers/platform.js +224 -107
- package/dist/server/handlers/remove-bg.d.ts +6 -0
- package/dist/server/handlers/remove-bg.js +96 -0
- package/dist/server/handlers/storefront.d.ts +6 -0
- package/dist/server/handlers/storefront.js +477 -0
- package/dist/server/handlers/supply-chain.js +21 -3
- package/dist/server/handlers/workflow-steps.js +87 -31
- package/dist/server/handlers/workflows.js +4 -1
- package/dist/server/index.js +334 -88
- package/dist/server/lib/clickhouse-buffer.d.ts +48 -0
- package/dist/server/lib/clickhouse-buffer.js +175 -0
- package/dist/server/lib/clickhouse-client.d.ts +112 -0
- package/dist/server/lib/clickhouse-client.js +141 -0
- package/dist/server/lib/coa-renderer.d.ts +91 -0
- package/dist/server/lib/coa-renderer.js +411 -0
- package/dist/server/lib/compaction-service.js +45 -1
- package/dist/server/lib/pdf-renderer.d.ts +143 -0
- package/dist/server/lib/pdf-renderer.js +867 -0
- package/dist/server/lib/react-pdf-layout.d.ts +40 -0
- package/dist/server/lib/react-pdf-layout.js +437 -0
- package/dist/server/lib/server-agent-loop.d.ts +2 -0
- package/dist/server/lib/server-agent-loop.js +61 -15
- package/dist/server/lib/server-subagent.d.ts +3 -0
- package/dist/server/lib/server-subagent.js +7 -4
- package/dist/server/lib/supabase-client.js +51 -3
- package/dist/server/lib/template-resolver.js +14 -4
- package/dist/server/lib/utils.js +15 -0
- package/dist/server/local-agent-gateway.d.ts +44 -0
- package/dist/server/local-agent-gateway.js +389 -49
- package/dist/server/providers/anthropic.js +12 -2
- package/dist/server/providers/gemini.js +17 -2
- package/dist/server/proxy-handlers.js +151 -0
- package/dist/server/tool-router.d.ts +2 -2
- package/dist/server/tool-router.js +25 -35
- package/dist/shared/agent-core.d.ts +5 -2
- package/dist/shared/agent-core.js +30 -4
- package/dist/shared/api-client.js +54 -3
- package/dist/shared/sse-parser.d.ts +1 -1
- package/dist/shared/sse-parser.js +5 -2
- package/dist/shared/tool-dispatch.js +1 -1
- package/package.json +16 -10
- package/dist/server/handlers/__test-utils__/mock-supabase.d.ts +0 -11
- package/dist/server/handlers/__test-utils__/mock-supabase.js +0 -393
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme Manager — shiki syntax highlighting + theme switching
|
|
3
|
+
*
|
|
4
|
+
* Synchronous shiki highlighter using the JavaScript regex engine.
|
|
5
|
+
* Manages active theme state and persists preference to config store.
|
|
6
|
+
*/
|
|
7
|
+
import { type ThemePreset } from "./theme-presets.js";
|
|
8
|
+
export type { ThemePreset } from "./theme-presets.js";
|
|
9
|
+
/**
|
|
10
|
+
* Highlight code synchronously using shiki.
|
|
11
|
+
* Returns ANSI-colored string. Falls back to plain text for unknown languages.
|
|
12
|
+
*/
|
|
13
|
+
export declare function highlightCode(code: string, lang: string): string;
|
|
14
|
+
export declare function setRebuildCallback(cb: () => void): void;
|
|
15
|
+
/**
|
|
16
|
+
* Switch the active theme. Mutates `colors` in place so all importers see the change.
|
|
17
|
+
* Returns true if theme was found and applied.
|
|
18
|
+
*/
|
|
19
|
+
export declare function switchTheme(presetId: string): boolean;
|
|
20
|
+
/** Get the current theme ID */
|
|
21
|
+
export declare function getCurrentThemeId(): string;
|
|
22
|
+
/** Get all available theme presets */
|
|
23
|
+
export declare function getThemePresets(): ThemePreset[];
|
|
24
|
+
/**
|
|
25
|
+
* Initialize theme from saved preference. Call once at startup.
|
|
26
|
+
*/
|
|
27
|
+
export declare function initTheme(): void;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme Manager — shiki syntax highlighting + theme switching
|
|
3
|
+
*
|
|
4
|
+
* Synchronous shiki highlighter using the JavaScript regex engine.
|
|
5
|
+
* Manages active theme state and persists preference to config store.
|
|
6
|
+
*/
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
import { createHighlighterCoreSync } from "shiki/core";
|
|
9
|
+
import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
|
|
10
|
+
// Bundled themes (tree-shaken imports)
|
|
11
|
+
import catppuccinMocha from "shiki/themes/catppuccin-mocha.mjs";
|
|
12
|
+
import tokyoNight from "shiki/themes/tokyo-night.mjs";
|
|
13
|
+
import dracula from "shiki/themes/dracula.mjs";
|
|
14
|
+
import nord from "shiki/themes/nord.mjs";
|
|
15
|
+
import oneDarkPro from "shiki/themes/one-dark-pro.mjs";
|
|
16
|
+
// Bundled languages (tree-shaken imports)
|
|
17
|
+
import langTypescript from "shiki/langs/typescript.mjs";
|
|
18
|
+
import langJavascript from "shiki/langs/javascript.mjs";
|
|
19
|
+
import langTsx from "shiki/langs/tsx.mjs";
|
|
20
|
+
import langJsx from "shiki/langs/jsx.mjs";
|
|
21
|
+
import langPython from "shiki/langs/python.mjs";
|
|
22
|
+
import langJson from "shiki/langs/json.mjs";
|
|
23
|
+
import langBash from "shiki/langs/bash.mjs";
|
|
24
|
+
import langCss from "shiki/langs/css.mjs";
|
|
25
|
+
import langHtml from "shiki/langs/html.mjs";
|
|
26
|
+
import langXml from "shiki/langs/xml.mjs";
|
|
27
|
+
import langRust from "shiki/langs/rust.mjs";
|
|
28
|
+
import langGo from "shiki/langs/go.mjs";
|
|
29
|
+
import langSql from "shiki/langs/sql.mjs";
|
|
30
|
+
import langYaml from "shiki/langs/yaml.mjs";
|
|
31
|
+
import langToml from "shiki/langs/toml.mjs";
|
|
32
|
+
import langMarkdown from "shiki/langs/markdown.mjs";
|
|
33
|
+
import langSwift from "shiki/langs/swift.mjs";
|
|
34
|
+
import langDiff from "shiki/langs/diff.mjs";
|
|
35
|
+
import { colors } from "./Theme.js";
|
|
36
|
+
import { THEME_PRESETS, DEFAULT_THEME_ID, getPresetById } from "./theme-presets.js";
|
|
37
|
+
import { loadPreferences, savePreferences } from "../services/config-store.js";
|
|
38
|
+
// ============================================================================
|
|
39
|
+
// Shiki highlighter — created synchronously at module load
|
|
40
|
+
// ============================================================================
|
|
41
|
+
const allThemes = [catppuccinMocha, tokyoNight, dracula, nord, oneDarkPro];
|
|
42
|
+
const allLangs = [
|
|
43
|
+
langTypescript, langJavascript, langTsx, langJsx,
|
|
44
|
+
langPython, langJson, langBash, langCss, langHtml, langXml,
|
|
45
|
+
langRust, langGo, langSql, langYaml, langToml,
|
|
46
|
+
langMarkdown, langSwift, langDiff,
|
|
47
|
+
];
|
|
48
|
+
const highlighter = createHighlighterCoreSync({
|
|
49
|
+
themes: [...allThemes],
|
|
50
|
+
langs: [...allLangs],
|
|
51
|
+
engine: createJavaScriptRegexEngine(),
|
|
52
|
+
});
|
|
53
|
+
// Set of known language IDs for fast lookup
|
|
54
|
+
const KNOWN_LANGS = new Set(highlighter.getLoadedLanguages());
|
|
55
|
+
// Map common aliases to shiki language IDs
|
|
56
|
+
const LANG_ALIASES = {
|
|
57
|
+
ts: "typescript",
|
|
58
|
+
js: "javascript",
|
|
59
|
+
py: "python",
|
|
60
|
+
rb: "ruby",
|
|
61
|
+
sh: "bash",
|
|
62
|
+
shell: "bash",
|
|
63
|
+
zsh: "bash",
|
|
64
|
+
yml: "yaml",
|
|
65
|
+
md: "markdown",
|
|
66
|
+
rs: "rust",
|
|
67
|
+
htm: "html",
|
|
68
|
+
jsonc: "json",
|
|
69
|
+
terminal: "bash",
|
|
70
|
+
};
|
|
71
|
+
// ============================================================================
|
|
72
|
+
// State
|
|
73
|
+
// ============================================================================
|
|
74
|
+
let currentThemeId = DEFAULT_THEME_ID;
|
|
75
|
+
// ============================================================================
|
|
76
|
+
// Syntax highlighting
|
|
77
|
+
// ============================================================================
|
|
78
|
+
/**
|
|
79
|
+
* Highlight code synchronously using shiki.
|
|
80
|
+
* Returns ANSI-colored string. Falls back to plain text for unknown languages.
|
|
81
|
+
*/
|
|
82
|
+
export function highlightCode(code, lang) {
|
|
83
|
+
// Resolve language alias
|
|
84
|
+
const resolvedLang = LANG_ALIASES[lang] || lang;
|
|
85
|
+
if (!resolvedLang || !KNOWN_LANGS.has(resolvedLang)) {
|
|
86
|
+
return code; // plain text fallback
|
|
87
|
+
}
|
|
88
|
+
const preset = getPresetById(currentThemeId);
|
|
89
|
+
const shikiTheme = preset?.shikiTheme || "catppuccin-mocha";
|
|
90
|
+
try {
|
|
91
|
+
const result = highlighter.codeToTokens(code, {
|
|
92
|
+
lang: resolvedLang,
|
|
93
|
+
theme: shikiTheme,
|
|
94
|
+
});
|
|
95
|
+
// Convert tokens to chalk ANSI
|
|
96
|
+
const lines = [];
|
|
97
|
+
for (const tokenLine of result.tokens) {
|
|
98
|
+
const parts = [];
|
|
99
|
+
for (const token of tokenLine) {
|
|
100
|
+
if (token.color) {
|
|
101
|
+
let styled = chalk.hex(token.color)(token.content);
|
|
102
|
+
// Apply font styles
|
|
103
|
+
if (token.fontStyle) {
|
|
104
|
+
if (token.fontStyle & 1)
|
|
105
|
+
styled = chalk.italic(chalk.hex(token.color)(token.content));
|
|
106
|
+
if (token.fontStyle & 2)
|
|
107
|
+
styled = chalk.bold(chalk.hex(token.color)(token.content));
|
|
108
|
+
}
|
|
109
|
+
parts.push(styled);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
parts.push(token.content);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
lines.push(parts.join(""));
|
|
116
|
+
}
|
|
117
|
+
return lines.join("\n");
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return code; // fallback on any error
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// ============================================================================
|
|
124
|
+
// Theme switching
|
|
125
|
+
// ============================================================================
|
|
126
|
+
/** Rebuild callback — set by markdown.ts to rebuild chalk instances */
|
|
127
|
+
let rebuildCallback = null;
|
|
128
|
+
export function setRebuildCallback(cb) {
|
|
129
|
+
rebuildCallback = cb;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Switch the active theme. Mutates `colors` in place so all importers see the change.
|
|
133
|
+
* Returns true if theme was found and applied.
|
|
134
|
+
*/
|
|
135
|
+
export function switchTheme(presetId) {
|
|
136
|
+
const preset = getPresetById(presetId);
|
|
137
|
+
if (!preset)
|
|
138
|
+
return false;
|
|
139
|
+
currentThemeId = preset.id;
|
|
140
|
+
// Mutate colors in place — all modules import the same object
|
|
141
|
+
Object.assign(colors, preset.chrome);
|
|
142
|
+
// Persist preference
|
|
143
|
+
try {
|
|
144
|
+
const prefs = loadPreferences();
|
|
145
|
+
prefs.theme = preset.id;
|
|
146
|
+
savePreferences(prefs);
|
|
147
|
+
}
|
|
148
|
+
catch { /* best effort */ }
|
|
149
|
+
// Rebuild markdown renderer chalk instances
|
|
150
|
+
if (rebuildCallback)
|
|
151
|
+
rebuildCallback();
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
/** Get the current theme ID */
|
|
155
|
+
export function getCurrentThemeId() {
|
|
156
|
+
return currentThemeId;
|
|
157
|
+
}
|
|
158
|
+
/** Get all available theme presets */
|
|
159
|
+
export function getThemePresets() {
|
|
160
|
+
return THEME_PRESETS;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Initialize theme from saved preference. Call once at startup.
|
|
164
|
+
*/
|
|
165
|
+
export function initTheme() {
|
|
166
|
+
try {
|
|
167
|
+
const prefs = loadPreferences();
|
|
168
|
+
if (prefs.theme) {
|
|
169
|
+
const preset = getPresetById(prefs.theme);
|
|
170
|
+
if (preset) {
|
|
171
|
+
currentThemeId = preset.id;
|
|
172
|
+
Object.assign(colors, preset.chrome);
|
|
173
|
+
// Rebuild will be called after markdown module loads
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch { /* use default */ }
|
|
178
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme Presets — bundled color themes for whale CLI
|
|
3
|
+
*
|
|
4
|
+
* Each preset defines:
|
|
5
|
+
* - shikiTheme: the shiki theme ID for syntax highlighting
|
|
6
|
+
* - chrome: full UI color overrides (all keys from DEFAULT_COLORS)
|
|
7
|
+
*/
|
|
8
|
+
export interface ThemePreset {
|
|
9
|
+
id: string;
|
|
10
|
+
label: string;
|
|
11
|
+
shikiTheme: string;
|
|
12
|
+
chrome: Record<string, string>;
|
|
13
|
+
}
|
|
14
|
+
export declare const DEFAULT_THEME_ID = "catppuccin-mocha";
|
|
15
|
+
export declare const THEME_PRESETS: ThemePreset[];
|
|
16
|
+
export declare function getPresetById(id: string): ThemePreset | undefined;
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme Presets — bundled color themes for whale CLI
|
|
3
|
+
*
|
|
4
|
+
* Each preset defines:
|
|
5
|
+
* - shikiTheme: the shiki theme ID for syntax highlighting
|
|
6
|
+
* - chrome: full UI color overrides (all keys from DEFAULT_COLORS)
|
|
7
|
+
*/
|
|
8
|
+
export const DEFAULT_THEME_ID = "catppuccin-mocha";
|
|
9
|
+
export const THEME_PRESETS = [
|
|
10
|
+
// ── Catppuccin Mocha — warm modern dark ──
|
|
11
|
+
{
|
|
12
|
+
id: "catppuccin-mocha",
|
|
13
|
+
label: "Catppuccin Mocha",
|
|
14
|
+
shikiTheme: "catppuccin-mocha",
|
|
15
|
+
chrome: {
|
|
16
|
+
brand: "#89B4FA",
|
|
17
|
+
brandDim: "#74C7EC",
|
|
18
|
+
success: "#A6E3A1",
|
|
19
|
+
error: "#F38BA8",
|
|
20
|
+
warning: "#FAB387",
|
|
21
|
+
info: "#94E2D5",
|
|
22
|
+
pink: "#F5C2E7",
|
|
23
|
+
purple: "#CBA6F7",
|
|
24
|
+
indigo: "#B4BEFE",
|
|
25
|
+
mint: "#94E2D5",
|
|
26
|
+
teal: "#89DCEB",
|
|
27
|
+
lavender: "#B4BEFE",
|
|
28
|
+
roseGold: "#F5C2E7",
|
|
29
|
+
text: "#CDD6F4",
|
|
30
|
+
secondary: "#A6ADC8",
|
|
31
|
+
tertiary: "#6C7086",
|
|
32
|
+
quaternary: "#45475A",
|
|
33
|
+
muted: "#A6ADC8",
|
|
34
|
+
dim: "#9399B2",
|
|
35
|
+
subtle: "#6C7086",
|
|
36
|
+
border: "#45475A",
|
|
37
|
+
user: "#CDD6F4",
|
|
38
|
+
assistant: "#CBA6F7",
|
|
39
|
+
tool: "#89B4FA",
|
|
40
|
+
localTool: "#CBA6F7",
|
|
41
|
+
serverTool: "#F5C2E7",
|
|
42
|
+
gain: "#A6E3A1",
|
|
43
|
+
loss: "#F38BA8",
|
|
44
|
+
panel: "#1E1E2E",
|
|
45
|
+
separator: "#45475A",
|
|
46
|
+
diffAddedBg: "#1E3A2F",
|
|
47
|
+
diffRemovedBg: "#3A1E2A",
|
|
48
|
+
diffWordAdded: "#2E5A45",
|
|
49
|
+
diffWordRemoved: "#5A2E3A",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
// ── Tokyo Night — cool blue-toned ──
|
|
53
|
+
{
|
|
54
|
+
id: "tokyo-night",
|
|
55
|
+
label: "Tokyo Night",
|
|
56
|
+
shikiTheme: "tokyo-night",
|
|
57
|
+
chrome: {
|
|
58
|
+
brand: "#7AA2F7",
|
|
59
|
+
brandDim: "#3D59A1",
|
|
60
|
+
success: "#9ECE6A",
|
|
61
|
+
error: "#F7768E",
|
|
62
|
+
warning: "#E0AF68",
|
|
63
|
+
info: "#7DCFFF",
|
|
64
|
+
pink: "#F7768E",
|
|
65
|
+
purple: "#BB9AF7",
|
|
66
|
+
indigo: "#7AA2F7",
|
|
67
|
+
mint: "#73DACA",
|
|
68
|
+
teal: "#2AC3DE",
|
|
69
|
+
lavender: "#C0CAF5",
|
|
70
|
+
roseGold: "#FF9E64",
|
|
71
|
+
text: "#C0CAF5",
|
|
72
|
+
secondary: "#9AA5CE",
|
|
73
|
+
tertiary: "#565F89",
|
|
74
|
+
quaternary: "#3B4261",
|
|
75
|
+
muted: "#9AA5CE",
|
|
76
|
+
dim: "#787C99",
|
|
77
|
+
subtle: "#565F89",
|
|
78
|
+
border: "#3B4261",
|
|
79
|
+
user: "#C0CAF5",
|
|
80
|
+
assistant: "#BB9AF7",
|
|
81
|
+
tool: "#7AA2F7",
|
|
82
|
+
localTool: "#BB9AF7",
|
|
83
|
+
serverTool: "#F7768E",
|
|
84
|
+
gain: "#9ECE6A",
|
|
85
|
+
loss: "#F7768E",
|
|
86
|
+
panel: "#1A1B26",
|
|
87
|
+
separator: "#3B4261",
|
|
88
|
+
diffAddedBg: "#1A2E1F",
|
|
89
|
+
diffRemovedBg: "#2E1A22",
|
|
90
|
+
diffWordAdded: "#2A4A32",
|
|
91
|
+
diffWordRemoved: "#4A2A35",
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
// ── Dracula — bold purple-heavy ──
|
|
95
|
+
{
|
|
96
|
+
id: "dracula",
|
|
97
|
+
label: "Dracula",
|
|
98
|
+
shikiTheme: "dracula",
|
|
99
|
+
chrome: {
|
|
100
|
+
brand: "#BD93F9",
|
|
101
|
+
brandDim: "#6272A4",
|
|
102
|
+
success: "#50FA7B",
|
|
103
|
+
error: "#FF5555",
|
|
104
|
+
warning: "#FFB86C",
|
|
105
|
+
info: "#8BE9FD",
|
|
106
|
+
pink: "#FF79C6",
|
|
107
|
+
purple: "#BD93F9",
|
|
108
|
+
indigo: "#6272A4",
|
|
109
|
+
mint: "#8BE9FD",
|
|
110
|
+
teal: "#8BE9FD",
|
|
111
|
+
lavender: "#BD93F9",
|
|
112
|
+
roseGold: "#FF79C6",
|
|
113
|
+
text: "#F8F8F2",
|
|
114
|
+
secondary: "#BFBFBF",
|
|
115
|
+
tertiary: "#6272A4",
|
|
116
|
+
quaternary: "#44475A",
|
|
117
|
+
muted: "#BFBFBF",
|
|
118
|
+
dim: "#6272A4",
|
|
119
|
+
subtle: "#6272A4",
|
|
120
|
+
border: "#44475A",
|
|
121
|
+
user: "#F8F8F2",
|
|
122
|
+
assistant: "#BD93F9",
|
|
123
|
+
tool: "#8BE9FD",
|
|
124
|
+
localTool: "#BD93F9",
|
|
125
|
+
serverTool: "#FF79C6",
|
|
126
|
+
gain: "#50FA7B",
|
|
127
|
+
loss: "#FF5555",
|
|
128
|
+
panel: "#282A36",
|
|
129
|
+
separator: "#44475A",
|
|
130
|
+
diffAddedBg: "#1A3A1A",
|
|
131
|
+
diffRemovedBg: "#3A1A1A",
|
|
132
|
+
diffWordAdded: "#2E5A2E",
|
|
133
|
+
diffWordRemoved: "#5A2E2E",
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
// ── Nord — muted Scandinavian ──
|
|
137
|
+
{
|
|
138
|
+
id: "nord",
|
|
139
|
+
label: "Nord",
|
|
140
|
+
shikiTheme: "nord",
|
|
141
|
+
chrome: {
|
|
142
|
+
brand: "#81A1C1",
|
|
143
|
+
brandDim: "#5E81AC",
|
|
144
|
+
success: "#A3BE8C",
|
|
145
|
+
error: "#BF616A",
|
|
146
|
+
warning: "#EBCB8B",
|
|
147
|
+
info: "#88C0D0",
|
|
148
|
+
pink: "#B48EAD",
|
|
149
|
+
purple: "#B48EAD",
|
|
150
|
+
indigo: "#81A1C1",
|
|
151
|
+
mint: "#8FBCBB",
|
|
152
|
+
teal: "#88C0D0",
|
|
153
|
+
lavender: "#B48EAD",
|
|
154
|
+
roseGold: "#D08770",
|
|
155
|
+
text: "#ECEFF4",
|
|
156
|
+
secondary: "#D8DEE9",
|
|
157
|
+
tertiary: "#4C566A",
|
|
158
|
+
quaternary: "#3B4252",
|
|
159
|
+
muted: "#D8DEE9",
|
|
160
|
+
dim: "#616E88",
|
|
161
|
+
subtle: "#4C566A",
|
|
162
|
+
border: "#3B4252",
|
|
163
|
+
user: "#ECEFF4",
|
|
164
|
+
assistant: "#B48EAD",
|
|
165
|
+
tool: "#81A1C1",
|
|
166
|
+
localTool: "#B48EAD",
|
|
167
|
+
serverTool: "#D08770",
|
|
168
|
+
gain: "#A3BE8C",
|
|
169
|
+
loss: "#BF616A",
|
|
170
|
+
panel: "#2E3440",
|
|
171
|
+
separator: "#3B4252",
|
|
172
|
+
diffAddedBg: "#2A3A2A",
|
|
173
|
+
diffRemovedBg: "#3A2A2A",
|
|
174
|
+
diffWordAdded: "#3A4A3A",
|
|
175
|
+
diffWordRemoved: "#4A3A3A",
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
// ── One Dark Pro — VS Code classic ──
|
|
179
|
+
{
|
|
180
|
+
id: "one-dark-pro",
|
|
181
|
+
label: "One Dark Pro",
|
|
182
|
+
shikiTheme: "one-dark-pro",
|
|
183
|
+
chrome: {
|
|
184
|
+
brand: "#61AFEF",
|
|
185
|
+
brandDim: "#528BFF",
|
|
186
|
+
success: "#98C379",
|
|
187
|
+
error: "#E06C75",
|
|
188
|
+
warning: "#D19A66",
|
|
189
|
+
info: "#56B6C2",
|
|
190
|
+
pink: "#E06C75",
|
|
191
|
+
purple: "#C678DD",
|
|
192
|
+
indigo: "#61AFEF",
|
|
193
|
+
mint: "#56B6C2",
|
|
194
|
+
teal: "#56B6C2",
|
|
195
|
+
lavender: "#C678DD",
|
|
196
|
+
roseGold: "#E5C07B",
|
|
197
|
+
text: "#ABB2BF",
|
|
198
|
+
secondary: "#828997",
|
|
199
|
+
tertiary: "#5C6370",
|
|
200
|
+
quaternary: "#3E4452",
|
|
201
|
+
muted: "#828997",
|
|
202
|
+
dim: "#636D83",
|
|
203
|
+
subtle: "#5C6370",
|
|
204
|
+
border: "#3E4452",
|
|
205
|
+
user: "#ABB2BF",
|
|
206
|
+
assistant: "#C678DD",
|
|
207
|
+
tool: "#61AFEF",
|
|
208
|
+
localTool: "#C678DD",
|
|
209
|
+
serverTool: "#E06C75",
|
|
210
|
+
gain: "#98C379",
|
|
211
|
+
loss: "#E06C75",
|
|
212
|
+
panel: "#282C34",
|
|
213
|
+
separator: "#3E4452",
|
|
214
|
+
diffAddedBg: "#1E3A1E",
|
|
215
|
+
diffRemovedBg: "#3A1E1E",
|
|
216
|
+
diffWordAdded: "#2E5A2E",
|
|
217
|
+
diffWordRemoved: "#5A2E2E",
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
// ── Apple Dark — original whale CLI colors (preserved as-is) ──
|
|
221
|
+
{
|
|
222
|
+
id: "apple-dark",
|
|
223
|
+
label: "Apple Dark",
|
|
224
|
+
shikiTheme: "one-dark-pro", // closest VS Code match for syntax
|
|
225
|
+
chrome: {
|
|
226
|
+
brand: "#0A84FF",
|
|
227
|
+
brandDim: "#0071E3",
|
|
228
|
+
success: "#30D158",
|
|
229
|
+
error: "#FF453A",
|
|
230
|
+
warning: "#FF9F0A",
|
|
231
|
+
info: "#64D2FF",
|
|
232
|
+
pink: "#FF375F",
|
|
233
|
+
purple: "#BF5AF2",
|
|
234
|
+
indigo: "#5E5CE6",
|
|
235
|
+
mint: "#66D4CF",
|
|
236
|
+
teal: "#6AC4DC",
|
|
237
|
+
lavender: "#D4BBFF",
|
|
238
|
+
roseGold: "#FFB5C2",
|
|
239
|
+
text: "#F5F5F7",
|
|
240
|
+
secondary: "#A1A1A6",
|
|
241
|
+
tertiary: "#6E6E73",
|
|
242
|
+
quaternary: "#48484A",
|
|
243
|
+
muted: "#A1A1A6",
|
|
244
|
+
dim: "#86868B",
|
|
245
|
+
subtle: "#6E6E73",
|
|
246
|
+
border: "#38383A",
|
|
247
|
+
user: "#F5F5F7",
|
|
248
|
+
assistant: "#BF5AF2",
|
|
249
|
+
tool: "#0A84FF",
|
|
250
|
+
localTool: "#BF5AF2",
|
|
251
|
+
serverTool: "#FF375F",
|
|
252
|
+
gain: "#30D158",
|
|
253
|
+
loss: "#FF453A",
|
|
254
|
+
panel: "#1C1C1E",
|
|
255
|
+
separator: "#38383A",
|
|
256
|
+
diffAddedBg: "#005f00",
|
|
257
|
+
diffRemovedBg: "#5f0000",
|
|
258
|
+
diffWordAdded: "#008700",
|
|
259
|
+
diffWordRemoved: "#870000",
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
];
|
|
263
|
+
export function getPresetById(id) {
|
|
264
|
+
return THEME_PRESETS.find((p) => p.id === id);
|
|
265
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -334,7 +334,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
334
334
|
};
|
|
335
335
|
}
|
|
336
336
|
// ── Remote tool execution ──
|
|
337
|
-
const startTime = Date.now();
|
|
338
337
|
const traceId = crypto.randomUUID();
|
|
339
338
|
// Allow tool-level store_id override (e.g. multi-store users switching context)
|
|
340
339
|
const effectiveStoreId = toolArgs.store_id || STORE_ID || undefined;
|
|
@@ -343,9 +342,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
343
342
|
}
|
|
344
343
|
console.error(`[MCP] Executing: ${toolName} → Fly.io [${traceId.slice(0, 8)}] store=${effectiveStoreId?.slice(0, 8) || "none"}`);
|
|
345
344
|
const result = await executeToolRemote(toolName, toolArgs, effectiveStoreId, traceId);
|
|
346
|
-
const durationMs = Date.now() - startTime;
|
|
347
|
-
// Fire-and-forget telemetry
|
|
348
|
-
logMcpToolCall(toolName, toolArgs.action, result.success, durationMs, traceId, result.error).catch(() => { });
|
|
349
345
|
if (result.success) {
|
|
350
346
|
return {
|
|
351
347
|
content: [{
|
|
@@ -366,53 +362,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
366
362
|
};
|
|
367
363
|
}
|
|
368
364
|
});
|
|
369
|
-
/**
|
|
370
|
-
* Fire-and-forget MCP proxy telemetry — logs to audit_logs as a third service
|
|
371
|
-
* ("mcp-proxy") alongside "whale-cli" and "agent-server", capturing the full
|
|
372
|
-
* network round-trip latency from MCP client → Fly.io server → response.
|
|
373
|
-
*/
|
|
374
|
-
async function logMcpToolCall(toolName, action, success, durationMs, traceId, error) {
|
|
375
|
-
if (!supabase)
|
|
376
|
-
return;
|
|
377
|
-
try {
|
|
378
|
-
const now = new Date();
|
|
379
|
-
const startTime = new Date(now.getTime() - durationMs);
|
|
380
|
-
const bytes = new Uint8Array(8);
|
|
381
|
-
crypto.getRandomValues(bytes);
|
|
382
|
-
const spanId = Array.from(bytes).map(b => b.toString(16).padStart(2, "0")).join("");
|
|
383
|
-
await supabase.from("audit_logs").insert({
|
|
384
|
-
action: `mcp.tool.${toolName}${action ? `.${action}` : ""}`,
|
|
385
|
-
severity: success ? "info" : "error",
|
|
386
|
-
store_id: STORE_ID || null,
|
|
387
|
-
user_id: USER_ID,
|
|
388
|
-
user_email: USER_EMAIL,
|
|
389
|
-
resource_type: "whale_code_mcp",
|
|
390
|
-
resource_id: toolName,
|
|
391
|
-
request_id: traceId,
|
|
392
|
-
conversation_id: SESSION_ID,
|
|
393
|
-
source: "whale_code_mcp",
|
|
394
|
-
details: {
|
|
395
|
-
session_id: SESSION_ID,
|
|
396
|
-
source: "whale_code_mcp",
|
|
397
|
-
tool_name: toolName,
|
|
398
|
-
action: action || null,
|
|
399
|
-
},
|
|
400
|
-
error_message: error || null,
|
|
401
|
-
duration_ms: durationMs,
|
|
402
|
-
trace_id: traceId,
|
|
403
|
-
span_id: spanId,
|
|
404
|
-
span_kind: "CLIENT",
|
|
405
|
-
service_name: "whale-code-mcp",
|
|
406
|
-
service_version: PKG_VERSION,
|
|
407
|
-
status_code: success ? "OK" : "ERROR",
|
|
408
|
-
start_time: startTime.toISOString(),
|
|
409
|
-
end_time: now.toISOString(),
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
|
-
catch {
|
|
413
|
-
// Telemetry must never break tool execution
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
365
|
// ============================================================================
|
|
417
366
|
// LOCAL AGENT (auto-spawned — shares this process with MCP server)
|
|
418
367
|
// ============================================================================
|
|
@@ -9,9 +9,19 @@ export declare class IMessageAdapter extends BaseAdapter {
|
|
|
9
9
|
readonly name: string;
|
|
10
10
|
private process;
|
|
11
11
|
private config;
|
|
12
|
+
private restartAttempts;
|
|
13
|
+
private maxRestartAttempts;
|
|
14
|
+
private lastError;
|
|
15
|
+
private restartTimer;
|
|
12
16
|
constructor(name: string, config?: IMessageConfig);
|
|
13
17
|
start(): Promise<void>;
|
|
14
18
|
stop(): Promise<void>;
|
|
19
|
+
/** Manual restart — resets attempt counter and restarts */
|
|
20
|
+
restart(): void;
|
|
21
|
+
/** Get the last stderr error message */
|
|
22
|
+
getLastError(): string | null;
|
|
23
|
+
/** Get current restart attempt count */
|
|
24
|
+
getRestartAttempts(): number;
|
|
15
25
|
sendMessage(msg: OutboundMessage): Promise<boolean>;
|
|
16
26
|
private handleLine;
|
|
17
27
|
}
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
2
3
|
import { BaseAdapter } from "./base.js";
|
|
4
|
+
// Resolve imsg binary — prefer /opt/homebrew/bin (macOS ARM) then fall back to PATH
|
|
5
|
+
const IMSG_BIN = existsSync("/opt/homebrew/bin/imsg") ? "/opt/homebrew/bin/imsg" : "imsg";
|
|
3
6
|
export class IMessageAdapter extends BaseAdapter {
|
|
4
7
|
type = "imessage";
|
|
5
8
|
name;
|
|
6
9
|
process = null;
|
|
7
10
|
config;
|
|
11
|
+
// Restart resilience
|
|
12
|
+
restartAttempts = 0;
|
|
13
|
+
maxRestartAttempts = 5;
|
|
14
|
+
lastError = null;
|
|
15
|
+
restartTimer = null;
|
|
8
16
|
constructor(name, config = {}) {
|
|
9
17
|
super();
|
|
10
18
|
this.name = name;
|
|
@@ -16,7 +24,7 @@ export class IMessageAdapter extends BaseAdapter {
|
|
|
16
24
|
this.running = true;
|
|
17
25
|
// Spawn imsg watch as a child process
|
|
18
26
|
const args = ["watch", "--json"];
|
|
19
|
-
this.process = spawn(
|
|
27
|
+
this.process = spawn(IMSG_BIN, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
20
28
|
let buffer = "";
|
|
21
29
|
this.process.stdout?.on("data", (chunk) => {
|
|
22
30
|
buffer += chunk.toString();
|
|
@@ -32,27 +40,56 @@ export class IMessageAdapter extends BaseAdapter {
|
|
|
32
40
|
}
|
|
33
41
|
});
|
|
34
42
|
this.process.stderr?.on("data", (chunk) => {
|
|
35
|
-
|
|
43
|
+
const msg = chunk.toString().trim();
|
|
44
|
+
this.lastError = msg;
|
|
45
|
+
console.error(`[imessage] stderr:`, msg);
|
|
36
46
|
});
|
|
37
47
|
this.process.on("exit", (code) => {
|
|
38
48
|
console.log(`[imessage] Process exited with code ${code}`);
|
|
39
49
|
const wasRunning = this.running;
|
|
40
50
|
this.running = false;
|
|
41
|
-
// Auto-restart
|
|
51
|
+
// Auto-restart with exponential backoff if unexpected exit
|
|
42
52
|
if (code !== 0 && wasRunning) {
|
|
43
|
-
|
|
44
|
-
|
|
53
|
+
if (this.restartAttempts >= this.maxRestartAttempts) {
|
|
54
|
+
console.error(`[imessage] Max restart attempts (${this.maxRestartAttempts}) reached. Giving up. Last error: ${this.lastError || "unknown"}`);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
this.restartAttempts++;
|
|
58
|
+
const delay = Math.min(5000 * Math.pow(2, this.restartAttempts - 1), 80_000);
|
|
59
|
+
console.log(`[imessage] Restarting in ${delay / 1000}s (attempt ${this.restartAttempts}/${this.maxRestartAttempts}, last error: ${this.lastError || "unknown"})...`);
|
|
60
|
+
this.restartTimer = setTimeout(() => {
|
|
61
|
+
this.restartTimer = null;
|
|
62
|
+
this.start();
|
|
63
|
+
}, delay);
|
|
45
64
|
}
|
|
46
65
|
});
|
|
47
66
|
console.log(`[imessage] Watching for messages...`);
|
|
48
67
|
}
|
|
49
68
|
async stop() {
|
|
50
69
|
this.running = false;
|
|
70
|
+
if (this.restartTimer) {
|
|
71
|
+
clearTimeout(this.restartTimer);
|
|
72
|
+
this.restartTimer = null;
|
|
73
|
+
}
|
|
51
74
|
if (this.process) {
|
|
52
75
|
this.process.kill("SIGTERM");
|
|
53
76
|
this.process = null;
|
|
54
77
|
}
|
|
55
78
|
}
|
|
79
|
+
/** Manual restart — resets attempt counter and restarts */
|
|
80
|
+
restart() {
|
|
81
|
+
this.restartAttempts = 0;
|
|
82
|
+
this.lastError = null;
|
|
83
|
+
this.stop().then(() => this.start());
|
|
84
|
+
}
|
|
85
|
+
/** Get the last stderr error message */
|
|
86
|
+
getLastError() {
|
|
87
|
+
return this.lastError;
|
|
88
|
+
}
|
|
89
|
+
/** Get current restart attempt count */
|
|
90
|
+
getRestartAttempts() {
|
|
91
|
+
return this.restartAttempts;
|
|
92
|
+
}
|
|
56
93
|
async sendMessage(msg) {
|
|
57
94
|
try {
|
|
58
95
|
const text = msg.content.substring(0, this.config.max_message_length || 4000);
|
|
@@ -61,7 +98,7 @@ export class IMessageAdapter extends BaseAdapter {
|
|
|
61
98
|
console.error("[imessage] No chat_id to send to");
|
|
62
99
|
return false;
|
|
63
100
|
}
|
|
64
|
-
const proc = spawn(
|
|
101
|
+
const proc = spawn(IMSG_BIN, ["send", "--chat-id", String(chatId), "--text", text]);
|
|
65
102
|
return new Promise((resolve) => {
|
|
66
103
|
proc.on("exit", (code) => {
|
|
67
104
|
if (code === 0) {
|
|
@@ -109,6 +146,8 @@ export class IMessageAdapter extends BaseAdapter {
|
|
|
109
146
|
attachments: data.attachments,
|
|
110
147
|
},
|
|
111
148
|
};
|
|
149
|
+
// Successful message receipt — reset restart counter
|
|
150
|
+
this.restartAttempts = 0;
|
|
112
151
|
this.stats.messages_in++;
|
|
113
152
|
this.stats.last_message_at = new Date().toISOString();
|
|
114
153
|
if (this.onInboundMessage) {
|