ccpoke 1.5.4 → 1.6.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.
Files changed (95) hide show
  1. package/README.en.md +1 -1
  2. package/README.md +1 -1
  3. package/dist/agent/agent-handler.d.ts +6 -2
  4. package/dist/agent/agent-handler.js +17 -3
  5. package/dist/agent/agent-handler.js.map +1 -1
  6. package/dist/agent/agent-registry.js.map +1 -1
  7. package/dist/agent/chat-session-resolver.d.ts +4 -0
  8. package/dist/agent/chat-session-resolver.js +2 -0
  9. package/dist/agent/chat-session-resolver.js.map +1 -0
  10. package/dist/agent/claude-code/claude-code-installer.d.ts +8 -2
  11. package/dist/agent/claude-code/claude-code-installer.js +112 -28
  12. package/dist/agent/claude-code/claude-code-installer.js.map +1 -1
  13. package/dist/agent/claude-code/claude-code-parser.js.map +1 -1
  14. package/dist/agent/claude-code/claude-code-provider.d.ts +5 -1
  15. package/dist/agent/claude-code/claude-code-provider.js +22 -9
  16. package/dist/agent/claude-code/claude-code-provider.js.map +1 -1
  17. package/dist/agent/cursor/cursor-installer.js +3 -3
  18. package/dist/agent/cursor/cursor-installer.js.map +1 -1
  19. package/dist/agent/cursor/cursor-parser.js +1 -1
  20. package/dist/agent/cursor/cursor-parser.js.map +1 -1
  21. package/dist/agent/cursor/cursor-provider.d.ts +5 -1
  22. package/dist/agent/cursor/cursor-provider.js +12 -9
  23. package/dist/agent/cursor/cursor-provider.js.map +1 -1
  24. package/dist/agent/cursor/cursor-state-reader.js +3 -3
  25. package/dist/agent/cursor/cursor-state-reader.js.map +1 -1
  26. package/dist/agent/types.d.ts +7 -0
  27. package/dist/channel/chat-types.d.ts +19 -0
  28. package/dist/channel/chat-types.js +2 -0
  29. package/dist/channel/chat-types.js.map +1 -0
  30. package/dist/channel/telegram/pending-reply-store.d.ts +15 -0
  31. package/dist/channel/telegram/pending-reply-store.js +54 -0
  32. package/dist/channel/telegram/pending-reply-store.js.map +1 -0
  33. package/dist/channel/telegram/telegram-channel.d.ts +8 -2
  34. package/dist/channel/telegram/telegram-channel.js +81 -6
  35. package/dist/channel/telegram/telegram-channel.js.map +1 -1
  36. package/dist/channel/telegram/telegram-sender.d.ts +1 -1
  37. package/dist/channel/telegram/telegram-sender.js +20 -11
  38. package/dist/channel/telegram/telegram-sender.js.map +1 -1
  39. package/dist/channel/types.d.ts +1 -0
  40. package/dist/commands/help.js +1 -1
  41. package/dist/commands/help.js.map +1 -1
  42. package/dist/commands/setup.js +4 -3
  43. package/dist/commands/setup.js.map +1 -1
  44. package/dist/commands/uninstall.js +3 -3
  45. package/dist/commands/uninstall.js.map +1 -1
  46. package/dist/commands/update.js +4 -4
  47. package/dist/commands/update.js.map +1 -1
  48. package/dist/config-manager.js +16 -7
  49. package/dist/config-manager.js.map +1 -1
  50. package/dist/i18n/index.js.map +1 -1
  51. package/dist/i18n/locales/en.js +26 -0
  52. package/dist/i18n/locales/en.js.map +1 -1
  53. package/dist/i18n/locales/vi.js +26 -0
  54. package/dist/i18n/locales/vi.js.map +1 -1
  55. package/dist/i18n/locales/zh.js +26 -0
  56. package/dist/i18n/locales/zh.js.map +1 -1
  57. package/dist/i18n/types.d.ts +26 -0
  58. package/dist/index.d.ts +0 -1
  59. package/dist/index.js +71 -13
  60. package/dist/index.js.map +1 -1
  61. package/dist/server/api-server.d.ts +3 -0
  62. package/dist/server/api-server.js +32 -5
  63. package/dist/server/api-server.js.map +1 -1
  64. package/dist/tmux/session-map.d.ts +38 -0
  65. package/dist/tmux/session-map.js +137 -0
  66. package/dist/tmux/session-map.js.map +1 -0
  67. package/dist/tmux/session-state.d.ts +22 -0
  68. package/dist/tmux/session-state.js +46 -0
  69. package/dist/tmux/session-state.js.map +1 -0
  70. package/dist/tmux/tmux-bridge.d.ts +9 -0
  71. package/dist/tmux/tmux-bridge.js +93 -0
  72. package/dist/tmux/tmux-bridge.js.map +1 -0
  73. package/dist/tmux/tmux-scanner.d.ts +22 -0
  74. package/dist/tmux/tmux-scanner.js +133 -0
  75. package/dist/tmux/tmux-scanner.js.map +1 -0
  76. package/dist/tmux/tmux-session-resolver.d.ts +14 -0
  77. package/dist/tmux/tmux-session-resolver.js +61 -0
  78. package/dist/tmux/tmux-session-resolver.js.map +1 -0
  79. package/dist/utils/constants.d.ts +1 -11
  80. package/dist/utils/constants.js +1 -11
  81. package/dist/utils/constants.js.map +1 -1
  82. package/dist/utils/git-collector.js +2 -1
  83. package/dist/utils/git-collector.js.map +1 -1
  84. package/dist/utils/install-detection.js +3 -2
  85. package/dist/utils/install-detection.js.map +1 -1
  86. package/dist/utils/paths.d.ts +1 -0
  87. package/dist/utils/paths.js +3 -2
  88. package/dist/utils/paths.js.map +1 -1
  89. package/dist/utils/response-store.js +5 -3
  90. package/dist/utils/response-store.js.map +1 -1
  91. package/dist/utils/tunnel.js +1 -1
  92. package/dist/utils/tunnel.js.map +1 -1
  93. package/dist/utils/version-check.js +5 -3
  94. package/dist/utils/version-check.js.map +1 -1
  95. package/package.json +2 -1
