@tongil_kim/clautunnel 1.5.1 → 1.6.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/dist/index.js +237 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1021,6 +1021,9 @@ import * as os2 from "os";
|
|
|
1021
1021
|
import { unstable_v2_createSession, unstable_v2_resumeSession } from "@anthropic-ai/claude-agent-sdk";
|
|
1022
1022
|
import { v4 as uuidv4 } from "uuid";
|
|
1023
1023
|
var MAX_TOOL_CONTENT = 1e4;
|
|
1024
|
+
var COMPACT_FALLBACK_GRACE_MS = 40;
|
|
1025
|
+
var COMPACT_QUEUED_DISPATCH_DELAY_MS = 40;
|
|
1026
|
+
var COMPACT_STALE_RESULT_GUARD_MS = COMPACT_FALLBACK_GRACE_MS + COMPACT_QUEUED_DISPATCH_DELAY_MS;
|
|
1024
1027
|
var UNSUPPORTED_COMMANDS = /* @__PURE__ */ new Set([
|
|
1025
1028
|
"keybindings-help",
|
|
1026
1029
|
"help",
|
|
@@ -1058,6 +1061,20 @@ var SdkSession = class extends EventEmitter2 {
|
|
|
1058
1061
|
pendingPrompt = null;
|
|
1059
1062
|
// Tracks prompt during a resume attempt — used to auto-retry with a fresh session on failure
|
|
1060
1063
|
resumeAttemptPrompt = null;
|
|
1064
|
+
// Tracks whether the current in-flight prompt is a manual /compact command.
|
|
1065
|
+
awaitingCompactCompletionFallback = false;
|
|
1066
|
+
// Monotonic counter for turn ordering.
|
|
1067
|
+
turnGeneration = 0;
|
|
1068
|
+
// Generation of the current in-flight turn.
|
|
1069
|
+
activeTurnGeneration = 0;
|
|
1070
|
+
// Timestamp when the current in-flight turn started.
|
|
1071
|
+
activeTurnStartedAtMs = 0;
|
|
1072
|
+
// Generation whose late result should be ignored after compact fallback.
|
|
1073
|
+
compactLateResultSuppressedGeneration = null;
|
|
1074
|
+
// Timer used to defer compact completion fallback in case result arrives.
|
|
1075
|
+
compactCompletionFallbackTimer = null;
|
|
1076
|
+
// Timer used to dispatch queued prompt shortly after compact fallback.
|
|
1077
|
+
deferredQueuedPromptTimer = null;
|
|
1061
1078
|
constructor(options) {
|
|
1062
1079
|
super();
|
|
1063
1080
|
this.options = options;
|
|
@@ -1203,6 +1220,10 @@ var SdkSession = class extends EventEmitter2 {
|
|
|
1203
1220
|
this.currentPermissionMode = mode;
|
|
1204
1221
|
if (modeChanged) {
|
|
1205
1222
|
this.clearPendingInteractionState("Session reconfigured");
|
|
1223
|
+
this.clearCompactCompletionFallbackTimer();
|
|
1224
|
+
this.clearDeferredQueuedPromptTimer();
|
|
1225
|
+
this.awaitingCompactCompletionFallback = false;
|
|
1226
|
+
this.clearCompactLateResultSuppression();
|
|
1206
1227
|
if (this.v2Session) {
|
|
1207
1228
|
this.v2Session.close();
|
|
1208
1229
|
this.v2Session = null;
|
|
@@ -1337,7 +1358,26 @@ var SdkSession = class extends EventEmitter2 {
|
|
|
1337
1358
|
});
|
|
1338
1359
|
this.emit("commands-updated", this.cachedCommands);
|
|
1339
1360
|
}
|
|
1361
|
+
} else if (message.type === "system" && "subtype" in message && message.subtype === "status" && "status" in message && message.status === null && this.awaitingCompactCompletionFallback && this.isProcessing) {
|
|
1362
|
+
this.scheduleCompactCompletionFallback();
|
|
1363
|
+
} else if (message.type === "system" && "subtype" in message && message.subtype === "compact_boundary" && this.awaitingCompactCompletionFallback && this.isProcessing) {
|
|
1364
|
+
this.scheduleCompactCompletionFallback();
|
|
1365
|
+
} else if (message.type === "auth_status") {
|
|
1366
|
+
const authMsg = message;
|
|
1367
|
+
if (authMsg.error) {
|
|
1368
|
+
this.emit("auth-error", {
|
|
1369
|
+
errorCode: "authentication_failed",
|
|
1370
|
+
message: authMsg.error
|
|
1371
|
+
});
|
|
1372
|
+
}
|
|
1340
1373
|
} else if (message.type === "assistant") {
|
|
1374
|
+
const assistantError = message.error;
|
|
1375
|
+
if (assistantError) {
|
|
1376
|
+
this.emit("auth-error", {
|
|
1377
|
+
errorCode: assistantError,
|
|
1378
|
+
message: assistantError
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1341
1381
|
if (message.message?.content) {
|
|
1342
1382
|
for (const block of message.message.content) {
|
|
1343
1383
|
if ("type" in block && block.type === "text" && "text" in block) {
|
|
@@ -1374,6 +1414,10 @@ var SdkSession = class extends EventEmitter2 {
|
|
|
1374
1414
|
}
|
|
1375
1415
|
}
|
|
1376
1416
|
} else if (message.type === "result") {
|
|
1417
|
+
this.clearCompactCompletionFallbackTimer();
|
|
1418
|
+
if (this.shouldIgnoreLateCompactResult()) {
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1377
1421
|
if (!this.currentAssistantResponse.trim() && "result" in message && message.result) {
|
|
1378
1422
|
const resultText = String(message.result);
|
|
1379
1423
|
this.emit("output", resultText);
|
|
@@ -1383,14 +1427,8 @@ var SdkSession = class extends EventEmitter2 {
|
|
|
1383
1427
|
this.conversationHistory.push({ role: "assistant", content: this.currentAssistantResponse.trim() });
|
|
1384
1428
|
}
|
|
1385
1429
|
this.currentAssistantResponse = "";
|
|
1386
|
-
this.
|
|
1387
|
-
|
|
1388
|
-
if (queued) {
|
|
1389
|
-
this.pendingPrompt = null;
|
|
1390
|
-
this.sendPrompt(queued.prompt, queued.attachments);
|
|
1391
|
-
} else {
|
|
1392
|
-
this.emit("complete");
|
|
1393
|
-
}
|
|
1430
|
+
this.awaitingCompactCompletionFallback = false;
|
|
1431
|
+
this.completeCurrentTurn();
|
|
1394
1432
|
} else if (message.type === "tool_progress") {
|
|
1395
1433
|
if ("tool_name" in message) {
|
|
1396
1434
|
this.emit("output", `
|
|
@@ -1410,6 +1448,14 @@ var SdkSession = class extends EventEmitter2 {
|
|
|
1410
1448
|
this.emit("request-queued");
|
|
1411
1449
|
return;
|
|
1412
1450
|
}
|
|
1451
|
+
this.clearCompactCompletionFallbackTimer();
|
|
1452
|
+
this.clearDeferredQueuedPromptTimer();
|
|
1453
|
+
this.awaitingCompactCompletionFallback = /^\/compact(?:\s|$)/.test(prompt2.trim());
|
|
1454
|
+
this.activeTurnGeneration = ++this.turnGeneration;
|
|
1455
|
+
this.activeTurnStartedAtMs = Date.now();
|
|
1456
|
+
if (this.compactLateResultSuppressedGeneration !== null && this.activeTurnGeneration > this.compactLateResultSuppressedGeneration + 1) {
|
|
1457
|
+
this.clearCompactLateResultSuppression();
|
|
1458
|
+
}
|
|
1413
1459
|
this.isProcessing = true;
|
|
1414
1460
|
this.currentAssistantResponse = "";
|
|
1415
1461
|
if (prompt2.trim()) {
|
|
@@ -1480,6 +1526,9 @@ ${contextLines.join("\n")}
|
|
|
1480
1526
|
`);
|
|
1481
1527
|
}
|
|
1482
1528
|
this.isProcessing = false;
|
|
1529
|
+
this.clearCompactCompletionFallbackTimer();
|
|
1530
|
+
this.clearDeferredQueuedPromptTimer();
|
|
1531
|
+
this.awaitingCompactCompletionFallback = false;
|
|
1483
1532
|
}
|
|
1484
1533
|
}
|
|
1485
1534
|
/**
|
|
@@ -1507,6 +1556,8 @@ ${contextLines.join("\n")}
|
|
|
1507
1556
|
}
|
|
1508
1557
|
cancel() {
|
|
1509
1558
|
this.clearPendingInteractionState("Session cancelled");
|
|
1559
|
+
this.clearCompactCompletionFallbackTimer();
|
|
1560
|
+
this.clearDeferredQueuedPromptTimer();
|
|
1510
1561
|
if (this.v2Session) {
|
|
1511
1562
|
this.v2Session.close();
|
|
1512
1563
|
this.v2Session = null;
|
|
@@ -1514,6 +1565,8 @@ ${contextLines.join("\n")}
|
|
|
1514
1565
|
}
|
|
1515
1566
|
this.isProcessing = false;
|
|
1516
1567
|
this.pendingPrompt = null;
|
|
1568
|
+
this.awaitingCompactCompletionFallback = false;
|
|
1569
|
+
this.clearCompactLateResultSuppression();
|
|
1517
1570
|
}
|
|
1518
1571
|
getSessionId() {
|
|
1519
1572
|
return this.sessionId;
|
|
@@ -1531,6 +1584,10 @@ ${contextLines.join("\n")}
|
|
|
1531
1584
|
this.sessionId = sessionId;
|
|
1532
1585
|
this.isProcessing = false;
|
|
1533
1586
|
this.pendingPrompt = null;
|
|
1587
|
+
this.clearCompactCompletionFallbackTimer();
|
|
1588
|
+
this.clearDeferredQueuedPromptTimer();
|
|
1589
|
+
this.awaitingCompactCompletionFallback = false;
|
|
1590
|
+
this.clearCompactLateResultSuppression();
|
|
1534
1591
|
this.conversationHistory = [];
|
|
1535
1592
|
this.emit("session-resumed", sessionId);
|
|
1536
1593
|
}
|
|
@@ -1550,6 +1607,8 @@ ${contextLines.join("\n")}
|
|
|
1550
1607
|
if (model === this.currentModel) return;
|
|
1551
1608
|
this.currentModel = model;
|
|
1552
1609
|
this.clearPendingInteractionState("Session reconfigured");
|
|
1610
|
+
this.clearCompactCompletionFallbackTimer();
|
|
1611
|
+
this.clearDeferredQueuedPromptTimer();
|
|
1553
1612
|
if (this.v2Session) {
|
|
1554
1613
|
this.v2Session.close();
|
|
1555
1614
|
this.v2Session = null;
|
|
@@ -1557,9 +1616,13 @@ ${contextLines.join("\n")}
|
|
|
1557
1616
|
this.sessionId = null;
|
|
1558
1617
|
this.isProcessing = false;
|
|
1559
1618
|
this.pendingPrompt = null;
|
|
1619
|
+
this.awaitingCompactCompletionFallback = false;
|
|
1620
|
+
this.clearCompactLateResultSuppression();
|
|
1560
1621
|
this.pendingContextTransfer = true;
|
|
1561
1622
|
} else if (this.sessionId) {
|
|
1562
1623
|
this.sessionId = null;
|
|
1624
|
+
this.awaitingCompactCompletionFallback = false;
|
|
1625
|
+
this.clearCompactLateResultSuppression();
|
|
1563
1626
|
this.pendingContextTransfer = true;
|
|
1564
1627
|
}
|
|
1565
1628
|
this.emit("model", model);
|
|
@@ -1572,6 +1635,8 @@ ${contextLines.join("\n")}
|
|
|
1572
1635
|
}
|
|
1573
1636
|
clearHistory() {
|
|
1574
1637
|
this.conversationHistory = [];
|
|
1638
|
+
this.clearCompactCompletionFallbackTimer();
|
|
1639
|
+
this.clearDeferredQueuedPromptTimer();
|
|
1575
1640
|
if (this.v2Session) {
|
|
1576
1641
|
this.v2Session.close();
|
|
1577
1642
|
this.v2Session = null;
|
|
@@ -1580,6 +1645,72 @@ ${contextLines.join("\n")}
|
|
|
1580
1645
|
this.sessionId = null;
|
|
1581
1646
|
this.isProcessing = false;
|
|
1582
1647
|
this.pendingPrompt = null;
|
|
1648
|
+
this.awaitingCompactCompletionFallback = false;
|
|
1649
|
+
this.clearCompactLateResultSuppression();
|
|
1650
|
+
}
|
|
1651
|
+
completeCompactCommandFallback() {
|
|
1652
|
+
this.clearCompactCompletionFallbackTimer();
|
|
1653
|
+
this.awaitingCompactCompletionFallback = false;
|
|
1654
|
+
this.compactLateResultSuppressedGeneration = this.activeTurnGeneration;
|
|
1655
|
+
this.currentAssistantResponse = "";
|
|
1656
|
+
this.completeCurrentTurn(COMPACT_QUEUED_DISPATCH_DELAY_MS);
|
|
1657
|
+
}
|
|
1658
|
+
scheduleCompactCompletionFallback() {
|
|
1659
|
+
if (this.compactCompletionFallbackTimer) return;
|
|
1660
|
+
this.compactCompletionFallbackTimer = setTimeout(() => {
|
|
1661
|
+
this.compactCompletionFallbackTimer = null;
|
|
1662
|
+
if (this.awaitingCompactCompletionFallback && this.isProcessing) {
|
|
1663
|
+
this.completeCompactCommandFallback();
|
|
1664
|
+
}
|
|
1665
|
+
}, COMPACT_FALLBACK_GRACE_MS);
|
|
1666
|
+
}
|
|
1667
|
+
clearCompactCompletionFallbackTimer() {
|
|
1668
|
+
if (this.compactCompletionFallbackTimer) {
|
|
1669
|
+
clearTimeout(this.compactCompletionFallbackTimer);
|
|
1670
|
+
this.compactCompletionFallbackTimer = null;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
clearDeferredQueuedPromptTimer() {
|
|
1674
|
+
if (this.deferredQueuedPromptTimer) {
|
|
1675
|
+
clearTimeout(this.deferredQueuedPromptTimer);
|
|
1676
|
+
this.deferredQueuedPromptTimer = null;
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
clearCompactLateResultSuppression() {
|
|
1680
|
+
this.compactLateResultSuppressedGeneration = null;
|
|
1681
|
+
}
|
|
1682
|
+
shouldIgnoreLateCompactResult() {
|
|
1683
|
+
const suppressedGeneration = this.compactLateResultSuppressedGeneration;
|
|
1684
|
+
if (suppressedGeneration === null) return false;
|
|
1685
|
+
this.clearCompactLateResultSuppression();
|
|
1686
|
+
if (this.activeTurnGeneration === suppressedGeneration) return true;
|
|
1687
|
+
if (this.activeTurnGeneration === suppressedGeneration + 1) {
|
|
1688
|
+
const elapsedSinceTurnStart = Date.now() - this.activeTurnStartedAtMs;
|
|
1689
|
+
return elapsedSinceTurnStart <= COMPACT_STALE_RESULT_GUARD_MS;
|
|
1690
|
+
}
|
|
1691
|
+
return false;
|
|
1692
|
+
}
|
|
1693
|
+
completeCurrentTurn(deferQueuedDispatchMs = 0) {
|
|
1694
|
+
if (!this.isProcessing) return;
|
|
1695
|
+
this.isProcessing = false;
|
|
1696
|
+
if (!this.pendingPrompt) {
|
|
1697
|
+
this.emit("complete");
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
if (deferQueuedDispatchMs > 0) {
|
|
1701
|
+
this.clearDeferredQueuedPromptTimer();
|
|
1702
|
+
this.deferredQueuedPromptTimer = setTimeout(() => {
|
|
1703
|
+
this.deferredQueuedPromptTimer = null;
|
|
1704
|
+
const deferred = this.pendingPrompt;
|
|
1705
|
+
if (!deferred || this.isProcessing) return;
|
|
1706
|
+
this.pendingPrompt = null;
|
|
1707
|
+
this.sendPrompt(deferred.prompt, deferred.attachments);
|
|
1708
|
+
}, deferQueuedDispatchMs);
|
|
1709
|
+
return;
|
|
1710
|
+
}
|
|
1711
|
+
const queued = this.pendingPrompt;
|
|
1712
|
+
this.pendingPrompt = null;
|
|
1713
|
+
this.sendPrompt(queued.prompt, queued.attachments);
|
|
1583
1714
|
}
|
|
1584
1715
|
clearPendingInteractionState(reason) {
|
|
1585
1716
|
if (this.pendingAnswerRequest) {
|
|
@@ -2121,6 +2252,28 @@ var Daemon = class extends EventEmitter3 {
|
|
|
2121
2252
|
this.sdkSession.on("error", (error) => {
|
|
2122
2253
|
this.emit("error", error);
|
|
2123
2254
|
});
|
|
2255
|
+
this.sdkSession.on("auth-error", async (errorInfo) => {
|
|
2256
|
+
const errorMessages = {
|
|
2257
|
+
"authentication_failed": 'Claude CLI authentication expired. Please run "claude login" on the host machine.',
|
|
2258
|
+
"billing_error": "Billing error. Please check your Anthropic account billing status.",
|
|
2259
|
+
"rate_limit": "Rate limit reached. Please wait a moment and try again.",
|
|
2260
|
+
"server_error": "Anthropic API server error. Please try again later."
|
|
2261
|
+
};
|
|
2262
|
+
const displayMessage = errorMessages[errorInfo.errorCode] || `API error: ${errorInfo.message}`;
|
|
2263
|
+
if (this.options.hybrid !== false) {
|
|
2264
|
+
process.stdout.write(`
|
|
2265
|
+
[Error] ${displayMessage}
|
|
2266
|
+
`);
|
|
2267
|
+
}
|
|
2268
|
+
this.emit("error", new Error(displayMessage));
|
|
2269
|
+
if (this.realtimeClient) {
|
|
2270
|
+
try {
|
|
2271
|
+
await this.realtimeClient.broadcastError(displayMessage, errorInfo.errorCode);
|
|
2272
|
+
await this.realtimeClient.broadcastComplete();
|
|
2273
|
+
} catch {
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
});
|
|
2124
2277
|
this.sdkSession.on("commands-updated", async () => {
|
|
2125
2278
|
await this.broadcastCommands();
|
|
2126
2279
|
});
|
|
@@ -3234,6 +3387,52 @@ function removePidFileUnchecked(pidFile) {
|
|
|
3234
3387
|
}
|
|
3235
3388
|
}
|
|
3236
3389
|
|
|
3390
|
+
// src/utils/claude-auth.ts
|
|
3391
|
+
import { execSync as execSync3 } from "child_process";
|
|
3392
|
+
function checkClaudeCliAuth() {
|
|
3393
|
+
try {
|
|
3394
|
+
const output = execSync3("claude auth status --json", {
|
|
3395
|
+
encoding: "utf-8",
|
|
3396
|
+
timeout: 1e4,
|
|
3397
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3398
|
+
});
|
|
3399
|
+
const status = JSON.parse(output.trim());
|
|
3400
|
+
if (status.loggedIn === true) {
|
|
3401
|
+
return {
|
|
3402
|
+
loggedIn: true,
|
|
3403
|
+
authMethod: status.authMethod,
|
|
3404
|
+
apiProvider: status.apiProvider
|
|
3405
|
+
};
|
|
3406
|
+
}
|
|
3407
|
+
return { loggedIn: false, failure: "not_logged_in" };
|
|
3408
|
+
} catch (error) {
|
|
3409
|
+
const execError = error;
|
|
3410
|
+
if (execError.stdout) {
|
|
3411
|
+
try {
|
|
3412
|
+
const stdout = typeof execError.stdout === "string" ? execError.stdout : execError.stdout.toString("utf-8");
|
|
3413
|
+
const status = JSON.parse(stdout.trim());
|
|
3414
|
+
if (status.loggedIn === true) {
|
|
3415
|
+
return {
|
|
3416
|
+
loggedIn: true,
|
|
3417
|
+
authMethod: status.authMethod,
|
|
3418
|
+
apiProvider: status.apiProvider
|
|
3419
|
+
};
|
|
3420
|
+
}
|
|
3421
|
+
return { loggedIn: false, failure: "not_logged_in" };
|
|
3422
|
+
} catch {
|
|
3423
|
+
}
|
|
3424
|
+
}
|
|
3425
|
+
const message = execError.message ?? String(error);
|
|
3426
|
+
if (message.includes("command not found") || message.includes("ENOENT") || message.includes("not recognized")) {
|
|
3427
|
+
return { loggedIn: false, failure: "cli_not_found" };
|
|
3428
|
+
}
|
|
3429
|
+
if (message.includes("Unknown command") || message.includes("unknown command") || message.includes("Invalid subcommand") || message.includes("invalid subcommand")) {
|
|
3430
|
+
return { loggedIn: false, failure: "subcommand_not_supported" };
|
|
3431
|
+
}
|
|
3432
|
+
return { loggedIn: false, failure: "unknown" };
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
|
|
3237
3436
|
// src/commands/start.ts
|
|
3238
3437
|
if (typeof globalThis.WebSocket === "undefined") {
|
|
3239
3438
|
globalThis.WebSocket = WebSocket;
|
|
@@ -3282,6 +3481,36 @@ function createStartCommand() {
|
|
|
3282
3481
|
}
|
|
3283
3482
|
});
|
|
3284
3483
|
spinner.update(`Authenticated as ${user.email}...`);
|
|
3484
|
+
spinner.update("Checking Claude CLI auth...");
|
|
3485
|
+
const claudeAuth = checkClaudeCliAuth();
|
|
3486
|
+
if (!claudeAuth.loggedIn) {
|
|
3487
|
+
switch (claudeAuth.failure) {
|
|
3488
|
+
case "cli_not_found":
|
|
3489
|
+
spinner.fail("Claude CLI not found");
|
|
3490
|
+
logger.error("Claude Code CLI is not installed or not in PATH.");
|
|
3491
|
+
logger.error("Install it first: https://docs.anthropic.com/en/docs/claude-code");
|
|
3492
|
+
removePidFile();
|
|
3493
|
+
process.exit(1);
|
|
3494
|
+
break;
|
|
3495
|
+
case "subcommand_not_supported":
|
|
3496
|
+
spinner.update('Claude CLI auth check skipped (CLI version does not support "auth status")');
|
|
3497
|
+
logger.warn('Could not verify Claude CLI auth \u2014 your CLI version may not support "claude auth status".');
|
|
3498
|
+
logger.warn('If sessions fail to start, try updating Claude Code and running "claude login".');
|
|
3499
|
+
break;
|
|
3500
|
+
case "not_logged_in":
|
|
3501
|
+
spinner.fail("Claude CLI is not logged in");
|
|
3502
|
+
logger.error("Claude Code requires authentication to use the Anthropic API.");
|
|
3503
|
+
logger.error('Run "claude login" first, then try "clautunnel start" again.');
|
|
3504
|
+
removePidFile();
|
|
3505
|
+
process.exit(1);
|
|
3506
|
+
break;
|
|
3507
|
+
default:
|
|
3508
|
+
spinner.update("Claude CLI auth check inconclusive");
|
|
3509
|
+
logger.warn("Could not verify Claude CLI authentication (unexpected error).");
|
|
3510
|
+
logger.warn('If sessions fail to start, run "claude login" and try again.');
|
|
3511
|
+
break;
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3285
3514
|
let fdaStatus = null;
|
|
3286
3515
|
if (isMacOS()) {
|
|
3287
3516
|
spinner.stop();
|