@kmmao/happy-agent 0.7.0 → 0.7.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.d.cts CHANGED
@@ -45,6 +45,27 @@ declare const MachineMetadataSchema: z.ZodObject<{
45
45
  happyLibDir: z.ZodString;
46
46
  }, z.core.$strip>;
47
47
  type MachineMetadata = z.infer<typeof MachineMetadataSchema>;
48
+ declare const TailscaleInfoSchema: z.ZodObject<{
49
+ status: z.ZodEnum<{
50
+ connected: "connected";
51
+ disconnected: "disconnected";
52
+ "not-installed": "not-installed";
53
+ }>;
54
+ ipv4: z.ZodOptional<z.ZodString>;
55
+ ipv6: z.ZodOptional<z.ZodString>;
56
+ hostname: z.ZodOptional<z.ZodString>;
57
+ tailnetName: z.ZodOptional<z.ZodString>;
58
+ version: z.ZodOptional<z.ZodString>;
59
+ serves: z.ZodOptional<z.ZodArray<z.ZodObject<{
60
+ port: z.ZodNumber;
61
+ path: z.ZodOptional<z.ZodString>;
62
+ protocol: z.ZodString;
63
+ target: z.ZodString;
64
+ funnel: z.ZodBoolean;
65
+ hostname: z.ZodString;
66
+ }, z.core.$strip>>>;
67
+ }, z.core.$strip>;
68
+ type TailscaleInfo = z.infer<typeof TailscaleInfoSchema>;
48
69
  declare const TunnelEntrySchema: z.ZodObject<{
49
70
  provider: z.ZodString;
50
71
  localPort: z.ZodNumber;
@@ -384,8 +405,8 @@ declare const ResolvedRuntimeProfileSchema: z$1.ZodObject<{
384
405
  }, z$1.core.$strip>>>;
385
406
  modelMappings: z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodString>>;
386
407
  defaultSessionType: z$1.ZodOptional<z$1.ZodEnum<{
387
- simple: "simple";
388
408
  worktree: "worktree";
409
+ simple: "simple";
389
410
  }>>;
390
411
  defaultPermissionMode: z$1.ZodOptional<z$1.ZodEnum<{
391
412
  default: "default";
@@ -508,10 +529,46 @@ declare function getOrCreateMachine(config: Config, creds: Credentials, metadata
508
529
  */
509
530
  declare function listMachines(config: Config, creds: Credentials): Promise<RawMachine[]>;
510
531
 
532
+ /**
533
+ * Outcome of a decrypt attempt.
534
+ *
535
+ * The low-level {@link decrypt} returns `unknown | null`, fusing "could not
536
+ * decrypt" (wrong key, tampered bytes, wrong variant, non-JSON plaintext)
537
+ * with a value that legitimately decrypted to something falsy.
538
+ * `DecryptResult` splits those apart: `{ ok: false }` is an authentication or
539
+ * parse failure, while `{ ok: true, value }` carries the recovered plaintext.
540
+ * Callers branch on `ok` instead of guessing from a collapsed `null`.
541
+ */
542
+ type DecryptResult = {
543
+ ok: true;
544
+ value: any;
545
+ } | {
546
+ ok: false;
547
+ };
548
+ /**
549
+ * A Cipher binds one AccessKey + encryption variant into a small interface.
550
+ *
551
+ * It is the single seam every transport client encrypts/decrypts through:
552
+ * hand it a value and get a wire-ready base64 string, or hand it a base64
553
+ * wire string and get a {@link DecryptResult}. The variant choice (legacy
554
+ * NaCl secretbox vs AES-256-GCM dataKey) and the base64 framing live behind
555
+ * this interface, so call sites never thread `(key, variant)` or call
556
+ * `encode`/`decodeBase64` themselves — a wrong-variant or wrong-key bug can
557
+ * only originate at the one `createCipher` call, not at the dozens of places
558
+ * that used to repeat the pattern.
559
+ */
560
+ interface Cipher {
561
+ /** Encrypt a JSON-serializable value, returning a base64 wire string. */
562
+ encrypt(data: any): string;
563
+ /** Decode + decrypt a base64 wire string. Never throws. */
564
+ decrypt(data: string): DecryptResult;
565
+ }
566
+
511
567
  /**
512
568
  * Common RPC types and interfaces for both session and machine clients.
513
569
  * Mirrors happy-cli/src/api/rpc/types.ts
514
570
  */
571
+
515
572
  /**
516
573
  * Generic RPC handler function type
517
574
  */
@@ -528,8 +585,7 @@ interface RpcRequest {
528
585
  */
529
586
  interface RpcHandlerConfig {
530
587
  scopePrefix: string;
531
- encryptionKey: Uint8Array;
532
- encryptionVariant: "legacy" | "dataKey";
588
+ cipher: Cipher;
533
589
  logger?: (message: string, data?: unknown) => void;
534
590
  }
535
591
 
@@ -544,8 +600,7 @@ interface RpcHandlerConfig {
544
600
  declare class RpcHandlerManager {
545
601
  private handlers;
546
602
  private readonly scopePrefix;
547
- private readonly encryptionKey;
548
- private readonly encryptionVariant;
603
+ private readonly cipher;
549
604
  private readonly logger;
550
605
  private socket;
551
606
  private reregisterInterval;
@@ -556,7 +611,18 @@ declare class RpcHandlerManager {
556
611
  */
557
612
  registerHandler<TRequest = any, TResponse = any>(method: string, handler: RpcHandler<TRequest, TResponse>): void;
558
613
  /**
559
- * Handle an incoming RPC request
614
+ * Route a decrypted RPC call to its handler. This is the plaintext core of
615
+ * the manager: it knows nothing about the wire (no base64, no cipher), so it
616
+ * is TOTAL — an unknown method or a throwing handler both resolve to an
617
+ * `{ error }` value rather than rejecting. That makes it the test surface for
618
+ * routing behaviour, exercised without any crypto setup.
619
+ */
620
+ dispatch(method: string, params: unknown): Promise<unknown>;
621
+ /**
622
+ * Handle an incoming wire RPC request: decrypt params, dispatch in plaintext,
623
+ * encrypt the result. The Cipher is the only encryption seam; on a decrypt
624
+ * failure the handler is still dispatched with `null` params (preserving the
625
+ * previous behaviour where a corrupt payload decrypted to `null`).
560
626
  */
561
627
  handleRequest(request: RpcRequest): Promise<string>;
562
628
  onSocketConnect(socket: Socket): void;
@@ -640,30 +706,6 @@ declare class SessionClient extends EventEmitter {
640
706
  private setupSocketListeners;
641
707
  }
642
708
 
643
- /**
644
- * Tailscale detection utility for happy-agent.
645
- *
646
- * Adapted from happy-cli/src/utils/tailscale.ts — keep in sync.
647
- */
648
- type TailscaleStatus = "connected" | "disconnected" | "not-installed";
649
- type TailscaleServeEntry = {
650
- port: number;
651
- path: string;
652
- protocol: string;
653
- target: string;
654
- funnel: boolean;
655
- hostname: string;
656
- };
657
- type TailscaleInfo = {
658
- status: TailscaleStatus;
659
- ipv4?: string;
660
- ipv6?: string;
661
- hostname?: string;
662
- tailnetName?: string;
663
- version?: string;
664
- serves?: TailscaleServeEntry[];
665
- };
666
-
667
709
  /**
668
710
  * Tunnel Provider abstraction — unified interface for all tunnel backends.
669
711
  *
package/dist/index.d.mts CHANGED
@@ -45,6 +45,27 @@ declare const MachineMetadataSchema: z.ZodObject<{
45
45
  happyLibDir: z.ZodString;
46
46
  }, z.core.$strip>;
47
47
  type MachineMetadata = z.infer<typeof MachineMetadataSchema>;
48
+ declare const TailscaleInfoSchema: z.ZodObject<{
49
+ status: z.ZodEnum<{
50
+ connected: "connected";
51
+ disconnected: "disconnected";
52
+ "not-installed": "not-installed";
53
+ }>;
54
+ ipv4: z.ZodOptional<z.ZodString>;
55
+ ipv6: z.ZodOptional<z.ZodString>;
56
+ hostname: z.ZodOptional<z.ZodString>;
57
+ tailnetName: z.ZodOptional<z.ZodString>;
58
+ version: z.ZodOptional<z.ZodString>;
59
+ serves: z.ZodOptional<z.ZodArray<z.ZodObject<{
60
+ port: z.ZodNumber;
61
+ path: z.ZodOptional<z.ZodString>;
62
+ protocol: z.ZodString;
63
+ target: z.ZodString;
64
+ funnel: z.ZodBoolean;
65
+ hostname: z.ZodString;
66
+ }, z.core.$strip>>>;
67
+ }, z.core.$strip>;
68
+ type TailscaleInfo = z.infer<typeof TailscaleInfoSchema>;
48
69
  declare const TunnelEntrySchema: z.ZodObject<{
49
70
  provider: z.ZodString;
50
71
  localPort: z.ZodNumber;
@@ -384,8 +405,8 @@ declare const ResolvedRuntimeProfileSchema: z$1.ZodObject<{
384
405
  }, z$1.core.$strip>>>;
385
406
  modelMappings: z$1.ZodOptional<z$1.ZodRecord<z$1.ZodString, z$1.ZodString>>;
386
407
  defaultSessionType: z$1.ZodOptional<z$1.ZodEnum<{
387
- simple: "simple";
388
408
  worktree: "worktree";
409
+ simple: "simple";
389
410
  }>>;
390
411
  defaultPermissionMode: z$1.ZodOptional<z$1.ZodEnum<{
391
412
  default: "default";
@@ -508,10 +529,46 @@ declare function getOrCreateMachine(config: Config, creds: Credentials, metadata
508
529
  */
509
530
  declare function listMachines(config: Config, creds: Credentials): Promise<RawMachine[]>;
510
531
 
532
+ /**
533
+ * Outcome of a decrypt attempt.
534
+ *
535
+ * The low-level {@link decrypt} returns `unknown | null`, fusing "could not
536
+ * decrypt" (wrong key, tampered bytes, wrong variant, non-JSON plaintext)
537
+ * with a value that legitimately decrypted to something falsy.
538
+ * `DecryptResult` splits those apart: `{ ok: false }` is an authentication or
539
+ * parse failure, while `{ ok: true, value }` carries the recovered plaintext.
540
+ * Callers branch on `ok` instead of guessing from a collapsed `null`.
541
+ */
542
+ type DecryptResult = {
543
+ ok: true;
544
+ value: any;
545
+ } | {
546
+ ok: false;
547
+ };
548
+ /**
549
+ * A Cipher binds one AccessKey + encryption variant into a small interface.
550
+ *
551
+ * It is the single seam every transport client encrypts/decrypts through:
552
+ * hand it a value and get a wire-ready base64 string, or hand it a base64
553
+ * wire string and get a {@link DecryptResult}. The variant choice (legacy
554
+ * NaCl secretbox vs AES-256-GCM dataKey) and the base64 framing live behind
555
+ * this interface, so call sites never thread `(key, variant)` or call
556
+ * `encode`/`decodeBase64` themselves — a wrong-variant or wrong-key bug can
557
+ * only originate at the one `createCipher` call, not at the dozens of places
558
+ * that used to repeat the pattern.
559
+ */
560
+ interface Cipher {
561
+ /** Encrypt a JSON-serializable value, returning a base64 wire string. */
562
+ encrypt(data: any): string;
563
+ /** Decode + decrypt a base64 wire string. Never throws. */
564
+ decrypt(data: string): DecryptResult;
565
+ }
566
+
511
567
  /**
512
568
  * Common RPC types and interfaces for both session and machine clients.
513
569
  * Mirrors happy-cli/src/api/rpc/types.ts
514
570
  */
571
+
515
572
  /**
516
573
  * Generic RPC handler function type
517
574
  */
@@ -528,8 +585,7 @@ interface RpcRequest {
528
585
  */
529
586
  interface RpcHandlerConfig {
530
587
  scopePrefix: string;
531
- encryptionKey: Uint8Array;
532
- encryptionVariant: "legacy" | "dataKey";
588
+ cipher: Cipher;
533
589
  logger?: (message: string, data?: unknown) => void;
534
590
  }
535
591
 
@@ -544,8 +600,7 @@ interface RpcHandlerConfig {
544
600
  declare class RpcHandlerManager {
545
601
  private handlers;
546
602
  private readonly scopePrefix;
547
- private readonly encryptionKey;
548
- private readonly encryptionVariant;
603
+ private readonly cipher;
549
604
  private readonly logger;
550
605
  private socket;
551
606
  private reregisterInterval;
@@ -556,7 +611,18 @@ declare class RpcHandlerManager {
556
611
  */
557
612
  registerHandler<TRequest = any, TResponse = any>(method: string, handler: RpcHandler<TRequest, TResponse>): void;
558
613
  /**
559
- * Handle an incoming RPC request
614
+ * Route a decrypted RPC call to its handler. This is the plaintext core of
615
+ * the manager: it knows nothing about the wire (no base64, no cipher), so it
616
+ * is TOTAL — an unknown method or a throwing handler both resolve to an
617
+ * `{ error }` value rather than rejecting. That makes it the test surface for
618
+ * routing behaviour, exercised without any crypto setup.
619
+ */
620
+ dispatch(method: string, params: unknown): Promise<unknown>;
621
+ /**
622
+ * Handle an incoming wire RPC request: decrypt params, dispatch in plaintext,
623
+ * encrypt the result. The Cipher is the only encryption seam; on a decrypt
624
+ * failure the handler is still dispatched with `null` params (preserving the
625
+ * previous behaviour where a corrupt payload decrypted to `null`).
560
626
  */
561
627
  handleRequest(request: RpcRequest): Promise<string>;
562
628
  onSocketConnect(socket: Socket): void;
@@ -640,30 +706,6 @@ declare class SessionClient extends EventEmitter {
640
706
  private setupSocketListeners;
641
707
  }
642
708
 
643
- /**
644
- * Tailscale detection utility for happy-agent.
645
- *
646
- * Adapted from happy-cli/src/utils/tailscale.ts — keep in sync.
647
- */
648
- type TailscaleStatus = "connected" | "disconnected" | "not-installed";
649
- type TailscaleServeEntry = {
650
- port: number;
651
- path: string;
652
- protocol: string;
653
- target: string;
654
- funnel: boolean;
655
- hostname: string;
656
- };
657
- type TailscaleInfo = {
658
- status: TailscaleStatus;
659
- ipv4?: string;
660
- ipv6?: string;
661
- hostname?: string;
662
- tailnetName?: string;
663
- version?: string;
664
- serves?: TailscaleServeEntry[];
665
- };
666
-
667
709
  /**
668
710
  * Tunnel Provider abstraction — unified interface for all tunnel backends.
669
711
  *