kumo-cli 1.0.1 → 1.0.3
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 +20 -31
- 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 +225 -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 +120 -1364
- 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/claudeAdapterProxy.js +104 -0
- package/dist/services/claudeAdapterProxy.js.map +1 -0
- package/dist/services/claudeService.js +239 -0
- package/dist/services/claudeService.js.map +1 -0
- package/dist/services/claudeService.test.js +39 -0
- package/dist/services/claudeService.test.js.map +1 -0
- package/dist/services/effortService.js +89 -0
- package/dist/services/effortService.js.map +1 -0
- package/dist/services/fileService.js +129 -0
- package/dist/services/fileService.js.map +1 -0
- package/dist/services/modelService.js +115 -0
- package/dist/services/modelService.js.map +1 -0
- package/dist/services/modelService.test.js +76 -0
- package/dist/services/modelService.test.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 +130 -4
- package/dist/sessionScanner.js.map +1 -1
- package/dist/snapshotScanner.js +3 -1
- package/dist/snapshotScanner.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 +6 -5
|
@@ -0,0 +1,89 @@
|
|
|
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
|
+
const VALID_EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max'];
|
|
6
|
+
// resolve ~/settings.json path
|
|
7
|
+
function settingsPath() {
|
|
8
|
+
return path.join(os.homedir(), 'settings.json');
|
|
9
|
+
}
|
|
10
|
+
// read the whole settings.json as an object, returns empty object on failure
|
|
11
|
+
function readSettings() {
|
|
12
|
+
try {
|
|
13
|
+
const raw = fs.readFileSync(settingsPath(), 'utf-8');
|
|
14
|
+
return JSON.parse(raw);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function readEffortLevel() {
|
|
21
|
+
const settings = readSettings();
|
|
22
|
+
const raw = settings['effortLevel'];
|
|
23
|
+
if (typeof raw === 'string' && VALID_EFFORTS.includes(raw)) {
|
|
24
|
+
return raw;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
// write settings back preserving existing keys
|
|
29
|
+
function writeSettings(data) {
|
|
30
|
+
const dir = path.dirname(settingsPath());
|
|
31
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
32
|
+
fs.writeFileSync(settingsPath(), JSON.stringify(data, null, 2), 'utf-8');
|
|
33
|
+
}
|
|
34
|
+
// strip any existing --effort / --effort=... flag pairs from args
|
|
35
|
+
export function removeEffortArgs(args) {
|
|
36
|
+
const result = [];
|
|
37
|
+
for (let i = 0; i < args.length; i++) {
|
|
38
|
+
const a = args[i];
|
|
39
|
+
if (a === '--effort') {
|
|
40
|
+
i++;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (a.startsWith('--effort='))
|
|
44
|
+
continue;
|
|
45
|
+
result.push(a);
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
// manages effort level stored in ~/settings.json and applied to claude args
|
|
50
|
+
export class EffortService {
|
|
51
|
+
current = null;
|
|
52
|
+
getCurrent() { return this.current; }
|
|
53
|
+
getEffective() {
|
|
54
|
+
return this.current ?? 'medium';
|
|
55
|
+
}
|
|
56
|
+
// load effort from ~/settings.json
|
|
57
|
+
load() {
|
|
58
|
+
const fromSettings = readEffortLevel();
|
|
59
|
+
if (fromSettings) {
|
|
60
|
+
this.current = fromSettings;
|
|
61
|
+
}
|
|
62
|
+
log(`[effort] loaded: ${this.current}`);
|
|
63
|
+
return this.current;
|
|
64
|
+
}
|
|
65
|
+
// persist effort to ~/settings.json and update current
|
|
66
|
+
save(level) {
|
|
67
|
+
if (!VALID_EFFORTS.includes(level))
|
|
68
|
+
return;
|
|
69
|
+
this.current = level;
|
|
70
|
+
const settings = readSettings();
|
|
71
|
+
settings['effortLevel'] = level;
|
|
72
|
+
writeSettings(settings);
|
|
73
|
+
log(`[effort] saved: ${level}`);
|
|
74
|
+
}
|
|
75
|
+
// apply effort flag to claude args, returns new args array
|
|
76
|
+
apply(args) {
|
|
77
|
+
const cleaned = removeEffortArgs(args);
|
|
78
|
+
if (this.current)
|
|
79
|
+
cleaned.push('--effort', this.current);
|
|
80
|
+
return cleaned;
|
|
81
|
+
}
|
|
82
|
+
// ensure effort is loaded before spawn, returns args with --effort flag
|
|
83
|
+
ensureBeforeSpawn(args) {
|
|
84
|
+
if (!this.current)
|
|
85
|
+
this.load();
|
|
86
|
+
return this.apply(args);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=effortService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"effortService.js","sourceRoot":"","sources":["../../src/services/effortService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { safePath, copyRecursive } from '../utils/safePath.js';
|
|
4
|
+
import { loadIgnoredNames } from '../utils/ignorePaths.js';
|
|
5
|
+
// translate any thrown error into a string message
|
|
6
|
+
function errMsg(e) {
|
|
7
|
+
return e instanceof Error ? e.message : String(e);
|
|
8
|
+
}
|
|
9
|
+
// wrap sync fs op into uniform envelope
|
|
10
|
+
function guard(fn) {
|
|
11
|
+
try {
|
|
12
|
+
return { ok: true, data: fn() };
|
|
13
|
+
}
|
|
14
|
+
catch (e) {
|
|
15
|
+
return { ok: false, error: errMsg(e) };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
// list directory entries (non-hidden) sorted dirs first
|
|
19
|
+
export function list(requestedPath, cwd) {
|
|
20
|
+
return guard(() => {
|
|
21
|
+
const projectRoot = cwd && path.isAbsolute(cwd) ? path.normalize(cwd) : process.cwd();
|
|
22
|
+
const resolved = requestedPath
|
|
23
|
+
? safePath(requestedPath)
|
|
24
|
+
: projectRoot;
|
|
25
|
+
if (!resolved.startsWith(projectRoot))
|
|
26
|
+
throw new Error('path traversal blocked');
|
|
27
|
+
const entries = fs.readdirSync(resolved, { withFileTypes: true })
|
|
28
|
+
.filter((e) => !e.name.startsWith('.'))
|
|
29
|
+
.map((e) => ({
|
|
30
|
+
name: e.name,
|
|
31
|
+
path: path.relative(projectRoot, path.resolve(resolved, e.name)).replace(/\\/g, '/'),
|
|
32
|
+
isDirectory: e.isDirectory(),
|
|
33
|
+
size: e.isDirectory() ? 0 : fs.statSync(path.resolve(resolved, e.name)).size,
|
|
34
|
+
}))
|
|
35
|
+
.sort((a, b) => a.isDirectory !== b.isDirectory ? (a.isDirectory ? -1 : 1) : a.name.localeCompare(b.name));
|
|
36
|
+
return { entries, projectName: path.basename(projectRoot) };
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
export function read(filePath) {
|
|
40
|
+
return guard(() => fs.readFileSync(safePath(filePath), 'utf-8'));
|
|
41
|
+
}
|
|
42
|
+
export function write(filePath, content) {
|
|
43
|
+
return guard(() => {
|
|
44
|
+
const projectRoot = process.cwd();
|
|
45
|
+
const resolved = path.resolve(projectRoot, path.normalize(filePath || '.'));
|
|
46
|
+
if (!resolved.startsWith(projectRoot))
|
|
47
|
+
throw new Error('path traversal blocked');
|
|
48
|
+
fs.writeFileSync(resolved, content, 'utf-8');
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
export function create(targetPath, isDir) {
|
|
52
|
+
return guard(() => {
|
|
53
|
+
const resolved = safePath(targetPath);
|
|
54
|
+
if (isDir)
|
|
55
|
+
fs.mkdirSync(resolved, { recursive: true });
|
|
56
|
+
else {
|
|
57
|
+
fs.mkdirSync(path.dirname(resolved), { recursive: true });
|
|
58
|
+
fs.writeFileSync(resolved, '', 'utf-8');
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
export function remove(targetPath) {
|
|
63
|
+
return guard(() => fs.rmSync(safePath(targetPath), { recursive: true, force: true }));
|
|
64
|
+
}
|
|
65
|
+
export function rename(oldPath, newName) {
|
|
66
|
+
return guard(() => {
|
|
67
|
+
const resolvedOld = safePath(oldPath);
|
|
68
|
+
const resolvedNew = path.join(path.dirname(resolvedOld), newName);
|
|
69
|
+
safePath(path.relative(process.cwd(), resolvedNew));
|
|
70
|
+
fs.renameSync(resolvedOld, resolvedNew);
|
|
71
|
+
return { newPath: path.relative(process.cwd(), resolvedNew).replace(/\\/g, '/') };
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
export function copy(src, dest) {
|
|
75
|
+
return guard(() => copyRecursive(safePath(src), safePath(dest)));
|
|
76
|
+
}
|
|
77
|
+
export function move(src, dest) {
|
|
78
|
+
return guard(() => fs.renameSync(safePath(src), safePath(dest)));
|
|
79
|
+
}
|
|
80
|
+
// recursive case-insensitive name search, bounded by limit
|
|
81
|
+
export function search(query, basePath, limit) {
|
|
82
|
+
return guard(() => {
|
|
83
|
+
const q = query.trim().toLowerCase();
|
|
84
|
+
if (!q)
|
|
85
|
+
return [];
|
|
86
|
+
const projectRoot = process.cwd();
|
|
87
|
+
const baseResolved = safePath(basePath || '.');
|
|
88
|
+
const ignored = loadIgnoredNames(projectRoot);
|
|
89
|
+
const results = [];
|
|
90
|
+
const stack = [baseResolved];
|
|
91
|
+
while (stack.length > 0 && results.length < limit) {
|
|
92
|
+
const dir = stack.pop();
|
|
93
|
+
let dirEntries;
|
|
94
|
+
try {
|
|
95
|
+
dirEntries = fs.readdirSync(dir, { withFileTypes: true });
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
for (const e of dirEntries) {
|
|
101
|
+
if (e.name.startsWith('.') || ignored.has(e.name))
|
|
102
|
+
continue;
|
|
103
|
+
const full = path.resolve(dir, e.name);
|
|
104
|
+
if (e.name.toLowerCase().includes(q)) {
|
|
105
|
+
let size = 0;
|
|
106
|
+
if (!e.isDirectory()) {
|
|
107
|
+
try {
|
|
108
|
+
size = fs.statSync(full).size;
|
|
109
|
+
}
|
|
110
|
+
catch { }
|
|
111
|
+
}
|
|
112
|
+
results.push({
|
|
113
|
+
name: e.name,
|
|
114
|
+
path: path.relative(projectRoot, full).replace(/\\/g, '/'),
|
|
115
|
+
isDirectory: e.isDirectory(),
|
|
116
|
+
size,
|
|
117
|
+
});
|
|
118
|
+
if (results.length >= limit)
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
if (e.isDirectory())
|
|
122
|
+
stack.push(full);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
results.sort((a, b) => a.isDirectory !== b.isDirectory ? (a.isDirectory ? -1 : 1) : a.name.localeCompare(b.name));
|
|
126
|
+
return results;
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=fileService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileService.js","sourceRoot":"","sources":["../../src/services/fileService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { httpRequest } from '../http/httpClient.js';
|
|
2
|
+
import { SERVER_HTTP } from '../core/config.js';
|
|
3
|
+
import { ClaudeAdapterProxy } from './claudeAdapterProxy.js';
|
|
4
|
+
// strip any existing --model / --model=... / -m flag pairs
|
|
5
|
+
export function removeModelArgs(args) {
|
|
6
|
+
const result = [];
|
|
7
|
+
for (let i = 0; i < args.length; i++) {
|
|
8
|
+
const a = args[i];
|
|
9
|
+
if (a === '--model' || a === '-m') {
|
|
10
|
+
i++;
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
if (a.startsWith('--model='))
|
|
14
|
+
continue;
|
|
15
|
+
result.push(a);
|
|
16
|
+
}
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
// orchestrates the currently-active model pushed from backend via ws
|
|
20
|
+
export class ModelService {
|
|
21
|
+
current = null;
|
|
22
|
+
adapterProxy = new ClaudeAdapterProxy();
|
|
23
|
+
getCurrent() { return this.current; }
|
|
24
|
+
// apply an incoming model payload to args and cache it for buildEnv
|
|
25
|
+
apply(payload, args) {
|
|
26
|
+
const cleaned = removeModelArgs(args);
|
|
27
|
+
if (payload && typeof payload === 'object') {
|
|
28
|
+
const p = payload;
|
|
29
|
+
const model = typeof p['model'] === 'string' ? p['model'] : '';
|
|
30
|
+
if (model) {
|
|
31
|
+
this.current = {
|
|
32
|
+
model,
|
|
33
|
+
baseURL: typeof p['baseURL'] === 'string' ? p['baseURL'] : undefined,
|
|
34
|
+
apiKey: typeof p['apiKey'] === 'string' ? p['apiKey'] : undefined,
|
|
35
|
+
};
|
|
36
|
+
cleaned.push('--model', model);
|
|
37
|
+
return cleaned;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
this.current = null;
|
|
41
|
+
return cleaned;
|
|
42
|
+
}
|
|
43
|
+
// build env overrides for claude pty based on current model
|
|
44
|
+
async buildEnv() {
|
|
45
|
+
const env = {
|
|
46
|
+
ANTHROPIC_BASE_URL: undefined,
|
|
47
|
+
ANTHROPIC_API_KEY: undefined,
|
|
48
|
+
ANTHROPIC_AUTH_TOKEN: undefined,
|
|
49
|
+
};
|
|
50
|
+
if (this.current?.baseURL) {
|
|
51
|
+
env.ANTHROPIC_BASE_URL = await this.resolveClaudeBaseURL(this.current);
|
|
52
|
+
env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
53
|
+
}
|
|
54
|
+
if (this.current?.apiKey)
|
|
55
|
+
env.ANTHROPIC_API_KEY = this.current.apiKey;
|
|
56
|
+
if (env.ANTHROPIC_BASE_URL?.startsWith('http://127.0.0.1:'))
|
|
57
|
+
env.ANTHROPIC_AUTH_TOKEN = 'default';
|
|
58
|
+
return env;
|
|
59
|
+
}
|
|
60
|
+
// normalize provider endpoints into Claude-compatible base urls
|
|
61
|
+
async resolveClaudeBaseURL(model) {
|
|
62
|
+
const baseURL = (model.baseURL ?? '').trim().replace(/\/+$/, '');
|
|
63
|
+
if (!baseURL)
|
|
64
|
+
return baseURL;
|
|
65
|
+
if (/\/v1\/messages$/i.test(baseURL)) {
|
|
66
|
+
await this.adapterProxy.stop();
|
|
67
|
+
return baseURL.replace(/\/v1\/messages$/i, '');
|
|
68
|
+
}
|
|
69
|
+
if (/\/v1\/chat\/completions$/i.test(baseURL)) {
|
|
70
|
+
return this.adapterProxy.start({
|
|
71
|
+
baseURL,
|
|
72
|
+
apiKey: model.apiKey ?? '',
|
|
73
|
+
model: model.model,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
await this.adapterProxy.stop();
|
|
77
|
+
return baseURL;
|
|
78
|
+
}
|
|
79
|
+
// fetch the user's current default model from backend using deviceToken
|
|
80
|
+
async fetchDefault(deviceToken) {
|
|
81
|
+
if (!deviceToken)
|
|
82
|
+
return null;
|
|
83
|
+
try {
|
|
84
|
+
const res = await httpRequest('GET', `${SERVER_HTTP()}/api/cli/default-model`, { token: deviceToken });
|
|
85
|
+
if (res.status !== 200 || !res.data || typeof res.data !== 'object')
|
|
86
|
+
return null;
|
|
87
|
+
const d = res.data;
|
|
88
|
+
const model = typeof d['model'] === 'string' ? d['model'] : '';
|
|
89
|
+
if (!model)
|
|
90
|
+
return null;
|
|
91
|
+
return {
|
|
92
|
+
model,
|
|
93
|
+
baseURL: typeof d['baseURL'] === 'string' ? d['baseURL'] : undefined,
|
|
94
|
+
apiKey: typeof d['apiKey'] === 'string' ? d['apiKey'] : undefined,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// refresh from backend when no current model is set; returns new args list
|
|
102
|
+
async ensureBeforeSpawn(args, deviceToken) {
|
|
103
|
+
if (this.current)
|
|
104
|
+
return args;
|
|
105
|
+
const def = await this.fetchDefault(deviceToken);
|
|
106
|
+
if (def)
|
|
107
|
+
return this.apply({ model: def.model, baseURL: def.baseURL, apiKey: def.apiKey }, args);
|
|
108
|
+
return args;
|
|
109
|
+
}
|
|
110
|
+
// release local adapter resources
|
|
111
|
+
async dispose() {
|
|
112
|
+
await this.adapterProxy.stop();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//# 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,76 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { ModelService } from './modelService.js';
|
|
4
|
+
import { convertRequestToOpenAI } from 'claude-adapter';
|
|
5
|
+
test('strips v1 messages suffix for Claude-compatible providers', async () => {
|
|
6
|
+
const service = new ModelService();
|
|
7
|
+
const url = await service.resolveClaudeBaseURL({
|
|
8
|
+
model: 'claude-haiku-4.5',
|
|
9
|
+
baseURL: 'https://api.example.com/root/v1/messages',
|
|
10
|
+
apiKey: 'sk-test',
|
|
11
|
+
});
|
|
12
|
+
assert.equal(url, 'https://api.example.com/root');
|
|
13
|
+
});
|
|
14
|
+
test('uses local adapter proxy for OpenAI chat completions providers', async () => {
|
|
15
|
+
const service = new ModelService();
|
|
16
|
+
const url = await service.resolveClaudeBaseURL({
|
|
17
|
+
model: 'deepseek-v4-flash-free',
|
|
18
|
+
baseURL: 'https://opencode.ai/zen/v1/chat/completions',
|
|
19
|
+
apiKey: 'public',
|
|
20
|
+
});
|
|
21
|
+
assert.match(url, /^http:\/\/127\.0\.0\.1:\d+$/);
|
|
22
|
+
await service.dispose();
|
|
23
|
+
});
|
|
24
|
+
test('keeps unknown provider url unchanged', async () => {
|
|
25
|
+
const service = new ModelService();
|
|
26
|
+
const url = await service.resolveClaudeBaseURL({
|
|
27
|
+
model: 'custom',
|
|
28
|
+
baseURL: 'https://provider.example.com/custom',
|
|
29
|
+
apiKey: 'sk-test',
|
|
30
|
+
});
|
|
31
|
+
assert.equal(url, 'https://provider.example.com/custom');
|
|
32
|
+
});
|
|
33
|
+
test('xml tool format avoids tool role messages for strict providers', () => {
|
|
34
|
+
const request = {
|
|
35
|
+
model: 'claude-sonnet-4',
|
|
36
|
+
max_tokens: 128,
|
|
37
|
+
messages: [
|
|
38
|
+
{ role: 'user', content: 'list files' },
|
|
39
|
+
{
|
|
40
|
+
role: 'assistant',
|
|
41
|
+
content: [
|
|
42
|
+
{
|
|
43
|
+
type: 'tool_use',
|
|
44
|
+
id: 'toolu_1',
|
|
45
|
+
name: 'ls',
|
|
46
|
+
input: { path: '.' },
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
role: 'user',
|
|
52
|
+
content: [
|
|
53
|
+
{
|
|
54
|
+
type: 'tool_result',
|
|
55
|
+
tool_use_id: 'toolu_1',
|
|
56
|
+
content: 'package.json',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
tools: [
|
|
62
|
+
{
|
|
63
|
+
name: 'ls',
|
|
64
|
+
description: 'list files',
|
|
65
|
+
input_schema: {
|
|
66
|
+
type: 'object',
|
|
67
|
+
properties: { path: { type: 'string' } },
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
const converted = convertRequestToOpenAI(request, 'deepseek-v4-flash-free', 'xml');
|
|
73
|
+
assert.deepEqual([...new Set(converted.messages.map((message) => message.role))].sort(), ['assistant', 'system', 'user']);
|
|
74
|
+
assert.equal(converted.messages.some((message) => message.role === 'tool'), false);
|
|
75
|
+
});
|
|
76
|
+
//# sourceMappingURL=modelService.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelService.test.js","sourceRoot":"","sources":["../../src/services/modelService.test.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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"}
|