happy-coder 0.9.0-0 → 0.9.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/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chalk = require('chalk');
|
|
4
|
-
var types$1 = require('./types-
|
|
4
|
+
var types$1 = require('./types-a-nJyP-e.cjs');
|
|
5
5
|
var node_crypto = require('node:crypto');
|
|
6
6
|
var node_child_process = require('node:child_process');
|
|
7
7
|
var node_path = require('node:path');
|
|
@@ -1623,11 +1623,20 @@ class PermissionHandler {
|
|
|
1623
1623
|
pendingRequests = /* @__PURE__ */ new Map();
|
|
1624
1624
|
session;
|
|
1625
1625
|
allowedTools = /* @__PURE__ */ new Set();
|
|
1626
|
+
allowedBashLiterals = /* @__PURE__ */ new Set();
|
|
1627
|
+
allowedBashPrefixes = /* @__PURE__ */ new Set();
|
|
1626
1628
|
permissionMode = "default";
|
|
1629
|
+
onPermissionRequestCallback;
|
|
1627
1630
|
constructor(session) {
|
|
1628
1631
|
this.session = session;
|
|
1629
1632
|
this.setupClientHandler();
|
|
1630
1633
|
}
|
|
1634
|
+
/**
|
|
1635
|
+
* Set callback to trigger when permission request is made
|
|
1636
|
+
*/
|
|
1637
|
+
setOnPermissionRequest(callback) {
|
|
1638
|
+
this.onPermissionRequestCallback = callback;
|
|
1639
|
+
}
|
|
1631
1640
|
handleModeChange(mode) {
|
|
1632
1641
|
this.permissionMode = mode;
|
|
1633
1642
|
}
|
|
@@ -1636,7 +1645,13 @@ class PermissionHandler {
|
|
|
1636
1645
|
*/
|
|
1637
1646
|
handlePermissionResponse(response, pending) {
|
|
1638
1647
|
if (response.allowTools && response.allowTools.length > 0) {
|
|
1639
|
-
response.allowTools.forEach((tool) =>
|
|
1648
|
+
response.allowTools.forEach((tool) => {
|
|
1649
|
+
if (tool.startsWith("Bash(") || tool === "Bash") {
|
|
1650
|
+
this.parseBashPermission(tool);
|
|
1651
|
+
} else {
|
|
1652
|
+
this.allowedTools.add(tool);
|
|
1653
|
+
}
|
|
1654
|
+
});
|
|
1640
1655
|
}
|
|
1641
1656
|
if (response.mode) {
|
|
1642
1657
|
this.permissionMode = response.mode;
|
|
@@ -1663,7 +1678,19 @@ class PermissionHandler {
|
|
|
1663
1678
|
* Creates the canCallTool callback for the SDK
|
|
1664
1679
|
*/
|
|
1665
1680
|
handleToolCall = async (toolName, input, mode, options) => {
|
|
1666
|
-
if (
|
|
1681
|
+
if (toolName === "Bash") {
|
|
1682
|
+
const inputObj = input;
|
|
1683
|
+
if (inputObj?.command) {
|
|
1684
|
+
if (this.allowedBashLiterals.has(inputObj.command)) {
|
|
1685
|
+
return { behavior: "allow", updatedInput: input };
|
|
1686
|
+
}
|
|
1687
|
+
for (const prefix of this.allowedBashPrefixes) {
|
|
1688
|
+
if (inputObj.command.startsWith(prefix)) {
|
|
1689
|
+
return { behavior: "allow", updatedInput: input };
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
} else if (this.allowedTools.has(toolName)) {
|
|
1667
1694
|
return { behavior: "allow", updatedInput: input };
|
|
1668
1695
|
}
|
|
1669
1696
|
const descriptor = getToolDescriptor(toolName);
|
|
@@ -1705,6 +1732,9 @@ class PermissionHandler {
|
|
|
1705
1732
|
toolName,
|
|
1706
1733
|
input
|
|
1707
1734
|
});
|
|
1735
|
+
if (this.onPermissionRequestCallback) {
|
|
1736
|
+
this.onPermissionRequestCallback(id);
|
|
1737
|
+
}
|
|
1708
1738
|
this.session.api.push().sendToAllDevices(
|
|
1709
1739
|
"Permission Request",
|
|
1710
1740
|
`Claude wants to ${getToolName(toolName)}`,
|
|
@@ -1729,6 +1759,26 @@ class PermissionHandler {
|
|
|
1729
1759
|
types$1.logger.debug(`Permission request sent for tool call ${id}: ${toolName}`);
|
|
1730
1760
|
});
|
|
1731
1761
|
}
|
|
1762
|
+
/**
|
|
1763
|
+
* Parses Bash permission strings into literal and prefix sets
|
|
1764
|
+
*/
|
|
1765
|
+
parseBashPermission(permission) {
|
|
1766
|
+
if (permission === "Bash") {
|
|
1767
|
+
return;
|
|
1768
|
+
}
|
|
1769
|
+
const bashPattern = /^Bash\((.+?)\)$/;
|
|
1770
|
+
const match = permission.match(bashPattern);
|
|
1771
|
+
if (!match) {
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1774
|
+
const command = match[1];
|
|
1775
|
+
if (command.endsWith(":*")) {
|
|
1776
|
+
const prefix = command.slice(0, -2);
|
|
1777
|
+
this.allowedBashPrefixes.add(prefix);
|
|
1778
|
+
} else {
|
|
1779
|
+
this.allowedBashLiterals.add(command);
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1732
1782
|
/**
|
|
1733
1783
|
* Resolves tool call ID based on tool name and input
|
|
1734
1784
|
*/
|
|
@@ -1797,6 +1847,9 @@ class PermissionHandler {
|
|
|
1797
1847
|
reset() {
|
|
1798
1848
|
this.toolCalls = [];
|
|
1799
1849
|
this.responses.clear();
|
|
1850
|
+
this.allowedTools.clear();
|
|
1851
|
+
this.allowedBashLiterals.clear();
|
|
1852
|
+
this.allowedBashPrefixes.clear();
|
|
1800
1853
|
for (const [, pending] of this.pendingRequests.entries()) {
|
|
1801
1854
|
pending.reject(new Error("Session reset"));
|
|
1802
1855
|
}
|
|
@@ -2203,6 +2256,136 @@ class SDKToLogConverter {
|
|
|
2203
2256
|
}
|
|
2204
2257
|
}
|
|
2205
2258
|
|
|
2259
|
+
class OutgoingMessageQueue {
|
|
2260
|
+
constructor(sendFunction) {
|
|
2261
|
+
this.sendFunction = sendFunction;
|
|
2262
|
+
}
|
|
2263
|
+
queue = [];
|
|
2264
|
+
nextId = 1;
|
|
2265
|
+
lock = new types$1.AsyncLock();
|
|
2266
|
+
processTimer;
|
|
2267
|
+
delayTimers = /* @__PURE__ */ new Map();
|
|
2268
|
+
/**
|
|
2269
|
+
* Add message to queue
|
|
2270
|
+
*/
|
|
2271
|
+
enqueue(logMessage, options) {
|
|
2272
|
+
this.lock.inLock(async () => {
|
|
2273
|
+
const item = {
|
|
2274
|
+
id: this.nextId++,
|
|
2275
|
+
logMessage,
|
|
2276
|
+
delayed: !!options?.delay,
|
|
2277
|
+
delayMs: options?.delay || 0,
|
|
2278
|
+
toolCallIds: options?.toolCallIds,
|
|
2279
|
+
released: !options?.delay,
|
|
2280
|
+
// Not delayed = already released
|
|
2281
|
+
sent: false
|
|
2282
|
+
};
|
|
2283
|
+
this.queue.push(item);
|
|
2284
|
+
if (item.delayed) {
|
|
2285
|
+
const timer = setTimeout(() => {
|
|
2286
|
+
this.releaseItem(item.id);
|
|
2287
|
+
}, item.delayMs);
|
|
2288
|
+
this.delayTimers.set(item.id, timer);
|
|
2289
|
+
}
|
|
2290
|
+
});
|
|
2291
|
+
this.scheduleProcessing();
|
|
2292
|
+
}
|
|
2293
|
+
/**
|
|
2294
|
+
* Release specific item by ID
|
|
2295
|
+
*/
|
|
2296
|
+
async releaseItem(itemId) {
|
|
2297
|
+
await this.lock.inLock(async () => {
|
|
2298
|
+
const item = this.queue.find((i) => i.id === itemId);
|
|
2299
|
+
if (item && !item.released) {
|
|
2300
|
+
item.released = true;
|
|
2301
|
+
const timer = this.delayTimers.get(itemId);
|
|
2302
|
+
if (timer) {
|
|
2303
|
+
clearTimeout(timer);
|
|
2304
|
+
this.delayTimers.delete(itemId);
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
});
|
|
2308
|
+
this.scheduleProcessing();
|
|
2309
|
+
}
|
|
2310
|
+
/**
|
|
2311
|
+
* Release all messages with specific tool call ID
|
|
2312
|
+
*/
|
|
2313
|
+
async releaseToolCall(toolCallId) {
|
|
2314
|
+
await this.lock.inLock(async () => {
|
|
2315
|
+
for (const item of this.queue) {
|
|
2316
|
+
if (item.toolCallIds?.includes(toolCallId) && !item.released) {
|
|
2317
|
+
item.released = true;
|
|
2318
|
+
const timer = this.delayTimers.get(item.id);
|
|
2319
|
+
if (timer) {
|
|
2320
|
+
clearTimeout(timer);
|
|
2321
|
+
this.delayTimers.delete(item.id);
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
});
|
|
2326
|
+
this.scheduleProcessing();
|
|
2327
|
+
}
|
|
2328
|
+
/**
|
|
2329
|
+
* Process queue - send messages in ID order that are released
|
|
2330
|
+
*/
|
|
2331
|
+
async processQueue() {
|
|
2332
|
+
await this.lock.inLock(async () => {
|
|
2333
|
+
this.queue.sort((a, b) => a.id - b.id);
|
|
2334
|
+
while (this.queue.length > 0) {
|
|
2335
|
+
const item = this.queue[0];
|
|
2336
|
+
if (!item.released) {
|
|
2337
|
+
break;
|
|
2338
|
+
}
|
|
2339
|
+
if (!item.sent) {
|
|
2340
|
+
if (item.logMessage.type !== "system") {
|
|
2341
|
+
this.sendFunction(item.logMessage);
|
|
2342
|
+
}
|
|
2343
|
+
item.sent = true;
|
|
2344
|
+
}
|
|
2345
|
+
this.queue.shift();
|
|
2346
|
+
}
|
|
2347
|
+
});
|
|
2348
|
+
}
|
|
2349
|
+
/**
|
|
2350
|
+
* Flush all messages immediately (for cleanup)
|
|
2351
|
+
*/
|
|
2352
|
+
async flush() {
|
|
2353
|
+
await this.lock.inLock(async () => {
|
|
2354
|
+
for (const timer of this.delayTimers.values()) {
|
|
2355
|
+
clearTimeout(timer);
|
|
2356
|
+
}
|
|
2357
|
+
this.delayTimers.clear();
|
|
2358
|
+
for (const item of this.queue) {
|
|
2359
|
+
item.released = true;
|
|
2360
|
+
}
|
|
2361
|
+
await this.processQueue();
|
|
2362
|
+
});
|
|
2363
|
+
}
|
|
2364
|
+
/**
|
|
2365
|
+
* Schedule processing on next tick
|
|
2366
|
+
*/
|
|
2367
|
+
scheduleProcessing() {
|
|
2368
|
+
if (this.processTimer) {
|
|
2369
|
+
clearTimeout(this.processTimer);
|
|
2370
|
+
}
|
|
2371
|
+
this.processTimer = setTimeout(() => {
|
|
2372
|
+
this.processQueue();
|
|
2373
|
+
}, 0);
|
|
2374
|
+
}
|
|
2375
|
+
/**
|
|
2376
|
+
* Cleanup timers and resources
|
|
2377
|
+
*/
|
|
2378
|
+
destroy() {
|
|
2379
|
+
if (this.processTimer) {
|
|
2380
|
+
clearTimeout(this.processTimer);
|
|
2381
|
+
}
|
|
2382
|
+
for (const timer of this.delayTimers.values()) {
|
|
2383
|
+
clearTimeout(timer);
|
|
2384
|
+
}
|
|
2385
|
+
this.delayTimers.clear();
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2206
2389
|
async function claudeRemoteLauncher(session) {
|
|
2207
2390
|
types$1.logger.debug("[claudeRemoteLauncher] Starting remote launcher");
|
|
2208
2391
|
const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
|
|
@@ -2260,6 +2443,12 @@ async function claudeRemoteLauncher(session) {
|
|
|
2260
2443
|
session.client.setHandler("abort", doAbort);
|
|
2261
2444
|
session.client.setHandler("switch", doSwitch);
|
|
2262
2445
|
const permissionHandler = new PermissionHandler(session);
|
|
2446
|
+
const messageQueue = new OutgoingMessageQueue(
|
|
2447
|
+
(logMessage) => session.client.sendClaudeSessionMessage(logMessage)
|
|
2448
|
+
);
|
|
2449
|
+
permissionHandler.setOnPermissionRequest((toolCallId) => {
|
|
2450
|
+
messageQueue.releaseToolCall(toolCallId);
|
|
2451
|
+
});
|
|
2263
2452
|
const sdkToLogConverter = new SDKToLogConverter({
|
|
2264
2453
|
sessionId: session.sessionId || "unknown",
|
|
2265
2454
|
cwd: session.path,
|
|
@@ -2298,6 +2487,7 @@ async function claudeRemoteLauncher(session) {
|
|
|
2298
2487
|
for (let c of umessage.message.content) {
|
|
2299
2488
|
if (c.type === "tool_result" && c.tool_use_id) {
|
|
2300
2489
|
ongoingToolCalls.delete(c.tool_use_id);
|
|
2490
|
+
messageQueue.releaseToolCall(c.tool_use_id);
|
|
2301
2491
|
}
|
|
2302
2492
|
}
|
|
2303
2493
|
}
|
|
@@ -2359,9 +2549,28 @@ async function claudeRemoteLauncher(session) {
|
|
|
2359
2549
|
}
|
|
2360
2550
|
}
|
|
2361
2551
|
}
|
|
2362
|
-
if (logMessage.type
|
|
2363
|
-
|
|
2552
|
+
if (logMessage.type === "assistant" && message.type === "assistant") {
|
|
2553
|
+
const assistantMsg = message;
|
|
2554
|
+
const toolCallIds = [];
|
|
2555
|
+
if (assistantMsg.message.content && Array.isArray(assistantMsg.message.content)) {
|
|
2556
|
+
for (const block of assistantMsg.message.content) {
|
|
2557
|
+
if (block.type === "tool_use" && block.id) {
|
|
2558
|
+
toolCallIds.push(block.id);
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
if (toolCallIds.length > 0) {
|
|
2563
|
+
const isSidechain = assistantMsg.parent_tool_use_id !== void 0;
|
|
2564
|
+
if (!isSidechain) {
|
|
2565
|
+
messageQueue.enqueue(logMessage, {
|
|
2566
|
+
delay: 250,
|
|
2567
|
+
toolCallIds
|
|
2568
|
+
});
|
|
2569
|
+
return;
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2364
2572
|
}
|
|
2573
|
+
messageQueue.enqueue(logMessage);
|
|
2365
2574
|
}
|
|
2366
2575
|
if (message.type === "assistant") {
|
|
2367
2576
|
let umessage = message;
|
|
@@ -2370,7 +2579,7 @@ async function claudeRemoteLauncher(session) {
|
|
|
2370
2579
|
if (c.type === "tool_use" && c.name === "Task" && c.input && typeof c.input.prompt === "string") {
|
|
2371
2580
|
const logMessage2 = sdkToLogConverter.convertSidechainUserMessage(c.id, c.input.prompt);
|
|
2372
2581
|
if (logMessage2) {
|
|
2373
|
-
|
|
2582
|
+
messageQueue.enqueue(logMessage2);
|
|
2374
2583
|
}
|
|
2375
2584
|
}
|
|
2376
2585
|
}
|
|
@@ -2461,6 +2670,8 @@ async function claudeRemoteLauncher(session) {
|
|
|
2461
2670
|
}
|
|
2462
2671
|
}
|
|
2463
2672
|
ongoingToolCalls.clear();
|
|
2673
|
+
await messageQueue.flush();
|
|
2674
|
+
messageQueue.destroy();
|
|
2464
2675
|
abortController = null;
|
|
2465
2676
|
abortFuture?.resolve(void 0);
|
|
2466
2677
|
abortFuture = null;
|
|
@@ -2534,7 +2745,7 @@ async function loop(opts) {
|
|
|
2534
2745
|
}
|
|
2535
2746
|
|
|
2536
2747
|
var name = "happy-coder";
|
|
2537
|
-
var version = "0.9.0-
|
|
2748
|
+
var version = "0.9.0-2";
|
|
2538
2749
|
var description = "Claude Code session sharing CLI";
|
|
2539
2750
|
var author = "Kirill Dubovitskiy";
|
|
2540
2751
|
var license = "MIT";
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { l as logger, b as backoff, d as delay, R as RawJSONLinesSchema, c as configuration,
|
|
2
|
+
import { l as logger, b as backoff, d as delay, R as RawJSONLinesSchema, e as AsyncLock, c as configuration, f as encodeBase64, g as encodeBase64Url, h as decodeBase64, A as ApiClient } from './types-DJOX-XG-.mjs';
|
|
3
3
|
import { randomUUID, randomBytes } from 'node:crypto';
|
|
4
4
|
import { spawn, execSync } from 'node:child_process';
|
|
5
5
|
import { resolve, join, dirname as dirname$1 } from 'node:path';
|
|
@@ -1602,11 +1602,20 @@ class PermissionHandler {
|
|
|
1602
1602
|
pendingRequests = /* @__PURE__ */ new Map();
|
|
1603
1603
|
session;
|
|
1604
1604
|
allowedTools = /* @__PURE__ */ new Set();
|
|
1605
|
+
allowedBashLiterals = /* @__PURE__ */ new Set();
|
|
1606
|
+
allowedBashPrefixes = /* @__PURE__ */ new Set();
|
|
1605
1607
|
permissionMode = "default";
|
|
1608
|
+
onPermissionRequestCallback;
|
|
1606
1609
|
constructor(session) {
|
|
1607
1610
|
this.session = session;
|
|
1608
1611
|
this.setupClientHandler();
|
|
1609
1612
|
}
|
|
1613
|
+
/**
|
|
1614
|
+
* Set callback to trigger when permission request is made
|
|
1615
|
+
*/
|
|
1616
|
+
setOnPermissionRequest(callback) {
|
|
1617
|
+
this.onPermissionRequestCallback = callback;
|
|
1618
|
+
}
|
|
1610
1619
|
handleModeChange(mode) {
|
|
1611
1620
|
this.permissionMode = mode;
|
|
1612
1621
|
}
|
|
@@ -1615,7 +1624,13 @@ class PermissionHandler {
|
|
|
1615
1624
|
*/
|
|
1616
1625
|
handlePermissionResponse(response, pending) {
|
|
1617
1626
|
if (response.allowTools && response.allowTools.length > 0) {
|
|
1618
|
-
response.allowTools.forEach((tool) =>
|
|
1627
|
+
response.allowTools.forEach((tool) => {
|
|
1628
|
+
if (tool.startsWith("Bash(") || tool === "Bash") {
|
|
1629
|
+
this.parseBashPermission(tool);
|
|
1630
|
+
} else {
|
|
1631
|
+
this.allowedTools.add(tool);
|
|
1632
|
+
}
|
|
1633
|
+
});
|
|
1619
1634
|
}
|
|
1620
1635
|
if (response.mode) {
|
|
1621
1636
|
this.permissionMode = response.mode;
|
|
@@ -1642,7 +1657,19 @@ class PermissionHandler {
|
|
|
1642
1657
|
* Creates the canCallTool callback for the SDK
|
|
1643
1658
|
*/
|
|
1644
1659
|
handleToolCall = async (toolName, input, mode, options) => {
|
|
1645
|
-
if (
|
|
1660
|
+
if (toolName === "Bash") {
|
|
1661
|
+
const inputObj = input;
|
|
1662
|
+
if (inputObj?.command) {
|
|
1663
|
+
if (this.allowedBashLiterals.has(inputObj.command)) {
|
|
1664
|
+
return { behavior: "allow", updatedInput: input };
|
|
1665
|
+
}
|
|
1666
|
+
for (const prefix of this.allowedBashPrefixes) {
|
|
1667
|
+
if (inputObj.command.startsWith(prefix)) {
|
|
1668
|
+
return { behavior: "allow", updatedInput: input };
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
} else if (this.allowedTools.has(toolName)) {
|
|
1646
1673
|
return { behavior: "allow", updatedInput: input };
|
|
1647
1674
|
}
|
|
1648
1675
|
const descriptor = getToolDescriptor(toolName);
|
|
@@ -1684,6 +1711,9 @@ class PermissionHandler {
|
|
|
1684
1711
|
toolName,
|
|
1685
1712
|
input
|
|
1686
1713
|
});
|
|
1714
|
+
if (this.onPermissionRequestCallback) {
|
|
1715
|
+
this.onPermissionRequestCallback(id);
|
|
1716
|
+
}
|
|
1687
1717
|
this.session.api.push().sendToAllDevices(
|
|
1688
1718
|
"Permission Request",
|
|
1689
1719
|
`Claude wants to ${getToolName(toolName)}`,
|
|
@@ -1708,6 +1738,26 @@ class PermissionHandler {
|
|
|
1708
1738
|
logger.debug(`Permission request sent for tool call ${id}: ${toolName}`);
|
|
1709
1739
|
});
|
|
1710
1740
|
}
|
|
1741
|
+
/**
|
|
1742
|
+
* Parses Bash permission strings into literal and prefix sets
|
|
1743
|
+
*/
|
|
1744
|
+
parseBashPermission(permission) {
|
|
1745
|
+
if (permission === "Bash") {
|
|
1746
|
+
return;
|
|
1747
|
+
}
|
|
1748
|
+
const bashPattern = /^Bash\((.+?)\)$/;
|
|
1749
|
+
const match = permission.match(bashPattern);
|
|
1750
|
+
if (!match) {
|
|
1751
|
+
return;
|
|
1752
|
+
}
|
|
1753
|
+
const command = match[1];
|
|
1754
|
+
if (command.endsWith(":*")) {
|
|
1755
|
+
const prefix = command.slice(0, -2);
|
|
1756
|
+
this.allowedBashPrefixes.add(prefix);
|
|
1757
|
+
} else {
|
|
1758
|
+
this.allowedBashLiterals.add(command);
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1711
1761
|
/**
|
|
1712
1762
|
* Resolves tool call ID based on tool name and input
|
|
1713
1763
|
*/
|
|
@@ -1776,6 +1826,9 @@ class PermissionHandler {
|
|
|
1776
1826
|
reset() {
|
|
1777
1827
|
this.toolCalls = [];
|
|
1778
1828
|
this.responses.clear();
|
|
1829
|
+
this.allowedTools.clear();
|
|
1830
|
+
this.allowedBashLiterals.clear();
|
|
1831
|
+
this.allowedBashPrefixes.clear();
|
|
1779
1832
|
for (const [, pending] of this.pendingRequests.entries()) {
|
|
1780
1833
|
pending.reject(new Error("Session reset"));
|
|
1781
1834
|
}
|
|
@@ -2182,6 +2235,136 @@ class SDKToLogConverter {
|
|
|
2182
2235
|
}
|
|
2183
2236
|
}
|
|
2184
2237
|
|
|
2238
|
+
class OutgoingMessageQueue {
|
|
2239
|
+
constructor(sendFunction) {
|
|
2240
|
+
this.sendFunction = sendFunction;
|
|
2241
|
+
}
|
|
2242
|
+
queue = [];
|
|
2243
|
+
nextId = 1;
|
|
2244
|
+
lock = new AsyncLock();
|
|
2245
|
+
processTimer;
|
|
2246
|
+
delayTimers = /* @__PURE__ */ new Map();
|
|
2247
|
+
/**
|
|
2248
|
+
* Add message to queue
|
|
2249
|
+
*/
|
|
2250
|
+
enqueue(logMessage, options) {
|
|
2251
|
+
this.lock.inLock(async () => {
|
|
2252
|
+
const item = {
|
|
2253
|
+
id: this.nextId++,
|
|
2254
|
+
logMessage,
|
|
2255
|
+
delayed: !!options?.delay,
|
|
2256
|
+
delayMs: options?.delay || 0,
|
|
2257
|
+
toolCallIds: options?.toolCallIds,
|
|
2258
|
+
released: !options?.delay,
|
|
2259
|
+
// Not delayed = already released
|
|
2260
|
+
sent: false
|
|
2261
|
+
};
|
|
2262
|
+
this.queue.push(item);
|
|
2263
|
+
if (item.delayed) {
|
|
2264
|
+
const timer = setTimeout(() => {
|
|
2265
|
+
this.releaseItem(item.id);
|
|
2266
|
+
}, item.delayMs);
|
|
2267
|
+
this.delayTimers.set(item.id, timer);
|
|
2268
|
+
}
|
|
2269
|
+
});
|
|
2270
|
+
this.scheduleProcessing();
|
|
2271
|
+
}
|
|
2272
|
+
/**
|
|
2273
|
+
* Release specific item by ID
|
|
2274
|
+
*/
|
|
2275
|
+
async releaseItem(itemId) {
|
|
2276
|
+
await this.lock.inLock(async () => {
|
|
2277
|
+
const item = this.queue.find((i) => i.id === itemId);
|
|
2278
|
+
if (item && !item.released) {
|
|
2279
|
+
item.released = true;
|
|
2280
|
+
const timer = this.delayTimers.get(itemId);
|
|
2281
|
+
if (timer) {
|
|
2282
|
+
clearTimeout(timer);
|
|
2283
|
+
this.delayTimers.delete(itemId);
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
});
|
|
2287
|
+
this.scheduleProcessing();
|
|
2288
|
+
}
|
|
2289
|
+
/**
|
|
2290
|
+
* Release all messages with specific tool call ID
|
|
2291
|
+
*/
|
|
2292
|
+
async releaseToolCall(toolCallId) {
|
|
2293
|
+
await this.lock.inLock(async () => {
|
|
2294
|
+
for (const item of this.queue) {
|
|
2295
|
+
if (item.toolCallIds?.includes(toolCallId) && !item.released) {
|
|
2296
|
+
item.released = true;
|
|
2297
|
+
const timer = this.delayTimers.get(item.id);
|
|
2298
|
+
if (timer) {
|
|
2299
|
+
clearTimeout(timer);
|
|
2300
|
+
this.delayTimers.delete(item.id);
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
});
|
|
2305
|
+
this.scheduleProcessing();
|
|
2306
|
+
}
|
|
2307
|
+
/**
|
|
2308
|
+
* Process queue - send messages in ID order that are released
|
|
2309
|
+
*/
|
|
2310
|
+
async processQueue() {
|
|
2311
|
+
await this.lock.inLock(async () => {
|
|
2312
|
+
this.queue.sort((a, b) => a.id - b.id);
|
|
2313
|
+
while (this.queue.length > 0) {
|
|
2314
|
+
const item = this.queue[0];
|
|
2315
|
+
if (!item.released) {
|
|
2316
|
+
break;
|
|
2317
|
+
}
|
|
2318
|
+
if (!item.sent) {
|
|
2319
|
+
if (item.logMessage.type !== "system") {
|
|
2320
|
+
this.sendFunction(item.logMessage);
|
|
2321
|
+
}
|
|
2322
|
+
item.sent = true;
|
|
2323
|
+
}
|
|
2324
|
+
this.queue.shift();
|
|
2325
|
+
}
|
|
2326
|
+
});
|
|
2327
|
+
}
|
|
2328
|
+
/**
|
|
2329
|
+
* Flush all messages immediately (for cleanup)
|
|
2330
|
+
*/
|
|
2331
|
+
async flush() {
|
|
2332
|
+
await this.lock.inLock(async () => {
|
|
2333
|
+
for (const timer of this.delayTimers.values()) {
|
|
2334
|
+
clearTimeout(timer);
|
|
2335
|
+
}
|
|
2336
|
+
this.delayTimers.clear();
|
|
2337
|
+
for (const item of this.queue) {
|
|
2338
|
+
item.released = true;
|
|
2339
|
+
}
|
|
2340
|
+
await this.processQueue();
|
|
2341
|
+
});
|
|
2342
|
+
}
|
|
2343
|
+
/**
|
|
2344
|
+
* Schedule processing on next tick
|
|
2345
|
+
*/
|
|
2346
|
+
scheduleProcessing() {
|
|
2347
|
+
if (this.processTimer) {
|
|
2348
|
+
clearTimeout(this.processTimer);
|
|
2349
|
+
}
|
|
2350
|
+
this.processTimer = setTimeout(() => {
|
|
2351
|
+
this.processQueue();
|
|
2352
|
+
}, 0);
|
|
2353
|
+
}
|
|
2354
|
+
/**
|
|
2355
|
+
* Cleanup timers and resources
|
|
2356
|
+
*/
|
|
2357
|
+
destroy() {
|
|
2358
|
+
if (this.processTimer) {
|
|
2359
|
+
clearTimeout(this.processTimer);
|
|
2360
|
+
}
|
|
2361
|
+
for (const timer of this.delayTimers.values()) {
|
|
2362
|
+
clearTimeout(timer);
|
|
2363
|
+
}
|
|
2364
|
+
this.delayTimers.clear();
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
|
|
2185
2368
|
async function claudeRemoteLauncher(session) {
|
|
2186
2369
|
logger.debug("[claudeRemoteLauncher] Starting remote launcher");
|
|
2187
2370
|
const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
|
|
@@ -2239,6 +2422,12 @@ async function claudeRemoteLauncher(session) {
|
|
|
2239
2422
|
session.client.setHandler("abort", doAbort);
|
|
2240
2423
|
session.client.setHandler("switch", doSwitch);
|
|
2241
2424
|
const permissionHandler = new PermissionHandler(session);
|
|
2425
|
+
const messageQueue = new OutgoingMessageQueue(
|
|
2426
|
+
(logMessage) => session.client.sendClaudeSessionMessage(logMessage)
|
|
2427
|
+
);
|
|
2428
|
+
permissionHandler.setOnPermissionRequest((toolCallId) => {
|
|
2429
|
+
messageQueue.releaseToolCall(toolCallId);
|
|
2430
|
+
});
|
|
2242
2431
|
const sdkToLogConverter = new SDKToLogConverter({
|
|
2243
2432
|
sessionId: session.sessionId || "unknown",
|
|
2244
2433
|
cwd: session.path,
|
|
@@ -2277,6 +2466,7 @@ async function claudeRemoteLauncher(session) {
|
|
|
2277
2466
|
for (let c of umessage.message.content) {
|
|
2278
2467
|
if (c.type === "tool_result" && c.tool_use_id) {
|
|
2279
2468
|
ongoingToolCalls.delete(c.tool_use_id);
|
|
2469
|
+
messageQueue.releaseToolCall(c.tool_use_id);
|
|
2280
2470
|
}
|
|
2281
2471
|
}
|
|
2282
2472
|
}
|
|
@@ -2338,9 +2528,28 @@ async function claudeRemoteLauncher(session) {
|
|
|
2338
2528
|
}
|
|
2339
2529
|
}
|
|
2340
2530
|
}
|
|
2341
|
-
if (logMessage.type
|
|
2342
|
-
|
|
2531
|
+
if (logMessage.type === "assistant" && message.type === "assistant") {
|
|
2532
|
+
const assistantMsg = message;
|
|
2533
|
+
const toolCallIds = [];
|
|
2534
|
+
if (assistantMsg.message.content && Array.isArray(assistantMsg.message.content)) {
|
|
2535
|
+
for (const block of assistantMsg.message.content) {
|
|
2536
|
+
if (block.type === "tool_use" && block.id) {
|
|
2537
|
+
toolCallIds.push(block.id);
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
if (toolCallIds.length > 0) {
|
|
2542
|
+
const isSidechain = assistantMsg.parent_tool_use_id !== void 0;
|
|
2543
|
+
if (!isSidechain) {
|
|
2544
|
+
messageQueue.enqueue(logMessage, {
|
|
2545
|
+
delay: 250,
|
|
2546
|
+
toolCallIds
|
|
2547
|
+
});
|
|
2548
|
+
return;
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2343
2551
|
}
|
|
2552
|
+
messageQueue.enqueue(logMessage);
|
|
2344
2553
|
}
|
|
2345
2554
|
if (message.type === "assistant") {
|
|
2346
2555
|
let umessage = message;
|
|
@@ -2349,7 +2558,7 @@ async function claudeRemoteLauncher(session) {
|
|
|
2349
2558
|
if (c.type === "tool_use" && c.name === "Task" && c.input && typeof c.input.prompt === "string") {
|
|
2350
2559
|
const logMessage2 = sdkToLogConverter.convertSidechainUserMessage(c.id, c.input.prompt);
|
|
2351
2560
|
if (logMessage2) {
|
|
2352
|
-
|
|
2561
|
+
messageQueue.enqueue(logMessage2);
|
|
2353
2562
|
}
|
|
2354
2563
|
}
|
|
2355
2564
|
}
|
|
@@ -2440,6 +2649,8 @@ async function claudeRemoteLauncher(session) {
|
|
|
2440
2649
|
}
|
|
2441
2650
|
}
|
|
2442
2651
|
ongoingToolCalls.clear();
|
|
2652
|
+
await messageQueue.flush();
|
|
2653
|
+
messageQueue.destroy();
|
|
2443
2654
|
abortController = null;
|
|
2444
2655
|
abortFuture?.resolve(void 0);
|
|
2445
2656
|
abortFuture = null;
|
|
@@ -2513,7 +2724,7 @@ async function loop(opts) {
|
|
|
2513
2724
|
}
|
|
2514
2725
|
|
|
2515
2726
|
var name = "happy-coder";
|
|
2516
|
-
var version = "0.9.0-
|
|
2727
|
+
var version = "0.9.0-2";
|
|
2517
2728
|
var description = "Claude Code session sharing CLI";
|
|
2518
2729
|
var author = "Kirill Dubovitskiy";
|
|
2519
2730
|
var license = "MIT";
|
package/dist/lib.cjs
CHANGED
package/dist/lib.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, l as logger } from './types-
|
|
1
|
+
export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, l as logger } from './types-DJOX-XG-.mjs';
|
|
2
2
|
import 'axios';
|
|
3
3
|
import 'chalk';
|
|
4
4
|
import 'fs';
|
|
@@ -1317,4 +1317,4 @@ const RawJSONLinesSchema = z.discriminatedUnion("type", [
|
|
|
1317
1317
|
}).passthrough()
|
|
1318
1318
|
]);
|
|
1319
1319
|
|
|
1320
|
-
export { ApiClient as A, RawJSONLinesSchema as R, ApiSessionClient as a, backoff as b, configuration as c, delay as d,
|
|
1320
|
+
export { ApiClient as A, RawJSONLinesSchema as R, ApiSessionClient as a, backoff as b, configuration as c, delay as d, AsyncLock as e, encodeBase64 as f, encodeBase64Url as g, decodeBase64 as h, logger as l };
|
|
@@ -1321,6 +1321,7 @@ const RawJSONLinesSchema = z.z.discriminatedUnion("type", [
|
|
|
1321
1321
|
|
|
1322
1322
|
exports.ApiClient = ApiClient;
|
|
1323
1323
|
exports.ApiSessionClient = ApiSessionClient;
|
|
1324
|
+
exports.AsyncLock = AsyncLock;
|
|
1324
1325
|
exports.RawJSONLinesSchema = RawJSONLinesSchema;
|
|
1325
1326
|
exports.backoff = backoff;
|
|
1326
1327
|
exports.configuration = configuration;
|