@hasna/bridge 0.1.0 → 0.1.2

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.
@@ -1,6 +1,7 @@
1
1
  import { type AgentConfig, type BridgeConfig, type ChannelConfig, type ProfileConfig, type RouteConfig } from "../types.js";
2
2
  export declare function emptyConfig(): BridgeConfig;
3
3
  export declare function parseConfig(value: unknown): BridgeConfig;
4
+ export declare function redactConfig(config: BridgeConfig): BridgeConfig;
4
5
  export declare function loadConfig(configPath?: string): Promise<BridgeConfig>;
5
6
  export declare function saveConfig(config: BridgeConfig, configPath?: string): Promise<void>;
6
7
  export declare function ensureConfig(configPath?: string): Promise<BridgeConfig>;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAmF5I,wBAAgB,WAAW,IAAI,YAAY,CAQ1C;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAGxD;AAED,wBAAsB,UAAU,CAAC,UAAU,SAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAUxF;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,YAAY,EAAE,UAAU,SAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKtG;AAED,wBAAsB,YAAY,CAAC,UAAU,SAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAI1F;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,EAAE,UAAU,SAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAKnH;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,EAAE,UAAU,SAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAKnH;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,UAAU,SAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAK7G;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,UAAU,SAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAK7G"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAqF5I,wBAAgB,WAAW,IAAI,YAAY,CAQ1C;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAGxD;AAeD,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAW/D;AAED,wBAAsB,UAAU,CAAC,UAAU,SAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAUxF;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,YAAY,EAAE,UAAU,SAAsB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKtG;AAED,wBAAsB,YAAY,CAAC,UAAU,SAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAI1F;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,EAAE,UAAU,SAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAKnH;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,EAAE,UAAU,SAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAKnH;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,UAAU,SAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAK7G;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,UAAU,SAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAK7G"}
