@0xmaxma/claude-gateway 1.2.31 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +23 -4
  2. package/config.template.json +5 -5
  3. package/dist/api/packages.d.ts +2 -0
  4. package/dist/api/packages.d.ts.map +1 -1
  5. package/dist/api/packages.js +125 -27
  6. package/dist/api/packages.js.map +1 -1
  7. package/dist/api/router.d.ts.map +1 -1
  8. package/dist/api/router.js +2 -4
  9. package/dist/api/router.js.map +1 -1
  10. package/dist/apps/agent-manager.d.ts.map +1 -1
  11. package/dist/apps/agent-manager.js +0 -1
  12. package/dist/apps/agent-manager.js.map +1 -1
  13. package/dist/config/watcher.d.ts.map +1 -1
  14. package/dist/config/watcher.js +21 -4
  15. package/dist/config/watcher.js.map +1 -1
  16. package/dist/index.js +8 -3
  17. package/dist/index.js.map +1 -1
  18. package/dist/session/process.d.ts.map +1 -1
  19. package/dist/session/process.js +32 -5
  20. package/dist/session/process.js.map +1 -1
  21. package/dist/shell/args.d.ts +22 -0
  22. package/dist/shell/args.d.ts.map +1 -0
  23. package/dist/shell/args.js +105 -0
  24. package/dist/shell/args.js.map +1 -0
  25. package/dist/shell/claude-pty-shell.d.ts +3 -0
  26. package/dist/shell/claude-pty-shell.d.ts.map +1 -0
  27. package/dist/shell/claude-pty-shell.js +407 -0
  28. package/dist/shell/claude-pty-shell.js.map +1 -0
  29. package/dist/shell/emitter.d.ts +36 -0
  30. package/dist/shell/emitter.d.ts.map +1 -0
  31. package/dist/shell/emitter.js +85 -0
  32. package/dist/shell/emitter.js.map +1 -0
  33. package/dist/shell/pty-host.d.ts +21 -0
  34. package/dist/shell/pty-host.d.ts.map +1 -0
  35. package/dist/shell/pty-host.js +102 -0
  36. package/dist/shell/pty-host.js.map +1 -0
  37. package/dist/shell/screen.d.ts +44 -0
  38. package/dist/shell/screen.d.ts.map +1 -0
  39. package/dist/shell/screen.js +89 -0
  40. package/dist/shell/screen.js.map +1 -0
  41. package/dist/shell/tailer.d.ts +64 -0
  42. package/dist/shell/tailer.d.ts.map +1 -0
  43. package/dist/shell/tailer.js +181 -0
  44. package/dist/shell/tailer.js.map +1 -0
  45. package/dist/types.d.ts +7 -1
  46. package/dist/types.d.ts.map +1 -1
  47. package/mcp/tools/telegram/dedup.ts +46 -0
  48. package/mcp/tools/telegram/receiver-server.ts +25 -0
  49. package/package.json +3 -1
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.PtyHost = void 0;
37
+ const path = __importStar(require("path"));
38
+ const pty = __importStar(require("node-pty"));
39
+ const WRITE_CHUNK_BYTES = 8 * 1024;
40
+ const WRITE_CHUNK_DELAY_MS = 10;
41
+ // Keys matching these prefixes are scrubbed before spawning the child PTY:
42
+ // CLAUDECODE/CLAUDE_CODE_ prevent nested-claude child-session behavior that
43
+ // silently stops transcript JSONL writes; CLAUDE_REAL_BIN is a wrapper-only
44
+ // variable and must not leak into the child.
45
+ const SCRUB_ENV_PREFIXES = ['CLAUDECODE', 'CLAUDE_CODE_', 'CLAUDE_REAL_BIN', 'PTY_SHELL_'];
46
+ // Auth vars that share the CLAUDE_CODE_ prefix but must be kept.
47
+ const SCRUB_ENV_KEEPLIST = new Set(['CLAUDE_CODE_OAUTH_TOKEN']);
48
+ function childEnv() {
49
+ const env = { ...process.env };
50
+ for (const key of Object.keys(env)) {
51
+ if (!SCRUB_ENV_KEEPLIST.has(key) &&
52
+ SCRUB_ENV_PREFIXES.some((p) => key.startsWith(p))) {
53
+ delete env[key];
54
+ }
55
+ }
56
+ return env;
57
+ }
58
+ /** Hosts the real interactive `claude` inside a pseudo-terminal. */
59
+ class PtyHost {
60
+ constructor(binary, args, opts) {
61
+ // Recursion guard: CLAUDE_BIN points at this wrapper; the wrapper must
62
+ // never resolve the "real" binary back to itself.
63
+ if (path.basename(binary).includes('claude-pty-shell')) {
64
+ throw new Error(`refusing to spawn self as claude binary: ${binary}`);
65
+ }
66
+ this.child = pty.spawn(binary, args, {
67
+ name: 'xterm-256color',
68
+ cols: opts.cols,
69
+ rows: opts.rows,
70
+ cwd: opts.cwd,
71
+ env: childEnv(),
72
+ });
73
+ this.child.onData(opts.onData);
74
+ this.child.onExit(({ exitCode }) => opts.onExit(exitCode));
75
+ }
76
+ /** Raw keystroke write (control sequences allowed — caller sanitizes user text). */
77
+ writeRaw(data) {
78
+ this.child.write(data);
79
+ }
80
+ /**
81
+ * Paste large text without overwhelming the PTY line discipline:
82
+ * chunked writes with small delays.
83
+ */
84
+ async writeChunked(data) {
85
+ for (let i = 0; i < data.length; i += WRITE_CHUNK_BYTES) {
86
+ this.child.write(data.slice(i, i + WRITE_CHUNK_BYTES));
87
+ if (i + WRITE_CHUNK_BYTES < data.length) {
88
+ await new Promise((r) => setTimeout(r, WRITE_CHUNK_DELAY_MS));
89
+ }
90
+ }
91
+ }
92
+ kill(signal) {
93
+ try {
94
+ this.child.kill(signal);
95
+ }
96
+ catch {
97
+ /* already dead */
98
+ }
99
+ }
100
+ }
101
+ exports.PtyHost = PtyHost;
102
+ //# sourceMappingURL=pty-host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pty-host.js","sourceRoot":"","sources":["../../src/shell/pty-host.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ export type DialogKind = 'bypass-permissions' | 'trust-folder' | 'unknown-select';
2
+ /**
3
+ * TUI string constants — verified against Claude Code v2.1.x.
4
+ * When upgrading Claude Code, re-check each constant against the new TUI output.
5
+ * All matchers live here so a UI change requires touching exactly one file.
6
+ *
7
+ * BUSY_MARKER status bar text during an active turn
8
+ * PROMPT_RE idle input caret pattern
9
+ * BYPASS_PERMS "Bypass Permissions" dialog markers
10
+ * TRUST_FOLDER workspace trust dialog marker
11
+ * NUMBERED_SELECT_RE generic numbered select — combined with CONFIRM_MARKER
12
+ */
13
+ export declare const TUI_BUSY_MARKER = "esc to interrupt";
14
+ export declare const TUI_PROMPT_RE: RegExp;
15
+ export declare const TUI_BYPASS_PERMS: readonly ["Bypass Permissions mode", "Yes, I accept"];
16
+ export declare const TUI_TRUST_FOLDER = "Do you trust the files in this folder";
17
+ export declare const TUI_NUMBERED_SELECT_RE: RegExp;
18
+ export declare const TUI_CONFIRM_MARKER = "Enter to confirm";
19
+ /**
20
+ * Virtual terminal fed with raw PTY bytes. Used ONLY for liveness signals
21
+ * (busy / idle / dialog detection) — assistant text is never parsed from
22
+ * the screen; the transcript JSONL is the text source of truth.
23
+ */
24
+ export declare class ScreenModel {
25
+ readonly cols: number;
26
+ readonly rows: number;
27
+ private term;
28
+ private lastDataTs;
29
+ /** Set when a busy marker is seen in a raw chunk; survives fast busy→idle flips between polls. */
30
+ private busySeenInRaw;
31
+ constructor(cols?: number, rows?: number);
32
+ write(data: string): void;
33
+ /** Milliseconds since the PTY last produced output. */
34
+ quietMs(): number;
35
+ text(): string;
36
+ /** Claude is processing a turn (spinner area shows "esc to interrupt"). */
37
+ isBusy(): boolean;
38
+ /** Consume the raw-chunk busy flag (catches turns faster than the poll interval). */
39
+ consumeBusySeen(): boolean;
40
+ /** Idle input prompt is on screen. */
41
+ hasPrompt(): boolean;
42
+ detectDialog(): DialogKind | null;
43
+ }
44
+ //# sourceMappingURL=screen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../../src/shell/screen.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,UAAU,GAAG,oBAAoB,GAAG,cAAc,GAAG,gBAAgB,CAAC;AAOlF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,qBAAqB,CAAC;AAClD,eAAO,MAAM,aAAa,QAAS,CAAC;AACpC,eAAO,MAAM,gBAAgB,uDAAwD,CAAC;AACtF,eAAO,MAAM,gBAAgB,0CAA0C,CAAC;AACxE,eAAO,MAAM,sBAAsB,QAAU,CAAC;AAC9C,eAAO,MAAM,kBAAkB,qBAAqB,CAAC;AAErD;;;;GAIG;AACH,qBAAa,WAAW;aAMM,IAAI;aAAwB,IAAI;IAL5D,OAAO,CAAC,IAAI,CAAW;IACvB,OAAO,CAAC,UAAU,CAAc;IAChC,kGAAkG;IAClG,OAAO,CAAC,aAAa,CAAS;gBAEF,IAAI,SAAM,EAAkB,IAAI,SAAK;IAIjE,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAMzB,uDAAuD;IACvD,OAAO,IAAI,MAAM;IAIjB,IAAI,IAAI,MAAM;IAUd,2EAA2E;IAC3E,MAAM,IAAI,OAAO;IAIjB,qFAAqF;IACrF,eAAe,IAAI,OAAO;IAM1B,sCAAsC;IACtC,SAAS,IAAI,OAAO;IAIpB,YAAY,IAAI,UAAU,GAAG,IAAI;CAclC"}
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ScreenModel = exports.TUI_CONFIRM_MARKER = exports.TUI_NUMBERED_SELECT_RE = exports.TUI_TRUST_FOLDER = exports.TUI_BYPASS_PERMS = exports.TUI_PROMPT_RE = exports.TUI_BUSY_MARKER = void 0;
4
+ const headless_1 = require("@xterm/headless");
5
+ /** The TUI renders spaces as U+00A0 (non-breaking) — normalize before matching. */
6
+ function normalize(text) {
7
+ return text.replace(/ /g, ' ');
8
+ }
9
+ /**
10
+ * TUI string constants — verified against Claude Code v2.1.x.
11
+ * When upgrading Claude Code, re-check each constant against the new TUI output.
12
+ * All matchers live here so a UI change requires touching exactly one file.
13
+ *
14
+ * BUSY_MARKER status bar text during an active turn
15
+ * PROMPT_RE idle input caret pattern
16
+ * BYPASS_PERMS "Bypass Permissions" dialog markers
17
+ * TRUST_FOLDER workspace trust dialog marker
18
+ * NUMBERED_SELECT_RE generic numbered select — combined with CONFIRM_MARKER
19
+ */
20
+ exports.TUI_BUSY_MARKER = 'esc to interrupt';
21
+ exports.TUI_PROMPT_RE = /^❯ /m;
22
+ exports.TUI_BYPASS_PERMS = ['Bypass Permissions mode', 'Yes, I accept'];
23
+ exports.TUI_TRUST_FOLDER = 'Do you trust the files in this folder';
24
+ exports.TUI_NUMBERED_SELECT_RE = /❯ 1\./;
25
+ exports.TUI_CONFIRM_MARKER = 'Enter to confirm';
26
+ /**
27
+ * Virtual terminal fed with raw PTY bytes. Used ONLY for liveness signals
28
+ * (busy / idle / dialog detection) — assistant text is never parsed from
29
+ * the screen; the transcript JSONL is the text source of truth.
30
+ */
31
+ class ScreenModel {
32
+ constructor(cols = 200, rows = 50) {
33
+ this.cols = cols;
34
+ this.rows = rows;
35
+ this.lastDataTs = Date.now();
36
+ /** Set when a busy marker is seen in a raw chunk; survives fast busy→idle flips between polls. */
37
+ this.busySeenInRaw = false;
38
+ this.term = new headless_1.Terminal({ cols, rows, allowProposedApi: true });
39
+ }
40
+ write(data) {
41
+ this.lastDataTs = Date.now();
42
+ if (normalize(data).includes(exports.TUI_BUSY_MARKER))
43
+ this.busySeenInRaw = true;
44
+ this.term.write(data);
45
+ }
46
+ /** Milliseconds since the PTY last produced output. */
47
+ quietMs() {
48
+ return Date.now() - this.lastDataTs;
49
+ }
50
+ text() {
51
+ const buf = this.term.buffer.active;
52
+ const lines = [];
53
+ for (let i = 0; i < this.term.rows; i++) {
54
+ const line = buf.getLine(buf.viewportY + i);
55
+ lines.push(line ? line.translateToString(true) : '');
56
+ }
57
+ return normalize(lines.join('\n'));
58
+ }
59
+ /** Claude is processing a turn (spinner area shows "esc to interrupt"). */
60
+ isBusy() {
61
+ return this.text().includes(exports.TUI_BUSY_MARKER);
62
+ }
63
+ /** Consume the raw-chunk busy flag (catches turns faster than the poll interval). */
64
+ consumeBusySeen() {
65
+ const seen = this.busySeenInRaw;
66
+ this.busySeenInRaw = false;
67
+ return seen;
68
+ }
69
+ /** Idle input prompt is on screen. */
70
+ hasPrompt() {
71
+ return exports.TUI_PROMPT_RE.test(this.text());
72
+ }
73
+ detectDialog() {
74
+ const text = this.text();
75
+ if (exports.TUI_BYPASS_PERMS.every((s) => text.includes(s))) {
76
+ return 'bypass-permissions';
77
+ }
78
+ if (text.includes(exports.TUI_TRUST_FOLDER)) {
79
+ return 'trust-folder';
80
+ }
81
+ // Generic numbered select dialog while no turn output is flowing.
82
+ if (!text.includes(exports.TUI_BUSY_MARKER) && exports.TUI_NUMBERED_SELECT_RE.test(text) && text.includes(exports.TUI_CONFIRM_MARKER)) {
83
+ return 'unknown-select';
84
+ }
85
+ return null;
86
+ }
87
+ }
88
+ exports.ScreenModel = ScreenModel;
89
+ //# sourceMappingURL=screen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screen.js","sourceRoot":"","sources":["../../src/shell/screen.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ export interface UsageInfo {
2
+ input_tokens?: number;
3
+ cache_read_input_tokens?: number;
4
+ cache_creation_input_tokens?: number;
5
+ output_tokens?: number;
6
+ }
7
+ export interface AssistantRecord {
8
+ type: 'assistant';
9
+ isSidechain?: boolean;
10
+ message: {
11
+ role: 'assistant';
12
+ content: Array<{
13
+ type: string;
14
+ [k: string]: unknown;
15
+ }>;
16
+ stop_reason?: string | null;
17
+ usage?: UsageInfo;
18
+ };
19
+ uuid?: string;
20
+ timestamp?: string;
21
+ }
22
+ export interface TailerEvents {
23
+ /** A new (non-sidechain) assistant record was appended. */
24
+ onAssistant: (record: AssistantRecord) => void;
25
+ /** Claude finished a turn (system/turn_duration record). */
26
+ onTurnEnd: (durationMs: number) => void;
27
+ onError: (err: Error) => void;
28
+ }
29
+ /**
30
+ * cwd → Claude Code project-dir slug (verified against v2.1.x: `/` and `.` both become `-`).
31
+ * If Claude Code ever changes this scheme, findFile()'s fallback UUID scan will still
32
+ * locate the transcript — the primary path is just an optimistic fast path.
33
+ */
34
+ export declare function projectSlug(cwd: string): string;
35
+ export declare function transcriptPath(cwd: string, sessionId: string): string;
36
+ /**
37
+ * Incrementally reads the session transcript JSONL that interactive Claude
38
+ * Code appends to *during* a turn. This is the text source of truth and the
39
+ * streaming source: records are surfaced the moment they hit the file.
40
+ */
41
+ export declare class TranscriptTailer {
42
+ private readonly cwd;
43
+ private readonly sessionId;
44
+ private readonly events;
45
+ private readonly pollMs;
46
+ private offset;
47
+ private partialLine;
48
+ private timer;
49
+ private resolvedPath;
50
+ /** Total records dispatched since start — non-zero means claude is writing output. */
51
+ seenRecords: number;
52
+ /** Timestamp of last fallback scan — caps expensive readdirSync to once per 2s. */
53
+ private lastFallbackScanMs;
54
+ private static readonly FALLBACK_SCAN_INTERVAL_MS;
55
+ constructor(cwd: string, sessionId: string, events: TailerEvents, pollMs?: number);
56
+ start(): void;
57
+ stop(): void;
58
+ /** Force one synchronous read (used right before emitting a result). */
59
+ flush(): void;
60
+ private findFile;
61
+ private poll;
62
+ private dispatch;
63
+ }
64
+ //# sourceMappingURL=tailer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tailer.d.ts","sourceRoot":"","sources":["../../src/shell/tailer.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,SAAS;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,WAAW,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE;QACP,IAAI,EAAE,WAAW,CAAC;QAClB,OAAO,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;SAAE,CAAC,CAAC;QACvD,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,KAAK,CAAC,EAAE,SAAS,CAAC;KACnB,CAAC;IACF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,2DAA2D;IAC3D,WAAW,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IAC/C,4DAA4D;IAC5D,SAAS,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,OAAO,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAErE;AAED;;;;GAIG;AACH,qBAAa,gBAAgB;IAYzB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAdzB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,YAAY,CAAuB;IAC3C,sFAAsF;IACtF,WAAW,SAAK;IAChB,mFAAmF;IACnF,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,yBAAyB,CAAS;gBAGvC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EACpB,MAAM,SAAM;IAG/B,KAAK,IAAI,IAAI;IAIb,IAAI,IAAI,IAAI;IAKZ,wEAAwE;IACxE,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,QAAQ;IA0BhB,OAAO,CAAC,IAAI;IA2CZ,OAAO,CAAC,QAAQ;CAejB"}
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.TranscriptTailer = void 0;
37
+ exports.projectSlug = projectSlug;
38
+ exports.transcriptPath = transcriptPath;
39
+ const fs = __importStar(require("fs"));
40
+ const os = __importStar(require("os"));
41
+ const path = __importStar(require("path"));
42
+ /**
43
+ * cwd → Claude Code project-dir slug (verified against v2.1.x: `/` and `.` both become `-`).
44
+ * If Claude Code ever changes this scheme, findFile()'s fallback UUID scan will still
45
+ * locate the transcript — the primary path is just an optimistic fast path.
46
+ */
47
+ function projectSlug(cwd) {
48
+ return cwd.replace(/[/.]/g, '-');
49
+ }
50
+ function transcriptPath(cwd, sessionId) {
51
+ return path.join(os.homedir(), '.claude', 'projects', projectSlug(cwd), `${sessionId}.jsonl`);
52
+ }
53
+ /**
54
+ * Incrementally reads the session transcript JSONL that interactive Claude
55
+ * Code appends to *during* a turn. This is the text source of truth and the
56
+ * streaming source: records are surfaced the moment they hit the file.
57
+ */
58
+ class TranscriptTailer {
59
+ constructor(cwd, sessionId, events, pollMs = 150) {
60
+ this.cwd = cwd;
61
+ this.sessionId = sessionId;
62
+ this.events = events;
63
+ this.pollMs = pollMs;
64
+ this.offset = 0;
65
+ this.partialLine = '';
66
+ this.timer = null;
67
+ this.resolvedPath = null;
68
+ /** Total records dispatched since start — non-zero means claude is writing output. */
69
+ this.seenRecords = 0;
70
+ /** Timestamp of last fallback scan — caps expensive readdirSync to once per 2s. */
71
+ this.lastFallbackScanMs = 0;
72
+ }
73
+ start() {
74
+ this.timer = setInterval(() => this.poll(), this.pollMs);
75
+ }
76
+ stop() {
77
+ if (this.timer)
78
+ clearInterval(this.timer);
79
+ this.timer = null;
80
+ }
81
+ /** Force one synchronous read (used right before emitting a result). */
82
+ flush() {
83
+ this.poll();
84
+ }
85
+ findFile() {
86
+ if (this.resolvedPath)
87
+ return this.resolvedPath;
88
+ const expected = transcriptPath(this.cwd, this.sessionId);
89
+ if (fs.existsSync(expected)) {
90
+ this.resolvedPath = expected;
91
+ return expected;
92
+ }
93
+ // Fallback if the slug scheme ever changes: scan project dirs for the uuid.
94
+ // Capped to once per 2s — readdirSync over hundreds of project dirs every 150ms poll
95
+ // is expensive before the transcript file has been created.
96
+ const now = Date.now();
97
+ if (now - this.lastFallbackScanMs < TranscriptTailer.FALLBACK_SCAN_INTERVAL_MS)
98
+ return null;
99
+ this.lastFallbackScanMs = now;
100
+ const projectsRoot = path.join(os.homedir(), '.claude', 'projects');
101
+ let dirs = [];
102
+ try {
103
+ dirs = fs.readdirSync(projectsRoot);
104
+ }
105
+ catch {
106
+ return null;
107
+ }
108
+ for (const dir of dirs) {
109
+ const candidate = path.join(projectsRoot, dir, `${this.sessionId}.jsonl`);
110
+ if (fs.existsSync(candidate)) {
111
+ this.resolvedPath = candidate;
112
+ return candidate;
113
+ }
114
+ }
115
+ return null;
116
+ }
117
+ poll() {
118
+ const file = this.findFile();
119
+ if (!file)
120
+ return;
121
+ let size;
122
+ try {
123
+ size = fs.statSync(file).size;
124
+ }
125
+ catch {
126
+ return; // transient: file rotated/removed
127
+ }
128
+ if (size <= this.offset)
129
+ return;
130
+ let chunk;
131
+ try {
132
+ const fd = fs.openSync(file, 'r');
133
+ const buf = Buffer.alloc(size - this.offset);
134
+ fs.readSync(fd, buf, 0, buf.length, this.offset);
135
+ fs.closeSync(fd);
136
+ chunk = buf.toString('utf8');
137
+ }
138
+ catch (err) {
139
+ this.events.onError(err instanceof Error ? err : new Error(String(err)));
140
+ return;
141
+ }
142
+ this.offset = size;
143
+ const data = this.partialLine + chunk;
144
+ const lines = data.split('\n');
145
+ this.partialLine = lines.pop() ?? '';
146
+ for (const line of lines) {
147
+ if (!line.trim())
148
+ continue;
149
+ let record;
150
+ try {
151
+ record = JSON.parse(line);
152
+ }
153
+ catch {
154
+ // Mid-write torn line should be impossible (we split on \n), so a bad
155
+ // line is real corruption — surface it, never swallow.
156
+ this.events.onError(new Error(`unparseable transcript line (${line.length} bytes)`));
157
+ continue;
158
+ }
159
+ this.dispatch(record);
160
+ }
161
+ }
162
+ dispatch(record) {
163
+ if (record.isSidechain === true)
164
+ return; // subagent-internal records
165
+ this.seenRecords++;
166
+ if (record.type === 'assistant') {
167
+ const message = record.message;
168
+ if (message && Array.isArray(message.content)) {
169
+ this.events.onAssistant(record);
170
+ }
171
+ return;
172
+ }
173
+ if (record.type === 'system' && record.subtype === 'turn_duration') {
174
+ const durationMs = typeof record.durationMs === 'number' ? record.durationMs : 0;
175
+ this.events.onTurnEnd(durationMs);
176
+ }
177
+ }
178
+ }
179
+ exports.TranscriptTailer = TranscriptTailer;
180
+ TranscriptTailer.FALLBACK_SCAN_INTERVAL_MS = 2000;
181
+ //# sourceMappingURL=tailer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tailer.js","sourceRoot":"","sources":["../../src/shell/tailer.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -29,7 +29,8 @@ export interface AgentConfig {
29
29
  };
