happy-coder 0.6.2 → 0.6.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/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-iMUxaPkI.cjs');
4
+ var types$1 = require('./types-BDtHM1DY.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');
@@ -2262,7 +2262,7 @@ async function loop(opts) {
2262
2262
  }
2263
2263
 
2264
2264
  var name = "happy-coder";
2265
- var version = "0.6.2";
2265
+ var version = "0.6.3";
2266
2266
  var description = "Claude Code session sharing CLI";
2267
2267
  var author = "Kirill Dubovitskiy";
2268
2268
  var license = "MIT";
@@ -3089,7 +3089,7 @@ async function start(credentials, options = {}) {
3089
3089
  types$1.logger.infoDeveloper(`Logs: ${logPath}`);
3090
3090
  session.updateAgentState((currentState) => ({
3091
3091
  ...currentState,
3092
- controlledByUser: options.startingMode === "local"
3092
+ controlledByUser: options.startingMode !== "remote"
3093
3093
  }));
3094
3094
  const caffeinateStarted = startCaffeinate();
3095
3095
  if (caffeinateStarted) {
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import chalk from 'chalk';
2
- import { l as logger, d as backoff, e as delay, R as RawJSONLinesSchema, c as configuration, f as encodeBase64, A as ApiClient, g as encodeBase64Url, h as decodeBase64, j as encrypt, b as initializeConfiguration, i as initLoggerWithGlobalConfiguration } from './types-DKVMGtcN.mjs';
2
+ import { l as logger, d as backoff, e as delay, R as RawJSONLinesSchema, c as configuration, f as encodeBase64, A as ApiClient, g as encodeBase64Url, h as decodeBase64, j as encrypt, b as initializeConfiguration, i as initLoggerWithGlobalConfiguration } from './types-Dz5kZrVh.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';
@@ -2241,7 +2241,7 @@ async function loop(opts) {
2241
2241
  }
2242
2242
 
2243
2243
  var name = "happy-coder";
2244
- var version = "0.6.2";
2244
+ var version = "0.6.3";
2245
2245
  var description = "Claude Code session sharing CLI";
2246
2246
  var author = "Kirill Dubovitskiy";
2247
2247
  var license = "MIT";
@@ -3068,7 +3068,7 @@ async function start(credentials, options = {}) {
3068
3068
  logger.infoDeveloper(`Logs: ${logPath}`);
3069
3069
  session.updateAgentState((currentState) => ({
3070
3070
  ...currentState,
3071
- controlledByUser: options.startingMode === "local"
3071
+ controlledByUser: options.startingMode !== "remote"
3072
3072
  }));
3073
3073
  const caffeinateStarted = startCaffeinate();
3074
3074
  if (caffeinateStarted) {
package/dist/lib.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var types = require('./types-iMUxaPkI.cjs');
3
+ var types = require('./types-BDtHM1DY.cjs');
4
4
  require('axios');
5
5
  require('chalk');
6
6
  require('fs');
package/dist/lib.d.cts CHANGED
@@ -449,6 +449,8 @@ declare class ApiSessionClient extends EventEmitter {
449
449
  private pendingMessages;
450
450
  private pendingMessageCallback;
451
451
  private rpcHandlers;
452
+ private agentStateLock;
453
+ private metadataLock;
452
454
  constructor(token: string, secret: Uint8Array, session: Session);
453
455
  onUserMessage(callback: (data: UserMessage) => void): void;
454
456
  /**
package/dist/lib.d.mts CHANGED
@@ -449,6 +449,8 @@ declare class ApiSessionClient extends EventEmitter {
449
449
  private pendingMessages;
450
450
  private pendingMessageCallback;
451
451
  private rpcHandlers;
452
+ private agentStateLock;
453
+ private metadataLock;
452
454
  constructor(token: string, secret: Uint8Array, session: Session);
453
455
  onUserMessage(callback: (data: UserMessage) => void): void;
454
456
  /**
package/dist/lib.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, i as initLoggerWithGlobalConfiguration, b as initializeConfiguration, l as logger } from './types-DKVMGtcN.mjs';
1
+ export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, i as initLoggerWithGlobalConfiguration, b as initializeConfiguration, l as logger } from './types-Dz5kZrVh.mjs';
2
2
  import 'axios';
3
3
  import 'chalk';
4
4
  import 'fs';
@@ -358,6 +358,40 @@ function createBackoff(opts) {
358
358
  }
359
359
  let backoff = createBackoff();
360
360
 
361
+ class AsyncLock {
362
+ permits = 1;
363
+ promiseResolverQueue = [];
364
+ async inLock(func) {
365
+ try {
366
+ await this.lock();
367
+ return await func();
368
+ } finally {
369
+ this.unlock();
370
+ }
371
+ }
372
+ async lock() {
373
+ if (this.permits > 0) {
374
+ this.permits = this.permits - 1;
375
+ return;
376
+ }
377
+ await new Promise((resolve) => this.promiseResolverQueue.push(resolve));
378
+ }
379
+ unlock() {
380
+ this.permits += 1;
381
+ if (this.permits > 1 && this.promiseResolverQueue.length > 0) {
382
+ throw new Error("this.permits should never be > 0 when there is someone waiting.");
383
+ } else if (this.permits === 1 && this.promiseResolverQueue.length > 0) {
384
+ this.permits -= 1;
385
+ const nextResolver = this.promiseResolverQueue.shift();
386
+ if (nextResolver) {
387
+ setTimeout(() => {
388
+ nextResolver(true);
389
+ }, 0);
390
+ }
391
+ }
392
+ }
393
+ }
394
+
361
395
  class ApiSessionClient extends node_events.EventEmitter {
362
396
  token;
363
397
  secret;
@@ -370,6 +404,8 @@ class ApiSessionClient extends node_events.EventEmitter {
370
404
  pendingMessages = [];
371
405
  pendingMessageCallback = null;
372
406
  rpcHandlers = /* @__PURE__ */ new Map();
407
+ agentStateLock = new AsyncLock();
408
+ metadataLock = new AsyncLock();
373
409
  constructor(token, secret, session) {
374
410
  super();
375
411
  this.token = token;
@@ -589,19 +625,21 @@ class ApiSessionClient extends node_events.EventEmitter {
589
625
  * @param handler - Handler function that returns the updated metadata
590
626
  */
591
627
  updateMetadata(handler) {
592
- backoff(async () => {
593
- let updated = handler(this.metadata);
594
- const answer = await this.socket.emitWithAck("update-metadata", { sid: this.sessionId, expectedVersion: this.metadataVersion, metadata: encodeBase64(encrypt(updated, this.secret)) });
595
- if (answer.result === "success") {
596
- this.metadata = decrypt(decodeBase64(answer.metadata), this.secret);
597
- this.metadataVersion = answer.version;
598
- } else if (answer.result === "version-mismatch") {
599
- if (answer.version > this.metadataVersion) {
600
- this.metadataVersion = answer.version;
628
+ this.metadataLock.inLock(async () => {
629
+ await backoff(async () => {
630
+ let updated = handler(this.metadata);
631
+ const answer = await this.socket.emitWithAck("update-metadata", { sid: this.sessionId, expectedVersion: this.metadataVersion, metadata: encodeBase64(encrypt(updated, this.secret)) });
632
+ if (answer.result === "success") {
601
633
  this.metadata = decrypt(decodeBase64(answer.metadata), this.secret);
602
- }
603
- throw new Error("Metadata version mismatch");
604
- } else if (answer.result === "error") ;
634
+ this.metadataVersion = answer.version;
635
+ } else if (answer.result === "version-mismatch") {
636
+ if (answer.version > this.metadataVersion) {
637
+ this.metadataVersion = answer.version;
638
+ this.metadata = decrypt(decodeBase64(answer.metadata), this.secret);
639
+ }
640
+ throw new Error("Metadata version mismatch");
641
+ } else if (answer.result === "error") ;
642
+ });
605
643
  });
606
644
  }
607
645
  /**
@@ -610,20 +648,22 @@ class ApiSessionClient extends node_events.EventEmitter {
610
648
  */
611
649
  updateAgentState(handler) {
612
650
  exports.logger.debugLargeJson("Updating agent state", this.agentState);
613
- backoff(async () => {
614
- let updated = handler(this.agentState || {});
615
- const answer = await this.socket.emitWithAck("update-state", { sid: this.sessionId, expectedVersion: this.agentStateVersion, agentState: updated ? encodeBase64(encrypt(updated, this.secret)) : null });
616
- if (answer.result === "success") {
617
- this.agentState = answer.agentState ? decrypt(decodeBase64(answer.agentState), this.secret) : null;
618
- this.agentStateVersion = answer.version;
619
- exports.logger.debug("Agent state updated", this.agentState);
620
- } else if (answer.result === "version-mismatch") {
621
- if (answer.version > this.agentStateVersion) {
622
- this.agentStateVersion = answer.version;
651
+ this.agentStateLock.inLock(async () => {
652
+ await backoff(async () => {
653
+ let updated = handler(this.agentState || {});
654
+ const answer = await this.socket.emitWithAck("update-state", { sid: this.sessionId, expectedVersion: this.agentStateVersion, agentState: updated ? encodeBase64(encrypt(updated, this.secret)) : null });
655
+ if (answer.result === "success") {
623
656
  this.agentState = answer.agentState ? decrypt(decodeBase64(answer.agentState), this.secret) : null;
624
- }
625
- throw new Error("Agent state version mismatch");
626
- } else if (answer.result === "error") ;
657
+ this.agentStateVersion = answer.version;
658
+ exports.logger.debug("Agent state updated", this.agentState);
659
+ } else if (answer.result === "version-mismatch") {
660
+ if (answer.version > this.agentStateVersion) {
661
+ this.agentStateVersion = answer.version;
662
+ this.agentState = answer.agentState ? decrypt(decodeBase64(answer.agentState), this.secret) : null;
663
+ }
664
+ throw new Error("Agent state version mismatch");
665
+ } else if (answer.result === "error") ;
666
+ });
627
667
  });
628
668
  }
629
669
  /**
@@ -356,6 +356,40 @@ function createBackoff(opts) {
356
356
  }
357
357
  let backoff = createBackoff();
358
358
 
359
+ class AsyncLock {
360
+ permits = 1;
361
+ promiseResolverQueue = [];
362
+ async inLock(func) {
363
+ try {
364
+ await this.lock();
365
+ return await func();
366
+ } finally {
367
+ this.unlock();
368
+ }
369
+ }
370
+ async lock() {
371
+ if (this.permits > 0) {
372
+ this.permits = this.permits - 1;
373
+ return;
374
+ }
375
+ await new Promise((resolve) => this.promiseResolverQueue.push(resolve));
376
+ }
377
+ unlock() {
378
+ this.permits += 1;
379
+ if (this.permits > 1 && this.promiseResolverQueue.length > 0) {
380
+ throw new Error("this.permits should never be > 0 when there is someone waiting.");
381
+ } else if (this.permits === 1 && this.promiseResolverQueue.length > 0) {
382
+ this.permits -= 1;
383
+ const nextResolver = this.promiseResolverQueue.shift();
384
+ if (nextResolver) {
385
+ setTimeout(() => {
386
+ nextResolver(true);
387
+ }, 0);
388
+ }
389
+ }
390
+ }
391
+ }
392
+
359
393
  class ApiSessionClient extends EventEmitter {
360
394
  token;
361
395
  secret;
@@ -368,6 +402,8 @@ class ApiSessionClient extends EventEmitter {
368
402
  pendingMessages = [];
369
403
  pendingMessageCallback = null;
370
404
  rpcHandlers = /* @__PURE__ */ new Map();
405
+ agentStateLock = new AsyncLock();
406
+ metadataLock = new AsyncLock();
371
407
  constructor(token, secret, session) {
372
408
  super();
373
409
  this.token = token;
@@ -587,19 +623,21 @@ class ApiSessionClient extends EventEmitter {
587
623
  * @param handler - Handler function that returns the updated metadata
588
624
  */
589
625
  updateMetadata(handler) {
590
- backoff(async () => {
591
- let updated = handler(this.metadata);
592
- const answer = await this.socket.emitWithAck("update-metadata", { sid: this.sessionId, expectedVersion: this.metadataVersion, metadata: encodeBase64(encrypt(updated, this.secret)) });
593
- if (answer.result === "success") {
594
- this.metadata = decrypt(decodeBase64(answer.metadata), this.secret);
595
- this.metadataVersion = answer.version;
596
- } else if (answer.result === "version-mismatch") {
597
- if (answer.version > this.metadataVersion) {
598
- this.metadataVersion = answer.version;
626
+ this.metadataLock.inLock(async () => {
627
+ await backoff(async () => {
628
+ let updated = handler(this.metadata);
629
+ const answer = await this.socket.emitWithAck("update-metadata", { sid: this.sessionId, expectedVersion: this.metadataVersion, metadata: encodeBase64(encrypt(updated, this.secret)) });
630
+ if (answer.result === "success") {
599
631
  this.metadata = decrypt(decodeBase64(answer.metadata), this.secret);
600
- }
601
- throw new Error("Metadata version mismatch");
602
- } else if (answer.result === "error") ;
632
+ this.metadataVersion = answer.version;
633
+ } else if (answer.result === "version-mismatch") {
634
+ if (answer.version > this.metadataVersion) {
635
+ this.metadataVersion = answer.version;
636
+ this.metadata = decrypt(decodeBase64(answer.metadata), this.secret);
637
+ }
638
+ throw new Error("Metadata version mismatch");
639
+ } else if (answer.result === "error") ;
640
+ });
603
641
  });
604
642
  }
605
643
  /**
@@ -608,20 +646,22 @@ class ApiSessionClient extends EventEmitter {
608
646
  */
609
647
  updateAgentState(handler) {
610
648
  logger.debugLargeJson("Updating agent state", this.agentState);
611
- backoff(async () => {
612
- let updated = handler(this.agentState || {});
613
- const answer = await this.socket.emitWithAck("update-state", { sid: this.sessionId, expectedVersion: this.agentStateVersion, agentState: updated ? encodeBase64(encrypt(updated, this.secret)) : null });
614
- if (answer.result === "success") {
615
- this.agentState = answer.agentState ? decrypt(decodeBase64(answer.agentState), this.secret) : null;
616
- this.agentStateVersion = answer.version;
617
- logger.debug("Agent state updated", this.agentState);
618
- } else if (answer.result === "version-mismatch") {
619
- if (answer.version > this.agentStateVersion) {
620
- this.agentStateVersion = answer.version;
649
+ this.agentStateLock.inLock(async () => {
650
+ await backoff(async () => {
651
+ let updated = handler(this.agentState || {});
652
+ const answer = await this.socket.emitWithAck("update-state", { sid: this.sessionId, expectedVersion: this.agentStateVersion, agentState: updated ? encodeBase64(encrypt(updated, this.secret)) : null });
653
+ if (answer.result === "success") {
621
654
  this.agentState = answer.agentState ? decrypt(decodeBase64(answer.agentState), this.secret) : null;
622
- }
623
- throw new Error("Agent state version mismatch");
624
- } else if (answer.result === "error") ;
655
+ this.agentStateVersion = answer.version;
656
+ logger.debug("Agent state updated", this.agentState);
657
+ } else if (answer.result === "version-mismatch") {
658
+ if (answer.version > this.agentStateVersion) {
659
+ this.agentStateVersion = answer.version;
660
+ this.agentState = answer.agentState ? decrypt(decodeBase64(answer.agentState), this.secret) : null;
661
+ }
662
+ throw new Error("Agent state version mismatch");
663
+ } else if (answer.result === "error") ;
664
+ });
625
665
  });
626
666
  }
627
667
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "happy-coder",
3
- "version": "0.6.2",
3
+ "version": "0.6.3",
4
4
  "description": "Claude Code session sharing CLI",
5
5
  "author": "Kirill Dubovitskiy",
6
6
  "license": "MIT",