@hera-al/server 1.6.1
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/LICENSE +21 -0
- package/README.md +325 -0
- package/bundled/apple-notes/SKILL.md +77 -0
- package/bundled/apple-reminders/SKILL.md +96 -0
- package/bundled/blogwatcher/SKILL.md +69 -0
- package/bundled/camsnap/SKILL.md +45 -0
- package/bundled/discord/SKILL.md +578 -0
- package/bundled/gemini/SKILL.md +43 -0
- package/bundled/gifgrep/SKILL.md +79 -0
- package/bundled/github/SKILL.md +77 -0
- package/bundled/gog/SKILL.md +116 -0
- package/bundled/goplaces/SKILL.md +52 -0
- package/bundled/himalaya/SKILL.md +257 -0
- package/bundled/himalaya/references/configuration.md +184 -0
- package/bundled/himalaya/references/message-composition.md +199 -0
- package/bundled/homebrew/SKILL.md +82 -0
- package/bundled/local-places/SERVER_README.md +101 -0
- package/bundled/local-places/SKILL.md +102 -0
- package/bundled/local-places/pyproject.toml +21 -0
- package/bundled/local-places/src/local_places/__init__.py +2 -0
- package/bundled/local-places/src/local_places/google_places.py +314 -0
- package/bundled/local-places/src/local_places/main.py +65 -0
- package/bundled/local-places/src/local_places/schemas.py +107 -0
- package/bundled/markitdown/SKILL.md +96 -0
- package/bundled/mcporter/SKILL.md +61 -0
- package/bundled/merge-pr/SKILL.md +187 -0
- package/bundled/merge-pr/agents/openai.yaml +4 -0
- package/bundled/nano-banana-pro/SKILL.md +58 -0
- package/bundled/nano-banana-pro/scripts/generate_image.py +184 -0
- package/bundled/nano-pdf/SKILL.md +38 -0
- package/bundled/open-prose/README.md +25 -0
- package/bundled/open-prose/index.ts +5 -0
- package/bundled/open-prose/openclaw.plugin.json +11 -0
- package/bundled/open-prose/package.json +15 -0
- package/bundled/open-prose/skills/prose/LICENSE +21 -0
- package/bundled/open-prose/skills/prose/SKILL.md +323 -0
- package/bundled/open-prose/skills/prose/alt-borges.md +141 -0
- package/bundled/open-prose/skills/prose/alts/arabian-nights.md +358 -0
- package/bundled/open-prose/skills/prose/alts/borges.md +360 -0
- package/bundled/open-prose/skills/prose/alts/folk.md +322 -0
- package/bundled/open-prose/skills/prose/alts/homer.md +346 -0
- package/bundled/open-prose/skills/prose/alts/kafka.md +373 -0
- package/bundled/open-prose/skills/prose/compiler.md +2971 -0
- package/bundled/open-prose/skills/prose/examples/01-hello-world.prose +4 -0
- package/bundled/open-prose/skills/prose/examples/02-research-and-summarize.prose +6 -0
- package/bundled/open-prose/skills/prose/examples/03-code-review.prose +17 -0
- package/bundled/open-prose/skills/prose/examples/04-write-and-refine.prose +14 -0
- package/bundled/open-prose/skills/prose/examples/05-debug-issue.prose +20 -0
- package/bundled/open-prose/skills/prose/examples/06-explain-codebase.prose +17 -0
- package/bundled/open-prose/skills/prose/examples/07-refactor.prose +20 -0
- package/bundled/open-prose/skills/prose/examples/08-blog-post.prose +20 -0
- package/bundled/open-prose/skills/prose/examples/09-research-with-agents.prose +25 -0
- package/bundled/open-prose/skills/prose/examples/10-code-review-agents.prose +32 -0
- package/bundled/open-prose/skills/prose/examples/11-skills-and-imports.prose +27 -0
- package/bundled/open-prose/skills/prose/examples/12-secure-agent-permissions.prose +43 -0
- package/bundled/open-prose/skills/prose/examples/13-variables-and-context.prose +51 -0
- package/bundled/open-prose/skills/prose/examples/14-composition-blocks.prose +48 -0
- package/bundled/open-prose/skills/prose/examples/15-inline-sequences.prose +23 -0
- package/bundled/open-prose/skills/prose/examples/16-parallel-reviews.prose +19 -0
- package/bundled/open-prose/skills/prose/examples/17-parallel-research.prose +19 -0
- package/bundled/open-prose/skills/prose/examples/18-mixed-parallel-sequential.prose +36 -0
- package/bundled/open-prose/skills/prose/examples/19-advanced-parallel.prose +71 -0
- package/bundled/open-prose/skills/prose/examples/20-fixed-loops.prose +20 -0
- package/bundled/open-prose/skills/prose/examples/21-pipeline-operations.prose +35 -0
- package/bundled/open-prose/skills/prose/examples/22-error-handling.prose +51 -0
- package/bundled/open-prose/skills/prose/examples/23-retry-with-backoff.prose +63 -0
- package/bundled/open-prose/skills/prose/examples/24-choice-blocks.prose +86 -0
- package/bundled/open-prose/skills/prose/examples/25-conditionals.prose +114 -0
- package/bundled/open-prose/skills/prose/examples/26-parameterized-blocks.prose +100 -0
- package/bundled/open-prose/skills/prose/examples/27-string-interpolation.prose +105 -0
- package/bundled/open-prose/skills/prose/examples/28-automated-pr-review.prose +37 -0
- package/bundled/open-prose/skills/prose/examples/28-gas-town.prose +1572 -0
- package/bundled/open-prose/skills/prose/examples/29-captains-chair.prose +218 -0
- package/bundled/open-prose/skills/prose/examples/30-captains-chair-simple.prose +42 -0
- package/bundled/open-prose/skills/prose/examples/31-captains-chair-with-memory.prose +145 -0
- package/bundled/open-prose/skills/prose/examples/33-pr-review-autofix.prose +168 -0
- package/bundled/open-prose/skills/prose/examples/34-content-pipeline.prose +204 -0
- package/bundled/open-prose/skills/prose/examples/35-feature-factory.prose +296 -0
- package/bundled/open-prose/skills/prose/examples/36-bug-hunter.prose +237 -0
- package/bundled/open-prose/skills/prose/examples/37-the-forge.prose +1474 -0
- package/bundled/open-prose/skills/prose/examples/38-skill-scan.prose +455 -0
- package/bundled/open-prose/skills/prose/examples/39-architect-by-simulation.prose +277 -0
- package/bundled/open-prose/skills/prose/examples/40-rlm-self-refine.prose +32 -0
- package/bundled/open-prose/skills/prose/examples/41-rlm-divide-conquer.prose +38 -0
- package/bundled/open-prose/skills/prose/examples/42-rlm-filter-recurse.prose +46 -0
- package/bundled/open-prose/skills/prose/examples/43-rlm-pairwise.prose +50 -0
- package/bundled/open-prose/skills/prose/examples/44-run-endpoint-ux-test.prose +261 -0
- package/bundled/open-prose/skills/prose/examples/45-plugin-release.prose +159 -0
- package/bundled/open-prose/skills/prose/examples/45-run-endpoint-ux-test-with-remediation.prose +637 -0
- package/bundled/open-prose/skills/prose/examples/46-run-endpoint-ux-test-fast.prose +148 -0
- package/bundled/open-prose/skills/prose/examples/46-workflow-crystallizer.prose +225 -0
- package/bundled/open-prose/skills/prose/examples/47-language-self-improvement.prose +356 -0
- package/bundled/open-prose/skills/prose/examples/48-habit-miner.prose +445 -0
- package/bundled/open-prose/skills/prose/examples/49-prose-run-retrospective.prose +210 -0
- package/bundled/open-prose/skills/prose/examples/README.md +391 -0
- package/bundled/open-prose/skills/prose/examples/roadmap/README.md +22 -0
- package/bundled/open-prose/skills/prose/examples/roadmap/iterative-refinement.prose +20 -0
- package/bundled/open-prose/skills/prose/examples/roadmap/parallel-review.prose +18 -0
- package/bundled/open-prose/skills/prose/examples/roadmap/simple-pipeline.prose +17 -0
- package/bundled/open-prose/skills/prose/examples/roadmap/syntax/open-prose-syntax.prose +223 -0
- package/bundled/open-prose/skills/prose/guidance/antipatterns.md +951 -0
- package/bundled/open-prose/skills/prose/guidance/patterns.md +700 -0
- package/bundled/open-prose/skills/prose/guidance/system-prompt.md +180 -0
- package/bundled/open-prose/skills/prose/help.md +144 -0
- package/bundled/open-prose/skills/prose/lib/README.md +108 -0
- package/bundled/open-prose/skills/prose/lib/calibrator.prose +215 -0
- package/bundled/open-prose/skills/prose/lib/cost-analyzer.prose +174 -0
- package/bundled/open-prose/skills/prose/lib/error-forensics.prose +250 -0
- package/bundled/open-prose/skills/prose/lib/inspector.prose +196 -0
- package/bundled/open-prose/skills/prose/lib/profiler.prose +460 -0
- package/bundled/open-prose/skills/prose/lib/program-improver.prose +275 -0
- package/bundled/open-prose/skills/prose/lib/project-memory.prose +118 -0
- package/bundled/open-prose/skills/prose/lib/user-memory.prose +93 -0
- package/bundled/open-prose/skills/prose/lib/vm-improver.prose +243 -0
- package/bundled/open-prose/skills/prose/primitives/session.md +593 -0
- package/bundled/open-prose/skills/prose/prose.md +1237 -0
- package/bundled/open-prose/skills/prose/state/filesystem.md +498 -0
- package/bundled/open-prose/skills/prose/state/in-context.md +384 -0
- package/bundled/open-prose/skills/prose/state/postgres.md +880 -0
- package/bundled/open-prose/skills/prose/state/sqlite.md +574 -0
- package/bundled/peekaboo/SKILL.md +190 -0
- package/bundled/prepare-pr/SKILL.md +277 -0
- package/bundled/prepare-pr/agents/openai.yaml +4 -0
- package/bundled/review-pr/SKILL.md +228 -0
- package/bundled/review-pr/agents/openai.yaml +4 -0
- package/bundled/sag/SKILL.md +87 -0
- package/bundled/skill-creator/SKILL.md +370 -0
- package/bundled/skill-creator/license.txt +202 -0
- package/bundled/skill-creator/scripts/init_skill.py +378 -0
- package/bundled/skill-creator/scripts/package_skill.py +111 -0
- package/bundled/skill-creator/scripts/quick_validate.py +101 -0
- package/bundled/spotify-player/SKILL.md +64 -0
- package/bundled/ssh/SKILL.md +119 -0
- package/bundled/summarize/SKILL.md +87 -0
- package/bundled/video-frames/SKILL.md +46 -0
- package/bundled/video-frames/scripts/frame.sh +81 -0
- package/bundled/voice-call/SKILL.md +45 -0
- package/bundled/wacli/SKILL.md +72 -0
- package/bundled/weather/SKILL.md +54 -0
- package/dist/agent/agent-service.d.ts +88 -0
- package/dist/agent/agent-service.js +1 -0
- package/dist/agent/message-queue.d.ts +24 -0
- package/dist/agent/message-queue.js +1 -0
- package/dist/agent/prompt-builder.d.ts +58 -0
- package/dist/agent/prompt-builder.js +1 -0
- package/dist/agent/session-agent.d.ts +197 -0
- package/dist/agent/session-agent.js +1 -0
- package/dist/agent/session-db.d.ts +26 -0
- package/dist/agent/session-db.js +1 -0
- package/dist/agent/session-error-handler.d.ts +37 -0
- package/dist/agent/session-error-handler.js +1 -0
- package/dist/agent/session-manager.d.ts +19 -0
- package/dist/agent/session-manager.js +1 -0
- package/dist/agent/workspace-files.d.ts +51 -0
- package/dist/agent/workspace-files.js +1 -0
- package/dist/auth/auth-middleware.d.ts +9 -0
- package/dist/auth/auth-middleware.js +1 -0
- package/dist/auth/node-signature-db.d.ts +30 -0
- package/dist/auth/node-signature-db.js +1 -0
- package/dist/auth/token-db.d.ts +38 -0
- package/dist/auth/token-db.js +1 -0
- package/dist/browser/browser-service.d.ts +9 -0
- package/dist/browser/browser-service.js +1 -0
- package/dist/channels/channel.d.ts +2 -0
- package/dist/channels/channel.js +1 -0
- package/dist/channels/responses.d.ts +21 -0
- package/dist/channels/responses.js +1 -0
- package/dist/commands/clear.d.ts +7 -0
- package/dist/commands/clear.js +1 -0
- package/dist/commands/cmd.d.ts +7 -0
- package/dist/commands/cmd.js +1 -0
- package/dist/commands/coder.d.ts +12 -0
- package/dist/commands/coder.js +1 -0
- package/dist/commands/command-registry.d.ts +12 -0
- package/dist/commands/command-registry.js +1 -0
- package/dist/commands/command.d.ts +22 -0
- package/dist/commands/command.js +1 -0
- package/dist/commands/compact.d.ts +7 -0
- package/dist/commands/compact.js +1 -0
- package/dist/commands/customsubagents.d.ts +15 -0
- package/dist/commands/customsubagents.js +1 -0
- package/dist/commands/help.d.ts +9 -0
- package/dist/commands/help.js +1 -0
- package/dist/commands/mcp.d.ts +9 -0
- package/dist/commands/mcp.js +1 -0
- package/dist/commands/model.d.ts +22 -0
- package/dist/commands/model.js +1 -0
- package/dist/commands/models.d.ts +11 -0
- package/dist/commands/models.js +1 -0
- package/dist/commands/new.d.ts +7 -0
- package/dist/commands/new.js +1 -0
- package/dist/commands/plugin.d.ts +7 -0
- package/dist/commands/plugin.js +1 -0
- package/dist/commands/sandbox.d.ts +12 -0
- package/dist/commands/sandbox.js +1 -0
- package/dist/commands/showtool.d.ts +12 -0
- package/dist/commands/showtool.js +1 -0
- package/dist/commands/status.d.ts +24 -0
- package/dist/commands/status.js +1 -0
- package/dist/commands/stop.d.ts +10 -0
- package/dist/commands/stop.js +1 -0
- package/dist/commands/subagents.d.ts +12 -0
- package/dist/commands/subagents.js +1 -0
- package/dist/commands/usage.d.ts +25 -0
- package/dist/commands/usage.js +1 -0
- package/dist/commands/useplugin.d.ts +7 -0
- package/dist/commands/useplugin.js +1 -0
- package/dist/config-watcher.d.ts +14 -0
- package/dist/config-watcher.js +1 -0
- package/dist/config.d.ts +267 -0
- package/dist/config.js +1 -0
- package/dist/cron/cron-service.d.ts +57 -0
- package/dist/cron/cron-service.js +1 -0
- package/dist/cron/heartbeat-token.d.ts +29 -0
- package/dist/cron/heartbeat-token.js +1 -0
- package/dist/cron/schedule.d.ts +3 -0
- package/dist/cron/schedule.js +1 -0
- package/dist/cron/store.d.ts +4 -0
- package/dist/cron/store.js +1 -0
- package/dist/cron/types.d.ts +47 -0
- package/dist/cron/types.js +1 -0
- package/dist/gateway/bridge.d.ts +38 -0
- package/dist/gateway/bridge.js +1 -0
- package/dist/gateway/channel-manager.d.ts +45 -0
- package/dist/gateway/channel-manager.js +1 -0
- package/dist/gateway/channels/qr-image.d.ts +5 -0
- package/dist/gateway/channels/qr-image.js +1 -0
- package/dist/gateway/channels/telegram.d.ts +39 -0
- package/dist/gateway/channels/telegram.js +1 -0
- package/dist/gateway/channels/webchat.d.ts +51 -0
- package/dist/gateway/channels/webchat.js +1 -0
- package/dist/gateway/channels/whatsapp.d.ts +40 -0
- package/dist/gateway/channels/whatsapp.js +1 -0
- package/dist/gateway/node-registry.d.ts +38 -0
- package/dist/gateway/node-registry.js +1 -0
- package/dist/heracli/index.d.ts +3 -0
- package/dist/heracli/index.js +2 -0
- package/dist/heracli/logs.d.ts +13 -0
- package/dist/heracli/logs.js +1 -0
- package/dist/heracli/security/audit.d.ts +17 -0
- package/dist/heracli/security/audit.js +1 -0
- package/dist/heracli/security/checks/channel-policies.d.ts +6 -0
- package/dist/heracli/security/checks/channel-policies.js +1 -0
- package/dist/heracli/security/checks/credentials.d.ts +6 -0
- package/dist/heracli/security/checks/credentials.js +1 -0
- package/dist/heracli/security/checks/fs-permissions.d.ts +6 -0
- package/dist/heracli/security/checks/fs-permissions.js +1 -0
- package/dist/heracli/security/checks/network.d.ts +4 -0
- package/dist/heracli/security/checks/network.js +1 -0
- package/dist/heracli/security/report.d.ts +4 -0
- package/dist/heracli/security/report.js +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/installer/hera.d.ts +3 -0
- package/dist/installer/hera.js +2 -0
- package/dist/media/message-processor.d.ts +23 -0
- package/dist/media/message-processor.js +1 -0
- package/dist/memory/memory-manager.d.ts +21 -0
- package/dist/memory/memory-manager.js +1 -0
- package/dist/memory/memory-provider.d.ts +22 -0
- package/dist/memory/memory-provider.js +1 -0
- package/dist/memory/memory-search.d.ts +102 -0
- package/dist/memory/memory-search.js +1 -0
- package/dist/memory/recall-strategies.d.ts +2 -0
- package/dist/memory/recall-strategies.js +1 -0
- package/dist/nostromo/auth.d.ts +29 -0
- package/dist/nostromo/auth.js +1 -0
- package/dist/nostromo/nostromo.d.ts +23 -0
- package/dist/nostromo/nostromo.js +1 -0
- package/dist/nostromo/ui-html-layout.d.ts +3 -0
- package/dist/nostromo/ui-html-layout.js +1 -0
- package/dist/nostromo/ui-html-modals.d.ts +3 -0
- package/dist/nostromo/ui-html-modals.js +1 -0
- package/dist/nostromo/ui-js-agent.d.ts +3 -0
- package/dist/nostromo/ui-js-agent.js +1 -0
- package/dist/nostromo/ui-js-channels.d.ts +3 -0
- package/dist/nostromo/ui-js-channels.js +1 -0
- package/dist/nostromo/ui-js-competences.d.ts +3 -0
- package/dist/nostromo/ui-js-competences.js +1 -0
- package/dist/nostromo/ui-js-config.d.ts +3 -0
- package/dist/nostromo/ui-js-config.js +1 -0
- package/dist/nostromo/ui-js-core.d.ts +3 -0
- package/dist/nostromo/ui-js-core.js +1 -0
- package/dist/nostromo/ui-js-ops.d.ts +3 -0
- package/dist/nostromo/ui-js-ops.js +1 -0
- package/dist/nostromo/ui-js-prompts.d.ts +3 -0
- package/dist/nostromo/ui-js-prompts.js +1 -0
- package/dist/nostromo/ui-styles.d.ts +3 -0
- package/dist/nostromo/ui-styles.js +1 -0
- package/dist/nostromo/ui.d.ts +2 -0
- package/dist/nostromo/ui.js +1 -0
- package/dist/server.d.ts +80 -0
- package/dist/server.js +1 -0
- package/dist/stt/local-whisper.d.ts +9 -0
- package/dist/stt/local-whisper.js +1 -0
- package/dist/stt/openai-whisper.d.ts +14 -0
- package/dist/stt/openai-whisper.js +1 -0
- package/dist/stt/stt-loader.d.ts +4 -0
- package/dist/stt/stt-loader.js +1 -0
- package/dist/stt/stt-provider.d.ts +4 -0
- package/dist/stt/stt-provider.js +1 -0
- package/dist/tools/browser-tools.d.ts +9 -0
- package/dist/tools/browser-tools.js +1 -0
- package/dist/tools/cron-tools.d.ts +4 -0
- package/dist/tools/cron-tools.js +1 -0
- package/dist/tools/memory-tools.d.ts +3 -0
- package/dist/tools/memory-tools.js +1 -0
- package/dist/tools/message-tools.d.ts +5 -0
- package/dist/tools/message-tools.js +1 -0
- package/dist/tools/node-tools.d.ts +3 -0
- package/dist/tools/node-tools.js +1 -0
- package/dist/tools/server-tools.d.ts +2 -0
- package/dist/tools/server-tools.js +1 -0
- package/dist/tools/tts-tools.d.ts +3 -0
- package/dist/tools/tts-tools.js +1 -0
- package/dist/tts/tts-service.d.ts +19 -0
- package/dist/tts/tts-service.js +1 -0
- package/dist/utils/chunk.d.ts +3 -0
- package/dist/utils/chunk.js +1 -0
- package/dist/utils/logger.d.ts +16 -0
- package/dist/utils/logger.js +1 -0
- package/dist/utils/markdown/fences.d.ts +11 -0
- package/dist/utils/markdown/fences.js +1 -0
- package/dist/utils/markdown/ir.d.ts +33 -0
- package/dist/utils/markdown/ir.js +1 -0
- package/dist/utils/markdown/render.d.ts +19 -0
- package/dist/utils/markdown/render.js +1 -0
- package/dist/utils/markdown/tables.d.ts +3 -0
- package/dist/utils/markdown/tables.js +1 -0
- package/dist/utils/media-response.d.ts +29 -0
- package/dist/utils/media-response.js +1 -0
- package/dist/utils/package-paths.d.ts +5 -0
- package/dist/utils/package-paths.js +1 -0
- package/dist/utils/telegram-format.d.ts +13 -0
- package/dist/utils/telegram-format.js +1 -0
- package/installationPkg/.env.example +26 -0
- package/installationPkg/AGENTS.md +143 -0
- package/installationPkg/BOOTSTRAP.md +45 -0
- package/installationPkg/CBINT.json +16 -0
- package/installationPkg/HEARTBEAT.md +5 -0
- package/installationPkg/IDENTITY.md +7 -0
- package/installationPkg/SOUL.md +36 -0
- package/installationPkg/SYSTEM_PROMPT.md +55 -0
- package/installationPkg/SYSTEM_PROMPT_SUBAGENT.md +40 -0
- package/installationPkg/TOOLS.md +36 -0
- package/installationPkg/USER.md +11 -0
- package/installationPkg/config.example.yaml +291 -0
- package/package.json +95 -0
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** Returns the resolved GMAB_PATH (home by default). */
|
|
3
|
+
export declare function getGmabPath(): string;
|
|
4
|
+
/** Returns the data directory ({gmabPath}/data/). */
|
|
5
|
+
export declare function getDataDir(): string;
|
|
6
|
+
/** Returns the path for .nostromo-key ({gmabPath}/data/.nostromo-key). */
|
|
7
|
+
export declare function getNostromoKeyPath(): string;
|
|
8
|
+
/**
|
|
9
|
+
* Rotate config backups before saving.
|
|
10
|
+
* Keeps up to 5 backups: .backup1 (oldest) → .backup5 (newest).
|
|
11
|
+
*/
|
|
12
|
+
export declare function backupConfig(configPath: string): void;
|
|
13
|
+
declare const AppConfigSchema: z.ZodObject<{
|
|
14
|
+
gmabPath: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
15
|
+
host: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
16
|
+
logLevel: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
|
|
17
|
+
debug: "debug";
|
|
18
|
+
info: "info";
|
|
19
|
+
warn: "warn";
|
|
20
|
+
error: "error";
|
|
21
|
+
}>>>;
|
|
22
|
+
verboseDebugLogs: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
23
|
+
timezone: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
24
|
+
fastProxyUrl: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
25
|
+
channels: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
26
|
+
telegram: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
27
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
28
|
+
accounts: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
29
|
+
botToken: z.ZodString;
|
|
30
|
+
dmPolicy: z.ZodDefault<z.ZodEnum<{
|
|
31
|
+
open: "open";
|
|
32
|
+
token: "token";
|
|
33
|
+
allowlist: "allowlist";
|
|
34
|
+
}>>;
|
|
35
|
+
allowFrom: z.ZodDefault<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>;
|
|
36
|
+
}, z.core.$strip>>>;
|
|
37
|
+
}, z.core.$strip>>>;
|
|
38
|
+
whatsapp: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
39
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
40
|
+
accounts: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
41
|
+
}, z.core.$strip>>>;
|
|
42
|
+
discord: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
43
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
44
|
+
accounts: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
45
|
+
}, z.core.$strip>>>;
|
|
46
|
+
slack: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
47
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
48
|
+
accounts: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
49
|
+
}, z.core.$strip>>>;
|
|
50
|
+
signal: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
51
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
52
|
+
accounts: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
53
|
+
}, z.core.$strip>>>;
|
|
54
|
+
msteams: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
55
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
56
|
+
accounts: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
57
|
+
}, z.core.$strip>>>;
|
|
58
|
+
googlechat: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
59
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
60
|
+
accounts: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
61
|
+
}, z.core.$strip>>>;
|
|
62
|
+
line: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
63
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
64
|
+
accounts: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
65
|
+
}, z.core.$strip>>>;
|
|
66
|
+
matrix: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
67
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
68
|
+
accounts: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
69
|
+
}, z.core.$strip>>>;
|
|
70
|
+
responses: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
71
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
72
|
+
port: z.ZodDefault<z.ZodNumber>;
|
|
73
|
+
}, z.core.$strip>>>;
|
|
74
|
+
}, z.core.$strip>>>;
|
|
75
|
+
models: z.ZodDefault<z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
76
|
+
id: z.ZodString;
|
|
77
|
+
name: z.ZodString;
|
|
78
|
+
types: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
79
|
+
internal: "internal";
|
|
80
|
+
external: "external";
|
|
81
|
+
"env-var": "env-var";
|
|
82
|
+
}>>>>;
|
|
83
|
+
proxy: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
|
|
84
|
+
"not-used": "not-used";
|
|
85
|
+
direct: "direct";
|
|
86
|
+
proxied: "proxied";
|
|
87
|
+
}>>>;
|
|
88
|
+
fastUrl: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
89
|
+
fastProxyApiKey: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
90
|
+
apiKey: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
91
|
+
baseURL: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
92
|
+
useEnvVar: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
93
|
+
}, z.core.$strip>>>>>;
|
|
94
|
+
stt: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
95
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
96
|
+
provider: z.ZodDefault<z.ZodString>;
|
|
97
|
+
"openai-whisper": z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
98
|
+
modelRef: z.ZodDefault<z.ZodString>;
|
|
99
|
+
model: z.ZodDefault<z.ZodString>;
|
|
100
|
+
language: z.ZodDefault<z.ZodString>;
|
|
101
|
+
}, z.core.$strip>>>;
|
|
102
|
+
"local-whisper": z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
103
|
+
binaryPath: z.ZodDefault<z.ZodString>;
|
|
104
|
+
model: z.ZodDefault<z.ZodString>;
|
|
105
|
+
}, z.core.$strip>>>;
|
|
106
|
+
}, z.core.$strip>>>;
|
|
107
|
+
tts: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
108
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
109
|
+
provider: z.ZodDefault<z.ZodEnum<{
|
|
110
|
+
edge: "edge";
|
|
111
|
+
openai: "openai";
|
|
112
|
+
elevenlabs: "elevenlabs";
|
|
113
|
+
}>>;
|
|
114
|
+
maxTextLength: z.ZodDefault<z.ZodNumber>;
|
|
115
|
+
timeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
116
|
+
edge: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
117
|
+
voice: z.ZodDefault<z.ZodString>;
|
|
118
|
+
lang: z.ZodDefault<z.ZodString>;
|
|
119
|
+
outputFormat: z.ZodDefault<z.ZodString>;
|
|
120
|
+
}, z.core.$strip>>>;
|
|
121
|
+
openai: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
122
|
+
modelRef: z.ZodDefault<z.ZodString>;
|
|
123
|
+
model: z.ZodDefault<z.ZodString>;
|
|
124
|
+
voice: z.ZodDefault<z.ZodString>;
|
|
125
|
+
}, z.core.$strip>>>;
|
|
126
|
+
elevenlabs: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
127
|
+
modelRef: z.ZodDefault<z.ZodString>;
|
|
128
|
+
voiceId: z.ZodDefault<z.ZodString>;
|
|
129
|
+
modelId: z.ZodDefault<z.ZodString>;
|
|
130
|
+
}, z.core.$strip>>>;
|
|
131
|
+
}, z.core.$strip>>>;
|
|
132
|
+
memory: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
133
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
134
|
+
recallStrategy: z.ZodDefault<z.ZodEnum<{
|
|
135
|
+
"builtin-only": "builtin-only";
|
|
136
|
+
search: "search";
|
|
137
|
+
}>>;
|
|
138
|
+
search: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
139
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
140
|
+
embeddingModel: z.ZodDefault<z.ZodString>;
|
|
141
|
+
embeddingDimensions: z.ZodDefault<z.ZodNumber>;
|
|
142
|
+
modelRef: z.ZodDefault<z.ZodString>;
|
|
143
|
+
prefixQuery: z.ZodDefault<z.ZodString>;
|
|
144
|
+
prefixDocument: z.ZodDefault<z.ZodString>;
|
|
145
|
+
updateDebounceMs: z.ZodDefault<z.ZodNumber>;
|
|
146
|
+
embedIntervalMs: z.ZodDefault<z.ZodNumber>;
|
|
147
|
+
maxResults: z.ZodDefault<z.ZodNumber>;
|
|
148
|
+
maxSnippetChars: z.ZodDefault<z.ZodNumber>;
|
|
149
|
+
maxInjectedChars: z.ZodDefault<z.ZodNumber>;
|
|
150
|
+
rrfK: z.ZodDefault<z.ZodNumber>;
|
|
151
|
+
}, z.core.$strip>>>;
|
|
152
|
+
}, z.core.$strip>>>;
|
|
153
|
+
agent: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
154
|
+
model: z.ZodDefault<z.ZodString>;
|
|
155
|
+
mainFallback: z.ZodDefault<z.ZodString>;
|
|
156
|
+
maxTurns: z.ZodDefault<z.ZodNumber>;
|
|
157
|
+
permissionMode: z.ZodDefault<z.ZodString>;
|
|
158
|
+
sessionTTL: z.ZodDefault<z.ZodNumber>;
|
|
159
|
+
queueMode: z.ZodDefault<z.ZodEnum<{
|
|
160
|
+
queue: "queue";
|
|
161
|
+
collect: "collect";
|
|
162
|
+
steer: "steer";
|
|
163
|
+
}>>;
|
|
164
|
+
queueDebounceMs: z.ZodDefault<z.ZodNumber>;
|
|
165
|
+
queueCap: z.ZodDefault<z.ZodNumber>;
|
|
166
|
+
queueDropPolicy: z.ZodDefault<z.ZodEnum<{
|
|
167
|
+
old: "old";
|
|
168
|
+
new: "new";
|
|
169
|
+
summarize: "summarize";
|
|
170
|
+
}>>;
|
|
171
|
+
allowedTools: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
172
|
+
disallowedTools: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
173
|
+
mcpServers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
174
|
+
command: z.ZodString;
|
|
175
|
+
args: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
176
|
+
env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
177
|
+
}, z.core.$loose>>>;
|
|
178
|
+
workspacePath: z.ZodDefault<z.ZodString>;
|
|
179
|
+
builtinCoderSkill: z.ZodDefault<z.ZodBoolean>;
|
|
180
|
+
settingSources: z.ZodDefault<z.ZodEnum<{
|
|
181
|
+
nothing: "nothing";
|
|
182
|
+
user: "user";
|
|
183
|
+
project: "project";
|
|
184
|
+
both: "both";
|
|
185
|
+
}>>;
|
|
186
|
+
customSubAgents: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
187
|
+
name: z.ZodString;
|
|
188
|
+
description: z.ZodString;
|
|
189
|
+
prompt: z.ZodString;
|
|
190
|
+
model: z.ZodDefault<z.ZodEnum<{
|
|
191
|
+
sonnet: "sonnet";
|
|
192
|
+
opus: "opus";
|
|
193
|
+
haiku: "haiku";
|
|
194
|
+
inherit: "inherit";
|
|
195
|
+
}>>;
|
|
196
|
+
tools: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
197
|
+
expandContext: z.ZodDefault<z.ZodBoolean>;
|
|
198
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
199
|
+
}, z.core.$strip>>>;
|
|
200
|
+
plugins: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
201
|
+
name: z.ZodString;
|
|
202
|
+
path: z.ZodString;
|
|
203
|
+
description: z.ZodDefault<z.ZodString>;
|
|
204
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
205
|
+
}, z.core.$strip>>>;
|
|
206
|
+
inflightTyping: z.ZodDefault<z.ZodBoolean>;
|
|
207
|
+
autoApproveTools: z.ZodDefault<z.ZodBoolean>;
|
|
208
|
+
autoRenew: z.ZodDefault<z.ZodNumber>;
|
|
209
|
+
}, z.core.$strip>>>;
|
|
210
|
+
cron: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
211
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
212
|
+
isolated: z.ZodDefault<z.ZodBoolean>;
|
|
213
|
+
broadcastEvents: z.ZodDefault<z.ZodBoolean>;
|
|
214
|
+
storePath: z.ZodDefault<z.ZodString>;
|
|
215
|
+
heartbeat: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
216
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
217
|
+
every: z.ZodDefault<z.ZodNumber>;
|
|
218
|
+
channel: z.ZodDefault<z.ZodString>;
|
|
219
|
+
chatId: z.ZodDefault<z.ZodString>;
|
|
220
|
+
message: z.ZodDefault<z.ZodString>;
|
|
221
|
+
ackMaxChars: z.ZodDefault<z.ZodNumber>;
|
|
222
|
+
}, z.core.$strip>>>;
|
|
223
|
+
}, z.core.$strip>>>;
|
|
224
|
+
nostromo: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
225
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
226
|
+
port: z.ZodDefault<z.ZodNumber>;
|
|
227
|
+
basePath: z.ZodDefault<z.ZodString>;
|
|
228
|
+
configCheckInterval: z.ZodDefault<z.ZodNumber>;
|
|
229
|
+
autoRestart: z.ZodDefault<z.ZodBoolean>;
|
|
230
|
+
}, z.core.$strip>>>;
|
|
231
|
+
browser: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
232
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
233
|
+
controlPort: z.ZodDefault<z.ZodNumber>;
|
|
234
|
+
headless: z.ZodDefault<z.ZodBoolean>;
|
|
235
|
+
noSandbox: z.ZodDefault<z.ZodBoolean>;
|
|
236
|
+
attachOnly: z.ZodDefault<z.ZodBoolean>;
|
|
237
|
+
executablePath: z.ZodOptional<z.ZodString>;
|
|
238
|
+
remoteCdpTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
239
|
+
profiles: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
240
|
+
cdpPort: z.ZodOptional<z.ZodNumber>;
|
|
241
|
+
cdpUrl: z.ZodOptional<z.ZodString>;
|
|
242
|
+
color: z.ZodDefault<z.ZodString>;
|
|
243
|
+
}, z.core.$strip>>>;
|
|
244
|
+
}, z.core.$strip>>>;
|
|
245
|
+
}, z.core.$strip>;
|
|
246
|
+
type RawAppConfig = z.infer<typeof AppConfigSchema>;
|
|
247
|
+
export type AppConfig = RawAppConfig & {
|
|
248
|
+
gmabPath: string;
|
|
249
|
+
dataDir: string;
|
|
250
|
+
dbPath: string;
|
|
251
|
+
memoryDir: string;
|
|
252
|
+
cronStorePath: string;
|
|
253
|
+
};
|
|
254
|
+
export declare function loadConfig(configPath?: string): AppConfig;
|
|
255
|
+
/**
|
|
256
|
+
* Load and parse config.yaml without env var interpolation or Zod validation.
|
|
257
|
+
* Returns the raw parsed YAML so ${VAR_NAME} strings stay intact.
|
|
258
|
+
*/
|
|
259
|
+
export declare function loadRawConfig(configPath?: string): any;
|
|
260
|
+
/**
|
|
261
|
+
* Resolve a model name to its actual model ID using the models registry.
|
|
262
|
+
* If the name matches a registry entry, returns the entry's id.
|
|
263
|
+
* Otherwise returns the raw value as-is (backwards compat / direct id usage).
|
|
264
|
+
*/
|
|
265
|
+
export declare function resolveModelId(config: AppConfig, name: string): string;
|
|
266
|
+
export {};
|
|
267
|
+
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readFileSync as e,writeFileSync as t,existsSync as a,mkdirSync as o,renameSync as n,unlinkSync as l}from"node:fs";import{resolve as r,join as s}from"node:path";import{homedir as i}from"node:os";import{config as u}from"dotenv";import{parse as d,stringify as c}from"yaml";import{z as f}from"zod";import{BrowserConfigSchema as p}from"@hera-al/browser-server/config";import{createLogger as m}from"./utils/logger.js";const b=m("Config");function g(e){return"~"===e||e.startsWith("~/")?e.replace("~",i()):e}let h=g(process.env.GMAB_PATH??"~/gmab"),y=s(h,"data");export function getGmabPath(){return h}export function getDataDir(){return y}export function getNostromoKeyPath(){return s(y,".nostromo-key")}export function backupConfig(o){if(a(o))try{const r=e=>`${o}.backup${e}`;a(r(1))&&l(r(1));for(let e=2;e<=5;e++)a(r(e))&&n(r(e),r(e-1));t(r(5),e(o)),b.debug(`Config backup created: ${r(5)}`)}catch(e){b.warn(`Failed to create config backup: ${e}`)}}const v=f.record(f.string(),f.any()),x=f.object({botToken:f.string(),dmPolicy:f.enum(["open","token","allowlist"]).default("allowlist"),allowFrom:f.array(f.union([f.string(),f.number()])).default([])}),w=f.object({enabled:f.boolean().default(!1),accounts:f.record(f.string(),x).default({})}),P=f.object({enabled:f.boolean().default(!1),accounts:v.default({})}),k=f.object({enabled:f.boolean().default(!0),port:f.number().default(3004)}),j=f.object({telegram:w.optional().default({enabled:!1,accounts:{}}),whatsapp:P.optional().default({enabled:!1,accounts:{}}),discord:P.optional().default({enabled:!1,accounts:{}}),slack:P.optional().default({enabled:!1,accounts:{}}),signal:P.optional().default({enabled:!1,accounts:{}}),msteams:P.optional().default({enabled:!1,accounts:{}}),googlechat:P.optional().default({enabled:!1,accounts:{}}),line:P.optional().default({enabled:!1,accounts:{}}),matrix:P.optional().default({enabled:!1,accounts:{}}),responses:k.optional().default({enabled:!0,port:3e3})}),M=f.object({modelRef:f.string().default(""),model:f.string().default("whisper-1"),language:f.string().default("")}),S=f.object({binaryPath:f.string().default("whisper"),model:f.string().default("base")}),R=f.object({enabled:f.boolean().default(!1),provider:f.string().default("openai-whisper"),"openai-whisper":M.optional().default({modelRef:"",model:"whisper-1",language:""}),"local-whisper":S.optional().default({binaryPath:"whisper",model:"base"})}),T=f.object({voice:f.string().default("en-US-MichelleNeural"),lang:f.string().default("en-US"),outputFormat:f.string().default("audio-24khz-48kbitrate-mono-mp3")}),C=f.object({modelRef:f.string().default(""),model:f.string().default("gpt-4o-mini-tts"),voice:f.string().default("alloy")}),A=f.object({modelRef:f.string().default(""),voiceId:f.string().default("pMsXgVXv3BLzUgSXRplE"),modelId:f.string().default("eleven_multilingual_v2")}),U=f.object({enabled:f.boolean().default(!1),provider:f.enum(["edge","openai","elevenlabs"]).default("openai"),maxTextLength:f.number().default(4096),timeoutMs:f.number().default(3e4),edge:T.optional().default({voice:"en-US-MichelleNeural",lang:"en-US",outputFormat:"audio-24khz-48kbitrate-mono-mp3"}),openai:C.optional().default({modelRef:"",model:"gpt-4o-mini-tts",voice:"alloy"}),elevenlabs:A.optional().default({modelRef:"",voiceId:"pMsXgVXv3BLzUgSXRplE",modelId:"eleven_multilingual_v2"})}),z=f.object({enabled:f.boolean().default(!1),embeddingModel:f.string().default("text-embedding-3-small"),embeddingDimensions:f.number().default(1536),modelRef:f.string().default(""),prefixQuery:f.string().default(""),prefixDocument:f.string().default(""),updateDebounceMs:f.number().default(3e3),embedIntervalMs:f.number().default(3e5),maxResults:f.number().default(6),maxSnippetChars:f.number().default(700),maxInjectedChars:f.number().default(4e3),rrfK:f.number().default(60)}),D={enabled:!1,embeddingModel:"text-embedding-3-small",embeddingDimensions:1536,modelRef:"",prefixQuery:"",prefixDocument:"",updateDebounceMs:3e3,embedIntervalMs:3e5,maxResults:6,maxSnippetChars:700,maxInjectedChars:4e3,rrfK:60},I=f.object({enabled:f.boolean().default(!0),recallStrategy:f.enum(["builtin-only","search"]).default("builtin-only"),search:z.optional().default(D)}),L=f.object({command:f.string(),args:f.array(f.string()).optional(),env:f.record(f.string(),f.string()).optional()}).passthrough(),E=f.object({id:f.string(),name:f.string(),types:f.array(f.enum(["internal","external","env-var"])).optional().default(["external"]),proxy:f.enum(["not-used","direct","proxied"]).optional().default("not-used"),fastUrl:f.string().optional().default(""),fastProxyApiKey:f.string().optional().default(""),apiKey:f.string().optional().default(""),baseURL:f.string().optional().default(""),useEnvVar:f.string().optional().default("")}),F=[{id:"claude-opus-4-6",name:"Claude Opus",types:["internal"],proxy:"not-used",fastUrl:"",fastProxyApiKey:"",apiKey:"",baseURL:"",useEnvVar:""},{id:"claude-sonnet-4-6",name:"Claude Sonnet",types:["internal"],proxy:"not-used",fastUrl:"",fastProxyApiKey:"",apiKey:"",baseURL:"",useEnvVar:""},{id:"claude-haiku-3-5-20241022",name:"Claude Haiku",types:["internal"],proxy:"not-used",fastUrl:"",fastProxyApiKey:"",apiKey:"",baseURL:"",useEnvVar:""}],K=f.array(E).default(F),_=f.object({name:f.string(),path:f.string(),description:f.string().default(""),enabled:f.boolean().default(!1)}),q=f.object({name:f.string(),description:f.string(),prompt:f.string(),model:f.enum(["sonnet","opus","haiku","inherit"]).default("inherit"),tools:f.array(f.string()).default(["Read","Write","Edit","Glob","Grep","WebSearch","WebFetch"]),expandContext:f.boolean().default(!1),enabled:f.boolean().default(!1)}),G=f.object({model:f.string().default("claude-opus-4-6"),mainFallback:f.string().default(""),maxTurns:f.number().default(50),permissionMode:f.string().default("bypassPermissions"),sessionTTL:f.number().default(3600),queueMode:f.enum(["queue","collect","steer"]).default("steer"),queueDebounceMs:f.number().default(1500),queueCap:f.number().default(20),queueDropPolicy:f.enum(["old","new","summarize"]).default("summarize"),allowedTools:f.array(f.string()).default([]),disallowedTools:f.array(f.string()).default([]),mcpServers:f.record(f.string(),L).default({}),workspacePath:f.string().default("./workspace"),builtinCoderSkill:f.boolean().default(!1),settingSources:f.enum(["nothing","user","project","both"]).default("project"),customSubAgents:f.array(q).default([]),plugins:f.array(_).default([]),inflightTyping:f.boolean().default(!0),autoApproveTools:f.boolean().default(!0),autoRenew:f.number().default(0)}),X={enabled:!1,provider:"openai",maxTextLength:4096,timeoutMs:3e4,edge:{voice:"en-US-MichelleNeural",lang:"en-US",outputFormat:"audio-24khz-48kbitrate-mono-mp3"},openai:{modelRef:"",model:"gpt-4o-mini-tts",voice:"alloy"},elevenlabs:{modelRef:"",voiceId:"pMsXgVXv3BLzUgSXRplE",modelId:"eleven_multilingual_v2"}},$={enabled:!0,recallStrategy:"builtin-only",dir:"",search:D},B={model:"claude-opus-4-6",mainFallback:"",maxTurns:50,permissionMode:"bypassPermissions",sessionTTL:3600,queueMode:"steer",queueDebounceMs:1500,queueCap:20,queueDropPolicy:"summarize",allowedTools:[],disallowedTools:[],mcpServers:{},workspacePath:"./workspace",builtinCoderSkill:!1,settingSources:"project",customSubAgents:[],plugins:[],inflightTyping:!0,autoApproveTools:!0,autoRenew:0},W=f.object({enabled:f.boolean().default(!1),every:f.number().default(18e5),channel:f.string().default(""),chatId:f.string().default(""),message:f.string().default(""),ackMaxChars:f.number().default(300)}),V={enabled:!1,every:18e5,channel:"",chatId:"",message:"",ackMaxChars:300},H=f.object({enabled:f.boolean().default(!0),isolated:f.boolean().default(!0),broadcastEvents:f.boolean().default(!1),storePath:f.string().default(""),heartbeat:W.optional().default(V)}),N={enabled:!0,isolated:!0,broadcastEvents:!1,storePath:"",heartbeat:V},O=f.object({enabled:f.boolean().default(!0),port:f.number().default(3001),basePath:f.string().default("/nostromo"),configCheckInterval:f.number().default(5),autoRestart:f.boolean().default(!0)}),Q={enabled:!0,port:3001,basePath:"/nostromo",configCheckInterval:5,autoRestart:!0},Z=f.object({gmabPath:f.string().optional().default("~/gmab"),host:f.string().optional().default("127.0.0.1"),logLevel:f.enum(["debug","info","warn","error"]).optional().default("info"),verboseDebugLogs:f.boolean().optional().default(!0),timezone:f.string().optional().default(""),fastProxyUrl:f.string().optional().default("http://localhost:4181"),channels:j.optional().default({telegram:{enabled:!1,accounts:{}},whatsapp:{enabled:!1,accounts:{}},discord:{enabled:!1,accounts:{}},slack:{enabled:!1,accounts:{}},signal:{enabled:!1,accounts:{}},msteams:{enabled:!1,accounts:{}},googlechat:{enabled:!1,accounts:{}},line:{enabled:!1,accounts:{}},matrix:{enabled:!1,accounts:{}},responses:{enabled:!0,port:3004}}),models:K.optional().default(F),stt:R.optional().default({enabled:!1,provider:"openai-whisper","openai-whisper":{modelRef:"",model:"whisper-1",language:""},"local-whisper":{binaryPath:"whisper",model:"base"}}),tts:U.optional().default(X),memory:I.optional().default($),agent:G.optional().default(B),cron:H.optional().default(N),nostromo:O.optional().default(Q),browser:p.optional().default({enabled:!1,controlPort:3002,headless:!1,noSandbox:!1,attachOnly:!1,remoteCdpTimeoutMs:1500,profiles:{default:{cdpPort:9222,color:"#FF4500"}}})});function J(e){const t=function(e){const t=new Set,a=/\$\{([^}]+)\}/g;let o;for(;null!==(o=a.exec(e));)t.add(o[1]);return Array.from(t)}(e),a=t.filter(e=>!process.env[e]);a.length>0&&b.warn(`Missing environment variables referenced in config: ${a.join(", ")}. Add them to .env or export them before starting.`)}function Y(e){if("string"==typeof e)return e.replace(/\$\{([^}]+)\}/g,(e,t)=>process.env[t]??"");if(Array.isArray(e))return e.map(Y);if(null!==e&&"object"==typeof e){const t={};for(const[a,o]of Object.entries(e))t[a]=Y(o);return t}return e}const ee={channels:{responses:{enabled:!0,port:3004}},stt:{enabled:!1},tts:X,memory:$,agent:{...B,permissionMode:"bypassPermissions",allowedTools:["Read","Grep","Bash","WebSearch","Glob","Write","Edit","WebFetch","Task","Skill"]},cron:N,nostromo:Q};export function loadConfig(n){const l=n??r(process.cwd(),"config.yaml"),i=r(process.cwd(),".env");if(a(i)&&(u({path:i}),b.info(`Loaded .env from ${i}`)),!a(l)){const e="# GrabMeABeer Configuration\n# Configure channels and settings via Nostromo: http://localhost:3001\n\n"+c({gmabPath:"~/gmab",...ee});t(l,e,"utf-8")}const f=e(l,"utf-8");J(f);const p=Y(d(f)),m=Z.parse(p);if(h=process.env.GMAB_PATH?g(process.env.GMAB_PATH):g(m.gmabPath),y=s(h,"data"),o(y,{recursive:!0}),!m.timezone){m.timezone=Intl.DateTimeFormat().resolvedOptions().timeZone;try{const a=d(e(l,"utf-8"))??{};a.timezone=m.timezone,t(l,c(a),"utf-8"),b.info(`Timezone auto-detected and saved: ${m.timezone}`)}catch(e){}}return function(e){o(y,{recursive:!0});const t=process.env.WORKSPACE_PATH??e.agent.workspacePath;e.agent.workspacePath=r(g(t)),o(e.agent.workspacePath,{recursive:!0});const a=s(y,"cron");o(a,{recursive:!0});const n=e.cron.storePath.trim()?r(g(e.cron.storePath)):s(a,"jobs.json");return{...e,gmabPath:h,dataDir:y,dbPath:s(y,"core.db"),memoryDir:s(y,"memory"),cronStorePath:n}}(m)}export function loadRawConfig(t){const o=t??r(process.cwd(),"config.yaml");if(!a(o))return{};const n=e(o,"utf-8");return d(n)??{}}export function resolveModelId(e,t){if(!t)return t;const a=e.models?.find(e=>e.name===t);return a?a.id:t}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { CronJob, CronJobCreate, CronJobPatch } from "./types.js";
|
|
2
|
+
export type CronExecuteResult = {
|
|
3
|
+
response: string;
|
|
4
|
+
delivered: boolean;
|
|
5
|
+
};
|
|
6
|
+
export type CronServiceOpts = {
|
|
7
|
+
storePath: string;
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
defaultTimezone?: string;
|
|
10
|
+
onExecute: (job: CronJob) => Promise<CronExecuteResult>;
|
|
11
|
+
};
|
|
12
|
+
export type CronStatusSummary = {
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
storePath: string;
|
|
15
|
+
jobs: number;
|
|
16
|
+
nextWakeAtMs: number | null;
|
|
17
|
+
};
|
|
18
|
+
export declare class CronService {
|
|
19
|
+
private store;
|
|
20
|
+
private timer;
|
|
21
|
+
private running;
|
|
22
|
+
private op;
|
|
23
|
+
private readonly storePath;
|
|
24
|
+
private readonly enabled;
|
|
25
|
+
private readonly defaultTimezone;
|
|
26
|
+
private readonly onExecute;
|
|
27
|
+
constructor(opts: CronServiceOpts);
|
|
28
|
+
private locked;
|
|
29
|
+
private ensureLoaded;
|
|
30
|
+
private persist;
|
|
31
|
+
private computeJobNextRunAtMs;
|
|
32
|
+
private recomputeNextRuns;
|
|
33
|
+
private nextWakeAtMs;
|
|
34
|
+
private armTimer;
|
|
35
|
+
private stopTimer;
|
|
36
|
+
private onTimer;
|
|
37
|
+
private runDueJobs;
|
|
38
|
+
private executeJob;
|
|
39
|
+
start(): Promise<void>;
|
|
40
|
+
stop(): void;
|
|
41
|
+
status(): Promise<CronStatusSummary>;
|
|
42
|
+
list(opts?: {
|
|
43
|
+
includeDisabled?: boolean;
|
|
44
|
+
}): Promise<CronJob[]>;
|
|
45
|
+
add(input: CronJobCreate): Promise<CronJob>;
|
|
46
|
+
update(id: string, patch: CronJobPatch): Promise<CronJob>;
|
|
47
|
+
remove(id: string): Promise<{
|
|
48
|
+
ok: boolean;
|
|
49
|
+
removed: boolean;
|
|
50
|
+
}>;
|
|
51
|
+
run(id: string, mode?: "due" | "force"): Promise<{
|
|
52
|
+
ok: boolean;
|
|
53
|
+
ran: boolean;
|
|
54
|
+
reason?: string;
|
|
55
|
+
}>;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=cron-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import t from"node:crypto";import{computeNextRunAtMs as e}from"./schedule.js";import{loadCronStore as s,saveCronStore as n}from"./store.js";import{createLogger as a}from"../utils/logger.js";const i=a("Cron"),o=t=>t.then(()=>{},()=>{});export class CronService{store=null;timer=null;running=!1;op=Promise.resolve();storePath;enabled;defaultTimezone;onExecute;constructor(t){this.storePath=t.storePath,this.enabled=t.enabled,this.defaultTimezone=t.defaultTimezone||"",this.onExecute=t.onExecute}async locked(t){const e=o(this.op).then(t),s=o(e);return this.op=s,await e}async ensureLoaded(t){this.store&&!t?.forceReload||(this.store=await s(this.storePath))}async persist(){this.store&&await n(this.storePath,this.store)}computeJobNextRunAtMs(t,s){if(t.enabled){if("at"===t.schedule.kind){if("ok"===t.state.lastStatus&&t.state.lastRunAtMs)return;const e=new Date(t.schedule.at).getTime();return Number.isFinite(e)?e:void 0}return e(t.schedule,s,this.defaultTimezone)}}recomputeNextRuns(){if(!this.store)return;const t=Date.now();for(const e of this.store.jobs){if(e.state||(e.state={}),!e.enabled){e.state.nextRunAtMs=void 0,e.state.runningAtMs=void 0;continue}const s=e.state.runningAtMs;"number"==typeof s&&t-s>72e5&&(i.warn(`Clearing stuck running marker for job ${e.id}`),e.state.runningAtMs=void 0),e.state.nextRunAtMs=this.computeJobNextRunAtMs(e,t)}}nextWakeAtMs(){const t=(this.store?.jobs??[]).filter(t=>t.enabled&&"number"==typeof t.state.nextRunAtMs);if(0!==t.length)return t.reduce((t,e)=>Math.min(t,e.state.nextRunAtMs),t[0].state.nextRunAtMs)}armTimer(){if(this.timer&&clearTimeout(this.timer),this.timer=null,!this.enabled)return;const t=this.nextWakeAtMs();if(!t)return;const e=Math.max(t-Date.now(),0),s=Math.min(e,2147483647);this.timer=setTimeout(()=>{this.onTimer().catch(t=>{i.error(`Timer tick failed: ${t}`)})},s)}stopTimer(){this.timer&&clearTimeout(this.timer),this.timer=null}async onTimer(){if(!this.running){this.running=!0;try{await this.locked(async()=>{await this.ensureLoaded({forceReload:!0}),await this.runDueJobs(),this.recomputeNextRuns(),await this.persist()})}finally{this.running=!1,this.armTimer()}}}async runDueJobs(){if(!this.store)return;const t=Date.now(),e=this.store.jobs.filter(e=>{if(!e.enabled)return!1;if("number"==typeof e.state.runningAtMs)return!1;const s=e.state.nextRunAtMs;return"number"==typeof s&&t>=s});for(const t of e)await this.executeJob(t,{forced:!1})}async executeJob(t,e){const s=Date.now();t.state.runningAtMs=s,t.state.lastError=void 0;let n=!1;const a=async(e,a)=>{const i=Date.now();t.state.runningAtMs=void 0,t.state.lastRunAtMs=s,t.state.lastStatus=e,t.state.lastDurationMs=Math.max(0,i-s),t.state.lastError=a;const o="at"===t.schedule.kind&&"ok"===e&&!0===t.deleteAfterRun;o||("at"===t.schedule.kind&&"ok"===e?(t.enabled=!1,t.state.nextRunAtMs=void 0):t.enabled?t.state.nextRunAtMs=this.computeJobNextRunAtMs(t,i):t.state.nextRunAtMs=void 0),o&&this.store&&(this.store.jobs=this.store.jobs.filter(e=>e.id!==t.id),n=!0)};try{i.info(`Executing job "${t.name}" (${t.id})`);const e=(await this.onExecute(t)).delivered?"ok (delivered)":"ok (suppressed)";i.info(`Job "${t.name}" finished: ${e}`),await a("ok")}catch(e){i.error(`Job "${t.name}" failed: ${e}`),await a("error",String(e))}finally{t.updatedAtMs=Date.now(),e.forced||!t.enabled||n||(t.state.nextRunAtMs=this.computeJobNextRunAtMs(t,Date.now()))}}async start(){await this.locked(async()=>{this.enabled?(await this.ensureLoaded(),this.recomputeNextRuns(),await this.persist(),this.armTimer(),i.info(`Cron started (${this.store?.jobs.length??0} jobs, next wake: ${this.nextWakeAtMs()??"none"})`)):i.info("Cron disabled")})}stop(){this.stopTimer()}async status(){return await this.locked(async()=>(await this.ensureLoaded(),{enabled:this.enabled,storePath:this.storePath,jobs:this.store?.jobs.length??0,nextWakeAtMs:this.enabled?this.nextWakeAtMs()??null:null}))}async list(t){return await this.locked(async()=>{await this.ensureLoaded();const e=!0===t?.includeDisabled;return[...(this.store?.jobs??[]).filter(t=>e||t.enabled)].sort((t,e)=>(t.state.nextRunAtMs??0)-(e.state.nextRunAtMs??0))})}async add(e){return await this.locked(async()=>{await this.ensureLoaded();const s=Date.now(),n=t.randomUUID(),a="boolean"==typeof e.deleteAfterRun?e.deleteAfterRun:"at"===e.schedule.kind||void 0,o="boolean"!=typeof e.enabled||e.enabled,r="boolean"!=typeof e.isolated||e.isolated,d={id:n,name:e.name.trim()||"Untitled",description:e.description?.trim()||void 0,enabled:o,isolated:r,deleteAfterRun:a,suppressToken:e.suppressToken,createdAtMs:s,updatedAtMs:s,schedule:e.schedule,channel:e.channel,chatId:e.chatId,message:e.message,state:{...e.state}};return d.state.nextRunAtMs=this.computeJobNextRunAtMs(d,s),this.store?.jobs.push(d),await this.persist(),this.armTimer(),i.info(`Job added: "${d.name}" (${d.id}), next run: ${d.state.nextRunAtMs??"none"}`),d})}async update(t,e){return await this.locked(async()=>{await this.ensureLoaded();const s=this.store?.jobs.find(e=>e.id===t);if(!s)throw new Error(`Unknown cron job id: ${t}`);const n=Date.now();return"name"in e&&"string"==typeof e.name&&(s.name=e.name.trim()||s.name),"description"in e&&(s.description=e.description?.trim()||void 0),"boolean"==typeof e.enabled&&(s.enabled=e.enabled),"boolean"==typeof e.isolated&&(s.isolated=e.isolated),"boolean"==typeof e.deleteAfterRun&&(s.deleteAfterRun=e.deleteAfterRun),"boolean"==typeof e.suppressToken&&(s.suppressToken=e.suppressToken),e.schedule&&(s.schedule=e.schedule),"string"==typeof e.channel&&(s.channel=e.channel),"string"==typeof e.chatId&&(s.chatId=e.chatId),"string"==typeof e.message&&(s.message=e.message),e.state&&(s.state={...s.state,...e.state}),s.updatedAtMs=n,s.enabled?s.state.nextRunAtMs=this.computeJobNextRunAtMs(s,n):(s.state.nextRunAtMs=void 0,s.state.runningAtMs=void 0),await this.persist(),this.armTimer(),s})}async remove(t){return await this.locked(async()=>{if(await this.ensureLoaded(),!this.store)return{ok:!1,removed:!1};const e=this.store.jobs.length;this.store.jobs=this.store.jobs.filter(e=>e.id!==t);const s=this.store.jobs.length!==e;return await this.persist(),this.armTimer(),s&&i.info(`Job removed: ${t}`),{ok:!0,removed:s}})}async run(t,e){return await this.locked(async()=>{await this.ensureLoaded();const s=this.store?.jobs.find(e=>e.id===t);if(!s)throw new Error(`Unknown cron job id: ${t}`);const n=Date.now(),a="force"===e;return a||s.enabled&&"number"==typeof s.state.nextRunAtMs&&n>=s.state.nextRunAtMs?(await this.executeJob(s,{forced:a}),await this.persist(),this.armTimer(),{ok:!0,ran:!0}):{ok:!0,ran:!1,reason:"not-due"}})}}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare const HEARTBEAT_TOKEN = "HEARTBEAT_OK";
|
|
2
|
+
/**
|
|
3
|
+
* Check if HEARTBEAT.md content is "effectively empty" — no actionable tasks.
|
|
4
|
+
* Skipping heartbeat when the file is empty saves API calls.
|
|
5
|
+
*
|
|
6
|
+
* A file is considered effectively empty if it contains only:
|
|
7
|
+
* - Whitespace / empty lines
|
|
8
|
+
* - Markdown headers (lines starting with # followed by space or EOL)
|
|
9
|
+
* - HTML comments (<!-- ... -->)
|
|
10
|
+
* - Empty markdown list items (- [ ])
|
|
11
|
+
*
|
|
12
|
+
* If the file doesn't exist (null/undefined), returns false so the
|
|
13
|
+
* heartbeat still runs and the agent can decide what to do.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isHeartbeatContentEffectivelyEmpty(content: string | undefined | null): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Strip the HEARTBEAT_OK token from a response and determine if the
|
|
18
|
+
* response should be suppressed (not sent to the channel).
|
|
19
|
+
*
|
|
20
|
+
* - Empty/missing response -> suppressed
|
|
21
|
+
* - No token present -> not suppressed, text returned as-is
|
|
22
|
+
* - Token stripped, remaining text <= ackMaxChars -> suppressed
|
|
23
|
+
* - Token stripped, remaining text > ackMaxChars -> not suppressed, stripped text returned
|
|
24
|
+
*/
|
|
25
|
+
export declare function stripHeartbeatToken(raw: string | undefined, ackMaxChars?: number): {
|
|
26
|
+
shouldSkip: boolean;
|
|
27
|
+
text: string;
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=heartbeat-token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const HEARTBEAT_TOKEN="HEARTBEAT_OK";export function isHeartbeatContentEffectivelyEmpty(t){if(null==t)return!1;if("string"!=typeof t)return!1;const e=t.replace(/<!--[\s\S]*?-->/g,"").split("\n");for(const t of e){const e=t.trim();if(e&&(!/^#+(\s|$)/.test(e)&&!/^[-*+]\s*(\[[\sXx]?\]\s*)?$/.test(e)))return!1}return!0}function t(t){let e=t.trim();if(!e)return{text:"",didStrip:!1};const r="HEARTBEAT_OK";if(!e.includes(r))return{text:e,didStrip:!1};let i=!1,n=!0;for(;n;){n=!1;const t=e.trim();t.startsWith(r)?(e=t.slice(12).trimStart(),i=!0,n=!0):t.endsWith(r)&&(e=t.slice(0,Math.max(0,t.length-12)).trimEnd(),i=!0,n=!0)}return{text:e.replace(/\s+/g," ").trim(),didStrip:i}}export function stripHeartbeatToken(e,r=300){if(!e)return{shouldSkip:!0,text:""};const i=e.trim();if(!i)return{shouldSkip:!0,text:""};const n=i.replace(/<[^>]*>/g," ").replace(/ /gi," ").replace(/^[*`~_]+/,"").replace(/[*`~_]+$/,"");if(!(i.includes("HEARTBEAT_OK")||n.includes("HEARTBEAT_OK")))return{shouldSkip:!1,text:i};const s=t(i),o=t(n),u=s.didStrip&&s.text?s:o;if(!u.didStrip)return{shouldSkip:!1,text:i};if(!u.text)return{shouldSkip:!0,text:""};const l=u.text.trim();return l.length<=r?{shouldSkip:!0,text:""}:{shouldSkip:!1,text:l}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Cron as t}from"croner";export function computeNextRunAtMs(e,r,n){if("at"===e.kind){const t=new Date(e.at).getTime();if(!Number.isFinite(t))return;return t>r?t:void 0}if("every"===e.kind){const t=Math.max(1,Math.floor(e.everyMs)),n=Math.max(0,Math.floor(e.anchorMs??r));if(r<n)return n;const o=r-n;return n+Math.max(1,Math.floor((o+t-1)/t))*t}const o=e.expr.trim();if(!o)return;const i=new t(o,{timezone:e.tz?.trim()||n||void 0}).nextRun(new Date(r));return i?i.getTime():void 0}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import r from"node:fs";import o from"node:path";export async function loadCronStore(o){try{const e=await r.promises.readFile(o,"utf-8"),t=JSON.parse(e);return{version:1,jobs:(Array.isArray(t?.jobs)?t.jobs:[]).filter(Boolean)}}catch{return{version:1,jobs:[]}}}export async function saveCronStore(e,t){await r.promises.mkdir(o.dirname(e),{recursive:!0});const i=`${e}.${process.pid}.${Math.random().toString(16).slice(2)}.tmp`,s=JSON.stringify(t,null,2);await r.promises.writeFile(i,s,"utf-8"),await r.promises.rename(i,e);try{await r.promises.copyFile(e,`${e}.bak`)}catch{}}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type CronSchedule = {
|
|
2
|
+
kind: "at";
|
|
3
|
+
at: string;
|
|
4
|
+
} | {
|
|
5
|
+
kind: "every";
|
|
6
|
+
everyMs: number;
|
|
7
|
+
anchorMs?: number;
|
|
8
|
+
} | {
|
|
9
|
+
kind: "cron";
|
|
10
|
+
expr: string;
|
|
11
|
+
tz?: string;
|
|
12
|
+
};
|
|
13
|
+
export type CronJobState = {
|
|
14
|
+
nextRunAtMs?: number;
|
|
15
|
+
runningAtMs?: number;
|
|
16
|
+
lastRunAtMs?: number;
|
|
17
|
+
lastStatus?: "ok" | "error" | "skipped";
|
|
18
|
+
lastError?: string;
|
|
19
|
+
lastDurationMs?: number;
|
|
20
|
+
};
|
|
21
|
+
export type CronJob = {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
enabled: boolean;
|
|
26
|
+
isolated: boolean;
|
|
27
|
+
deleteAfterRun?: boolean;
|
|
28
|
+
suppressToken: boolean;
|
|
29
|
+
createdAtMs: number;
|
|
30
|
+
updatedAtMs: number;
|
|
31
|
+
schedule: CronSchedule;
|
|
32
|
+
channel: string;
|
|
33
|
+
chatId: string;
|
|
34
|
+
message: string;
|
|
35
|
+
state: CronJobState;
|
|
36
|
+
};
|
|
37
|
+
export type CronStoreFile = {
|
|
38
|
+
version: 1;
|
|
39
|
+
jobs: CronJob[];
|
|
40
|
+
};
|
|
41
|
+
export type CronJobCreate = Omit<CronJob, "id" | "createdAtMs" | "updatedAtMs" | "state"> & {
|
|
42
|
+
state?: Partial<CronJobState>;
|
|
43
|
+
};
|
|
44
|
+
export type CronJobPatch = Partial<Omit<CronJob, "id" | "createdAtMs" | "state">> & {
|
|
45
|
+
state?: Partial<CronJobState>;
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface Attachment {
|
|
2
|
+
type: "image" | "voice" | "audio" | "document" | "video" | "video_note" | "sticker" | "location" | "contact";
|
|
3
|
+
mimeType?: string;
|
|
4
|
+
fileName?: string;
|
|
5
|
+
fileSize?: number;
|
|
6
|
+
duration?: number;
|
|
7
|
+
caption?: string;
|
|
8
|
+
metadata?: Record<string, unknown>;
|
|
9
|
+
getBuffer(): Promise<Buffer>;
|
|
10
|
+
}
|
|
11
|
+
export interface IncomingMessage {
|
|
12
|
+
chatId: string;
|
|
13
|
+
userId: string;
|
|
14
|
+
channelName: string;
|
|
15
|
+
text?: string;
|
|
16
|
+
attachments: Attachment[];
|
|
17
|
+
username?: string;
|
|
18
|
+
rawContext?: unknown;
|
|
19
|
+
}
|
|
20
|
+
export type MessageHandler = (msg: IncomingMessage) => Promise<string>;
|
|
21
|
+
export type InlineButton = {
|
|
22
|
+
text: string;
|
|
23
|
+
callbackData?: string;
|
|
24
|
+
url?: string;
|
|
25
|
+
};
|
|
26
|
+
export interface ChannelAdapter {
|
|
27
|
+
readonly name: string;
|
|
28
|
+
start(onMessage: MessageHandler): Promise<void>;
|
|
29
|
+
sendText(chatId: string, text: string): Promise<void>;
|
|
30
|
+
sendAudio?(chatId: string, filePath: string, asVoice?: boolean): Promise<void>;
|
|
31
|
+
sendButtons?(chatId: string, text: string, buttons: InlineButton[]): Promise<void>;
|
|
32
|
+
setTyping?(chatId: string): Promise<void>;
|
|
33
|
+
clearTyping?(chatId: string): Promise<void>;
|
|
34
|
+
/** Cooperative typing release: decrements refcount instead of force-clearing. */
|
|
35
|
+
releaseTyping?(chatId: string): Promise<void>;
|
|
36
|
+
stop(): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { AppConfig } from "../config.js";
|
|
2
|
+
import type { TokenDB } from "../auth/token-db.js";
|
|
3
|
+
import type { ChannelAdapter, InlineButton, MessageHandler } from "./bridge.js";
|
|
4
|
+
export declare class ChannelManager {
|
|
5
|
+
private config;
|
|
6
|
+
private tokenDb;
|
|
7
|
+
private onMessage;
|
|
8
|
+
private adapters;
|
|
9
|
+
constructor(config: AppConfig, tokenDb: TokenDB, onMessage: MessageHandler);
|
|
10
|
+
registerAdapter(adapter: ChannelAdapter): void;
|
|
11
|
+
startAll(): Promise<void>;
|
|
12
|
+
sendToChannel(channelName: string, chatId: string, text: string): Promise<void>;
|
|
13
|
+
sendAudio(channelName: string, chatId: string, filePath: string, asVoice?: boolean): Promise<void>;
|
|
14
|
+
sendButtons(channelName: string, chatId: string, text: string, buttons: InlineButton[]): Promise<void>;
|
|
15
|
+
setTyping(channelName: string, chatId: string): Promise<void>;
|
|
16
|
+
clearTyping(channelName: string, chatId: string): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Cooperative typing release: decrements the inflight refcount instead of
|
|
19
|
+
* force-clearing. The typing interval will self-destruct when no more
|
|
20
|
+
* handlers are in-flight. Use this in the normal message-completion path;
|
|
21
|
+
* reserve clearTyping() as a safety-net for hard resets.
|
|
22
|
+
*/
|
|
23
|
+
releaseTyping(channelName: string, chatId: string): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Send a full response that may contain MEDIA: lines.
|
|
26
|
+
* Parses out media entries, sends audio via sendAudio, and sends remaining text via sendText.
|
|
27
|
+
*/
|
|
28
|
+
sendResponse(channelName: string, chatId: string, fullResponse: string): Promise<void>;
|
|
29
|
+
getAdapter(name: string): ChannelAdapter | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Get a channel by name (alias for getAdapter for compatibility)
|
|
32
|
+
*/
|
|
33
|
+
getChannel(name: string): ChannelAdapter | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Send a system message directly to a channel
|
|
36
|
+
* This bypasses the normal message flow and sends directly
|
|
37
|
+
*/
|
|
38
|
+
sendSystemMessage(channelName: string, chatId: string, message: string): Promise<void>;
|
|
39
|
+
listAdapters(): Array<{
|
|
40
|
+
name: string;
|
|
41
|
+
active: boolean;
|
|
42
|
+
}>;
|
|
43
|
+
stopAll(): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=channel-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{parseMediaLines as t}from"../utils/media-response.js";import{createLogger as e}from"../utils/logger.js";const s=e("ChannelManager");export class ChannelManager{config;tokenDb;onMessage;adapters=new Map;constructor(t,e,s){this.config=t,this.tokenDb=e,this.onMessage=s}registerAdapter(t){this.adapters.set(t.name,t)}async startAll(){const t=[];for(const[e,n]of this.adapters)s.info(`Starting channel: ${e}`),t.push(n.start(this.onMessage).then(()=>{s.info(`Channel started: ${e}`)}).catch(t=>{s.error(`Failed to start channel ${e}: ${t}`)}));await Promise.allSettled(t)}async sendToChannel(t,e,n){const a=this.adapters.get(t);a?await a.sendText(e,n):s.error(`Channel not found: ${t}`)}async sendAudio(t,e,n,a){const r=this.adapters.get(t);r?r.sendAudio?await r.sendAudio(e,n,a):s.warn(`Channel ${t} does not support audio, skipping`):s.error(`Channel not found: ${t}`)}async sendButtons(t,e,n,a){const r=this.adapters.get(t);if(r)if(r.sendButtons)await r.sendButtons(e,n,a);else{const t=a.map((t,e)=>`${e+1}. ${t.text}`).join("\n");await r.sendText(e,`${n}\n\n${t}\n\nReply with your choice or type your answer.`)}else s.error(`Channel not found: ${t}`)}async setTyping(t,e){const s=this.adapters.get(t);if(s&&s.setTyping)try{await s.setTyping(e)}catch{}}async clearTyping(t,e){const s=this.adapters.get(t);if(s&&s.clearTyping)try{await s.clearTyping(e)}catch{}}async releaseTyping(t,e){const s=this.adapters.get(t);if(s)if(s.releaseTyping)try{await s.releaseTyping(e)}catch{}else if(s.clearTyping)try{await s.clearTyping(e)}catch{}}async sendResponse(e,n,a){const{textParts:r,mediaEntries:o}=t(a);for(const t of o)try{await this.sendAudio(e,n,t.path,t.asVoice)}catch(t){s.error(`Failed to send audio to ${e}:${n}: ${t}`)}const i=r.join("\n").trim();i&&await this.sendToChannel(e,n,i)}getAdapter(t){return this.adapters.get(t)}getChannel(t){return this.adapters.get(t)}async sendSystemMessage(t,e,n){const a=this.adapters.get(t);if(a)try{await a.sendText(e,n),s.debug(`System message sent to ${t}:${e}`)}catch(n){s.error(`Failed to send system message to ${t}:${e}: ${n}`)}else s.warn(`Cannot send system message: channel ${t} not found`)}listAdapters(){return[...this.adapters.entries()].map(([t])=>({name:t,active:!0}))}async stopAll(){const t=[];for(const[e,n]of this.adapters)s.info(`Stopping channel: ${e}`),t.push(n.stop().catch(t=>{s.error(`Error stopping channel ${e}: ${t}`)}));await Promise.allSettled(t)}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{deflateSync as r}from"node:zlib";import t from"qrcode-terminal/vendor/QRCode/index.js";import o from"qrcode-terminal/vendor/QRCode/QRErrorCorrectLevel.js";const e=t,n=o;function f(r,t,o,e,n,f,c,l=255){const u=4*(o*e+t);r[u]=n,r[u+1]=f,r[u+2]=c,r[u+3]=l}const c=function(){const r=new Uint32Array(256);for(let t=0;t<256;t+=1){let o=t;for(let r=0;r<8;r+=1)o=1&o?3988292384^o>>>1:o>>>1;r[t]=o>>>0}return r}();function l(r,t){const o=Buffer.from(r,"ascii"),e=Buffer.alloc(4);e.writeUInt32BE(t.length,0);const n=function(r){let t=4294967295;for(let o=0;o<r.length;o+=1)t=c[255&(t^r[o])]^t>>>8;return(4294967295^t)>>>0}(Buffer.concat([o,t])),f=Buffer.alloc(4);return f.writeUInt32BE(n,0),Buffer.concat([e,o,t,f])}export async function renderQrPngBase64(t,o={}){const{scale:c=6,marginModules:u=4}=o,i=function(r){const t=new e(-1,n.L);return t.addData(r),t.make(),t}(t),a=i.getModuleCount(),s=(a+2*u)*c,B=Buffer.alloc(s*s*4,255);for(let r=0;r<a;r+=1)for(let t=0;t<a;t+=1){if(!i.isDark(r,t))continue;const o=(t+u)*c,e=(r+u)*c;for(let r=0;r<c;r+=1)for(let t=0;t<c;t+=1)f(B,o+t,e+r,s,0,0,0,255)}return function(t,o,e){const n=4*o,f=Buffer.alloc((n+1)*e);for(let r=0;r<e;r+=1){const o=r*(n+1);f[o]=0,t.copy(f,o+1,r*n,r*n+n)}const c=r(f),u=Buffer.from([137,80,78,71,13,10,26,10]),i=Buffer.alloc(13);return i.writeUInt32BE(o,0),i.writeUInt32BE(e,4),i[8]=8,i[9]=6,i[10]=0,i[11]=0,i[12]=0,Buffer.concat([u,l("IHDR",i),l("IDAT",c),l("IEND",Buffer.alloc(0))])}(B,s,s).toString("base64")}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ChannelAdapter, MessageHandler, InlineButton } from "../bridge.js";
|
|
2
|
+
import type { TokenDB } from "../../auth/token-db.js";
|
|
3
|
+
interface TelegramAccountConfig {
|
|
4
|
+
botToken: string;
|
|
5
|
+
dmPolicy: string;
|
|
6
|
+
allowFrom: Array<string | number>;
|
|
7
|
+
}
|
|
8
|
+
export declare class TelegramChannel implements ChannelAdapter {
|
|
9
|
+
readonly name = "telegram";
|
|
10
|
+
private bot;
|
|
11
|
+
private config;
|
|
12
|
+
private tokenDb;
|
|
13
|
+
private typingIntervals;
|
|
14
|
+
private inflightTyping;
|
|
15
|
+
private inflightCount;
|
|
16
|
+
constructor(config: TelegramAccountConfig, tokenDb: TokenDB, inflightTyping?: boolean);
|
|
17
|
+
start(onMessage: MessageHandler): Promise<void>;
|
|
18
|
+
sendText(chatId: string, text: string): Promise<void>;
|
|
19
|
+
setTyping(chatId: string): Promise<void>;
|
|
20
|
+
/** Re-send typing if an interval is active (another handler is still processing). */
|
|
21
|
+
private resendTypingIfActive;
|
|
22
|
+
clearTyping(chatId: string): Promise<void>;
|
|
23
|
+
releaseTyping(chatId: string): Promise<void>;
|
|
24
|
+
private startTypingInterval;
|
|
25
|
+
private stopTypingInterval;
|
|
26
|
+
sendButtons(chatId: string, text: string, buttons: InlineButton[]): Promise<void>;
|
|
27
|
+
sendAudio(chatId: string, filePath: string, asVoice?: boolean): Promise<void>;
|
|
28
|
+
stop(): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Process an incoming message in the background.
|
|
31
|
+
* Not awaited by the Grammy handler so the polling loop stays unblocked.
|
|
32
|
+
*/
|
|
33
|
+
private handleIncoming;
|
|
34
|
+
private buildIncomingMessage;
|
|
35
|
+
private downloadFile;
|
|
36
|
+
private sendChunked;
|
|
37
|
+
}
|
|
38
|
+
export {};
|
|
39
|
+
//# sourceMappingURL=telegram.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Bot as t,InputFile as e,InlineKeyboard as i}from"grammy";import{validateChannelUser as a}from"../../auth/auth-middleware.js";import{parseMediaLines as n}from"../../utils/media-response.js";import{markdownToTelegramHtmlChunks as o}from"../../utils/telegram-format.js";import{createLogger as s}from"../../utils/logger.js";const r=s("Telegram");export class TelegramChannel{name="telegram";bot;config;tokenDb;typingIntervals=new Map;inflightTyping;inflightCount=new Map;constructor(e,i,a=!0){this.config=e,this.tokenDb=i,this.inflightTyping=a,this.bot=new t(e.botToken)}async start(t){this.bot.on("message",e=>{this.handleIncoming(e,t)}),this.bot.on("callback_query:data",e=>{const i=e.callbackQuery.data,a=String(e.from?.id??"unknown"),n=String(e.chat?.id??e.callbackQuery.message?.chat?.id??"unknown"),o=e.from?.username;e.answerCallbackQuery().catch(()=>{}),this.startTypingInterval(n);t({chatId:n,userId:a,channelName:"telegram",text:i,attachments:[],username:o,rawContext:e}).then(async t=>{t&&t.trim()&&await this.sendText(n,t)}).catch(t=>{r.error(`Error handling callback from ${a}: ${t}`)})}),r.info("Starting Telegram bot..."),this.bot.start({onStart:t=>{r.info(`Telegram bot started: @${t.username}`)}})}async sendText(t,e){const i=o(e,4096);for(const a of i)try{await this.bot.api.sendMessage(t,a,{parse_mode:"HTML"})}catch{const i=c(e,4096);for(const e of i)await this.bot.api.sendMessage(t,e);break}await this.resendTypingIfActive(t)}async setTyping(t){this.typingIntervals.has(t)?await this.bot.api.sendChatAction(t,"typing").catch(()=>{}):this.startTypingInterval(t)}async resendTypingIfActive(t){this.typingIntervals.has(t)&&await this.bot.api.sendChatAction(t,"typing").catch(()=>{})}async clearTyping(t){this.inflightCount.delete(t);const e=this.typingIntervals.get(t);e&&(clearInterval(e),this.typingIntervals.delete(t))}async releaseTyping(t){this.stopTypingInterval(t)}startTypingInterval(t){if(this.inflightTyping){const e=(this.inflightCount.get(t)??0)+1;if(this.inflightCount.set(t,e),e>1)return}const e=this.typingIntervals.get(t);e&&(clearInterval(e),this.typingIntervals.delete(t)),this.bot.api.sendChatAction(t,"typing").catch(()=>{});const i=setInterval(()=>{const e=this.inflightCount.get(t)??0;!this.inflightTyping||e>0?this.bot.api.sendChatAction(t,"typing").catch(()=>{}):(clearInterval(i),this.typingIntervals.delete(t))},4e3);this.typingIntervals.set(t,i)}stopTypingInterval(t){if(this.inflightTyping){const e=(this.inflightCount.get(t)??1)-1;return void(e>0?this.inflightCount.set(t,e):this.inflightCount.delete(t))}const e=this.typingIntervals.get(t);e&&(clearInterval(e),this.typingIntervals.delete(t))}async sendButtons(t,e,a){const n=new i;for(const t of a)t.url?n.url(t.text,t.url):n.text(t.text,t.callbackData??t.text),n.row();const s=o(e,4096);for(let i=0;i<s.length-1;i++)try{await this.bot.api.sendMessage(t,s[i],{parse_mode:"HTML"})}catch{await this.bot.api.sendMessage(t,e.slice(0,4096))}const r=s[s.length-1]??e;try{await this.bot.api.sendMessage(t,r,{parse_mode:"HTML",reply_markup:n})}catch{await this.bot.api.sendMessage(t,e.slice(0,4096),{reply_markup:n})}await this.resendTypingIfActive(t)}async sendAudio(t,i,a){const n=new e(i);a?await this.bot.api.sendVoice(t,n):await this.bot.api.sendAudio(t,n),await this.resendTypingIfActive(t)}async stop(){try{await this.bot.stop(),r.info("Telegram bot stopped"),await new Promise(t=>setTimeout(t,100))}catch(t){r.warn(`Error stopping Telegram bot: ${t}`)}}async handleIncoming(t,i){const o=String(t.from?.id??"unknown"),s=String(t.chat?.id??"unknown"),c=t.from?.username,l=a(this.tokenDb,o,"telegram",this.config.dmPolicy,this.config.allowFrom);if(!l.authorized)return r.warn(`Unauthorized message from ${o} (@${c})`),void await t.reply(l.reason??"Not authorized.");this.startTypingInterval(s);try{const a=await this.buildIncomingMessage(t,s,o,c),l=await i(a),{textParts:h,mediaEntries:d}=n(l);for(const i of d)try{const a=new e(i.path);i.asVoice?await t.replyWithVoice(a):await t.replyWithAudio(a)}catch(t){r.error(`Failed to send audio: ${t}`)}const p=h.join("\n").trim();p&&await this.sendChunked(t,p),await this.resendTypingIfActive(s)}catch(e){r.error(`Error handling message from ${o}: ${e}`),await t.reply("An error occurred while processing your message.").catch(()=>{})}}async buildIncomingMessage(t,e,i,a){const n=t.message,o=[];let s=n.text??n.caption??void 0;if(n.photo&&n.photo.length>0){const e=n.photo[n.photo.length-1];o.push({type:"image",mimeType:"image/jpeg",fileSize:e.file_size,caption:n.caption,getBuffer:()=>this.downloadFile(t,e.file_id)})}return n.voice&&o.push({type:"voice",mimeType:n.voice.mime_type??"audio/ogg",duration:n.voice.duration,fileSize:n.voice.file_size,getBuffer:()=>this.downloadFile(t,n.voice.file_id)}),n.audio&&o.push({type:"audio",mimeType:n.audio.mime_type??"audio/mpeg",fileName:n.audio.file_name,duration:n.audio.duration,fileSize:n.audio.file_size,caption:n.caption,getBuffer:()=>this.downloadFile(t,n.audio.file_id)}),n.document&&o.push({type:"document",mimeType:n.document.mime_type,fileName:n.document.file_name,fileSize:n.document.file_size,caption:n.caption,getBuffer:()=>this.downloadFile(t,n.document.file_id)}),n.video&&o.push({type:"video",mimeType:n.video.mime_type??"video/mp4",fileName:n.video.file_name,duration:n.video.duration,fileSize:n.video.file_size,caption:n.caption,getBuffer:()=>this.downloadFile(t,n.video.file_id)}),n.video_note&&o.push({type:"video_note",mimeType:"video/mp4",duration:n.video_note.duration,fileSize:n.video_note.file_size,getBuffer:()=>this.downloadFile(t,n.video_note.file_id)}),n.sticker&&o.push({type:"sticker",mimeType:n.sticker.is_animated?"application/x-tgsticker":n.sticker.is_video?"video/webm":"image/webp",metadata:{emoji:n.sticker.emoji,setName:n.sticker.set_name},getBuffer:()=>this.downloadFile(t,n.sticker.file_id)}),n.location&&o.push({type:"location",metadata:{latitude:n.location.latitude,longitude:n.location.longitude},getBuffer:async()=>Buffer.alloc(0)}),n.contact&&o.push({type:"contact",metadata:{phoneNumber:n.contact.phone_number,firstName:n.contact.first_name,lastName:n.contact.last_name,userId:n.contact.user_id},getBuffer:async()=>Buffer.alloc(0)}),{chatId:e,userId:i,channelName:"telegram",text:s,attachments:o,username:a,rawContext:t}}async downloadFile(t,e){const i=await t.api.getFile(e),a=`https://api.telegram.org/file/bot${this.config.botToken}/${i.file_path}`,n=await fetch(a);if(!n.ok)throw new Error(`Failed to download file: ${n.statusText}`);return Buffer.from(await n.arrayBuffer())}async sendChunked(t,e){const i=o(e,4096);for(const a of i)try{await t.reply(a,{parse_mode:"HTML"})}catch{const i=c(e,4096);for(const e of i)await t.reply(e);break}}}function c(t,e){if(t.length<=e)return[t];const i=[];let a=t;for(;a.length>0;){if(a.length<=e){i.push(a);break}let t=a.lastIndexOf("\n",e);t<=0&&(t=e),i.push(a.slice(0,t)),a=a.slice(t).trimStart()}return i}
|