kumo-cli 1.0.0 → 1.0.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.
- package/README.md +21 -32
- package/dist/auth/credentialsStore.js +41 -0
- package/dist/auth/credentialsStore.js.map +1 -0
- package/dist/claude/generateHookSettings.js +23 -0
- package/dist/claude/generateHookSettings.js.map +1 -0
- package/dist/claude/hookServer.js +33 -0
- package/dist/claude/hookServer.js.map +1 -0
- package/dist/claude/sessionScanner.js +250 -0
- package/dist/claude/sessionScanner.js.map +1 -0
- package/dist/config.js +40 -0
- package/dist/config.js.map +1 -0
- package/dist/core/config.js +48 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/loadDotEnv.js +46 -0
- package/dist/core/loadDotEnv.js.map +1 -0
- package/dist/core/logger.js +29 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/handlers/messageRouter.js +223 -0
- package/dist/handlers/messageRouter.js.map +1 -0
- package/dist/http/httpClient.js +41 -0
- package/dist/http/httpClient.js.map +1 -0
- package/dist/index.js +141 -1090
- package/dist/index.js.map +1 -1
- package/dist/logger.js +29 -0
- package/dist/logger.js.map +1 -0
- package/dist/pty/ansiCodes.js +14 -0
- package/dist/pty/ansiCodes.js.map +1 -0
- package/dist/pty/ptyMenuParser.js +357 -0
- package/dist/pty/ptyMenuParser.js.map +1 -0
- package/dist/pty/ptySnapshotRenderer.js +71 -0
- package/dist/pty/ptySnapshotRenderer.js.map +1 -0
- package/dist/pty/spawn.js +77 -0
- package/dist/pty/spawn.js.map +1 -0
- package/dist/pty/terminalManager.js +66 -0
- package/dist/pty/terminalManager.js.map +1 -0
- package/dist/services/claudeService.js +218 -0
- package/dist/services/claudeService.js.map +1 -0
- package/dist/services/effortService.js +89 -0
- package/dist/services/effortService.js.map +1 -0
- package/dist/services/fileService.js +127 -0
- package/dist/services/fileService.js.map +1 -0
- package/dist/services/modelService.js +84 -0
- package/dist/services/modelService.js.map +1 -0
- package/dist/services/pairingService.js +129 -0
- package/dist/services/pairingService.js.map +1 -0
- package/dist/services/sessionService.js +168 -0
- package/dist/services/sessionService.js.map +1 -0
- package/dist/services/tunnelService.js +47 -0
- package/dist/services/tunnelService.js.map +1 -0
- package/dist/sessionScanner.js +131 -5
- package/dist/sessionScanner.js.map +1 -1
- package/dist/snapshotScanner.js +3 -1
- package/dist/snapshotScanner.js.map +1 -1
- package/dist/spawn.js +12 -2
- package/dist/spawn.js.map +1 -1
- package/dist/transport/directWsServer.js +135 -0
- package/dist/transport/directWsServer.js.map +1 -0
- package/dist/transport/wsClient.js +87 -0
- package/dist/transport/wsClient.js.map +1 -0
- package/dist/utils/ignorePaths.js +54 -0
- package/dist/utils/ignorePaths.js.map +1 -0
- package/dist/utils/ptyBuffer.js +46 -0
- package/dist/utils/ptyBuffer.js.map +1 -0
- package/dist/utils/safePath.js +43 -0
- package/dist/utils/safePath.js.map +1 -0
- package/package.json +2 -4
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { httpRequest } from '../http/httpClient.js';
|
|
2
|
+
import { SERVER_HTTP } from '../core/config.js';
|
|
3
|
+
// strip any existing --model / --model=... / -m flag pairs
|
|
4
|
+
export function removeModelArgs(args) {
|
|
5
|
+
const result = [];
|
|
6
|
+
for (let i = 0; i < args.length; i++) {
|
|
7
|
+
const a = args[i];
|
|
8
|
+
if (a === '--model' || a === '-m') {
|
|
9
|
+
i++;
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
if (a.startsWith('--model='))
|
|
13
|
+
continue;
|
|
14
|
+
result.push(a);
|
|
15
|
+
}
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
// orchestrates the currently-active model pushed from backend via ws
|
|
19
|
+
export class ModelService {
|
|
20
|
+
current = null;
|
|
21
|
+
getCurrent() { return this.current; }
|
|
22
|
+
// apply an incoming model payload to args and cache it for buildEnv
|
|
23
|
+
apply(payload, args) {
|
|
24
|
+
const cleaned = removeModelArgs(args);
|
|
25
|
+
if (payload && typeof payload === 'object') {
|
|
26
|
+
const p = payload;
|
|
27
|
+
const model = typeof p['model'] === 'string' ? p['model'] : '';
|
|
28
|
+
if (model) {
|
|
29
|
+
this.current = {
|
|
30
|
+
model,
|
|
31
|
+
baseURL: typeof p['baseURL'] === 'string' ? p['baseURL'] : undefined,
|
|
32
|
+
apiKey: typeof p['apiKey'] === 'string' ? p['apiKey'] : undefined,
|
|
33
|
+
};
|
|
34
|
+
cleaned.push('--model', model);
|
|
35
|
+
return cleaned;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
this.current = null;
|
|
39
|
+
return cleaned;
|
|
40
|
+
}
|
|
41
|
+
// build env overrides for claude pty based on current model
|
|
42
|
+
buildEnv() {
|
|
43
|
+
const env = { ANTHROPIC_BASE_URL: undefined, ANTHROPIC_API_KEY: undefined };
|
|
44
|
+
if (this.current?.baseURL) {
|
|
45
|
+
env.ANTHROPIC_BASE_URL = this.current.baseURL;
|
|
46
|
+
env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
47
|
+
}
|
|
48
|
+
if (this.current?.apiKey)
|
|
49
|
+
env.ANTHROPIC_API_KEY = this.current.apiKey;
|
|
50
|
+
return env;
|
|
51
|
+
}
|
|
52
|
+
// fetch the user's current default model from backend using deviceToken
|
|
53
|
+
async fetchDefault(deviceToken) {
|
|
54
|
+
if (!deviceToken)
|
|
55
|
+
return null;
|
|
56
|
+
try {
|
|
57
|
+
const res = await httpRequest('GET', `${SERVER_HTTP()}/api/cli/default-model`, { token: deviceToken });
|
|
58
|
+
if (res.status !== 200 || !res.data || typeof res.data !== 'object')
|
|
59
|
+
return null;
|
|
60
|
+
const d = res.data;
|
|
61
|
+
const model = typeof d['model'] === 'string' ? d['model'] : '';
|
|
62
|
+
if (!model)
|
|
63
|
+
return null;
|
|
64
|
+
return {
|
|
65
|
+
model,
|
|
66
|
+
baseURL: typeof d['baseURL'] === 'string' ? d['baseURL'] : undefined,
|
|
67
|
+
apiKey: typeof d['apiKey'] === 'string' ? d['apiKey'] : undefined,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// refresh from backend when no current model is set; returns new args list
|
|
75
|
+
async ensureBeforeSpawn(args, deviceToken) {
|
|
76
|
+
if (this.current)
|
|
77
|
+
return args;
|
|
78
|
+
const def = await this.fetchDefault(deviceToken);
|
|
79
|
+
if (def)
|
|
80
|
+
return this.apply({ model: def.model, baseURL: def.baseURL, apiKey: def.apiKey }, args);
|
|
81
|
+
return args;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=modelService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelService.js","sourceRoot":"","sources":["../../src/services/modelService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import readline from 'node:readline';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { httpRequest } from '../http/httpClient.js';
|
|
5
|
+
import { SERVER_HTTP } from '../core/config.js';
|
|
6
|
+
import { saveCredentials, credentialsPath } from '../auth/credentialsStore.js';
|
|
7
|
+
// poll pair status until approved/rejected or timeout (5 min)
|
|
8
|
+
async function pollPairStatus(requestId) {
|
|
9
|
+
const deadline = Date.now() + 5 * 60 * 1000;
|
|
10
|
+
while (Date.now() < deadline) {
|
|
11
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
12
|
+
try {
|
|
13
|
+
const res = await httpRequest('GET', `${SERVER_HTTP()}/api/cli/pair/${requestId}`);
|
|
14
|
+
if (res.status !== 200)
|
|
15
|
+
continue;
|
|
16
|
+
const data = res.data;
|
|
17
|
+
if (data.status === 'approved' && data.deviceToken && data.device)
|
|
18
|
+
return data;
|
|
19
|
+
if (data.status === 'rejected') {
|
|
20
|
+
console.warn('[kumo-cli] pairing rejected by app');
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch { }
|
|
25
|
+
}
|
|
26
|
+
console.warn('[kumo-cli] pairing timed out');
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
// quick reachability check against backend health endpoint
|
|
30
|
+
export async function checkBackendReachable() {
|
|
31
|
+
try {
|
|
32
|
+
const res = await httpRequest('GET', `${SERVER_HTTP()}/api/health`);
|
|
33
|
+
return res.status === 200;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
// verify cached deviceToken against backend; returns refreshed creds or null when revoked/deleted
|
|
40
|
+
export async function verifyCredentials(creds) {
|
|
41
|
+
try {
|
|
42
|
+
const res = await httpRequest('POST', `${SERVER_HTTP()}/api/cli/connect`, { body: { deviceToken: creds.deviceToken } });
|
|
43
|
+
if (res.status !== 200)
|
|
44
|
+
return null;
|
|
45
|
+
const data = res.data;
|
|
46
|
+
return { ...creds, user: data.user ?? creds.user, deviceUuid: data.device?.deviceUuid ?? creds.deviceUuid };
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function printBanner() {
|
|
53
|
+
const cloud = [
|
|
54
|
+
" \x1b[1;36m_ __ _ _ __ __ ____ \x1b[0m",
|
|
55
|
+
" \x1b[1;36m| |/ /| | | || \\/ | / __ \\\x1b[0m",
|
|
56
|
+
" \x1b[1;36m| ' / | | | || \\ / || | | |\x1b[0m",
|
|
57
|
+
" \x1b[1;36m| . \\ | |_| || |\\/| || |__| |\x1b[0m",
|
|
58
|
+
" \x1b[1;36m|_|\\_\\ \\___/ |_| |_| \\____/\x1b[0m",
|
|
59
|
+
];
|
|
60
|
+
console.log('');
|
|
61
|
+
for (const line of cloud)
|
|
62
|
+
console.log(line);
|
|
63
|
+
console.log('');
|
|
64
|
+
console.log(' \x1b[2mwhere your code drifts between devices, softly.\x1b[0m');
|
|
65
|
+
console.log('');
|
|
66
|
+
console.log(' \x1b[36m›\x1b[0m open the \x1b[1mKumo app\x1b[0m, sign in, and tap \x1b[1m"generate"\x1b[0m on the pairing tab.');
|
|
67
|
+
console.log(' \x1b[36m›\x1b[0m whisper the 9 digits back to me below — spaces are fine.');
|
|
68
|
+
console.log('');
|
|
69
|
+
}
|
|
70
|
+
// interactive pairing flow: ask for code, submit to backend, poll until approved
|
|
71
|
+
export async function pairFlow() {
|
|
72
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
73
|
+
const ask = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a)));
|
|
74
|
+
printBanner();
|
|
75
|
+
try {
|
|
76
|
+
while (true) {
|
|
77
|
+
const raw = (await ask('\x1b[1;33m✦ pairing code ›\x1b[0m ')).trim();
|
|
78
|
+
if (!raw) {
|
|
79
|
+
console.warn('[kumo-cli] pairing code is required, please try again');
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const code = raw.replace(/\D+/g, '');
|
|
83
|
+
if (!/^\d{9}$/.test(code)) {
|
|
84
|
+
console.warn('[kumo-cli] pairing code must be exactly 9 digits, please try again');
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const deviceUuid = randomUUID();
|
|
88
|
+
const deviceName = `${os.userInfo().username}@${os.hostname()}`;
|
|
89
|
+
let result;
|
|
90
|
+
try {
|
|
91
|
+
result = await httpRequest('POST', `${SERVER_HTTP()}/api/cli/pair`, { body: { pairingCode: code, deviceUuid, deviceName } });
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
console.warn(`[kumo-cli] pair error: ${err.message}, please try again`);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (result.status !== 200 && result.status !== 201) {
|
|
98
|
+
const data = result.data;
|
|
99
|
+
const msg = data?.error ?? data?.message ?? `HTTP ${result.status}`;
|
|
100
|
+
console.warn(`[kumo-cli] pair failed: ${msg}, please try again`);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const initial = result.data;
|
|
104
|
+
if (!initial.requestId) {
|
|
105
|
+
console.warn('[kumo-cli] pair failed: missing requestId, please try again');
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
console.log('[kumo-cli] waiting for approval in the Kumo app...');
|
|
109
|
+
const approved = await pollPairStatus(initial.requestId);
|
|
110
|
+
if (!approved) {
|
|
111
|
+
console.warn('[kumo-cli] please try again with a fresh code');
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const creds = {
|
|
115
|
+
deviceUuid: approved.device.deviceUuid,
|
|
116
|
+
deviceToken: approved.deviceToken,
|
|
117
|
+
serverUrl: SERVER_HTTP(),
|
|
118
|
+
user: approved.user ?? undefined,
|
|
119
|
+
};
|
|
120
|
+
saveCredentials(creds);
|
|
121
|
+
console.log(`[kumo-cli] paired and saved to ${credentialsPath()}`);
|
|
122
|
+
return creds;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
rl.close();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=pairingService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairingService.js","sourceRoot":"","sources":["../../src/services/pairingService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { log } from '../core/logger.js';
|
|
5
|
+
import { cwdToProjectFolder } from '../utils/safePath.js';
|
|
6
|
+
import { readFileHistoryPrev } from '../claude/sessionScanner.js';
|
|
7
|
+
// remove any `--resume <id>` or `-r <id>` flag pairs
|
|
8
|
+
export function removeResumeArgs(args) {
|
|
9
|
+
const result = [];
|
|
10
|
+
for (let i = 0; i < args.length; i++) {
|
|
11
|
+
if (args[i] === '--resume' || args[i] === '-r')
|
|
12
|
+
i++;
|
|
13
|
+
else
|
|
14
|
+
result.push(args[i]);
|
|
15
|
+
}
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
// collect session ids currently active in ~/.claude/sessions
|
|
19
|
+
export async function getActiveSessionIds() {
|
|
20
|
+
const sessionsDir = path.join(os.homedir(), '.claude', 'sessions');
|
|
21
|
+
const active = new Set();
|
|
22
|
+
try {
|
|
23
|
+
const files = fs.readdirSync(sessionsDir).filter((f) => f.endsWith('.json'));
|
|
24
|
+
for (const file of files) {
|
|
25
|
+
try {
|
|
26
|
+
const data = JSON.parse(fs.readFileSync(path.join(sessionsDir, file), 'utf-8'));
|
|
27
|
+
if (data.sessionId)
|
|
28
|
+
active.add(data.sessionId);
|
|
29
|
+
}
|
|
30
|
+
catch { }
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch { }
|
|
34
|
+
return active;
|
|
35
|
+
}
|
|
36
|
+
// list sessions for a given cwd with their first user message and last activity time
|
|
37
|
+
export async function getSessionListForCwd(cwdOverride) {
|
|
38
|
+
const cwd = cwdOverride && cwdOverride.length > 0 ? cwdOverride : process.cwd();
|
|
39
|
+
const folderName = cwdToProjectFolder(cwd);
|
|
40
|
+
const projectDir = path.join(os.homedir(), '.claude', 'projects', folderName);
|
|
41
|
+
log(`[session_list] cwd=${cwd}, folder=${folderName}, dir=${projectDir}`);
|
|
42
|
+
const sessions = [];
|
|
43
|
+
try {
|
|
44
|
+
if (!fs.existsSync(projectDir))
|
|
45
|
+
return sessions;
|
|
46
|
+
const files = fs.readdirSync(projectDir).filter((f) => f.endsWith('.jsonl'));
|
|
47
|
+
for (const file of files) {
|
|
48
|
+
const sessionId = path.basename(file, '.jsonl');
|
|
49
|
+
try {
|
|
50
|
+
const raw = fs.readFileSync(path.join(projectDir, file), 'utf-8');
|
|
51
|
+
const lines = raw.split('\n').filter((l) => l.trim());
|
|
52
|
+
let firstMessage = '';
|
|
53
|
+
let lastTimestamp = '';
|
|
54
|
+
let hasMessages = false;
|
|
55
|
+
for (const line of lines) {
|
|
56
|
+
try {
|
|
57
|
+
const entry = JSON.parse(line);
|
|
58
|
+
if (entry['type'] !== 'user' && entry['type'] !== 'assistant')
|
|
59
|
+
continue;
|
|
60
|
+
const msgObj = entry['message'];
|
|
61
|
+
if (!msgObj || !msgObj['role'] || msgObj['role'] === 'system')
|
|
62
|
+
continue;
|
|
63
|
+
hasMessages = true;
|
|
64
|
+
const ts = entry['timestamp'];
|
|
65
|
+
if (ts)
|
|
66
|
+
lastTimestamp = ts;
|
|
67
|
+
if (!firstMessage && msgObj['role'] === 'user') {
|
|
68
|
+
let text = '';
|
|
69
|
+
if (typeof msgObj['content'] === 'string')
|
|
70
|
+
text = msgObj['content'].slice(0, 200);
|
|
71
|
+
else if (Array.isArray(msgObj['content'])) {
|
|
72
|
+
text = msgObj['content']
|
|
73
|
+
.filter((b) => b['type'] === 'text' && b['text'])
|
|
74
|
+
.map((b) => b['text']).join('').slice(0, 200);
|
|
75
|
+
}
|
|
76
|
+
if (!/<local-command-/i.test(text))
|
|
77
|
+
firstMessage = text;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch { }
|
|
81
|
+
}
|
|
82
|
+
if (hasMessages)
|
|
83
|
+
sessions.push({ sessionId, firstMessage, lastTimestamp });
|
|
84
|
+
}
|
|
85
|
+
catch { }
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch { }
|
|
89
|
+
sessions.sort((a, b) => (b.lastTimestamp || '').localeCompare(a.lastTimestamp || ''));
|
|
90
|
+
return sessions;
|
|
91
|
+
}
|
|
92
|
+
// delete a session jsonl file for the given cwd
|
|
93
|
+
export async function deleteSessionFile(sessionId, cwdOverride) {
|
|
94
|
+
const cwd = cwdOverride && cwdOverride.length > 0 ? cwdOverride : process.cwd();
|
|
95
|
+
const folderName = cwdToProjectFolder(cwd);
|
|
96
|
+
const projectDir = path.join(os.homedir(), '.claude', 'projects', folderName);
|
|
97
|
+
const sessionFile = path.join(projectDir, `${sessionId}.jsonl`);
|
|
98
|
+
if (fs.existsSync(sessionFile)) {
|
|
99
|
+
fs.unlinkSync(sessionFile);
|
|
100
|
+
log(`[delete_session] deleted ${sessionFile}`);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
log(`[delete_session] file not found: ${sessionFile}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// read a session jsonl file and materialize messages for the app
|
|
107
|
+
export function readSessionHistory(sessionId) {
|
|
108
|
+
const folderName = cwdToProjectFolder(process.cwd());
|
|
109
|
+
const sessionFile = path.join(os.homedir(), '.claude', 'projects', folderName, `${sessionId}.jsonl`);
|
|
110
|
+
if (!fs.existsSync(sessionFile))
|
|
111
|
+
return [];
|
|
112
|
+
const raw = fs.readFileSync(sessionFile, 'utf-8');
|
|
113
|
+
const messages = [];
|
|
114
|
+
const seenUuids = new Set();
|
|
115
|
+
for (const line of raw.split('\n')) {
|
|
116
|
+
if (!line.trim())
|
|
117
|
+
continue;
|
|
118
|
+
try {
|
|
119
|
+
const entry = JSON.parse(line);
|
|
120
|
+
if (entry['type'] !== 'user' && entry['type'] !== 'assistant')
|
|
121
|
+
continue;
|
|
122
|
+
const msgObj = entry['message'];
|
|
123
|
+
if (!msgObj)
|
|
124
|
+
continue;
|
|
125
|
+
const role = msgObj['role'];
|
|
126
|
+
if (!role || role === 'system')
|
|
127
|
+
continue;
|
|
128
|
+
if (entry['isMeta'] || entry['sourceToolUseID'])
|
|
129
|
+
continue;
|
|
130
|
+
const uuid = entry['uuid'];
|
|
131
|
+
if (uuid && seenUuids.has(uuid))
|
|
132
|
+
continue;
|
|
133
|
+
if (uuid)
|
|
134
|
+
seenUuids.add(uuid);
|
|
135
|
+
const timestamp = entry['timestamp'];
|
|
136
|
+
let content = '';
|
|
137
|
+
const toolUses = [];
|
|
138
|
+
if (typeof msgObj['content'] === 'string')
|
|
139
|
+
content = msgObj['content'];
|
|
140
|
+
else if (Array.isArray(msgObj['content'])) {
|
|
141
|
+
const arr = msgObj['content'];
|
|
142
|
+
content = arr
|
|
143
|
+
.filter((b) => b['type'] === 'text' && b['text'] && b['text'].trim() !== 'No response requested.')
|
|
144
|
+
.map((b) => b['text']).join('');
|
|
145
|
+
for (const b of arr) {
|
|
146
|
+
if (b['type'] === 'tool_use' && b['name']) {
|
|
147
|
+
const tu = { toolName: b['name'], input: b['input'] ?? {} };
|
|
148
|
+
if (tu.toolName === 'Write') {
|
|
149
|
+
const fp = tu.input['file_path'] ?? tu.input['path'] ?? '';
|
|
150
|
+
if (fp) {
|
|
151
|
+
const oldContent = readFileHistoryPrev(sessionId, fp);
|
|
152
|
+
if (oldContent !== null)
|
|
153
|
+
tu.input['old_content'] = oldContent;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
toolUses.push(tu);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (!content && toolUses.length === 0)
|
|
161
|
+
continue;
|
|
162
|
+
messages.push({ role, content, id: uuid, timestamp, toolUses: toolUses.length > 0 ? toolUses : undefined });
|
|
163
|
+
}
|
|
164
|
+
catch { }
|
|
165
|
+
}
|
|
166
|
+
return messages;
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=sessionService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionService.js","sourceRoot":"","sources":["../../src/services/sessionService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { log } from '../core/logger.js';
|
|
2
|
+
// proxy a http request from the server through this cli to a local service
|
|
3
|
+
export async function handleTunnelRequest(req) {
|
|
4
|
+
const targetHost = req.host || 'localhost';
|
|
5
|
+
const url = `http://${targetHost}:${req.port}${req.path}`;
|
|
6
|
+
const filteredHeaders = Object.fromEntries(Object.entries(req.headers)
|
|
7
|
+
.filter(([k]) => {
|
|
8
|
+
const lower = k.toLowerCase();
|
|
9
|
+
return !lower.startsWith('sec-') && !['host', 'connection', 'accept-encoding', 'upgrade-insecure-requests'].includes(lower);
|
|
10
|
+
})
|
|
11
|
+
.map(([k, v]) => {
|
|
12
|
+
const lower = k.toLowerCase();
|
|
13
|
+
if (lower === 'origin' || lower === 'referer') {
|
|
14
|
+
return [k, v.replace(/http:\/\/localhost:\d+/g, `http://${targetHost}:${req.port}`)];
|
|
15
|
+
}
|
|
16
|
+
return [k, v];
|
|
17
|
+
}));
|
|
18
|
+
log(`[tunnel] ${req.method} ${url}`);
|
|
19
|
+
try {
|
|
20
|
+
const res = await fetch(url, {
|
|
21
|
+
method: req.method,
|
|
22
|
+
headers: filteredHeaders,
|
|
23
|
+
body: req.body && req.method !== 'GET' && req.method !== 'HEAD' ? Buffer.from(req.body, 'base64') : undefined,
|
|
24
|
+
redirect: 'manual',
|
|
25
|
+
});
|
|
26
|
+
const buf = Buffer.from(await res.arrayBuffer());
|
|
27
|
+
log(`[tunnel] response ${res.status} ${url} (${buf.length} bytes)`);
|
|
28
|
+
const respHeaders = Object.fromEntries([...res.headers.entries()].filter(([k]) => !['content-encoding', 'transfer-encoding', 'content-length'].includes(k.toLowerCase())));
|
|
29
|
+
const remoteOrigin = `http://${targetHost}:${req.port}`;
|
|
30
|
+
if (respHeaders['location'])
|
|
31
|
+
respHeaders['location'] = respHeaders['location'].replace(remoteOrigin, '');
|
|
32
|
+
if (respHeaders['set-cookie']) {
|
|
33
|
+
respHeaders['set-cookie'] = respHeaders['set-cookie'].replace(/;\s*domain=[^;]*/gi, '').replace(/;\s*secure/gi, '');
|
|
34
|
+
}
|
|
35
|
+
return { type: 'tunnel_response', requestId: req.requestId, status: res.status, headers: respHeaders, body: buf.toString('base64') };
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
return {
|
|
39
|
+
type: 'tunnel_response',
|
|
40
|
+
requestId: req.requestId,
|
|
41
|
+
status: 502,
|
|
42
|
+
headers: {},
|
|
43
|
+
body: Buffer.from('Bad Gateway: ' + err.message).toString('base64'),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=tunnelService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnelService.js","sourceRoot":"","sources":["../../src/services/tunnelService.ts"],"names":[],"mappings":"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"}
|