kumo-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kumo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,130 @@
1
+ # Kumo CLI
2
+
3
+ > _where your code drifts between devices, softly._
4
+
5
+ [![npm version](https://img.shields.io/npm/v/kumo-cli.svg?style=flat-square)](https://www.npmjs.com/package/kumo-cli)
6
+ [![node](https://img.shields.io/node/v/kumo-cli.svg?style=flat-square)](https://nodejs.org)
7
+ [![license](https://img.shields.io/npm/l/kumo-cli.svg?style=flat-square)](./LICENSE)
8
+
9
+ **Kumo CLI** is the desktop companion for the [Kumo app](https://github.com/kumo-app/kumo). It pairs your terminal with the mobile/desktop client over a secure WebSocket bridge so you can drive Claude Code (and any PTY session) from anywhere — without giving up the comfort of your local shell.
10
+
11
+ ---
12
+
13
+ ## ✨ Features
14
+
15
+ - 🔗 **One-tap pairing** — link the CLI to your Kumo account with a 9-digit code from the app.
16
+ - 🖥️ **Real PTY streaming** — spawns Claude Code (or any process) via `node-pty` and mirrors the terminal byte-for-byte.
17
+ - 📡 **Bi-directional bridge** — keystrokes from the app flow back into the local PTY in real time.
18
+ - 🪝 **Session hooks** — auto-injects Claude Code session hooks so your remote views stay in sync.
19
+ - 🧠 **Smart snapshots** — periodic ANSI snapshots keep newly connected viewers up to date instantly.
20
+ - 🌐 **Bonjour discovery** — optional local discovery for zero-config LAN setups.
21
+ - 🔒 **Local credentials** — device tokens are stored under your user config dir, never echoed.
22
+
23
+ ---
24
+
25
+ ## 🚀 Quick start
26
+
27
+ ```bash
28
+ # run on demand (no install)
29
+ npx kumo-cli@latest
30
+
31
+ # or install globally
32
+ npm install -g kumo-cli
33
+ kumo
34
+ ```
35
+
36
+ On first launch you'll see a prompt:
37
+
38
+ ```
39
+ ✦ pairing code › ___
40
+ ```
41
+
42
+ Open the **Kumo app**, sign in, tap **"generate"** on the pairing tab, and type the 9 digits back. That's it — your CLI is paired and a session is live.
43
+
44
+ ---
45
+
46
+ ## 📦 Requirements
47
+
48
+ - **Node.js ≥ 18** (LTS recommended)
49
+ - A working C/C++ toolchain for [`node-pty`](https://github.com/microsoft/node-pty#dependencies):
50
+ - **Windows:** Visual Studio Build Tools + Python 3
51
+ - **macOS:** Xcode Command Line Tools (`xcode-select --install`)
52
+ - **Linux:** `build-essential`, `python3`, `make`, `gcc`
53
+ - [Claude Code CLI](https://docs.anthropic.com/claude/docs/claude-code) installed and on `PATH` (only if you plan to use the Claude session features)
54
+
55
+ ---
56
+
57
+ ## 🛠️ Configuration
58
+
59
+ Kumo CLI is configured via environment variables. They can be exported in your shell or placed in a `.env` file in the working directory.
60
+
61
+ | Variable | Default | Description |
62
+ | --- | --- | --- |
63
+ | `KUMO_SERVER_URL` | `http://localhost:3579` | Base URL of the Kumo backend. Auto-derives HTTP & WS endpoints. |
64
+ | `KUMO_SERVER_HTTP_URL` | derived | Override the HTTP endpoint explicitly. |
65
+ | `KUMO_SERVER_WS_URL` | derived | Override the WebSocket endpoint explicitly. |
66
+ | `KUMO_PORT` | `3579` | Local port hint (used by some discovery flows). |
67
+
68
+ Credentials are persisted to your OS config dir (e.g. `%APPDATA%\kumo\credentials.json` on Windows, `~/.config/kumo/credentials.json` on Linux/macOS).
69
+
70
+ ---
71
+
72
+ ## 🧭 Usage
73
+
74
+ ```bash
75
+ kumo # interactive: pair if needed, then start a session
76
+ kumo --help # show available flags (if applicable)
77
+ ```
78
+
79
+ To unpair, simply delete the credentials file printed on first pairing, or run:
80
+
81
+ ```bash
82
+ node -e "require('node:fs').rmSync(require('node:path').join(require('node:os').homedir(), '.config', 'kumo', 'credentials.json'))"
83
+ ```
84
+
85
+ ---
86
+
87
+ ## 🧱 How it works
88
+
89
+ ```
90
+ ┌─────────────┐ WebSocket ┌──────────────┐ PTY ┌─────────────┐
91
+ │ Kumo app │ ◄────────────► │ Kumo CLI │ ◄───────► │ Claude Code │
92
+ └─────────────┘ └──────────────┘ └─────────────┘
93
+ ▲ │
94
+ │ HTTP (pairing) │
95
+ └────────────────────────────────┘
96
+ Kumo backend
97
+ ```
98
+
99
+ 1. The CLI pairs with the backend using the 9-digit code → receives a long-lived `deviceToken`.
100
+ 2. It opens a WebSocket to the backend and announces itself.
101
+ 3. When the app requests a session, the CLI spawns Claude Code under a real PTY.
102
+ 4. STDIN/STDOUT are streamed both ways; periodic snapshots and Claude session hooks keep state coherent.
103
+
104
+ ---
105
+
106
+ ## 🐞 Troubleshooting
107
+
108
+ - **`node-pty` build fails** — make sure your toolchain (above) is installed, then re-run `npm install -g kumo-cli`.
109
+ - **Pairing code rejected** — codes are single-use and expire quickly. Generate a fresh one in the app.
110
+ - **Cannot connect** — verify `KUMO_SERVER_URL` matches the backend the app is talking to.
111
+ - **Hooks not firing** — make sure Claude Code is on `PATH` and you launched a session through the CLI (the hook settings file is auto-generated under the system temp dir).
112
+
113
+ ---
114
+
115
+ ## 🤝 Contributing
116
+
117
+ Issues and PRs are welcome at [github.com/kumo-app/kumo](https://github.com/kumo-app/kumo).
118
+
119
+ ```bash
120
+ git clone https://github.com/kumo-app/kumo.git
121
+ cd kumo/cli
122
+ npm install
123
+ npm run dev
124
+ ```
125
+
126
+ ---
127
+
128
+ ## 📄 License
129
+
130
+ [MIT](./LICENSE) © Kumo
@@ -0,0 +1,107 @@
1
+ export const ESC = "\x1b";
2
+ export const CSI = `${ESC}[`;
3
+ export const ANSI = {
4
+ RESET: `${CSI}m`,
5
+ CURSOR: {
6
+ HOME: `${CSI}H`,
7
+ HIDE: `${CSI}?25l`,
8
+ SHOW: `${CSI}?25h`,
9
+ MOVE_RIGHT: (n) => `${CSI}${n}C`,
10
+ MOVE_UP: (n) => `${CSI}${n}A`,
11
+ MOVE_DOWN: (n) => `${CSI}${n}B`,
12
+ MOVE_LEFT: (n) => `${CSI}${n}D`,
13
+ SET_POSITION: (row, col) => `${CSI}${row};${col}H`,
14
+ SAVE: `${CSI}s`,
15
+ RESTORE: `${CSI}u`,
16
+ },
17
+ SCREEN: {
18
+ CLEAR: `${CSI}2J`,
19
+ CLEAR_LINE: `${CSI}K`,
20
+ CLEAR_LINE_FROM_CURSOR: `${CSI}0K`,
21
+ CLEAR_LINE_TO_CURSOR: `${CSI}1K`,
22
+ ERASE_CHARS: (n) => `${CSI}${n}X`,
23
+ },
24
+ STYLE: {
25
+ BOLD: `${CSI}1m`,
26
+ BOLD_OFF: `${CSI}22m`,
27
+ DIM: `${CSI}2m`,
28
+ ITALIC: `${CSI}3m`,
29
+ ITALIC_OFF: `${CSI}23m`,
30
+ UNDERLINE: `${CSI}4m`,
31
+ UNDERLINE_OFF: `${CSI}24m`,
32
+ INVERSE: `${CSI}7m`,
33
+ INVERSE_OFF: `${CSI}27m`,
34
+ },
35
+ COLOR: {
36
+ FG_RGB: (r, g, b) => `${CSI}38;2;${r};${g};${b}m`,
37
+ BG_RGB: (r, g, b) => `${CSI}48;2;${r};${g};${b}m`,
38
+ FG_DEFAULT: `${CSI}39m`,
39
+ BG_DEFAULT: `${CSI}49m`,
40
+ },
41
+ MODE: {
42
+ WIN32_INPUT: `${CSI}?9001h`,
43
+ WIN32_INPUT_OFF: `${CSI}?9001l`,
44
+ FOCUS_EVENT: `${CSI}?1004h`,
45
+ FOCUS_EVENT_OFF: `${CSI}?1004l`,
46
+ BRACKETED_PASTE: `${CSI}?2004h`,
47
+ BRACKETED_PASTE_OFF: `${CSI}?2004l`,
48
+ SYNC_UPDATE: `${CSI}?2026h`,
49
+ SYNC_UPDATE_OFF: `${CSI}?2026l`,
50
+ EXTENDED_KEYS: `${CSI}?2031h`,
51
+ EXTENDED_KEYS_OFF: `${CSI}?2031l`,
52
+ },
53
+ WINDOW: {
54
+ SET_SIZE: (rows, cols) => `${CSI}8;${rows};${cols}t`,
55
+ SET_TITLE: (title) => `${ESC}]0;${title}\x07`,
56
+ },
57
+ KEY_MODIFIERS: {
58
+ REPORT: `${CSI}>0q`,
59
+ },
60
+ };
61
+ export const ANSI_REGEX = {
62
+ CSI_SEQUENCE: /\x1b\[[0-9;?><=]*[A-Za-z~]/g,
63
+ SGR_COLOR: /\x1b\[(?:38|48);2;(\d+);(\d+);(\d+)m/g,
64
+ CURSOR_POSITION: /\x1b\[(\d+);(\d+)H/g,
65
+ PRIVATE_MODE: /\x1b\[\?(\d+)([hl])/g,
66
+ OSC_SEQUENCE: /\x1b\](\d+);([^\x07]*)\x07/g,
67
+ WINDOW_SIZE: /\x1b\[8;(\d+);(\d+)t/g,
68
+ ERASE_CHARS: /\x1b\[(\d+)X/g,
69
+ MOVE_CURSOR: /\x1b\[(\d+)([ABCD])/g,
70
+ ALL: /\x1b(?:\[[0-9;?><=]*[A-Za-z~]|\][^\x07]*\x07|[>=][0-9]*[a-z]?)/g,
71
+ };
72
+ export const CLAUDE_CODE_COLORS = {
73
+ ORANGE: { r: 215, g: 119, b: 87 },
74
+ GRAY: { r: 153, g: 153, b: 153 },
75
+ DARK_GRAY: { r: 136, g: 136, b: 136 },
76
+ RED: { r: 255, g: 107, b: 128 },
77
+ PURPLE: { r: 177, g: 185, b: 249 },
78
+ BG_DARK: { r: 55, g: 55, b: 55 },
79
+ BG_GRAY: { r: 80, g: 80, b: 80 },
80
+ BLACK: { r: 0, g: 0, b: 0 },
81
+ WHITE: { r: 255, g: 255, b: 255 },
82
+ };
83
+ export function stripAnsi(str) {
84
+ return str.replace(ANSI_REGEX.ALL, "");
85
+ }
86
+ export function parseRgbColor(seq) {
87
+ const match = seq.match(/\[(?:38|48);2;(\d+);(\d+);(\d+)m/);
88
+ if (match) {
89
+ return { r: parseInt(match[1]), g: parseInt(match[2]), b: parseInt(match[3]) };
90
+ }
91
+ return null;
92
+ }
93
+ export function parseCursorPosition(seq) {
94
+ const match = seq.match(/\[(\d+);(\d+)H/);
95
+ if (match) {
96
+ return { row: parseInt(match[1]), col: parseInt(match[2]) };
97
+ }
98
+ return null;
99
+ }
100
+ export function parseWindowSize(seq) {
101
+ const match = seq.match(/\[8;(\d+);(\d+)t/);
102
+ if (match) {
103
+ return { rows: parseInt(match[1]), cols: parseInt(match[2]) };
104
+ }
105
+ return null;
106
+ }
107
+ //# sourceMappingURL=ansiCodes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ansiCodes.js","sourceRoot":"","sources":["../src/ansiCodes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ const CRED_DIR = path.join(os.homedir(), '.kumo');
5
+ const CRED_FILE = path.join(CRED_DIR, 'credentials.json');
6
+ // load credentials from ~/.kumo/credentials.json if present
7
+ export function loadCredentials() {
8
+ try {
9
+ if (!fs.existsSync(CRED_FILE))
10
+ return null;
11
+ const raw = fs.readFileSync(CRED_FILE, 'utf-8');
12
+ const parsed = JSON.parse(raw);
13
+ if (!parsed.deviceUuid || !parsed.deviceToken)
14
+ return null;
15
+ return parsed;
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ }
21
+ // persist credentials; 0600 permissions on posix
22
+ export function saveCredentials(creds) {
23
+ if (!fs.existsSync(CRED_DIR))
24
+ fs.mkdirSync(CRED_DIR, { recursive: true });
25
+ fs.writeFileSync(CRED_FILE, JSON.stringify(creds, null, 2), { encoding: 'utf-8', mode: 0o600 });
26
+ }
27
+ // remove credentials file; called by `kumo reset`
28
+ export function clearCredentials() {
29
+ try {
30
+ if (!fs.existsSync(CRED_FILE))
31
+ return false;
32
+ fs.unlinkSync(CRED_FILE);
33
+ return true;
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
39
+ export function credentialsPath() {
40
+ return CRED_FILE;
41
+ }
42
+ // parse a kumo:// pairing uri produced by the app's QR code
43
+ export function parsePairingUri(input) {
44
+ try {
45
+ const normalized = input.startsWith('kumo://') ? input.replace('kumo://', 'http://x/') : input;
46
+ const u = new URL(normalized);
47
+ const code = u.searchParams.get('code');
48
+ const host = u.searchParams.get('host') ?? undefined;
49
+ if (!code)
50
+ return null;
51
+ return { code: code.toUpperCase(), host };
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ }
57
+ //# sourceMappingURL=credentialsStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentialsStore.js","sourceRoot":"","sources":["../src/credentialsStore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAS7B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,kBAAkB,CAAC,CAAC;AAE1D,4DAA4D;AAC5D,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,IAAI,CAAC;QAC3C,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAChD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAoB,CAAC;QAClD,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,CAAC,MAAM,CAAC,WAAW;YAAE,OAAO,IAAI,CAAC;QAC3D,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,eAAe,CAAC,KAAsB;IACpD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1E,EAAE,CAAC,aAAa,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAClG,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,gBAAgB;IAC9B,IAAI,CAAC;QACH,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,KAAK,CAAC;QAC5C,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAC/F,MAAM,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;QAC9B,MAAM,IAAI,GAAG,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACxC,MAAM,IAAI,GAAG,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC;QACrD,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAC;QACvB,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,30 @@
1
+ import os from 'os';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import { fileURLToPath } from 'url';
5
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
+ export function generateHookSettingsFile(hookPort) {
7
+ const forwarderPath = path.resolve(__dirname, '../scripts/session_hook_forwarder.cjs');
8
+ const settingsDir = path.join(os.tmpdir(), 'kumo-hooks');
9
+ fs.mkdirSync(settingsDir, { recursive: true });
10
+ const settingsPath = path.join(settingsDir, `settings-${Date.now()}.json`);
11
+ const command = `node "${forwarderPath}" --port ${hookPort}`;
12
+ const settings = {
13
+ hooks: {
14
+ SessionStart: [
15
+ {
16
+ matcher: '',
17
+ hooks: [
18
+ {
19
+ type: 'command',
20
+ command,
21
+ },
22
+ ],
23
+ },
24
+ ],
25
+ },
26
+ };
27
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
28
+ return settingsPath;
29
+ }
30
+ //# sourceMappingURL=generateHookSettings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generateHookSettings.js","sourceRoot":"","sources":["../src/generateHookSettings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE/D,MAAM,UAAU,wBAAwB,CAAC,QAAgB;IACvD,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,uCAAuC,CAAC,CAAC;IACvF,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,YAAY,CAAC,CAAC;IACzD,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAE3E,MAAM,OAAO,GAAG,SAAS,aAAa,YAAY,QAAQ,EAAE,CAAC;IAE7D,MAAM,QAAQ,GAAG;QACf,KAAK,EAAE;YACL,YAAY,EAAE;gBACZ;oBACE,OAAO,EAAE,EAAE;oBACX,KAAK,EAAE;wBACL;4BACE,IAAI,EAAE,SAAS;4BACf,OAAO;yBACR;qBACF;iBACF;aACF;SACF;KACF,CAAC;IAEF,EAAE,CAAC,aAAa,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAC3E,OAAO,YAAY,CAAC;AACtB,CAAC"}
@@ -0,0 +1,32 @@
1
+ import http from 'http';
2
+ export function startHookServer(onSessionFound) {
3
+ return new Promise((resolve, reject) => {
4
+ const server = http.createServer((req, res) => {
5
+ if (req.method === 'POST' && req.url === '/hook/session-start') {
6
+ let body = '';
7
+ req.on('data', (chunk) => { body += chunk; });
8
+ req.on('end', () => {
9
+ try {
10
+ const data = JSON.parse(body);
11
+ if (data.sessionId)
12
+ onSessionFound(data.sessionId);
13
+ }
14
+ catch { }
15
+ res.writeHead(200);
16
+ res.end('ok');
17
+ });
18
+ }
19
+ else {
20
+ res.writeHead(404);
21
+ res.end();
22
+ }
23
+ });
24
+ server.listen(0, '127.0.0.1', () => {
25
+ const addr = server.address();
26
+ const port = typeof addr === 'object' && addr ? addr.port : 0;
27
+ resolve({ port, close: () => server.close() });
28
+ });
29
+ server.on('error', reject);
30
+ });
31
+ }
32
+ //# sourceMappingURL=hookServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hookServer.js","sourceRoot":"","sources":["../src/hookServer.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAOxB,MAAM,UAAU,eAAe,CAAC,cAA2C;IACzE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC5C,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,IAAI,GAAG,CAAC,GAAG,KAAK,qBAAqB,EAAE,CAAC;gBAC/D,IAAI,IAAI,GAAG,EAAE,CAAC;gBACd,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC9C,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;oBACjB,IAAI,CAAC;wBACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAA2B,CAAC;wBACxD,IAAI,IAAI,CAAC,SAAS;4BAAE,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;oBACrD,CAAC;oBAAC,MAAM,CAAC,CAAA,CAAC;oBACV,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;oBACnB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBAChB,CAAC,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACnB,GAAG,CAAC,GAAG,EAAE,CAAC;YACZ,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE;YACjC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;YAC9B,MAAM,IAAI,GAAG,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9D,OAAO,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,68 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ // common noisy folders/files to always ignore across ecosystems
4
+ const DEFAULT_IGNORES = [
5
+ // vcs / editor / os
6
+ '.git', '.svn', '.hg', '.idea', '.vscode', '.DS_Store', 'Thumbs.db',
7
+ // node / js / ts
8
+ 'node_modules', 'bower_components', 'dist', 'build', 'out', 'coverage',
9
+ '.nyc_output', '.next', '.nuxt', '.turbo', '.cache', '.parcel-cache',
10
+ '.svelte-kit', '.vercel', '.netlify', '.output', '.astro',
11
+ // python
12
+ '__pycache__', '.venv', 'venv', 'env', '.pytest_cache', '.mypy_cache',
13
+ '.tox', '.ruff_cache', '.ipynb_checkpoints',
14
+ // java / gradle / maven / kotlin
15
+ 'target', '.gradle', '.mvn', '.kotlin',
16
+ // rust / go
17
+ // 'target' already listed
18
+ // dart / flutter
19
+ '.dart_tool', '.pub-cache', '.pub', '.flutter-plugins',
20
+ '.flutter-plugins-dependencies',
21
+ // mobile
22
+ '.expo', '.expo-shared', 'Pods', 'DerivedData', '.gradle',
23
+ // misc
24
+ 'tmp', 'temp', 'logs', 'vendor', '.terraform', '.serverless',
25
+ ];
26
+ let cache = null;
27
+ // parse .gitignore into a set of simple name patterns
28
+ // only supports basic names and name/ entries (ignores complex globs)
29
+ function parseGitignore(content) {
30
+ const names = [];
31
+ for (const rawLine of content.split(/\r?\n/)) {
32
+ const line = rawLine.trim();
33
+ if (!line || line.startsWith('#') || line.startsWith('!'))
34
+ continue;
35
+ // strip leading slash and trailing slash
36
+ let p = line.replace(/^\/+/, '').replace(/\/+$/, '');
37
+ // skip patterns with wildcards or nested paths
38
+ if (p.includes('*') || p.includes('?') || p.includes('[') || p.includes('/'))
39
+ continue;
40
+ if (p)
41
+ names.push(p);
42
+ }
43
+ return names;
44
+ }
45
+ // load ignored names set combining defaults + .gitignore in given root
46
+ // cached by mtime to avoid re-reading on each call
47
+ export function loadIgnoredNames(projectRoot) {
48
+ const gitignore = path.join(projectRoot, '.gitignore');
49
+ let mtime = 0;
50
+ try {
51
+ mtime = fs.statSync(gitignore).mtimeMs;
52
+ }
53
+ catch { }
54
+ if (cache && cache.mtime === mtime)
55
+ return cache.set;
56
+ const set = new Set(DEFAULT_IGNORES);
57
+ if (mtime > 0) {
58
+ try {
59
+ const content = fs.readFileSync(gitignore, 'utf-8');
60
+ for (const name of parseGitignore(content))
61
+ set.add(name);
62
+ }
63
+ catch { }
64
+ }
65
+ cache = { mtime, set };
66
+ return set;
67
+ }
68
+ //# sourceMappingURL=ignorePaths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ignorePaths.js","sourceRoot":"","sources":["../src/ignorePaths.ts"],"names":[],"mappings":"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"}