@@ -0,0 +1,123 @@
1
+ import type { BridgeConfig } from "../types.js";
2
+ export type DaemonSupervisor = "process" | "launchd" | "systemd";
3
+ export type DaemonSupervisorOption = DaemonSupervisor | "auto";
4
+ export interface DaemonPaths {
5
+ dir: string;
6
+ lockDir: string;
7
+ metadataFile: string;
8
+ stdoutLog: string;
9
+ stderrLog: string;
10
+ launchdPlist: string;
11
+ systemdUnit: string;
12
+ }
13
+ export interface DaemonMetadata {
14
+ version: 1;
15
+ supervisor: "process";
16
+ pid: number;
17
+ pgid?: number;
18
+ startedAt: string;
19
+ identity: {
20
+ command: string;
21
+ cwd: string;
22
+ configPath: string;
23
+ statePath: string;
24
+ daemonDir: string;
25
+ bridgeHome: string;
26
+ };
27
+ command: string[];
28
+ cwd: string;
29
+ configPath: string;
30
+ statePath: string;
31
+ intervalMs: number;
32
+ serveJson: boolean;
33
+ daemonDir: string;
34
+ bridgeHome: string;
35
+ stdoutLog: string;
36
+ stderrLog: string;
37
+ }
38
+ export interface DaemonStatus {
39
+ running: boolean;
40
+ stale: boolean;
41
+ supervisor: DaemonSupervisor;
42
+ pid?: number;
43
+ startedAt?: string;
44
+ uptimeSeconds?: number;
45
+ detail?: string;
46
+ installedDetail?: string;
47
+ metadata?: DaemonMetadata;
48
+ paths: DaemonPaths;
49
+ installed: {
50
+ launchd: boolean;
51
+ systemd: boolean;
52
+ };
53
+ telegramApiBase: {
54
+ overridden: boolean;
55
+ origin: string;
56
+ pathname: string;
57
+ error?: string;
58
+ };
59
+ }
60
+ export interface DaemonStartOptions {
61
+ supervisor?: DaemonSupervisorOption;
62
+ daemonDir?: string;
63
+ configPath?: string;
64
+ statePath?: string;
65
+ intervalMs?: number;
66
+ serveJson?: boolean;
67
+ }
68
+ export interface DaemonStopOptions {
69
+ supervisor?: DaemonSupervisorOption;
70
+ daemonDir?: string;
71
+ timeoutMs?: number;
72
+ force?: boolean;
73
+ }
74
+ export interface DaemonInstallOptions {
75
+ supervisor?: DaemonSupervisorOption;
76
+ daemonDir?: string;
77
+ configPath?: string;
78
+ statePath?: string;
79
+ intervalMs?: number;
80
+ serveJson?: boolean;
81
+ }
82
+ export interface DaemonInstallResult {
83
+ supervisor: DaemonSupervisor;
84
+ path: string;
85
+ command: string[];
86
+ requiredEnv: string[];
87
+ warning?: string;
88
+ }
89
+ export declare function resolveSupervisor(supervisor?: DaemonSupervisorOption): DaemonSupervisor;
90
+ export declare function defaultDaemonDir(): string;
91
+ export declare function daemonPaths(daemonDir?: string): DaemonPaths;
92
+ export declare function ensureDaemonDir(dir?: string): Promise<DaemonPaths>;
93
+ export declare function requiredTelegramEnvVars(config: BridgeConfig): string[];
94
+ export declare function daemonStatus(options?: {
95
+ daemonDir?: string;
96
+ supervisor?: DaemonSupervisorOption;
97
+ }): Promise<DaemonStatus>;
98
+ export declare function startProcessDaemon(options?: DaemonStartOptions): Promise<DaemonStatus>;
99
+ export declare function stopProcessDaemon(options?: DaemonStopOptions): Promise<DaemonStatus>;
100
+ export declare function restartProcessDaemon(options?: DaemonStartOptions & DaemonStopOptions): Promise<DaemonStatus>;
101
+ export declare function renderLaunchdPlist(command: string[], paths: DaemonPaths): string;
102
+ export declare function renderSystemdUnit(command: string[], paths: DaemonPaths): string;
103
+ export declare function installDaemon(options?: DaemonInstallOptions): Promise<DaemonInstallResult>;
104
+ export declare function startInstalledDaemon(options?: DaemonInstallOptions): Promise<DaemonInstallResult>;
105
+ export declare function stopInstalledDaemon(options?: DaemonStopOptions): Promise<void>;
106
+ export declare function restartInstalledDaemon(options?: DaemonInstallOptions & DaemonStopOptions): Promise<DaemonInstallResult | DaemonStatus>;
107
+ export declare function uninstallDaemon(options?: {
108
+ supervisor?: DaemonSupervisorOption;
109
+ daemonDir?: string;
110
+ }): Promise<{
111
+ supervisor: DaemonSupervisor;
112
+ removed: string[];
113
+ }>;
114
+ export declare function tailFile(path: string, lines: number): Promise<string>;
115
+ export declare function daemonLogs(options?: {
116
+ daemonDir?: string;
117
+ lines?: number;
118
+ }): Promise<{
119
+ stdout: string;
120
+ stderr: string;
121
+ paths: DaemonPaths;
122
+ }>;
123
+ //# sourceMappingURL=daemon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../../src/lib/daemon.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,3 @@
1
1
  import type { DoctorReport } from "../types.js";
2
- export declare function doctor(configPath?: string): Promise<DoctorReport>;
2
+ export declare function doctor(configPath?: string, statePath?: string): Promise<DoctorReport>;
3
3
  //# sourceMappingURL=doctor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/lib/doctor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAe,YAAY,EAAE,MAAM,aAAa,CAAC;AAU7D,wBAAsB,MAAM,CAAC,UAAU,SAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CA2CpF"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/lib/doctor.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAe,YAAY,EAAE,MAAM,aAAa,CAAC;AAsC7D,wBAAsB,MAAM,CAAC,UAAU,SAAsB,EAAE,SAAS,SAAqB,GAAG,OAAO,CAAC,YAAY,CAAC,CA+DpH"}
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/lib/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAiB,mBAAmB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAChH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,mBAAmB,EAAsC,MAAM,eAAe,CAAC;AAExF,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,EAAE,OAAO,QAAQ,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,mBAAmB,CAAC;IAC1C,YAAY,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC;CACjD;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,GAAG,WAAW,EAAE,CAa1F;AAMD,wBAAsB,YAAY,CAChC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,aAAa,EACtB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CA2BhC"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/lib/router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAiB,mBAAmB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAChH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,mBAAmB,EAAsC,MAAM,eAAe,CAAC;AAExF,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,EAAE,OAAO,QAAQ,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,mBAAmB,CAAC;IAC1C,YAAY,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC;CACjD;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,GAAG,WAAW,EAAE,CAc1F;AAMD,wBAAsB,YAAY,CAChC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,aAAa,EACtB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAgChC"}
@@ -17,6 +17,12 @@ export interface TelegramUpdate {
17
17
  date?: number;
18
18
  };
