kumo-cli 1.1.0 → 1.2.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/README.md +140 -131
- package/dist/auth/credentialsStore.js +19 -6
- package/dist/auth/credentialsStore.js.map +1 -1
- package/dist/claude/conversation.js +272 -0
- package/dist/claude/conversation.js.map +1 -0
- package/dist/claude/transcriptParser.js +98 -93
- package/dist/claude/transcriptParser.js.map +1 -1
- package/dist/commands/cliArgs.js +1 -0
- package/dist/commands/cliArgs.js.map +1 -1
- package/dist/core/config.js +0 -5
- package/dist/core/config.js.map +1 -1
- package/dist/core/logger.js +47 -6
- package/dist/core/logger.js.map +1 -1
- package/dist/core/projectFileWatcher.js +100 -36
- package/dist/core/projectFileWatcher.js.map +1 -1
- package/dist/engine/claudeArgs.js +4 -0
- package/dist/engine/claudeArgs.js.map +1 -1
- package/dist/engine/claudeBinary.js +14 -5
- package/dist/engine/claudeBinary.js.map +1 -1
- package/dist/engine/claudeConfig.js +20 -6
- package/dist/engine/claudeConfig.js.map +1 -1
- package/dist/engine/claudeLogin.js +76 -0
- package/dist/engine/claudeLogin.js.map +1 -0
- package/dist/engine/controlChannel.js +13 -1
- package/dist/engine/controlChannel.js.map +1 -1
- package/dist/engine/liveTranscript.js +70 -21
- package/dist/engine/liveTranscript.js.map +1 -1
- package/dist/engine/ndjson.js +15 -10
- package/dist/engine/ndjson.js.map +1 -1
- package/dist/engine/permissions.js +5 -2
- package/dist/engine/permissions.js.map +1 -1
- package/dist/engine/probePool.js +51 -0
- package/dist/engine/probePool.js.map +1 -0
- package/dist/engine/processTree.js +45 -0
- package/dist/engine/processTree.js.map +1 -0
- package/dist/engine/remoteConfig.js +17 -0
- package/dist/engine/remoteConfig.js.map +1 -0
- package/dist/engine/sessionManager.js +99 -84
- package/dist/engine/sessionManager.js.map +1 -1
- package/dist/engine/settingsCommands.js +56 -0
- package/dist/engine/settingsCommands.js.map +1 -1
- package/dist/engine/streamSession.js +59 -66
- package/dist/engine/streamSession.js.map +1 -1
- package/dist/engine/taskTracker.js +1 -1
- package/dist/engine/taskTracker.js.map +1 -1
- package/dist/engine/toolLabels.js +32 -0
- package/dist/engine/toolLabels.js.map +1 -0
- package/dist/handlers/controlRequest.js +17 -0
- package/dist/handlers/controlRequest.js.map +1 -0
- package/dist/handlers/fileHandlers.js +12 -0
- package/dist/handlers/fileHandlers.js.map +1 -1
- package/dist/handlers/messageRouter.js +8 -1
- package/dist/handlers/messageRouter.js.map +1 -1
- package/dist/handlers/promptImages.js +0 -55
- package/dist/handlers/promptImages.js.map +1 -1
- package/dist/handlers/runtimeHandlers.js +51 -151
- package/dist/handlers/runtimeHandlers.js.map +1 -1
- package/dist/handlers/sessionHandlers.js +48 -14
- package/dist/handlers/sessionHandlers.js.map +1 -1
- package/dist/handlers/terminalHandlers.js +9 -2
- package/dist/handlers/terminalHandlers.js.map +1 -1
- package/dist/http/httpClient.js +6 -0
- package/dist/http/httpClient.js.map +1 -1
- package/dist/index.js +58 -16
- package/dist/index.js.map +1 -1
- package/dist/pty/terminalManager.js +58 -11
- package/dist/pty/terminalManager.js.map +1 -1
- package/dist/services/bootstrapService.js +17 -1
- package/dist/services/bootstrapService.js.map +1 -1
- package/dist/services/claudeAdapterProxy.js +68 -21
- package/dist/services/claudeAdapterProxy.js.map +1 -1
- package/dist/services/cliRuntime.js +5 -5
- package/dist/services/cliRuntime.js.map +1 -1
- package/dist/services/fileService.js +50 -24
- package/dist/services/fileService.js.map +1 -1
- package/dist/services/forkService.js +1 -1
- package/dist/services/forkService.js.map +1 -1
- package/dist/services/modelService.js +62 -18
- package/dist/services/modelService.js.map +1 -1
- package/dist/services/pairingService.js +21 -15
- package/dist/services/pairingService.js.map +1 -1
- package/dist/services/sessionService.js +204 -85
- package/dist/services/sessionService.js.map +1 -1
- package/dist/services/tunnelHelpers.js +9 -5
- package/dist/services/tunnelHelpers.js.map +1 -1
- package/dist/services/tunnelService.js +17 -0
- package/dist/services/tunnelService.js.map +1 -1
- package/dist/transport/wsClient.js +60 -38
- package/dist/transport/wsClient.js.map +1 -1
- package/dist/utils/json.js +7 -0
- package/dist/utils/json.js.map +1 -0
- package/dist/utils/localHost.js +17 -0
- package/dist/utils/localHost.js.map +1 -0
- package/dist/utils/safePath.js +51 -20
- package/dist/utils/safePath.js.map +1 -1
- package/package.json +62 -62
|
@@ -32,8 +32,15 @@ export function createTerminalHandlers() {
|
|
|
32
32
|
log(`[terminal] closed: ${terminalId}`);
|
|
33
33
|
}
|
|
34
34
|
},
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
// replay: an app back on screen redraws each terminal from its recent output
|
|
36
|
+
terminal_list: (msg, ctx) => {
|
|
37
|
+
const terminals = ctx.terminals.list();
|
|
38
|
+
ctx.send({ type: 'terminal_list', terminals });
|
|
39
|
+
if (msg['replay'] !== true)
|
|
40
|
+
return;
|
|
41
|
+
for (const terminalId of terminals) {
|
|
42
|
+
ctx.send({ type: 'terminal_output', terminalId, data: ctx.terminals.recentOutput(terminalId), replace: true });
|
|
43
|
+
}
|
|
37
44
|
},
|
|
38
45
|
};
|
|
39
46
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminalHandlers.js","sourceRoot":"","sources":["../../src/handlers/terminalHandlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAGxC,uEAAuE;AACvE,MAAM,UAAU,sBAAsB;IACpC,OAAO;QACL,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC5B,MAAM,IAAI,GAAI,GAAG,CAAC,MAAM,CAAY,IAAI,GAAG,CAAC;YAC5C,MAAM,IAAI,GAAI,GAAG,CAAC,MAAM,CAAY,IAAI,EAAE,CAAC;YAC3C,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAuB,CAAC;YAC7C,MAAM,UAAU,GAAG,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;YACzD,GAAG,CAAC,uBAAuB,UAAU,EAAE,CAAC,CAAC;YACzC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,UAAU,EAAE,CAAC,CAAC;QACrD,CAAC;QACD,cAAc,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC3B,MAAM,UAAU,GAAG,GAAG,CAAC,YAAY,CAAW,CAAC;YAC/C,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAW,CAAC;YACnC,IAAI,UAAU,IAAI,IAAI;gBAAE,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAChE,CAAC;QACD,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC5B,MAAM,UAAU,GAAG,GAAG,CAAC,YAAY,CAAW,CAAC;YAC/C,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAW,CAAC;YACnC,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAW,CAAC;YACnC,IAAI,UAAU,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;gBAC/B,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;gBAC7C,GAAG,CAAC,qBAAqB,UAAU,OAAO,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;QACD,cAAc,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC3B,MAAM,UAAU,GAAG,GAAG,CAAC,YAAY,CAAW,CAAC;YAC/C,IAAI,UAAU,EAAE,CAAC;gBACf,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;gBAChC,GAAG,CAAC,sBAAsB,UAAU,EAAE,CAAC,CAAC;YAC1C,CAAC;QACH,CAAC;QACD,aAAa,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"terminalHandlers.js","sourceRoot":"","sources":["../../src/handlers/terminalHandlers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAGxC,uEAAuE;AACvE,MAAM,UAAU,sBAAsB;IACpC,OAAO;QACL,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC5B,MAAM,IAAI,GAAI,GAAG,CAAC,MAAM,CAAY,IAAI,GAAG,CAAC;YAC5C,MAAM,IAAI,GAAI,GAAG,CAAC,MAAM,CAAY,IAAI,EAAE,CAAC;YAC3C,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAuB,CAAC;YAC7C,MAAM,UAAU,GAAG,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;YACzD,GAAG,CAAC,uBAAuB,UAAU,EAAE,CAAC,CAAC;YACzC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,UAAU,EAAE,CAAC,CAAC;QACrD,CAAC;QACD,cAAc,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC3B,MAAM,UAAU,GAAG,GAAG,CAAC,YAAY,CAAW,CAAC;YAC/C,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAW,CAAC;YACnC,IAAI,UAAU,IAAI,IAAI;gBAAE,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAChE,CAAC;QACD,eAAe,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC5B,MAAM,UAAU,GAAG,GAAG,CAAC,YAAY,CAAW,CAAC;YAC/C,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAW,CAAC;YACnC,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAW,CAAC;YACnC,IAAI,UAAU,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;gBAC/B,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;gBAC7C,GAAG,CAAC,qBAAqB,UAAU,OAAO,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;QACD,cAAc,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC3B,MAAM,UAAU,GAAG,GAAG,CAAC,YAAY,CAAW,CAAC;YAC/C,IAAI,UAAU,EAAE,CAAC;gBACf,GAAG,CAAC,SAAS,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;gBAChC,GAAG,CAAC,sBAAsB,UAAU,EAAE,CAAC,CAAC;YAC1C,CAAC;QACH,CAAC;QACD,6EAA6E;QAC7E,aAAa,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC1B,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;YACvC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,CAAC,CAAC;YAC/C,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,IAAI;gBAAE,OAAO;YACnC,KAAK,MAAM,UAAU,IAAI,SAAS,EAAE,CAAC;gBACnC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,CAAC,SAAS,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACjH,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/http/httpClient.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import http from 'node:http';
|
|
2
2
|
import https from 'node:https';
|
|
3
|
+
// a server that stops answering (a half-open connection) fails the request instead of
|
|
4
|
+
// hanging the spawn or startup waiting on it
|
|
5
|
+
const REQUEST_TIMEOUT_MS = 10_000;
|
|
3
6
|
// unified http request with optional bearer auth and json body. parses json when possible.
|
|
4
7
|
export function httpRequest(method, url, opts = {}) {
|
|
5
8
|
return new Promise((resolve, reject) => {
|
|
@@ -21,6 +24,8 @@ export function httpRequest(method, url, opts = {}) {
|
|
|
21
24
|
method,
|
|
22
25
|
headers,
|
|
23
26
|
}, (res) => {
|
|
27
|
+
// decode as a stream, so a character split across chunks stays whole
|
|
28
|
+
res.setEncoding('utf8');
|
|
24
29
|
let raw = '';
|
|
25
30
|
res.on('data', (chunk) => { raw += chunk; });
|
|
26
31
|
res.on('end', () => {
|
|
@@ -32,6 +37,7 @@ export function httpRequest(method, url, opts = {}) {
|
|
|
32
37
|
}
|
|
33
38
|
});
|
|
34
39
|
});
|
|
40
|
+
req.setTimeout(opts.timeoutMs ?? REQUEST_TIMEOUT_MS, () => req.destroy(new Error(`request timed out: ${method} ${urlObj.pathname}`)));
|
|
35
41
|
req.on('error', reject);
|
|
36
42
|
if (payload)
|
|
37
43
|
req.write(payload);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"httpClient.js","sourceRoot":"","sources":["../../src/http/httpClient.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,KAAK,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"httpClient.js","sourceRoot":"","sources":["../../src/http/httpClient.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,KAAK,MAAM,YAAY,CAAC;AAc/B,sFAAsF;AACtF,6CAA6C;AAC7C,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAElC,2FAA2F;AAC3F,MAAM,UAAU,WAAW,CACzB,MAAyC,EACzC,GAAW,EACX,OAA2B,EAAE;IAE7B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC;QAC7C,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;QACzC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAClE,MAAM,OAAO,GAA2B,EAAE,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,CAAC;QACpE,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;YAC7C,OAAO,CAAC,gBAAgB,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;QACjE,CAAC;QACD,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO,CAAC,eAAe,CAAC,GAAG,UAAU,IAAI,CAAC,KAAK,EAAE,CAAC;QAClE,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAC;YAC5B,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YACzC,IAAI,EAAE,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM;YACrC,MAAM;YACN,OAAO;SACR,EAAE,CAAC,GAAG,EAAE,EAAE;YACT,qEAAqE;YACrE,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YACxB,IAAI,GAAG,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,GAAG,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7C,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;gBACjB,IAAI,CAAC;oBAAC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,UAAU,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAAC,CAAC;gBACxE,MAAM,CAAC;oBAAC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,UAAU,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;gBAAC,CAAC;YAChE,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,IAAI,kBAAkB,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,sBAAsB,MAAM,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;QACtI,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACxB,IAAI,OAAO;YAAE,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAChC,GAAG,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,21 +5,35 @@ import { loadDotEnv } from './core/loadDotEnv.js';
|
|
|
5
5
|
import { log } from './core/logger.js';
|
|
6
6
|
import { ProjectFileWatcher } from './core/projectFileWatcher.js';
|
|
7
7
|
import { SessionManager } from './engine/sessionManager.js';
|
|
8
|
+
import { allowRemoteUserConfig } from './engine/remoteConfig.js';
|
|
9
|
+
import { SERVER_HTTP, SERVER_WS } from './core/config.js';
|
|
10
|
+
import { isCleartextRemote } from './utils/localHost.js';
|
|
8
11
|
import { ModelService } from './services/modelService.js';
|
|
9
12
|
import { EffortService } from './services/effortService.js';
|
|
10
13
|
import { printBanner } from './services/pairingService.js';
|
|
11
14
|
import { resolveActiveCredentials } from './services/bootstrapService.js';
|
|
12
15
|
import { createSpawnSpecFactory, createWsOpenHandler, resolveLaunchConfig } from './services/cliRuntime.js';
|
|
13
16
|
import { WsClient } from './transport/wsClient.js';
|
|
17
|
+
import { resolveClaudeCommand } from './engine/claudeBinary.js';
|
|
14
18
|
import { createMessageRouter } from './handlers/messageRouter.js';
|
|
15
19
|
import { chooseAgent } from './commands/agentPrompt.js';
|
|
16
20
|
import { extractEnvFileArg, parseCommand, printHelp } from './commands/cliArgs.js';
|
|
21
|
+
// sessions get this long to close on ctrl+c before the cli exits anyway
|
|
22
|
+
const SHUTDOWN_DEADLINE_MS = 5000;
|
|
17
23
|
const { envFilePath, cliArgs: bootArgs } = extractEnvFileArg(process.argv.slice(2));
|
|
18
24
|
if (envFilePath) {
|
|
19
25
|
loadDotEnv(envFilePath);
|
|
20
26
|
}
|
|
21
27
|
let activeCredentials = null;
|
|
22
|
-
|
|
28
|
+
// last resort: one bad callback (a watcher, a socket) must not take every claude session and
|
|
29
|
+
// terminal down with it
|
|
30
|
+
process.on('uncaughtException', (error) => {
|
|
31
|
+
log(`[fatal] uncaught exception: ${error.stack ?? error}`);
|
|
32
|
+
console.error(`[kumo-cli] unexpected error: ${error.message}`);
|
|
33
|
+
});
|
|
34
|
+
process.on('unhandledRejection', (reason) => {
|
|
35
|
+
log(`[fatal] unhandled rejection: ${reason instanceof Error ? reason.stack : String(reason)}`);
|
|
36
|
+
});
|
|
23
37
|
async function main() {
|
|
24
38
|
const command = parseCommand(bootArgs);
|
|
25
39
|
if (command.action === 'help') {
|
|
@@ -40,9 +54,16 @@ async function main() {
|
|
|
40
54
|
return;
|
|
41
55
|
}
|
|
42
56
|
const launch = resolveLaunchConfig(command.args);
|
|
57
|
+
allowRemoteUserConfig(launch.allowRemoteConfig);
|
|
43
58
|
printBanner();
|
|
59
|
+
if (isCleartextRemote(SERVER_HTTP()) || isCleartextRemote(SERVER_WS())) {
|
|
60
|
+
console.warn('\x1b[33m[kumo-cli] warning: the backend url is not https/wss, so the device token and everything relayed travel in the clear\x1b[0m');
|
|
61
|
+
}
|
|
44
62
|
let ws;
|
|
45
63
|
const send = (data) => ws.send(data);
|
|
64
|
+
// whether an app shows this cli's output; while none does, the live transcript is held back
|
|
65
|
+
// (the app reloads the history page when it comes back)
|
|
66
|
+
let watched = true;
|
|
46
67
|
const fileWatcher = new ProjectFileWatcher(send);
|
|
47
68
|
const model = new ModelService();
|
|
48
69
|
const effort = new EffortService();
|
|
@@ -52,8 +73,12 @@ async function main() {
|
|
|
52
73
|
send({ type: 'terminal_closed', terminalId, exitCode });
|
|
53
74
|
});
|
|
54
75
|
const sessions = new SessionManager({
|
|
55
|
-
|
|
56
|
-
|
|
76
|
+
emit: (msg) => {
|
|
77
|
+
if (!watched && msg['type'] === 'transcript')
|
|
78
|
+
return;
|
|
79
|
+
// cli_status from a session carries no effort; fill it so the backend keeps it
|
|
80
|
+
send(msg['type'] === 'cli_status' ? { effort: effort.getEffective(), ...msg } : msg);
|
|
81
|
+
},
|
|
57
82
|
spawnSpec: createSpawnSpecFactory({
|
|
58
83
|
model,
|
|
59
84
|
effort,
|
|
@@ -66,10 +91,13 @@ async function main() {
|
|
|
66
91
|
});
|
|
67
92
|
let shuttingDown = false;
|
|
68
93
|
const shutdown = (code) => {
|
|
94
|
+
// a second ctrl+c means now, whatever is still closing
|
|
69
95
|
if (shuttingDown)
|
|
70
|
-
|
|
96
|
+
process.exit(code);
|
|
71
97
|
shuttingDown = true;
|
|
72
|
-
console.log('\n[kumo-cli] closing claude sessions...');
|
|
98
|
+
console.log('\n[kumo-cli] closing claude sessions... (ctrl+c again to quit now)');
|
|
99
|
+
// a session stuck starting must not keep the cli from exiting
|
|
100
|
+
setTimeout(() => process.exit(code), SHUTDOWN_DEADLINE_MS).unref();
|
|
73
101
|
void Promise.allSettled([sessions.closeAll(), model.dispose()]).finally(() => {
|
|
74
102
|
ws?.close();
|
|
75
103
|
process.exit(code);
|
|
@@ -89,28 +117,42 @@ async function main() {
|
|
|
89
117
|
process.exit(1);
|
|
90
118
|
}
|
|
91
119
|
console.log(`\x1b[32m✓ Connected as ${activeCredentials.user?.email ?? activeCredentials.user?.id ?? 'device'}\x1b[0m`);
|
|
120
|
+
if (launch.allowRemoteConfig)
|
|
121
|
+
console.log('[kumo-cli] the app may change user-wide hooks, permission rules and mcp servers');
|
|
92
122
|
console.log(`[kumo-cli] claude sessions run on demand (permission mode: ${launch.permissionMode}). press ctrl+c to quit.\n`);
|
|
93
123
|
const router = createMessageRouter({
|
|
94
124
|
send,
|
|
95
|
-
sessions,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
125
|
+
sessions, effort, terminals,
|
|
126
|
+
files: fileWatcher,
|
|
127
|
+
onAppPresence: (watching) => {
|
|
128
|
+
log(`[presence] app ${watching ? 'on screen' : 'away'}`);
|
|
129
|
+
watched = watching;
|
|
130
|
+
terminals.setLive(watching);
|
|
131
|
+
if (!watching)
|
|
132
|
+
fileWatcher.stop();
|
|
133
|
+
},
|
|
134
|
+
onAuthError: () => {
|
|
135
|
+
ws.close();
|
|
136
|
+
shutdown(1);
|
|
101
137
|
},
|
|
102
|
-
onAuthError: () => { ws.close(); },
|
|
103
138
|
});
|
|
104
139
|
ws = new WsClient(createWsOpenHandler({
|
|
105
140
|
send,
|
|
106
141
|
credentials: () => activeCredentials,
|
|
107
142
|
liveSessionIds: () => sessions.liveSessionIds(),
|
|
108
143
|
effort: () => effort.getEffective(),
|
|
109
|
-
restartWatcher: () => fileWatcher.restart(),
|
|
110
144
|
refreshAuth: () => sessions.refreshAuth(),
|
|
111
|
-
}), router,
|
|
112
|
-
|
|
113
|
-
|
|
145
|
+
}), router,
|
|
146
|
+
// the app subscribes again after a reconnect
|
|
147
|
+
() => fileWatcher.stop());
|
|
148
|
+
// find (or install) claude now: a first-run npm install, run synchronously, would otherwise
|
|
149
|
+
// freeze the open socket's heartbeats for its whole length
|
|
150
|
+
try {
|
|
151
|
+
resolveClaudeCommand();
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
console.warn(`[kumo-cli] ${error.message}`);
|
|
155
|
+
}
|
|
114
156
|
ws.connect();
|
|
115
157
|
}
|
|
116
158
|
main().catch((err) => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EACL,gBAAgB,EAChB,gBAAgB,GAEjB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC5G,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEnF,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACpF,IAAI,WAAW,EAAE,CAAC;IAChB,UAAU,CAAC,WAAW,CAAC,CAAC;AAC1B,CAAC;AAED,IAAI,iBAAiB,GAA2B,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,34 +2,62 @@ import * as pty from 'node-pty';
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import { randomUUID } from 'node:crypto';
|
|
4
4
|
import { safeCwd } from '../utils/safePath.js';
|
|
5
|
+
// pty output is coalesced into one frame per window instead of one frame per chunk
|
|
6
|
+
const OUTPUT_FLUSH_MS = 30;
|
|
7
|
+
const RECENT_OUTPUT_CHARS = 256 * 1024;
|
|
8
|
+
const clamp = (value, min, max) => Math.min(Math.max(Math.trunc(value), min), max);
|
|
9
|
+
const clampSize = (cols, rows) => ({ cols: clamp(cols, 20, 300), rows: clamp(rows, 5, 100) });
|
|
5
10
|
// manages multiple user-facing shell sessions backed by node-pty
|
|
6
11
|
export class TerminalManager {
|
|
7
12
|
onData;
|
|
8
13
|
onExit;
|
|
9
14
|
terminals = new Map();
|
|
15
|
+
// output only streams while an app shows it; otherwise it just piles up in `recent`
|
|
16
|
+
live = true;
|
|
10
17
|
constructor(onData, onExit) {
|
|
11
18
|
this.onData = onData;
|
|
12
19
|
this.onExit = onExit;
|
|
13
20
|
}
|
|
14
|
-
create(cols
|
|
21
|
+
create(cols, rows, cwd) {
|
|
15
22
|
const id = randomUUID();
|
|
16
23
|
const shell = os.platform() === 'win32' ? 'cmd.exe' : process.env.SHELL || '/bin/bash';
|
|
17
|
-
const safeCols = Math.min(Math.max(Math.trunc(cols), 20), 300);
|
|
18
|
-
const safeRows = Math.min(Math.max(Math.trunc(rows), 5), 100);
|
|
19
24
|
const child = pty.spawn(shell, [], {
|
|
20
25
|
name: 'xterm-256color',
|
|
21
|
-
cols
|
|
26
|
+
...clampSize(cols, rows),
|
|
22
27
|
cwd: safeCwd(cwd),
|
|
23
28
|
env: process.env,
|
|
24
29
|
});
|
|
25
|
-
|
|
30
|
+
const session = { id, pty: child, pending: '', flushTimer: null, recent: [], recentChars: 0 };
|
|
31
|
+
child.onData((data) => {
|
|
32
|
+
this.remember(session, data);
|
|
33
|
+
if (!this.live)
|
|
34
|
+
return;
|
|
35
|
+
session.pending += data;
|
|
36
|
+
session.flushTimer ??= setTimeout(() => this.flush(session), OUTPUT_FLUSH_MS);
|
|
37
|
+
});
|
|
26
38
|
child.onExit(({ exitCode }) => {
|
|
39
|
+
this.flush(session);
|
|
27
40
|
this.terminals.delete(id);
|
|
28
41
|
this.onExit(id, exitCode);
|
|
29
42
|
});
|
|
30
|
-
this.terminals.set(id,
|
|
43
|
+
this.terminals.set(id, session);
|
|
31
44
|
return id;
|
|
32
45
|
}
|
|
46
|
+
setLive(live) {
|
|
47
|
+
this.live = live;
|
|
48
|
+
if (live)
|
|
49
|
+
return;
|
|
50
|
+
for (const session of this.terminals.values()) {
|
|
51
|
+
if (session.flushTimer)
|
|
52
|
+
clearTimeout(session.flushTimer);
|
|
53
|
+
session.flushTimer = null;
|
|
54
|
+
session.pending = '';
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
recentOutput(id) {
|
|
58
|
+
const session = this.terminals.get(id);
|
|
59
|
+
return session ? session.recent.join('').slice(-RECENT_OUTPUT_CHARS) : '';
|
|
60
|
+
}
|
|
33
61
|
write(id, data) {
|
|
34
62
|
const session = this.terminals.get(id);
|
|
35
63
|
if (!session)
|
|
@@ -42,11 +70,8 @@ export class TerminalManager {
|
|
|
42
70
|
if (!session)
|
|
43
71
|
return false;
|
|
44
72
|
try {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
session.pty.resize(safeCols, safeRows);
|
|
48
|
-
session.cols = safeCols;
|
|
49
|
-
session.rows = safeRows;
|
|
73
|
+
const size = clampSize(cols, rows);
|
|
74
|
+
session.pty.resize(size.cols, size.rows);
|
|
50
75
|
return true;
|
|
51
76
|
}
|
|
52
77
|
catch {
|
|
@@ -57,6 +82,8 @@ export class TerminalManager {
|
|
|
57
82
|
const session = this.terminals.get(id);
|
|
58
83
|
if (!session)
|
|
59
84
|
return false;
|
|
85
|
+
if (session.flushTimer)
|
|
86
|
+
clearTimeout(session.flushTimer);
|
|
60
87
|
try {
|
|
61
88
|
session.pty.kill();
|
|
62
89
|
}
|
|
@@ -67,5 +94,25 @@ export class TerminalManager {
|
|
|
67
94
|
list() {
|
|
68
95
|
return Array.from(this.terminals.keys());
|
|
69
96
|
}
|
|
97
|
+
// keep the chunks, dropping whole old ones once they hold twice what a replay needs
|
|
98
|
+
remember(session, data) {
|
|
99
|
+
session.recent.push(data);
|
|
100
|
+
session.recentChars += data.length;
|
|
101
|
+
if (session.recentChars <= 2 * RECENT_OUTPUT_CHARS)
|
|
102
|
+
return;
|
|
103
|
+
while (session.recentChars - session.recent[0].length >= RECENT_OUTPUT_CHARS) {
|
|
104
|
+
session.recentChars -= session.recent.shift().length;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
flush(session) {
|
|
108
|
+
if (session.flushTimer)
|
|
109
|
+
clearTimeout(session.flushTimer);
|
|
110
|
+
session.flushTimer = null;
|
|
111
|
+
if (!session.pending)
|
|
112
|
+
return;
|
|
113
|
+
const data = session.pending;
|
|
114
|
+
session.pending = '';
|
|
115
|
+
this.onData(session.id, data);
|
|
116
|
+
}
|
|
70
117
|
}
|
|
71
118
|
//# sourceMappingURL=terminalManager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminalManager.js","sourceRoot":"","sources":["../../src/pty/terminalManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"terminalManager.js","sourceRoot":"","sources":["../../src/pty/terminalManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,22 @@ export async function ensureBackendReachableOrThrow() {
|
|
|
7
7
|
return;
|
|
8
8
|
throw new Error(`[kumo-cli] cannot reach backend at ${SERVER_HTTP()}. check KUMO_SERVER_URL and that the server is running.`);
|
|
9
9
|
}
|
|
10
|
+
const VERIFY_ATTEMPTS = 3;
|
|
11
|
+
// a passing backend hiccup gets a few tries; past that the cli stops with the token kept
|
|
12
|
+
async function verifyWithRetry(creds, delayMs = 2000) {
|
|
13
|
+
for (let attempt = 1;; attempt++) {
|
|
14
|
+
try {
|
|
15
|
+
return await verifyCredentials(creds);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (attempt >= VERIFY_ATTEMPTS) {
|
|
19
|
+
throw new Error(`[kumo-cli] could not check the saved device token (${error.message}). it is kept; try again in a moment.`);
|
|
20
|
+
}
|
|
21
|
+
console.warn(`[kumo-cli] backend not ready (${error.message}), retrying...`);
|
|
22
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs * attempt));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
10
26
|
// restore cached credentials when possible, otherwise fall back to a fresh pairing flow
|
|
11
27
|
export async function resolveActiveCredentials() {
|
|
12
28
|
await ensureBackendReachableOrThrow();
|
|
@@ -15,7 +31,7 @@ export async function resolveActiveCredentials() {
|
|
|
15
31
|
return pairFlow();
|
|
16
32
|
}
|
|
17
33
|
ensureDeviceUuid(activeCredentials.deviceUuid);
|
|
18
|
-
const verified = await
|
|
34
|
+
const verified = await verifyWithRetry(activeCredentials);
|
|
19
35
|
if (!verified) {
|
|
20
36
|
console.warn('[kumo-cli] cached device token was revoked or deleted, clearing credentials and re-pairing with the existing device identity...');
|
|
21
37
|
clearCredentials();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrapService.js","sourceRoot":"","sources":["../../src/services/bootstrapService.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,kBAAkB,GAEnB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAEzF,0DAA0D;AAC1D,MAAM,CAAC,KAAK,UAAU,6BAA6B;IACjD,IAAI,MAAM,qBAAqB,EAAE;QAAE,OAAO;IAC1C,MAAM,IAAI,KAAK,CAAC,sCAAsC,WAAW,EAAE,yDAAyD,CAAC,CAAC;AAChI,CAAC;AAED,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,wBAAwB;IAC5C,MAAM,6BAA6B,EAAE,CAAC;IAEtC,IAAI,iBAAiB,GAAG,eAAe,EAAE,CAAC;IAC1C,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,OAAO,QAAQ,EAAE,CAAC;IACpB,CAAC;IAED,gBAAgB,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"bootstrapService.js","sourceRoot":"","sources":["../../src/services/bootstrapService.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,kBAAkB,GAEnB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAEzF,0DAA0D;AAC1D,MAAM,CAAC,KAAK,UAAU,6BAA6B;IACjD,IAAI,MAAM,qBAAqB,EAAE;QAAE,OAAO;IAC1C,MAAM,IAAI,KAAK,CAAC,sCAAsC,WAAW,EAAE,yDAAyD,CAAC,CAAC;AAChI,CAAC;AAED,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B,yFAAyF;AACzF,KAAK,UAAU,eAAe,CAAC,KAAsB,EAAE,OAAO,GAAG,IAAI;IACnE,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,OAAO,MAAM,iBAAiB,CAAC,KAAK,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,OAAO,IAAI,eAAe,EAAE,CAAC;gBAC/B,MAAM,IAAI,KAAK,CAAC,sDAAuD,KAAe,CAAC,OAAO,uCAAuC,CAAC,CAAC;YACzI,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,iCAAkC,KAAe,CAAC,OAAO,gBAAgB,CAAC,CAAC;YACxF,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;AACH,CAAC;AAED,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,wBAAwB;IAC5C,MAAM,6BAA6B,EAAE,CAAC;IAEtC,IAAI,iBAAiB,GAAG,eAAe,EAAE,CAAC;IAC1C,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,OAAO,QAAQ,EAAE,CAAC;IACpB,CAAC;IAED,gBAAgB,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;IAC/C,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,iBAAiB,CAAC,CAAC;IAC1D,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,CAAC,IAAI,CAAC,iIAAiI,CAAC,CAAC;QAChJ,gBAAgB,EAAE,CAAC;QACnB,OAAO,QAAQ,EAAE,CAAC;IACpB,CAAC;IAED,kBAAkB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IACxC,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC1B,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -1,24 +1,53 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
2
|
import Fastify from 'fastify';
|
|
3
3
|
import OpenAI from 'openai';
|
|
4
4
|
import { convertRequestToOpenAI, convertResponseToAnthropic, createErrorResponse, } from 'claude-adapter';
|
|
5
5
|
import { streamXmlOpenAIToAnthropic } from 'claude-adapter/dist/converters/xmlStreaming.js';
|
|
6
6
|
// local Anthropic-compatible proxy that forwards to OpenAI chat/completions upstream
|
|
7
|
-
// bypasses claude-adapter's strict validation to support all Claude Code message roles
|
|
7
|
+
// bypasses claude-adapter's strict validation to support all Claude Code message roles.
|
|
8
|
+
// it spends the provider key, so only a caller holding its random token (the claude it was
|
|
9
|
+
// started for) and naming it by its loopback host gets through
|
|
8
10
|
export class ClaudeAdapterProxy {
|
|
9
11
|
app = null;
|
|
10
12
|
key = null;
|
|
11
13
|
url = null;
|
|
14
|
+
secret = null;
|
|
15
|
+
// starts and stops run one at a time, so two spawns never leave a second server behind
|
|
16
|
+
queue = Promise.resolve();
|
|
17
|
+
// the bearer token claude must send (ANTHROPIC_AUTH_TOKEN)
|
|
18
|
+
get token() { return this.secret; }
|
|
12
19
|
// start or reuse a local proxy for OpenAI chat completions
|
|
13
|
-
|
|
20
|
+
start(config) {
|
|
21
|
+
return this.serial(() => this.startNow(config));
|
|
22
|
+
}
|
|
23
|
+
// stop the current adapter proxy if one is running
|
|
24
|
+
stop() {
|
|
25
|
+
return this.serial(() => this.stopNow());
|
|
26
|
+
}
|
|
27
|
+
serial(run) {
|
|
28
|
+
const next = this.queue.then(run);
|
|
29
|
+
this.queue = next.catch(() => undefined);
|
|
30
|
+
return next;
|
|
31
|
+
}
|
|
32
|
+
async startNow(config) {
|
|
14
33
|
const key = `${config.baseURL}|${config.apiKey}|${config.model}`;
|
|
15
34
|
if (this.app && this.key === key && this.url)
|
|
16
35
|
return this.url;
|
|
17
|
-
await this.
|
|
18
|
-
const port = await findAvailablePort();
|
|
36
|
+
await this.stopNow();
|
|
19
37
|
const upstreamBaseUrl = config.baseURL.replace(/\/chat\/completions\/?$/i, '');
|
|
20
38
|
const openai = new OpenAI({ baseURL: upstreamBaseUrl, apiKey: config.apiKey });
|
|
21
39
|
const app = Fastify({ logger: false });
|
|
40
|
+
const secret = randomBytes(24).toString('hex');
|
|
41
|
+
let port = 0;
|
|
42
|
+
app.addHook('onRequest', async (request, reply) => {
|
|
43
|
+
const host = request.headers.host ?? '';
|
|
44
|
+
const auth = request.headers.authorization ?? '';
|
|
45
|
+
const apiKey = request.headers['x-api-key'];
|
|
46
|
+
const allowedHost = host === `127.0.0.1:${port}` || host === `localhost:${port}`;
|
|
47
|
+
if (!allowedHost || (auth !== `Bearer ${secret}` && apiKey !== secret)) {
|
|
48
|
+
return reply.code(401).send({ type: 'error', error: { type: 'authentication_error', message: 'unauthorized' } });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
22
51
|
app.post('/v1/messages', async (request, reply) => {
|
|
23
52
|
try {
|
|
24
53
|
const body = request.body;
|
|
@@ -29,7 +58,8 @@ export class ClaudeAdapterProxy {
|
|
|
29
58
|
stripAdapterBranding(openaiRequest.messages);
|
|
30
59
|
if (isStreaming) {
|
|
31
60
|
const stream = await openai.chat.completions.create({ ...openaiRequest, stream: true });
|
|
32
|
-
|
|
61
|
+
const counted = reportPromptTokens(stream, reply.raw);
|
|
62
|
+
await streamXmlOpenAIToAnthropic(counted, reply, body.model, upstreamBaseUrl);
|
|
33
63
|
}
|
|
34
64
|
else {
|
|
35
65
|
const response = await openai.chat.completions.create({ ...openaiRequest, stream: false });
|
|
@@ -42,23 +72,52 @@ export class ClaudeAdapterProxy {
|
|
|
42
72
|
reply.code(errResp.status).send({ error: errResp.error });
|
|
43
73
|
}
|
|
44
74
|
});
|
|
45
|
-
|
|
75
|
+
// port 0: the os picks a free port at bind time, no check-then-bind race
|
|
76
|
+
await app.listen({ port: 0, host: '127.0.0.1' });
|
|
77
|
+
port = app.server.address().port;
|
|
46
78
|
this.app = app;
|
|
47
79
|
this.key = key;
|
|
80
|
+
this.secret = secret;
|
|
48
81
|
this.url = `http://127.0.0.1:${port}`;
|
|
49
82
|
return this.url;
|
|
50
83
|
}
|
|
51
|
-
|
|
52
|
-
async stop() {
|
|
84
|
+
async stopNow() {
|
|
53
85
|
if (!this.app)
|
|
54
86
|
return;
|
|
55
87
|
const app = this.app;
|
|
56
88
|
this.app = null;
|
|
57
89
|
this.key = null;
|
|
58
90
|
this.url = null;
|
|
91
|
+
this.secret = null;
|
|
59
92
|
await app.close().catch(() => undefined);
|
|
60
93
|
}
|
|
61
94
|
}
|
|
95
|
+
// the prompt's tokens only come with the stream's last chunk: claude-adapter has sent message_start
|
|
96
|
+
// with 0 by then, and its message_delta leaves them out. they go into that message_delta, so claude
|
|
97
|
+
// knows how big each turn was (and when to compact) instead of reading 0
|
|
98
|
+
function reportPromptTokens(stream, raw) {
|
|
99
|
+
let prompt;
|
|
100
|
+
let cached = 0;
|
|
101
|
+
const write = raw.write.bind(raw);
|
|
102
|
+
raw.write = ((chunk, ...rest) => {
|
|
103
|
+
if (prompt !== undefined && typeof chunk === 'string' && chunk.startsWith('data: ') && chunk.includes('"message_delta"')) {
|
|
104
|
+
const event = JSON.parse(chunk.slice('data: '.length));
|
|
105
|
+
// anthropic counts cache reads apart from the input, openai inside it
|
|
106
|
+
event.usage = { ...event.usage, input_tokens: Math.max(prompt - cached, 0), cache_read_input_tokens: cached };
|
|
107
|
+
chunk = `data: ${JSON.stringify(event)}\n\n`;
|
|
108
|
+
}
|
|
109
|
+
return write(chunk, ...rest);
|
|
110
|
+
});
|
|
111
|
+
return (async function* () {
|
|
112
|
+
for await (const chunk of stream) {
|
|
113
|
+
if (chunk?.usage) {
|
|
114
|
+
prompt = chunk.usage.prompt_tokens ?? 0;
|
|
115
|
+
cached = chunk.usage.prompt_tokens_details?.cached_tokens ?? 0;
|
|
116
|
+
}
|
|
117
|
+
yield chunk;
|
|
118
|
+
}
|
|
119
|
+
})();
|
|
120
|
+
}
|
|
62
121
|
// replace Claude Code identifier before claude-adapter can inject its own branding
|
|
63
122
|
function normalizeSystemBranding(body) {
|
|
64
123
|
const CLAUDE_IDENTIFIER = "You are Claude Code, Anthropic's official CLI for Claude.";
|
|
@@ -89,16 +148,4 @@ function stripAdapterBranding(messages) {
|
|
|
89
148
|
}
|
|
90
149
|
}
|
|
91
150
|
}
|
|
92
|
-
// ask the OS for a currently free local TCP port
|
|
93
|
-
async function findAvailablePort() {
|
|
94
|
-
return new Promise((resolve, reject) => {
|
|
95
|
-
const server = net.createServer();
|
|
96
|
-
server.once('error', reject);
|
|
97
|
-
server.listen(0, '127.0.0.1', () => {
|
|
98
|
-
const address = server.address();
|
|
99
|
-
const port = typeof address === 'object' && address ? address.port : 0;
|
|
100
|
-
server.close(() => resolve(port));
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
151
|
//# sourceMappingURL=claudeAdapterProxy.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claudeAdapterProxy.js","sourceRoot":"","sources":["../../src/services/claudeAdapterProxy.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"claudeAdapterProxy.js","sourceRoot":"","sources":["../../src/services/claudeAdapterProxy.ts"],"names":[],"mappings":"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"}
|