30
30
  claude: {
31
31
  model: string;
32
- dangerouslySkipPermissions: boolean;
32
+ /** @deprecated --dangerously-skip-permissions is always passed now; this field is ignored. */
33
+ dangerouslySkipPermissions?: boolean;
33
34
  extraFlags: string[];
34
35
  };
35
36
  /** Heartbeat / cron settings */
@@ -81,6 +82,11 @@ export interface GatewayConfig {
81
82
  api?: {
82
83
  keys: ApiKey[];
83
84
  };
85
+ /**
86
+ * true (default) = headless backend (claude --print + stream-json).
87
+ * false = interactive backend: claude TUI under the claude-pty-shell PTY wrapper.
88
+ */
89
+ headless?: boolean;
84
90
  /** Global history retention/cleanup defaults */
85
91
  history?: HistoryConfig & {
86
92
  cleanupHour?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import { mkdirSync, openSync, closeSync, readdirSync, statSync, rmSync } from 'fs'
2
+ import { join } from 'path'
3
+
4
+ /** Marker files older than this are pruned on next cleanup pass. */
5
+ export const DEDUP_TTL_MS = 60_000 // 60 s — covers receiver restart overlap, avoids stale drops
6
+
7
+ /**
8
+ * Ensure the dedup directory exists. Call once at startup rather than on
9
+ * every message so hot paths don't pay the mkdirSync cost.
10
+ */
11
+ export function initDedupDir(dedupDir: string): void {
12
+ mkdirSync(dedupDir, { recursive: true })
13
+ }
14
+
15
+ /**
16
+ * Atomic check-and-mark using O_EXCL (create-or-fail).
17
+ * Returns true if this (chatId, msgId) was already claimed by any receiver instance.
18
+ * Safe across multiple bun processes sharing the same filesystem.
19
+ */
20
+ export function isDuplicate(dedupDir: string, chatId: string, msgId: number): boolean {
21
+ const file = join(dedupDir, `${chatId}-${msgId}`)
22
+ try {
23
+ const fd = openSync(file, 'wx') // O_CREAT | O_EXCL — fails with EEXIST if file exists
24
+ closeSync(fd)
25
+ return false
26
+ } catch (err) {
27
+ if ((err as NodeJS.ErrnoException).code === 'EEXIST') return true
28
+ return false // unexpected I/O error — let it through rather than dropping messages
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Remove marker files older than ttlMs. Safe to call concurrently from multiple
34
+ * processes — rmSync({ force: true }) is idempotent.
35
+ */
36
+ export function pruneDedup(dedupDir: string, ttlMs = DEDUP_TTL_MS): void {
37
+ try {
38
+ const now = Date.now()
39
+ for (const f of readdirSync(dedupDir)) {
40
+ try {
41
+ const file = join(dedupDir, f)
42
+ if (now - statSync(file).mtimeMs > ttlMs) rmSync(file, { force: true })
43
+ } catch {}
44
+ }
45
+ } catch {}
46
+ }
@@ -29,6 +29,7 @@ import { readFileSync, writeFileSync, mkdirSync, readdirSync, rmSync, statSync,
29
29
  import { homedir } from 'os'
30
30
  import { join, extname, sep } from 'path'
31
31
  import { createWorkingStateManager } from './typing'
32
+ import { initDedupDir, isDuplicate as _isDuplicate, pruneDedup as _pruneDedup } from './dedup'
32
33
  import { hasMarkdown, toTelegramHtml } from './pure'
33
34
 
34
35
  // Standalone fallback: default state dir to ~/.claude/channels/telegram
@@ -60,6 +61,24 @@ if (!TOKEN) {
60
61
  }
61
62
 
62
63
  const INBOX_DIR = join(STATE_DIR, 'inbox')
64
+ const DEDUP_DIR = join(STATE_DIR, 'dedup')
65
+
66
+ // Initialize dedup dir once at startup (not on every message).
67
+ initDedupDir(DEDUP_DIR)
68
+ // Prune stale markers left over from before this process started.
69
+ _pruneDedup(DEDUP_DIR)
70
+
71
+ function isDuplicate(chatId: string, msgId: number): boolean {
72
+ return _isDuplicate(DEDUP_DIR, chatId, msgId)
73
+ }
74
+
75
+ // Spread 11 concurrent receivers across the minute window with random jitter
76
+ // so they don't all hit the dedup dir simultaneously on each tick.
77
+ const pruneJitter = Math.floor(Math.random() * 60_000)
78
+ setTimeout(() => {
79
+ _pruneDedup(DEDUP_DIR)
80
+ setInterval(() => _pruneDedup(DEDUP_DIR), 60_000).unref()
81
+ }, pruneJitter).unref()
63
82
 
64
83
  // Last-resort safety net — without these the process dies silently on any
65
84
  // unhandled promise rejection. With them it logs and keeps serving tools.
@@ -731,6 +750,12 @@ async function handleInbound(
731
750
  downloadImage: (() => Promise<string | undefined>) | undefined,
732
751
  attachment?: AttachmentMeta,
733
752
  ): Promise<void> {
753
+ // Dedup BEFORE gate: prevent duplicate processing for all message types, including
754
+ // pairing flows (avoids double pairing-reply messages during receiver restart overlap).
755
+ const earlyChat = ctx.chat?.id
756
+ const earlyMsgId = ctx.message?.message_id
757
+ if (earlyChat != null && earlyMsgId != null && isDuplicate(String(earlyChat), earlyMsgId)) return
758
+
734
759
  const result = gate(ctx)
735
760
 
736
761
  if (result.action === 'drop') return