19
19
  }
20
+ export interface TelegramApiBaseInfo {
21
+ overridden: boolean;
22
+ origin: string;
23
+ pathname: string;
24
+ }
25
+ export declare function telegramApiBaseInfo(): TelegramApiBaseInfo;
20
26
  export declare function telegramToken(channel: TelegramChannelConfig): string;
21
27
  export declare function telegramChatAllowed(channel: TelegramChannelConfig, chatId: string | undefined): boolean;
22
28
  export declare function sendTelegramMessage(token: string, chatId: string, text: string): Promise<unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"telegram.d.ts","sourceRoot":"","sources":["../../src/lib/telegram.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAExE,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE;QACR,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,EAAE;YAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAChE,IAAI,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;YAAC,UAAU,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACvE,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,CAKpE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAIvG;AAED,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASvG;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAO,GACzD,OAAO,CAAC,cAAc,EAAE,CAAC,CAU3B;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,aAAa,GAAG,SAAS,CAa5G"}
1
+ {"version":3,"file":"telegram.d.ts","sourceRoot":"","sources":["../../src/lib/telegram.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAExE,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE;QACR,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,EAAE;YAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAChE,IAAI,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;YAAC,UAAU,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACvE,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;CACH;AAID,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAiBD,wBAAgB,mBAAmB,IAAI,mBAAmB,CAOzD;AAUD,wBAAgB,aAAa,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,CAKpE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,qBAAqB,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAIvG;AAED,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASvG;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAO,GACzD,OAAO,CAAC,cAAc,EAAE,CAAC,CAU3B;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,aAAa,GAAG,SAAS,CAa5G"}
package/dist/mcp/index.js CHANGED
@@ -4101,6 +4101,7 @@ function defaultConfigPath() {
4101
4101
  }
4102
4102
 
4103
4103
  // src/lib/config.ts
4104
+ var REDACTED_VALUE = "[redacted]";
4104
4105
  var channelSchema = exports_external.discriminatedUnion("kind", [
4105
4106
  exports_external.object({
4106
4107
  id: exports_external.string().min(1),
@@ -4188,6 +4189,31 @@ function parseConfig(value) {
4188
4189
  const parsed = configSchema.parse(value);
4189
4190
  return parsed;
4190
4191
  }
4192
+ function redactEnv(env) {
4193
+ if (!env)
4194
+ return;
4195
+ return Object.fromEntries(Object.keys(env).map((key) => [key, REDACTED_VALUE]));
4196
+ }
4197
+ function redactEnvRecord(items) {
4198
+ return Object.fromEntries(Object.entries(items).map(([id, item]) => {
4199
+ const clone = { ...item };
4200
+ if (item.env)
4201
+ clone.env = redactEnv(item.env);
4202
+ return [id, clone];
4203
+ }));
4204
+ }
4205
+ function redactConfig(config) {
4206
+ return {
4207
+ ...config,
4208
+ channels: Object.fromEntries(Object.entries(config.channels).map(([id, channel]) => [id, { ...channel }])),
4209
+ profiles: redactEnvRecord(config.profiles),
4210
+ agents: redactEnvRecord(config.agents),
4211
+ routes: config.routes.map((route) => ({
4212
+ ...route,
4213
+ match: route.match ? { ...route.match, chatIds: route.match.chatIds ? [...route.match.chatIds] : undefined } : undefined
4214
+ }))
4215
+ };
4216
+ }
4191
4217
  async function loadConfig(configPath = defaultConfigPath()) {
4192
4218
  try {
4193
4219
  const raw = await readFile(configPath, "utf-8");
@@ -4199,8 +4225,275 @@ async function loadConfig(configPath = defaultConfigPath()) {
4199
4225
  throw err;
4200
4226
  }
4201
4227
  }
4228
+ // src/lib/daemon.ts
4229
+ import { chmod as chmod2, mkdir as mkdir2, readFile as readFile2, rename, rm, rmdir, stat, writeFile as writeFile2 } from "fs/promises";
4230
+ import { dirname as dirname2, join as join3, resolve } from "path";
4231
+
4232
+ // src/lib/state.ts
4233
+ import { dirname, join as join2 } from "path";
4234
+ function defaultStatePath() {
4235
+ return process.env["BRIDGE_STATE"] || join2(bridgeHome(), "state.json");
4236
+ }
4237
+
4238
+ // src/lib/telegram.ts
4239
+ var DEFAULT_TELEGRAM_API_BASE = "https://api.telegram.org";
4240
+ function telegramApiBase() {
4241
+ const raw = process.env["BRIDGE_TELEGRAM_API_BASE"] || DEFAULT_TELEGRAM_API_BASE;
4242
+ const parsed = new URL(raw);
4243
+ if (!["http:", "https:"].includes(parsed.protocol)) {
4244
+ throw new Error("BRIDGE_TELEGRAM_API_BASE must use http or https");
4245
+ }
4246
+ if (parsed.username || parsed.password) {
4247
+ throw new Error("BRIDGE_TELEGRAM_API_BASE must not contain credentials");
4248
+ }
4249
+ if (parsed.search || parsed.hash) {
4250
+ throw new Error("BRIDGE_TELEGRAM_API_BASE must not contain query strings or fragments");
4251
+ }
4252
+ return parsed;
4253
+ }
4254
+ function telegramApiBaseInfo() {
4255
+ const parsed = telegramApiBase();
4256
+ return {
4257
+ overridden: parsed.href.replace(/\/$/, "") !== DEFAULT_TELEGRAM_API_BASE,
4258
+ origin: parsed.origin,
4259
+ pathname: parsed.pathname
4260
+ };
4261
+ }
4262
+ function telegramMethodUrl(token, method) {
4263
+ const base = telegramApiBase();
4264
+ const prefix = base.pathname.replace(/\/$/, "");
4265
+ base.pathname = `${prefix}/bot${token}/${method}`;
4266
+ base.search = "";
4267
+ return base.toString();
4268
+ }
4269
+ function telegramToken(channel) {
4270
+ const envName = channel.botTokenEnv || "TELEGRAM_BOT_TOKEN";
4271
+ const token = process.env[envName];
4272
+ if (!token)
4273
+ throw new Error(`Missing Telegram bot token env var: ${envName}`);
4274
+ return token;
4275
+ }
4276
+ function telegramChatAllowed(channel, chatId) {
4277
+ if (channel.allowAllChats)
4278
+ return true;
4279
+ if (!channel.allowedChatIds?.length)
4280
+ return false;
4281
+ return Boolean(chatId && channel.allowedChatIds.includes(chatId));
4282
+ }
4283
+ async function sendTelegramMessage(token, chatId, text) {
4284
+ const response = await fetch(telegramMethodUrl(token, "sendMessage"), {
4285
+ method: "POST",
4286
+ headers: { "content-type": "application/json" },
4287
+ body: JSON.stringify({ chat_id: chatId, text })
4288
+ });
4289
+ const body = await response.json().catch(() => {
4290
+ return;
4291
+ });
4292
+ if (!response.ok)
4293
+ throw new Error(`Telegram sendMessage failed (${response.status}): ${JSON.stringify(body)}`);
4294
+ return body;
4295
+ }
4296
+
4297
+ // src/lib/daemon.ts
4298
+ function isNotFound(err) {
4299
+ return Boolean(err && typeof err === "object" && "code" in err && err.code === "ENOENT");
4300
+ }
4301
+ function currentPlatformSupervisor() {
4302
+ if (process.platform === "darwin")
4303
+ return "launchd";
4304
+ if (process.platform === "linux")
4305
+ return "systemd";
4306
+ return "process";
4307
+ }
4308
+ function resolveSupervisor(supervisor = "process") {
4309
+ return supervisor === "auto" ? currentPlatformSupervisor() : supervisor;
4310
+ }
4311
+ function defaultDaemonDir() {
4312
+ return join3(bridgeHome(), "daemon");
4313
+ }
4314
+ function daemonPaths(daemonDir = defaultDaemonDir()) {
4315
+ const dir = resolve(daemonDir);
4316
+ return {
4317
+ dir,
4318
+ lockDir: join3(dir, "lock"),
4319
+ metadataFile: join3(dir, "bridge-daemon.json"),
4320
+ stdoutLog: join3(dir, "bridge.out.log"),
4321
+ stderrLog: join3(dir, "bridge.err.log"),
4322
+ launchdPlist: join3(process.env["HOME"] || process.cwd(), "Library", "LaunchAgents", "com.hasna.bridge.plist"),
4323
+ systemdUnit: join3(process.env["HOME"] || process.cwd(), ".config", "systemd", "user", "hasna-bridge.service")
4324
+ };
4325
+ }
4326
+ async function fileExists(path) {
4327
+ try {
4328
+ await stat(path);
4329
+ return true;
4330
+ } catch (err) {
4331
+ if (isNotFound(err))
4332
+ return false;
4333
+ throw err;
4334
+ }
4335
+ }
4336
+ async function readMetadata(paths) {
4337
+ try {
4338
+ return JSON.parse(await readFile2(paths.metadataFile, "utf-8"));
4339
+ } catch (err) {
4340
+ if (isNotFound(err))
4341
+ return;
4342
+ throw err;
4343
+ }
4344
+ }
4345
+ function pidAlive(pid) {
4346
+ try {
4347
+ process.kill(pid, 0);
4348
+ return true;
4349
+ } catch {
4350
+ return false;
4351
+ }
4352
+ }
4353
+ async function processCommand(pid) {
4354
+ const proc = Bun.spawn(["ps", "-p", String(pid), "-o", "command="], {
4355
+ stdout: "pipe",
4356
+ stderr: "ignore"
4357
+ });
4358
+ if (await proc.exited !== 0)
4359
+ return;
4360
+ return (await new Response(proc.stdout).text()).trim();
4361
+ }
4362
+ async function processPgid(pid) {
4363
+ const proc = Bun.spawn(["ps", "-p", String(pid), "-o", "pgid="], {
4364
+ stdout: "pipe",
4365
+ stderr: "ignore"
4366
+ });
4367
+ if (await proc.exited !== 0)
4368
+ return;
4369
+ const parsed = Number.parseInt((await new Response(proc.stdout).text()).trim(), 10);
4370
+ return Number.isInteger(parsed) ? parsed : undefined;
4371
+ }
4372
+ async function processMatches(metadata) {
4373
+ if (!pidAlive(metadata.pid))
4374
+ return false;
4375
+ const command = await processCommand(metadata.pid);
4376
+ if (!command)
4377
+ return false;
4378
+ if (!metadata.pgid)
4379
+ return false;
4380
+ const pgid = await processPgid(metadata.pid);
4381
+ if (pgid !== metadata.pgid)
4382
+ return false;
4383
+ const requiredArgs = [
4384
+ metadata.command[1],
4385
+ "serve",
4386
+ "--config",
4387
+ metadata.configPath,
4388
+ "--state",
4389
+ metadata.statePath,
4390
+ "--interval",
4391
+ String(metadata.intervalMs)
4392
+ ].filter((arg) => Boolean(arg));
4393
+ if (metadata.serveJson)
4394
+ requiredArgs.push("--json");
4395
+ return requiredArgs.every((arg) => command.includes(arg));
4396
+ }
4397
+ function safeTelegramApiBaseInfo() {
4398
+ try {
4399
+ return telegramApiBaseInfo();
4400
+ } catch (err) {
4401
+ return {
4402
+ overridden: true,
4403
+ origin: "",
4404
+ pathname: "",
4405
+ error: err instanceof Error ? err.message : String(err)
4406
+ };
4407
+ }
4408
+ }
4409
+ async function runCapture(command) {
4410
+ const proc = Bun.spawn(command, { stdout: "pipe", stderr: "pipe" });
4411
+ const [exitCode, stdout, stderr] = await Promise.all([
4412
+ proc.exited,
4413
+ new Response(proc.stdout).text(),
4414
+ new Response(proc.stderr).text()
4415
+ ]);
4416
+ return { exitCode, stdout, stderr };
4417
+ }
4418
+ async function installedSupervisorStatus(supervisor, paths) {
4419
+ if (supervisor === "launchd") {
4420
+ if (!await fileExists(paths.launchdPlist))
4421
+ return { running: false, detail: "launchd plist not installed" };
4422
+ const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
4423
+ if (uid === undefined)
4424
+ return { running: false, detail: "launchd status requires a numeric uid" };
4425
+ const result = await runCapture(["launchctl", "print", `gui/${uid}/com.hasna.bridge`]);
4426
+ if (result.exitCode !== 0)
4427
+ return { running: false, detail: result.stderr.trim() || result.stdout.trim() || "launchd service not loaded" };
4428
+ const running = /state\s*=\s*running/.test(result.stdout);
4429
+ return { running, detail: running ? "launchd running" : "launchd loaded but not running" };
4430
+ }
4431
+ if (supervisor === "systemd") {
4432
+ if (!await fileExists(paths.systemdUnit))
4433
+ return { running: false, detail: "systemd unit not installed" };
4434
+ const result = await runCapture(["systemctl", "--user", "is-active", "hasna-bridge.service"]);
4435
+ const state = result.stdout.trim() || result.stderr.trim() || "unknown";
4436
+ return { running: result.exitCode === 0 && state === "active", detail: `systemd ${state}` };
4437
+ }
4438
+ return { running: false, detail: "process supervisor has no installed status" };
4439
+ }
4440
+ async function daemonStatus(options = {}) {
4441
+ const supervisor = resolveSupervisor(options.supervisor);
4442
+ const paths = daemonPaths(options.daemonDir);
4443
+ const metadata = await readMetadata(paths);
4444
+ const live = metadata ? await processMatches(metadata) : false;
4445
+ const stale = Boolean(metadata && !live);
4446
+ const startedAt = metadata?.startedAt;
4447
+ const uptimeSeconds = live && startedAt ? Math.max(0, Math.floor((Date.now() - Date.parse(startedAt)) / 1000)) : undefined;
4448
+ const installed = {
4449
+ launchd: await fileExists(paths.launchdPlist),
4450
+ systemd: await fileExists(paths.systemdUnit)
4451
+ };
4452
+ const installedRuntime = supervisor === "process" ? undefined : await installedSupervisorStatus(supervisor, paths);
4453
+ return {
4454
+ running: installedRuntime ? installedRuntime.running : live,
4455
+ stale: installedRuntime ? false : stale,
4456
+ supervisor,
4457
+ pid: metadata?.pid,
4458
+ startedAt,
4459
+ uptimeSeconds,
4460
+ detail: installedRuntime?.detail || (stale ? "stale process metadata" : live ? "running" : "not running"),
4461
+ installedDetail: installedRuntime?.detail,
4462
+ metadata,
4463
+ paths,
4464
+ installed,
4465
+ telegramApiBase: safeTelegramApiBaseInfo()
4466
+ };
4467
+ }
4202
4468
  // src/lib/doctor.ts
4203
- import { access } from "fs/promises";
4469
+ import { stat as stat2 } from "fs/promises";
4470
+ function isNotFound2(err) {
4471
+ return Boolean(err && typeof err === "object" && "code" in err && err.code === "ENOENT");
4472
+ }
4473
+ async function privateFileCheck(name, path) {
4474
+ try {
4475
+ const info = await stat2(path);
4476
+ const mode = info.mode & 511;
4477
+ const ok = (mode & 63) === 0;
4478
+ return { name, ok, detail: `${path} mode=${mode.toString(8)}` };
4479
+ } catch (err) {
4480
+ if (isNotFound2(err))
4481
+ return { name, ok: true, detail: `not created yet: ${path}` };
4482
+ return { name, ok: false, detail: `${path}: ${err instanceof Error ? err.message : String(err)}` };
4483
+ }
4484
+ }
4485
+ async function privateDirCheck(name, path) {
4486
+ try {
4487
+ const info = await stat2(path);
4488
+ const mode = info.mode & 511;
4489
+ const ok = (mode & 63) === 0;
4490
+ return { name, ok, detail: `${path} mode=${mode.toString(8)}` };
4491
+ } catch (err) {
4492
+ if (isNotFound2(err))
4493
+ return { name, ok: true, detail: `not created yet: ${path}` };
4494
+ return { name, ok: false, detail: `${path}: ${err instanceof Error ? err.message : String(err)}` };
4495
+ }
4496
+ }
4204
4497
  async function commandExists(command) {
4205
4498
  const proc = Bun.spawn(["sh", "-lc", `command -v ${command} >/dev/null 2>&1`], {
4206
4499
  stdout: "ignore",
@@ -4208,14 +4501,33 @@ async function commandExists(command) {
4208
4501
  });
4209
4502
  return await proc.exited === 0;
4210
4503
  }
4211
- async function doctor(configPath = defaultConfigPath()) {
4504
+ async function doctor(configPath = defaultConfigPath(), statePath = defaultStatePath()) {
4212
4505
  const checks = [];
4213
- let config = await loadConfig(configPath);
4506
+ const config = await loadConfig(configPath);
4507
+ const daemon = await daemonStatus();
4508
+ const paths = daemonPaths();
4509
+ checks.push(await privateFileCheck("config", configPath));
4510
+ checks.push(await privateFileCheck("state", statePath));
4511
+ checks.push(await privateDirCheck("daemon-dir", paths.dir));
4512
+ checks.push(await privateFileCheck("daemon-metadata", paths.metadataFile));
4513
+ checks.push({
4514
+ name: "daemon-status",
4515
+ ok: !daemon.stale,
4516
+ detail: daemon.running ? `running pid=${daemon.pid}` : daemon.stale ? `stale pid=${daemon.pid}` : "not running"
4517
+ });
4214
4518
  try {
4215
- await access(configPath);
4216
- checks.push({ name: "config", ok: true, detail: configPath });
4217
- } catch {
4218
- checks.push({ name: "config", ok: true, detail: `not created yet: ${configPath}` });
4519
+ const apiBase = telegramApiBaseInfo();
4520
+ checks.push({
4521
+ name: "telegram-api-base",
4522
+ ok: true,
4523
+ detail: apiBase.overridden ? `overridden: ${apiBase.origin}${apiBase.pathname}` : apiBase.origin
4524
+ });
4525
+ } catch (err) {
4526
+ checks.push({
4527
+ name: "telegram-api-base",
4528
+ ok: false,
4529
+ detail: err instanceof Error ? err.message : String(err)
4530
+ });
4219
4531
  }
4220
4532
  for (const command of ["bridge", "codewith", "claude", "aicopilot"]) {
4221
4533
  checks.push({
@@ -4247,38 +4559,11 @@ async function doctor(configPath = defaultConfigPath()) {
4247
4559
  }
4248
4560
  return { ok: checks.every((check) => check.ok), configPath, checks };
4249
4561
  }
4250
- // src/lib/telegram.ts
4251
- function telegramToken(channel) {
4252
- const envName = channel.botTokenEnv || "TELEGRAM_BOT_TOKEN";
4253
- const token = process.env[envName];
4254
- if (!token)
4255
- throw new Error(`Missing Telegram bot token env var: ${envName}`);
4256
- return token;
4257
- }
4258
- function telegramChatAllowed(channel, chatId) {
4259
- if (channel.allowAllChats)
4260
- return true;
4261
- if (!channel.allowedChatIds?.length)
4262
- return false;
4263
- return Boolean(chatId && channel.allowedChatIds.includes(chatId));
4264
- }
4265
- async function sendTelegramMessage(token, chatId, text) {
4266
- const response = await fetch(`https://api.telegram.org/bot${token}/sendMessage`, {
4267
- method: "POST",
4268
- headers: { "content-type": "application/json" },
4269
- body: JSON.stringify({ chat_id: chatId, text })
4270
- });
4271
- const body = await response.json().catch(() => {
4272
- return;
4273
- });
4274
- if (!response.ok)
4275
- throw new Error(`Telegram sendMessage failed (${response.status}): ${JSON.stringify(body)}`);
4276
- return body;
4277
- }
4278
-
4279
4562
  // src/lib/router.ts
4280
4563
  function matchingRoutes(config, message) {
4281
4564
  const channel = config.channels[message.channelId];
4565
+ if (!channel || channel.enabled === false)
4566
+ return [];
4282
4567
  if (channel?.kind === "telegram" && !telegramChatAllowed(channel, message.chatId)) {
4283
4568
  return [];
4284
4569
  }
@@ -4306,6 +4591,10 @@ async function routeMessage(config, message, options = {}) {
4306
4591
  let deliveredResponse = false;
4307
4592
  const channel = responseChannel(config, route, message);
4308
4593
  const responseText = agent.stdout.trim();
4594
+ if (channel?.enabled === false) {
4595
+ results.push({ route, agent, deliveredResponse });
4596
+ continue;
4597
+ }
4309
4598
  if (responseText && channel?.kind === "telegram" && message.chatId) {
4310
4599
  if (!telegramChatAllowed(channel, message.chatId)) {
4311
4600
  results.push({ route, agent, deliveredResponse });
@@ -4327,9 +4616,9 @@ function text(value) {
4327
4616
  return { content: [{ type: "text", text: typeof value === "string" ? value : JSON.stringify(value, null, 2) }] };
4328
4617
  }
4329
4618
  function buildServer() {
4330
- const server = new McpServer({ name: "bridge", version: "0.1.0" });
4619
+ const server = new McpServer({ name: "bridge", version: "0.1.1" });
4331
4620
  server.tool("bridge_status", {}, async () => text(await doctor()));
4332
- server.tool("bridge_config", {}, async () => text(await loadConfig()));
4621
+ server.tool("bridge_config", {}, async () => text(redactConfig(await loadConfig())));
4333
4622
  server.tool("bridge_route_message", {
4334
4623
  channelId: exports_external.string(),
4335
4624
  text: exports_external.string(),
@@ -43,5 +43,40 @@ Telegram channels fail closed unless `allowedChatIds` are configured or
43
43
  before route matching. Routes can also add narrower `match.chatIds` filters, but
44
44
  they cannot expand beyond the channel allowlist.
45
45
 
46
+ Disabled channels do not match inbound routes and do not deliver responses.
47
+ MCP config inspection redacts profile and agent environment values so local
48
+ secrets are not exposed through `bridge_config`.
49
+
46
50
  Long-poll offsets are persisted in a private state file so process restarts do
47
51
  not replay already-seen updates and re-run agents.
52
+
53
+ `BRIDGE_TELEGRAM_API_BASE` can override the Telegram API origin for local tests.
54
+ The override accepts only `http` or `https` URLs without credentials. It is not
55
+ intended for normal production use because bot tokens are part of Telegram API
56
+ request paths.
57
+
58
+ ## Daemon Model
59
+
60
+ The foreground runtime remains `bridge serve`. Daemon commands are lifecycle
61
+ wrappers around that same runtime:
62
+
63
+ - `bridge daemon start` uses a local process supervisor by default.
64
+ - `bridge daemon status` reads private metadata and verifies the recorded
65
+ process still looks like `bridge serve`.
66
+ - `bridge daemon stop` terminates the process group and removes stale metadata.
67
+ - `bridge daemon logs` reads private stdout/stderr logs.
68
+ - `bridge daemon install` writes user `launchd` or user `systemd` files.
69
+
70
+ The process supervisor is the quickest local testing path because it inherits
71
+ the shell environment, including Telegram token env vars. Installed launchd and
72
+ systemd services do not store token values by default; operators must make token
73
+ env vars available through their service manager.
74
+
75
+ Daemon files live under `~/.hasna/bridge/daemon` by default. The directory is
76
+ `0700`; metadata and log files are `0600`. Logs are considered sensitive because
77
+ they can contain prompts, Telegram text, agent stdout/stderr, and routing
78
+ errors.
79
+
80
+ `bridge serve` handles per-channel poll errors without exiting in long-running
81
+ mode. `serve --once` still fails fast so health checks and tests can catch
82
+ misconfiguration.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/bridge",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Agent messaging bridge for Telegram and other channels",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -29,7 +29,7 @@
29
29
  "dev:cli": "bun run src/cli/index.ts",
30
30
  "dev:mcp": "bun run src/mcp/index.ts",
31
31
  "prepublishOnly": "bun run build",
32
- "postinstall": "bun -e \"const fs=require('node:fs');const path=require('node:path');const dir=path.join(process.env.HOME||process.cwd(),'.hasna','bridge');fs.mkdirSync(dir,{recursive:true,mode:0o700});try{fs.chmodSync(dir,0o700)}catch{}\""
32
+ "postinstall": "node -e \"const fs=require('node:fs');const path=require('node:path');const dir=path.join(process.env.HOME||process.cwd(),'.hasna','bridge');fs.mkdirSync(dir,{recursive:true,mode:0o700});try{fs.chmodSync(dir,0o700)}catch{}\""
33
33
  },
34
34
  "keywords": [
35
35
  "bridge",