@@ -0,0 +1,38 @@
1
+ import type { TmuxBridge } from "./tmux-bridge.js";
2
+ export declare const SessionState: {
3
+ readonly Idle: "idle";
4
+ readonly Busy: "busy";
5
+ readonly Unknown: "unknown";
6
+ };
7
+ export type SessionState = (typeof SessionState)[keyof typeof SessionState];
8
+ export interface TmuxSession {
9
+ sessionId: string;
10
+ tmuxTarget: string;
11
+ project: string;
12
+ cwd: string;
13
+ label: string;
14
+ state: SessionState;
15
+ lastActivity: Date;
16
+ }
17
+ export interface ScanResult {
18
+ discovered: TmuxSession[];
19
+ removed: TmuxSession[];
20
+ total: number;
21
+ }
22
+ export declare class SessionMap {
23
+ private sessions;
24
+ private scanInterval;
25
+ register(sessionId: string, tmuxTarget: string, project: string, cwd?: string, label?: string): void;
26
+ unregister(sessionId: string): void;
27
+ getBySessionId(sessionId: string): TmuxSession | undefined;
28
+ getByProject(project: string): TmuxSession[];
29
+ getAllActive(): TmuxSession[];
30
+ updateState(sessionId: string, state: SessionState): void;
31
+ updateLabel(sessionId: string, label: string): void;
32
+ touch(sessionId: string): void;
33
+ save(): void;
34
+ load(): void;
35
+ refreshFromTmux(tmuxBridge: TmuxBridge): ScanResult;
36
+ startPeriodicScan(tmuxBridge: TmuxBridge, intervalMs: number, onResult?: (result: ScanResult) => void): void;
37
+ stopPeriodicScan(): void;
38
+ }
@@ -0,0 +1,137 @@
1
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
2
+ import { basename } from "node:path";
3
+ import { paths } from "../utils/paths.js";
4
+ import { isClaudeAliveInPane, scanClaudePanes } from "./tmux-scanner.js";
5
+ export const SessionState = {
6
+ Idle: "idle",
7
+ Busy: "busy",
8
+ Unknown: "unknown",
9
+ };
10
+ const SESSIONS_FILE = "sessions.json";
11
+ const MAX_SESSIONS = 200;
12
+ export class SessionMap {
13
+ sessions = new Map();
14
+ scanInterval = null;
15
+ register(sessionId, tmuxTarget, project, cwd = "", label = "") {
16
+ if (this.sessions.size >= MAX_SESSIONS && !this.sessions.has(sessionId)) {
17
+ const oldest = [...this.sessions.entries()].sort((a, b) => a[1].lastActivity.getTime() - b[1].lastActivity.getTime())[0];
18
+ if (oldest)
19
+ this.sessions.delete(oldest[0]);
20
+ }
21
+ this.sessions.set(sessionId, {
22
+ sessionId,
23
+ tmuxTarget,
24
+ project,
25
+ cwd,
26
+ label,
27
+ state: SessionState.Idle,
28
+ lastActivity: new Date(),
29
+ });
30
+ }
31
+ unregister(sessionId) {
32
+ this.sessions.delete(sessionId);
33
+ }
34
+ getBySessionId(sessionId) {
35
+ return this.sessions.get(sessionId);
36
+ }
37
+ getByProject(project) {
38
+ return [...this.sessions.values()].filter((s) => s.project === project);
39
+ }
40
+ getAllActive() {
41
+ return [...this.sessions.values()];
42
+ }
43
+ updateState(sessionId, state) {
44
+ const session = this.sessions.get(sessionId);
45
+ if (session) {
46
+ session.state = state;
47
+ session.lastActivity = new Date();
48
+ }
49
+ }
50
+ updateLabel(sessionId, label) {
51
+ const session = this.sessions.get(sessionId);
52
+ if (session)
53
+ session.label = label;
54
+ }
55
+ touch(sessionId) {
56
+ const session = this.sessions.get(sessionId);
57
+ if (session)
58
+ session.lastActivity = new Date();
59
+ }
60
+ save() {
61
+ const data = [...this.sessions.values()].map((s) => ({
62
+ ...s,
63
+ lastActivity: s.lastActivity.toISOString(),
64
+ }));
65
+ mkdirSync(paths.ccpokeDir, { recursive: true });
66
+ const tmpPath = `${paths.ccpokeDir}/${SESSIONS_FILE}.tmp`;
67
+ const finalPath = `${paths.ccpokeDir}/${SESSIONS_FILE}`;
68
+ writeFileSync(tmpPath, JSON.stringify({ sessions: data }, null, 2));
69
+ renameSync(tmpPath, finalPath);
70
+ }
71
+ load() {
72
+ try {
73
+ const raw = readFileSync(`${paths.ccpokeDir}/${SESSIONS_FILE}`, "utf-8");
74
+ const parsed = JSON.parse(raw);
75
+ for (const s of parsed.sessions) {
76
+ if (!s.sessionId || !s.tmuxTarget || !s.project)
77
+ continue;
78
+ const date = new Date(s.lastActivity);
79
+ if (isNaN(date.getTime()))
80
+ continue;
81
+ this.sessions.set(s.sessionId, {
82
+ ...s,
83
+ lastActivity: date,
84
+ });
85
+ }
86
+ }
87
+ catch {
88
+ // no persisted sessions
89
+ }
90
+ }
91
+ refreshFromTmux(tmuxBridge) {
92
+ const { panes, tree } = scanClaudePanes();
93
+ const discovered = [];
94
+ const removed = [];
95
+ const knownTargets = new Set([...this.sessions.values()].map((s) => s.tmuxTarget));
96
+ for (const pane of panes) {
97
+ if (knownTargets.has(pane.target))
98
+ continue;
99
+ const syntheticId = `tmux-${pane.target.replace(/[:.]/g, "-")}`;
100
+ const project = basename(pane.cwd) || "unknown";
101
+ const state = tmuxBridge.isClaudeIdle(pane.target) ? SessionState.Idle : SessionState.Unknown;
102
+ this.register(syntheticId, pane.target, project, pane.cwd);
103
+ this.updateState(syntheticId, state);
104
+ discovered.push(this.sessions.get(syntheticId));
105
+ }
106
+ // Remove dead sessions (reuse process tree from scan)
107
+ for (const [id, session] of this.sessions) {
108
+ if (!isClaudeAliveInPane(session.tmuxTarget, tree)) {
109
+ removed.push(session);
110
+ this.sessions.delete(id);
111
+ }
112
+ }
113
+ return { discovered, removed, total: this.sessions.size };
114
+ }
115
+ startPeriodicScan(tmuxBridge, intervalMs, onResult) {
116
+ this.stopPeriodicScan();
117
+ this.scanInterval = setInterval(() => {
118
+ try {
119
+ const result = this.refreshFromTmux(tmuxBridge);
120
+ if (result.discovered.length > 0 || result.removed.length > 0) {
121
+ this.save();
122
+ }
123
+ onResult?.(result);
124
+ }
125
+ catch {
126
+ // scan failure non-fatal, retry next interval
127
+ }
128
+ }, intervalMs);
129
+ }
130
+ stopPeriodicScan() {
131
+ if (this.scanInterval) {
132
+ clearInterval(this.scanInterval);
133
+ this.scanInterval = null;
134
+ }
135
+ }
136
+ }
137
+ //# sourceMappingURL=session-map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-map.js","sourceRoot":"","sources":["../../src/tmux/session-map.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { type SessionMap } from "./session-map.js";
2
+ import type { TmuxBridge } from "./tmux-bridge.js";
3
+ export type InjectResult = {
4
+ sent: true;
5
+ } | {
6
+ empty: true;
7
+ } | {
8
+ busy: true;
9
+ } | {
10
+ desktopActive: true;
11
+ } | {
12
+ sessionNotFound: true;
13
+ } | {
14
+ tmuxDead: true;
15
+ };
16
+ export declare class SessionStateManager {
17
+ private sessionMap;
18
+ private tmuxBridge;
19
+ constructor(sessionMap: SessionMap, tmuxBridge: TmuxBridge);
20
+ injectMessage(sessionId: string, text: string): InjectResult;
21
+ onStopHook(sessionId: string): void;
22
+ }
@@ -0,0 +1,46 @@
1
+ import { SessionState } from "./session-map.js";
2
+ import { isPaneAlive } from "./tmux-scanner.js";
3
+ const MAX_MESSAGE_LENGTH = 10_000;
4
+ export class SessionStateManager {
5
+ sessionMap;
6
+ tmuxBridge;
7
+ constructor(sessionMap, tmuxBridge) {
8
+ this.sessionMap = sessionMap;
9
+ this.tmuxBridge = tmuxBridge;
10
+ }
11
+ injectMessage(sessionId, text) {
12
+ const session = this.sessionMap.getBySessionId(sessionId);
13
+ if (!session)
14
+ return { sessionNotFound: true };
15
+ if (!isPaneAlive(session.tmuxTarget)) {
16
+ this.sessionMap.unregister(sessionId);
17
+ return { tmuxDead: true };
18
+ }
19
+ const trimmed = text.trim();
20
+ if (trimmed.length === 0)
21
+ return { empty: true };
22
+ const safeText = trimmed.length > MAX_MESSAGE_LENGTH ? trimmed.slice(0, MAX_MESSAGE_LENGTH) : trimmed;
23
+ if (this.tmuxBridge.hasUncommittedInput(session.tmuxTarget)) {
24
+ return { desktopActive: true };
25
+ }
26
+ // Always check actual pane state instead of trusting cached session.state
27
+ if (!this.tmuxBridge.isClaudeIdle(session.tmuxTarget)) {
28
+ return { busy: true };
29
+ }
30
+ try {
31
+ this.tmuxBridge.sendKeys(session.tmuxTarget, safeText);
32
+ this.sessionMap.updateState(sessionId, SessionState.Busy);
33
+ this.sessionMap.touch(sessionId);
34
+ return { sent: true };
35
+ }
36
+ catch {
37
+ this.sessionMap.unregister(sessionId);
38
+ return { tmuxDead: true };
39
+ }
40
+ }
41
+ onStopHook(sessionId) {
42
+ this.sessionMap.updateState(sessionId, SessionState.Idle);
43
+ this.sessionMap.touch(sessionId);
44
+ }
45
+ }
46
+ //# sourceMappingURL=session-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-state.js","sourceRoot":"","sources":["../../src/tmux/session-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAmB,MAAM,kBAAkB,CAAC;AAEjE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAUhD,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC,MAAM,OAAO,mBAAmB;IAEpB;IACA;IAFV,YACU,UAAsB,EACtB,UAAsB;QADtB,eAAU,GAAV,UAAU,CAAY;QACtB,eAAU,GAAV,UAAU,CAAY;IAC7B,CAAC;IAEJ,aAAa,CAAC,SAAiB,EAAE,IAAY;QAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;QAC1D,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;QAE/C,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YACrC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;YACtC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC5B,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QAEjD,MAAM,QAAQ,GACZ,OAAO,CAAC,MAAM,GAAG,kBAAkB,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QAEvF,IAAI,IAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5D,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;QACjC,CAAC;QAED,0EAA0E;QAC1E,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YACtD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACxB,CAAC;QAED,IAAI,CAAC;YACH,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YACvD,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,SAAS,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;YAC1D,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YACjC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;YACtC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,UAAU,CAAC,SAAiB;QAC1B,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,SAAS,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;QAC1D,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACnC,CAAC;CACF"}
@@ -0,0 +1,9 @@
1
+ export declare class TmuxBridge {
2
+ private available;
3
+ isTmuxAvailable(): boolean;
4
+ sendKeys(target: string, text: string): void;
5
+ capturePane(target: string, lineCount?: number): string;
6
+ isClaudeIdle(target: string): boolean;
7
+ hasUncommittedInput(target: string): boolean;
8
+ }
9
+ export declare function escapeShellArg(arg: string): string;
@@ -0,0 +1,93 @@
1
+ import { execSync } from "node:child_process";
2
+ const CLAUDE_PROMPT_PATTERNS = [/[❯>]\s*$/, /\$\s*$/];
3
+ const IDLE_SCAN_LINES = 5;
4
+ export class TmuxBridge {
5
+ available = null;
6
+ isTmuxAvailable() {
7
+ if (this.available !== null)
8
+ return this.available;
9
+ try {
10
+ execSync("tmux -V", { stdio: "pipe" });
11
+ this.available = true;
12
+ }
13
+ catch {
14
+ this.available = false;
15
+ }
16
+ return this.available;
17
+ }
18
+ sendKeys(target, text) {
19
+ const tgt = escapeShellArg(target);
20
+ const lines = text.split("\n");
21
+ for (let i = 0; i < lines.length; i++) {
22
+ const line = lines[i];
23
+ if (line.length === 0 && i < lines.length - 1)
24
+ continue;
25
+ const escaped = escapeTmuxText(line);
26
+ execSync(`tmux send-keys -t ${tgt} -l ${escaped}`, {
27
+ stdio: "pipe",
28
+ timeout: 5000,
29
+ });
30
+ execSync(`tmux send-keys -t ${tgt} Enter`, {
31
+ stdio: "pipe",
32
+ timeout: 5000,
33
+ });
34
+ }
35
+ }
36
+ capturePane(target, lineCount = 50) {
37
+ return execSync(`tmux capture-pane -t ${escapeShellArg(target)} -p -S -${lineCount}`, {
38
+ encoding: "utf-8",
39
+ stdio: "pipe",
40
+ timeout: 5000,
41
+ }).trimEnd();
42
+ }
43
+ isClaudeIdle(target) {
44
+ try {
45
+ const content = this.capturePane(target, IDLE_SCAN_LINES);
46
+ const lines = content.split("\n");
47
+ for (let i = lines.length - 1; i >= 0; i--) {
48
+ const line = lines[i].trimEnd();
49
+ if (line.length === 0)
50
+ continue;
51
+ if (CLAUDE_PROMPT_PATTERNS.some((pattern) => pattern.test(line)))
52
+ return true;
53
+ return false;
54
+ }
55
+ return false;
56
+ }
57
+ catch {
58
+ return false;
59
+ }
60
+ }
61
+ hasUncommittedInput(target) {
62
+ try {
63
+ const content = this.capturePane(target, 10);
64
+ const lines = content.split("\n");
65
+ for (let i = lines.length - 1; i >= 0; i--) {
66
+ const line = lines[i].trimEnd();
67
+ const promptMatch = line.match(/[❯>]\s*/);
68
+ if (promptMatch && promptMatch.index !== undefined) {
69
+ const afterPrompt = line.slice(promptMatch.index + promptMatch[0].length);
70
+ return afterPrompt.trim().length > 0;
71
+ }
72
+ }
73
+ return false;
74
+ }
75
+ catch {
76
+ return false;
77
+ }
78
+ }
79
+ }
80
+ function escapeTmuxText(text) {
81
+ const escaped = text
82
+ .replace(/\r/g, "")
83
+ .replace(/\\/g, "\\\\")
84
+ .replace(/"/g, '\\"')
85
+ .replace(/\$/g, "\\$")
86
+ .replace(/`/g, "\\`")
87
+ .replace(/;/g, "\\;");
88
+ return `"${escaped}"`;
89
+ }
90
+ export function escapeShellArg(arg) {
91
+ return `'${arg.replace(/'/g, "'\\''")}'`;
92
+ }
93
+ //# sourceMappingURL=tmux-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmux-bridge.js","sourceRoot":"","sources":["../../src/tmux/tmux-bridge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export interface TmuxPaneInfo {
2
+ target: string;
3
+ paneTitle: string;
4
+ cwd: string;
5
+ panePid: string;
6
+ }
7
+ interface ProcessEntry {
8
+ pid: string;
9
+ ppid: string;
10
+ command: string;
11
+ }
12
+ export type ProcessTree = Map<string, ProcessEntry[]>;
13
+ export declare function buildProcessTree(): ProcessTree;
14
+ export declare function hasClaudeDescendant(panePid: string, tree?: ProcessTree): boolean;
15
+ export interface ScanOutput {
16
+ panes: TmuxPaneInfo[];
17
+ tree: ProcessTree;
18
+ }
19
+ export declare function scanClaudePanes(): ScanOutput;
20
+ export declare function isClaudeAliveInPane(target: string, tree?: ProcessTree): boolean;
21
+ export declare function isPaneAlive(target: string): boolean;
22
+ export {};
@@ -0,0 +1,133 @@
1
+ import { execSync } from "node:child_process";
2
+ import { escapeShellArg } from "./tmux-bridge.js";
3
+ const CLAUDE_TITLE_PATTERN = /claude code/i;
4
+ const FORMAT_STRING = "#{session_name}:#{window_index}.#{pane_index}|#{pane_title}|#{pane_current_path}|#{pane_pid}";
5
+ const MAX_DESCENDANT_DEPTH = 4;
6
+ export function buildProcessTree() {
7
+ try {
8
+ const output = execSync("ps -e -o pid=,ppid=,command=", {
9
+ encoding: "utf-8",
10
+ stdio: "pipe",
11
+ timeout: 3000,
12
+ });
13
+ const tree = new Map();
14
+ for (const line of output.trim().split("\n")) {
15
+ const match = line.trim().match(/^(\d+)\s+(\d+)\s+(.+)$/);
16
+ if (!match)
17
+ continue;
18
+ const entry = { pid: match[1], ppid: match[2], command: match[3] };
19
+ const siblings = tree.get(entry.ppid) ?? [];
20
+ siblings.push(entry);
21
+ tree.set(entry.ppid, siblings);
22
+ }
23
+ return tree;
24
+ }
25
+ catch {
26
+ return new Map();
27
+ }
28
+ }
29
+ export function hasClaudeDescendant(panePid, tree) {
30
+ const processTree = tree ?? buildProcessTree();
31
+ function search(pid, depth) {
32
+ if (depth >= MAX_DESCENDANT_DEPTH)
33
+ return false;
34
+ const children = processTree.get(pid);
35
+ if (!children)
36
+ return false;
37
+ for (const child of children) {
38
+ if (/\bclaude\b/i.test(child.command))
39
+ return true;
40
+ if (search(child.pid, depth + 1))
41
+ return true;
42
+ }
43
+ return false;
44
+ }
45
+ return search(panePid, 0);
46
+ }
47
+ function isClaudePane(pane, tree) {
48
+ if (CLAUDE_TITLE_PATTERN.test(pane.paneTitle))
49
+ return true;
50
+ return hasClaudeDescendant(pane.panePid, tree);
51
+ }
52
+ export function scanClaudePanes() {
53
+ try {
54
+ const output = execSync(`tmux list-panes -a -F '${FORMAT_STRING}'`, {
55
+ encoding: "utf-8",
56
+ stdio: "pipe",
57
+ timeout: 5000,
58
+ });
59
+ const tree = buildProcessTree();
60
+ const panes = output
61
+ .trim()
62
+ .split("\n")
63
+ .filter((line) => line.length > 0)
64
+ .map((line) => {
65
+ const parts = line.split("|");
66
+ if (parts.length < 4)
67
+ return null;
68
+ const target = parts[0];
69
+ const panePid = parts[parts.length - 1];
70
+ const cwd = parts[parts.length - 2];
71
+ const paneTitle = parts.slice(1, parts.length - 2).join("|");
72
+ return { target, paneTitle, cwd, panePid };
73
+ })
74
+ .filter((pane) => pane !== null && isClaudePane(pane, tree));
75
+ return { panes, tree };
76
+ }
77
+ catch {
78
+ return { panes: [], tree: new Map() };
79
+ }
80
+ }
81
+ export function isClaudeAliveInPane(target, tree) {
82
+ const sessionName = target.split(":")[0];
83
+ if (!sessionName)
84
+ return false;
85
+ try {
86
+ execSync(`tmux has-session -t ${escapeShellArg(sessionName)}`, {
87
+ stdio: "pipe",
88
+ timeout: 3000,
89
+ });
90
+ }
91
+ catch {
92
+ return false;
93
+ }
94
+ try {
95
+ const title = execSync(`tmux display-message -t ${escapeShellArg(target)} -p '#{pane_title}'`, {
96
+ encoding: "utf-8",
97
+ stdio: "pipe",
98
+ timeout: 3000,
99
+ }).trim();
100
+ if (CLAUDE_TITLE_PATTERN.test(title))
101
+ return true;
102
+ }
103
+ catch {
104
+ // pane may not exist anymore
105
+ }
106
+ try {
107
+ const panePid = execSync(`tmux display-message -t ${escapeShellArg(target)} -p '#{pane_pid}'`, {
108
+ encoding: "utf-8",
109
+ stdio: "pipe",
110
+ timeout: 3000,
111
+ }).trim();
112
+ return hasClaudeDescendant(panePid, tree);
113
+ }
114
+ catch {
115
+ return false;
116
+ }
117
+ }
118
+ export function isPaneAlive(target) {
119
+ const sessionName = target.split(":")[0];
120
+ if (!sessionName)
121
+ return false;
122
+ try {
123
+ execSync(`tmux has-session -t ${escapeShellArg(sessionName)}`, {
124
+ stdio: "pipe",
125
+ timeout: 3000,
126
+ });
127
+ return true;
128
+ }
129
+ catch {
130
+ return false;
131
+ }
132
+ }
133
+ //# sourceMappingURL=tmux-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmux-scanner.js","sourceRoot":"","sources":["../../src/tmux/tmux-scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { ChatSessionResolver } from "../agent/chat-session-resolver.js";
2
+ import type { SessionMap } from "./session-map.js";
3
+ import type { SessionStateManager } from "./session-state.js";
4
+ export declare class TmuxSessionResolver implements ChatSessionResolver {
5
+ private sessionMap;
6
+ private stateManager;
7
+ private agentToTmux;
8
+ constructor(sessionMap: SessionMap, stateManager: SessionStateManager);
9
+ resolveSessionId(agentSessionId: string, projectName: string, cwd?: string, tmuxTarget?: string): string | undefined;
10
+ onStopHook(sessionId: string): void;
11
+ private findByTmuxTarget;
12
+ private cacheAgent;
13
+ private findByProject;
14
+ }
@@ -0,0 +1,61 @@
1
+ import { log } from "../utils/log.js";
2
+ const MAX_AGENT_CACHE = 100;
3
+ export class TmuxSessionResolver {
4
+ sessionMap;
5
+ stateManager;
6
+ agentToTmux = new Map();
7
+ constructor(sessionMap, stateManager) {
8
+ this.sessionMap = sessionMap;
9
+ this.stateManager = stateManager;
10
+ }
11
+ resolveSessionId(agentSessionId, projectName, cwd, tmuxTarget) {
12
+ if (tmuxTarget) {
13
+ const exactMatch = this.findByTmuxTarget(tmuxTarget);
14
+ if (exactMatch) {
15
+ if (agentSessionId)
16
+ this.cacheAgent(agentSessionId, exactMatch);
17
+ return exactMatch;
18
+ }
19
+ }
20
+ if (agentSessionId) {
21
+ const cached = this.agentToTmux.get(agentSessionId);
22
+ if (cached && this.sessionMap.getBySessionId(cached))
23
+ return cached;
24
+ this.agentToTmux.delete(agentSessionId);
25
+ }
26
+ const tmuxSessionId = this.findByProject(projectName, cwd);
27
+ if (tmuxSessionId && agentSessionId) {
28
+ this.cacheAgent(agentSessionId, tmuxSessionId);
29
+ }
30
+ return tmuxSessionId;
31
+ }
32
+ onStopHook(sessionId) {
33
+ this.stateManager.onStopHook(sessionId);
34
+ }
35
+ findByTmuxTarget(tmuxTarget) {
36
+ for (const session of this.sessionMap.getAllActive()) {
37
+ if (session.tmuxTarget === tmuxTarget) {
38
+ log(`session linked by tmux_target: ${session.sessionId} (${tmuxTarget})`);
39
+ return session.sessionId;
40
+ }
41
+ }
42
+ return undefined;
43
+ }
44
+ cacheAgent(agentSessionId, tmuxSessionId) {
45
+ this.agentToTmux.set(agentSessionId, tmuxSessionId);
46
+ if (this.agentToTmux.size > MAX_AGENT_CACHE) {
47
+ const oldest = this.agentToTmux.keys().next().value;
48
+ if (oldest)
49
+ this.agentToTmux.delete(oldest);
50
+ }
51
+ }
52
+ findByProject(projectName, cwd) {
53
+ const matches = this.sessionMap.getByProject(projectName);
54
+ if (matches.length === 0)
55
+ return undefined;
56
+ const existing = (cwd && matches.length > 1 ? matches.find((s) => s.cwd === cwd) : undefined) ?? matches[0];
57
+ log(`session linked: ${existing.sessionId} (${projectName})`);
58
+ return existing.sessionId;
59
+ }
60
+ }
61
+ //# sourceMappingURL=tmux-session-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmux-session-resolver.js","sourceRoot":"","sources":["../../src/tmux/tmux-session-resolver.ts"],"names":[],"mappings":"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"}
@@ -29,19 +29,9 @@ export declare const PackageManager: {
29
29
  export type PackageManager = (typeof PackageManager)[keyof typeof PackageManager];
30
30
  export declare const ApiRoute: {
31
31
  readonly HookStop: "/hook/stop";
32
+ readonly HookSessionStart: "/hook/session-start";
32
33
  readonly ResponseData: "/api/responses/:id";
33
34
  readonly Health: "/health";
34
35
  };
35
36
  export declare const DEFAULT_HOOK_PORT = 9377;
36
- export declare const TUNNEL_TIMEOUT_MS = 30000;
37
- export declare const SETUP_WAIT_TIMEOUT_MS = 120000;
38
- export declare const TRANSCRIPT_SETTLE_DELAY_MS = 500;
39
- export declare const MAX_STORED_RESPONSES = 100;
40
- export declare const RESPONSE_EXPIRE_MS: number;
41
- export declare const DEFAULT_FALLBACK_DURATION_MS = 1000;
42
- export declare const SPLIT_LOOKBACK_RANGE = 200;
43
- export declare const MAX_GIT_SEARCH_DEPTH = 5;
44
- export declare const GIT_TIMEOUT_MS = 10000;
45
37
  export declare const MINI_APP_BASE_URL = "https://palooza-kaida.github.io/ccpoke";
46
- export declare const NPM_REGISTRY_URL = "https://registry.npmjs.org/ccpoke/latest";
47
- export declare const VERSION_CHECK_TIMEOUT_MS = 5000;