happy-coder 0.10.0-3 → 0.10.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/lib.d.mts CHANGED
@@ -308,11 +308,9 @@ interface RpcRequest {
308
308
  * Configuration for RPC handler manager
309
309
  */
310
310
  interface RpcHandlerConfig {
311
- /** Prefix to add to all method names (e.g., sessionId or machineId) */
312
311
  scopePrefix: string;
313
- /** Secret key for encryption/decryption */
314
- secret: Uint8Array;
315
- /** Logger function for debugging */
312
+ encryptionKey: Uint8Array;
313
+ encryptionVariant: 'legacy' | 'dataKey';
316
314
  logger?: (message: string, data?: any) => void;
317
315
  }
318
316
 
@@ -324,7 +322,8 @@ interface RpcHandlerConfig {
324
322
  declare class RpcHandlerManager {
325
323
  private handlers;
326
324
  private readonly scopePrefix;
327
- private readonly secret;
325
+ private readonly encryptionKey;
326
+ private readonly encryptionVariant;
328
327
  private readonly logger;
329
328
  private socket;
330
329
  constructor(config: RpcHandlerConfig);
@@ -364,7 +363,6 @@ declare class RpcHandlerManager {
364
363
 
365
364
  declare class ApiSessionClient extends EventEmitter {
366
365
  private readonly token;
367
- private readonly secret;
368
366
  readonly sessionId: string;
369
367
  private metadata;
370
368
  private metadataVersion;
@@ -376,7 +374,9 @@ declare class ApiSessionClient extends EventEmitter {
376
374
  readonly rpcHandlerManager: RpcHandlerManager;
377
375
  private agentStateLock;
378
376
  private metadataLock;
379
- constructor(token: string, secret: Uint8Array, session: Session);
377
+ private encryptionKey;
378
+ private encryptionVariant;
379
+ constructor(token: string, session: Session);
380
380
  onUserMessage(callback: (data: UserMessage) => void): void;
381
381
  /**
382
382
  * Send message to session
@@ -434,53 +434,16 @@ type Usage = z.infer<typeof UsageSchema>;
434
434
  /**
435
435
  * Session information
436
436
  */
437
- declare const SessionSchema: z.ZodObject<{
438
- createdAt: z.ZodNumber;
439
- id: z.ZodString;
440
- seq: z.ZodNumber;
441
- updatedAt: z.ZodNumber;
442
- metadata: z.ZodAny;
443
- metadataVersion: z.ZodNumber;
444
- agentState: z.ZodNullable<z.ZodAny>;
445
- agentStateVersion: z.ZodNumber;
446
- connectivityStatus: z.ZodOptional<z.ZodUnion<[z.ZodEnum<["neverConnected", "online", "offline"]>, z.ZodString]>>;
447
- connectivityStatusSince: z.ZodOptional<z.ZodNumber>;
448
- connectivityStatusReason: z.ZodOptional<z.ZodString>;
449
- state: z.ZodOptional<z.ZodUnion<[z.ZodEnum<["running", "archiveRequested", "archived"]>, z.ZodString]>>;
450
- stateSince: z.ZodOptional<z.ZodNumber>;
451
- stateReason: z.ZodOptional<z.ZodString>;
452
- }, "strip", z.ZodTypeAny, {
453
- id: string;
454
- seq: number;
455
- createdAt: number;
456
- updatedAt: number;
457
- metadataVersion: number;
458
- agentStateVersion: number;
459
- metadata?: any;
460
- agentState?: any;
461
- connectivityStatus?: string | undefined;
462
- connectivityStatusSince?: number | undefined;
463
- connectivityStatusReason?: string | undefined;
464
- state?: string | undefined;
465
- stateSince?: number | undefined;
466
- stateReason?: string | undefined;
467
- }, {
437
+ type Session = {
468
438
  id: string;
469
439
  seq: number;
470
- createdAt: number;
471
- updatedAt: number;
440
+ encryptionKey: Uint8Array;
441
+ encryptionVariant: 'legacy' | 'dataKey';
442
+ metadata: Metadata;
472
443
  metadataVersion: number;
444
+ agentState: AgentState | null;
473
445
  agentStateVersion: number;
474
- metadata?: any;
475
- agentState?: any;
476
- connectivityStatus?: string | undefined;
477
- connectivityStatusSince?: number | undefined;
478
- connectivityStatusReason?: string | undefined;
479
- state?: string | undefined;
480
- stateSince?: number | undefined;
481
- stateReason?: string | undefined;
482
- }>;
483
- type Session = z.infer<typeof SessionSchema>;
446
+ };
484
447
  /**
485
448
  * Machine metadata - static information (rarely changes)
486
449
  */
@@ -533,59 +496,15 @@ declare const DaemonStateSchema: z.ZodObject<{
533
496
  shutdownSource?: string | undefined;
534
497
  }>;
535
498
  type DaemonState = z.infer<typeof DaemonStateSchema>;
536
- /**
537
- * Machine information - similar to Session
538
- */
539
- declare const MachineSchema: z.ZodObject<{
540
- id: z.ZodString;
541
- metadata: z.ZodAny;
542
- metadataVersion: z.ZodNumber;
543
- daemonState: z.ZodNullable<z.ZodAny>;
544
- daemonStateVersion: z.ZodNumber;
545
- active: z.ZodBoolean;
546
- activeAt: z.ZodNumber;
547
- createdAt: z.ZodNumber;
548
- updatedAt: z.ZodNumber;
549
- connectivityStatus: z.ZodOptional<z.ZodUnion<[z.ZodEnum<["neverConnected", "online", "offline"]>, z.ZodString]>>;
550
- connectivityStatusSince: z.ZodOptional<z.ZodNumber>;
551
- connectivityStatusReason: z.ZodOptional<z.ZodString>;
552
- state: z.ZodOptional<z.ZodUnion<[z.ZodEnum<["running", "archiveRequested", "archived"]>, z.ZodString]>>;
553
- stateSince: z.ZodOptional<z.ZodNumber>;
554
- stateReason: z.ZodOptional<z.ZodString>;
555
- }, "strip", z.ZodTypeAny, {
556
- id: string;
557
- createdAt: number;
558
- updatedAt: number;
559
- metadataVersion: number;
560
- daemonStateVersion: number;
561
- active: boolean;
562
- activeAt: number;
563
- metadata?: any;
564
- daemonState?: any;
565
- connectivityStatus?: string | undefined;
566
- connectivityStatusSince?: number | undefined;
567
- connectivityStatusReason?: string | undefined;
568
- state?: string | undefined;
569
- stateSince?: number | undefined;
570
- stateReason?: string | undefined;
571
- }, {
499
+ type Machine = {
572
500
  id: string;
573
- createdAt: number;
574
- updatedAt: number;
501
+ encryptionKey: Uint8Array;
502
+ encryptionVariant: 'legacy' | 'dataKey';
503
+ metadata: MachineMetadata;
575
504
  metadataVersion: number;
505
+ daemonState: DaemonState | null;
576
506
  daemonStateVersion: number;
577
- active: boolean;
578
- activeAt: number;
579
- metadata?: any;
580
- daemonState?: any;
581
- connectivityStatus?: string | undefined;
582
- connectivityStatusSince?: number | undefined;
583
- connectivityStatusReason?: string | undefined;
584
- state?: string | undefined;
585
- stateSince?: number | undefined;
586
- stateReason?: string | undefined;
587
- }>;
588
- type Machine = z.infer<typeof MachineSchema>;
507
+ };
589
508
  declare const UserMessageSchema: z.ZodObject<{
590
509
  role: z.ZodLiteral<"user">;
591
510
  content: z.ZodObject<{
@@ -719,6 +638,8 @@ interface SpawnSessionOptions {
719
638
  directory: string;
720
639
  sessionId?: string;
721
640
  approvedNewDirectoryCreation?: boolean;
641
+ agent?: 'claude' | 'codex';
642
+ token?: string;
722
643
  }
723
644
  type SpawnSessionResult = {
724
645
  type: 'success';
@@ -743,12 +664,11 @@ type MachineRpcHandlers = {
743
664
  };
744
665
  declare class ApiMachineClient {
745
666
  private token;
746
- private secret;
747
667
  private machine;
748
668
  private socket;
749
669
  private keepAliveInterval;
750
670
  private rpcHandlerManager;
751
- constructor(token: string, secret: Uint8Array, machine: Machine);
671
+ constructor(token: string, machine: Machine);
752
672
  setRPCHandlers({ spawnSession, stopSession, requestShutdown }: MachineRpcHandlers): void;
753
673
  /**
754
674
  * Update machine metadata
@@ -796,11 +716,29 @@ declare class PushNotificationClient {
796
716
  sendToAllDevices(title: string, body: string, data?: Record<string, any>): void;
797
717
  }
798
718
 
719
+ /**
720
+ * Minimal persistence functions for happy CLI
721
+ *
722
+ * Handles settings and private key storage in ~/.happy/ or local .happy/
723
+ */
724
+
725
+ type Credentials = {
726
+ token: string;
727
+ encryption: {
728
+ type: 'legacy';
729
+ secret: Uint8Array;
730
+ } | {
731
+ type: 'dataKey';
732
+ publicKey: Uint8Array;
733
+ machineKey: Uint8Array;
734
+ };
735
+ };
736
+
799
737
  declare class ApiClient {
800
- private readonly token;
801
- private readonly secret;
738
+ static create(credential: Credentials): Promise<ApiClient>;
739
+ private readonly credential;
802
740
  private readonly pushClient;
803
- constructor(token: string, secret: Uint8Array);
741
+ private constructor();
804
742
  /**
805
743
  * Create a new session or load existing one with the given tag
806
744
  */
@@ -809,11 +747,6 @@ declare class ApiClient {
809
747
  metadata: Metadata;
810
748
  state: AgentState | null;
811
749
  }): Promise<Session>;
812
- /**
813
- * Get machine by ID from the server
814
- * Returns the current machine state from the server with decrypted metadata and daemonState
815
- */
816
- getMachine(machineId: string): Promise<Machine | null>;
817
750
  /**
818
751
  * Register or update machine with the server
819
752
  * Returns the current machine state from the server with decrypted metadata and daemonState
@@ -864,6 +797,7 @@ declare let logger: Logger;
864
797
  */
865
798
  declare class Configuration {
866
799
  readonly serverUrl: string;
800
+ readonly webappUrl: string;
867
801
  readonly isDaemonProcess: boolean;
868
802
  readonly happyHomeDir: string;
869
803
  readonly logsDir: string;
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-xds_c-JJ.mjs';
1
+ export { A as ApiClient, a as ApiSessionClient, R as RawJSONLinesSchema, c as configuration, l as logger } from './types-8Ad05p3x.mjs';
2
2
  import 'axios';
3
3
  import 'chalk';
4
4
  import 'fs';
@@ -1,12 +1,12 @@
1
1
  import { useStdout, useInput, Box, Text, render } from 'ink';
2
2
  import React, { useState, useRef, useEffect, useCallback } from 'react';
3
- import { l as logger, A as ApiClient, r as readSettings, p as projectPath, c as configuration, b as packageJson } from './types-xds_c-JJ.mjs';
3
+ import { l as logger, A as ApiClient, r as readSettings, p as projectPath, c as configuration, b as packageJson } from './types-8Ad05p3x.mjs';
4
4
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
5
5
  import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
6
6
  import { z } from 'zod';
7
7
  import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js';
8
8
  import { randomUUID } from 'node:crypto';
9
- import { i as initialMachineMetadata, M as MessageQueue2, h as hashObject, a as MessageBuffer, s as startHappyServer, t as trimIdent } from './index-DPVbp4Yx.mjs';
9
+ import { i as initialMachineMetadata, n as notifyDaemonSessionStarted, M as MessageQueue2, h as hashObject, r as registerKillSessionHandler, a as MessageBuffer, s as startHappyServer, t as trimIdent, b as stopCaffeinate } from './index-BI37NnoW.mjs';
10
10
  import os from 'node:os';
11
11
  import { resolve, join } from 'node:path';
12
12
  import fs from 'node:fs';
@@ -30,6 +30,7 @@ import 'node:readline';
30
30
  import 'node:url';
31
31
  import 'ps-list';
32
32
  import 'cross-spawn';
33
+ import 'tmp';
33
34
  import 'qrcode-terminal';
34
35
  import 'open';
35
36
  import 'fastify';
@@ -678,7 +679,8 @@ const CodexDisplay = ({ messageBuffer, logPath, onExit }) => {
678
679
 
679
680
  async function runCodex(opts) {
680
681
  const sessionTag = randomUUID();
681
- const api = new ApiClient(opts.token, opts.secret);
682
+ const api = await ApiClient.create(opts.credentials);
683
+ logger.debug(`[codex] Starting with options: startedBy=${opts.startedBy || "terminal"}`);
682
684
  const settings = await readSettings();
683
685
  let machineId = settings?.machineId;
684
686
  if (!machineId) {
@@ -703,9 +705,9 @@ async function runCodex(opts) {
703
705
  happyHomeDir: configuration.happyHomeDir,
704
706
  happyLibDir: projectPath(),
705
707
  happyToolsDir: resolve(projectPath(), "tools", "unpacked"),
706
- startedFromDaemon: false,
708
+ startedFromDaemon: opts.startedBy === "daemon",
707
709
  hostPid: process.pid,
708
- startedBy: "terminal",
710
+ startedBy: opts.startedBy || "terminal",
709
711
  // Initialize lifecycle state
710
712
  lifecycleState: "running",
711
713
  lifecycleStateSince: Date.now(),
@@ -713,6 +715,17 @@ async function runCodex(opts) {
713
715
  };
714
716
  const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
715
717
  const session = api.sessionSyncClient(response);
718
+ try {
719
+ logger.debug(`[START] Reporting session ${response.id} to daemon`);
720
+ const result = await notifyDaemonSessionStarted(response.id, metadata);
721
+ if (result.error) {
722
+ logger.debug(`[START] Failed to report to daemon (may not be running):`, result.error);
723
+ } else {
724
+ logger.debug(`[START] Reported session ${response.id} to daemon`);
725
+ }
726
+ } catch (error) {
727
+ logger.debug("[START] Failed to report to daemon (may not be running):", error);
728
+ }
716
729
  const messageQueue = new MessageQueue2((mode) => hashObject({
717
730
  permissionMode: mode.permissionMode,
718
731
  model: mode.model
@@ -781,7 +794,34 @@ async function runCodex(opts) {
781
794
  abortController = new AbortController();
782
795
  }
783
796
  }
797
+ const cleanup = async () => {
798
+ logger.debug("[Codex] Cleanup start");
799
+ await handleAbort();
800
+ logger.debug("[Codex] Cleanup completed");
801
+ try {
802
+ if (session) {
803
+ session.updateMetadata((currentMetadata) => ({
804
+ ...currentMetadata,
805
+ lifecycleState: "archived",
806
+ lifecycleStateSince: Date.now(),
807
+ archivedBy: "cli",
808
+ archiveReason: "User terminated"
809
+ }));
810
+ session.sendSessionDeath();
811
+ await session.flush();
812
+ await session.close();
813
+ }
814
+ stopCaffeinate();
815
+ happyServer.stop();
816
+ logger.debug("[Codex] Cleanup complete, exiting");
817
+ process.exit(0);
818
+ } catch (error) {
819
+ logger.debug("[Codex] Error during cleanup:", error);
820
+ process.exit(1);
821
+ }
822
+ };
784
823
  session.rpcHandlerManager.registerHandler("abort", handleAbort);
824
+ registerKillSessionHandler(session.rpcHandlerManager, cleanup);
785
825
  const messageBuffer = new MessageBuffer();
786
826
  const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
787
827
  let inkInstance = null;
@@ -829,7 +869,8 @@ async function runCodex(opts) {
829
869
  return acc;
830
870
  };
831
871
  var collectFilesRecursive = collectFilesRecursive2;
832
- const rootDir = join(os.homedir(), ".codex", "sessions");
872
+ const codexHomeDir = process.env.CODEX_HOME || join(os.homedir(), ".codex");
873
+ const rootDir = join(codexHomeDir, "sessions");
833
874
  const candidates = collectFilesRecursive2(rootDir).filter((full) => full.endsWith(`-${sessionId}.jsonl`)).filter((full) => {
834
875
  try {
835
876
  return fs.statSync(full).isFile();
@@ -2,13 +2,13 @@
2
2
 
3
3
  var ink = require('ink');
4
4
  var React = require('react');
5
- var types = require('./types-CsJGQvQ3.cjs');
5
+ var types = require('./types-CQOz_mPp.cjs');
6
6
  var index_js = require('@modelcontextprotocol/sdk/client/index.js');
7
7
  var stdio_js = require('@modelcontextprotocol/sdk/client/stdio.js');
8
8
  var z = require('zod');
9
9
  var types_js = require('@modelcontextprotocol/sdk/types.js');
10
10
  var node_crypto = require('node:crypto');
11
- var index = require('./index-tqOLc1Il.cjs');
11
+ var index = require('./index-ettJex_e.cjs');
12
12
  var os = require('node:os');
13
13
  var node_path = require('node:path');
14
14
  var fs = require('node:fs');
@@ -32,6 +32,7 @@ require('node:readline');
32
32
  require('node:url');
33
33
  require('ps-list');
34
34
  require('cross-spawn');
35
+ require('tmp');
35
36
  require('qrcode-terminal');
36
37
  require('open');
37
38
  require('fastify');
@@ -680,7 +681,8 @@ const CodexDisplay = ({ messageBuffer, logPath, onExit }) => {
680
681
 
681
682
  async function runCodex(opts) {
682
683
  const sessionTag = node_crypto.randomUUID();
683
- const api = new types.ApiClient(opts.token, opts.secret);
684
+ const api = await types.ApiClient.create(opts.credentials);
685
+ types.logger.debug(`[codex] Starting with options: startedBy=${opts.startedBy || "terminal"}`);
684
686
  const settings = await types.readSettings();
685
687
  let machineId = settings?.machineId;
686
688
  if (!machineId) {
@@ -705,9 +707,9 @@ async function runCodex(opts) {
705
707
  happyHomeDir: types.configuration.happyHomeDir,
706
708
  happyLibDir: types.projectPath(),
707
709
  happyToolsDir: node_path.resolve(types.projectPath(), "tools", "unpacked"),
708
- startedFromDaemon: false,
710
+ startedFromDaemon: opts.startedBy === "daemon",
709
711
  hostPid: process.pid,
710
- startedBy: "terminal",
712
+ startedBy: opts.startedBy || "terminal",
711
713
  // Initialize lifecycle state
712
714
  lifecycleState: "running",
713
715
  lifecycleStateSince: Date.now(),
@@ -715,6 +717,17 @@ async function runCodex(opts) {
715
717
  };
716
718
  const response = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
717
719
  const session = api.sessionSyncClient(response);
720
+ try {
721
+ types.logger.debug(`[START] Reporting session ${response.id} to daemon`);
722
+ const result = await index.notifyDaemonSessionStarted(response.id, metadata);
723
+ if (result.error) {
724
+ types.logger.debug(`[START] Failed to report to daemon (may not be running):`, result.error);
725
+ } else {
726
+ types.logger.debug(`[START] Reported session ${response.id} to daemon`);
727
+ }
728
+ } catch (error) {
729
+ types.logger.debug("[START] Failed to report to daemon (may not be running):", error);
730
+ }
718
731
  const messageQueue = new index.MessageQueue2((mode) => index.hashObject({
719
732
  permissionMode: mode.permissionMode,
720
733
  model: mode.model
@@ -783,7 +796,34 @@ async function runCodex(opts) {
783
796
  abortController = new AbortController();
784
797
  }
785
798
  }
799
+ const cleanup = async () => {
800
+ types.logger.debug("[Codex] Cleanup start");
801
+ await handleAbort();
802
+ types.logger.debug("[Codex] Cleanup completed");
803
+ try {
804
+ if (session) {
805
+ session.updateMetadata((currentMetadata) => ({
806
+ ...currentMetadata,
807
+ lifecycleState: "archived",
808
+ lifecycleStateSince: Date.now(),
809
+ archivedBy: "cli",
810
+ archiveReason: "User terminated"
811
+ }));
812
+ session.sendSessionDeath();
813
+ await session.flush();
814
+ await session.close();
815
+ }
816
+ index.stopCaffeinate();
817
+ happyServer.stop();
818
+ types.logger.debug("[Codex] Cleanup complete, exiting");
819
+ process.exit(0);
820
+ } catch (error) {
821
+ types.logger.debug("[Codex] Error during cleanup:", error);
822
+ process.exit(1);
823
+ }
824
+ };
786
825
  session.rpcHandlerManager.registerHandler("abort", handleAbort);
826
+ index.registerKillSessionHandler(session.rpcHandlerManager, cleanup);
787
827
  const messageBuffer = new index.MessageBuffer();
788
828
  const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
789
829
  let inkInstance = null;
@@ -831,7 +871,8 @@ async function runCodex(opts) {
831
871
  return acc;
832
872
  };
833
873
  var collectFilesRecursive = collectFilesRecursive2;
834
- const rootDir = node_path.join(os.homedir(), ".codex", "sessions");
874
+ const codexHomeDir = process.env.CODEX_HOME || node_path.join(os.homedir(), ".codex");
875
+ const rootDir = node_path.join(codexHomeDir, "sessions");
835
876
  const candidates = collectFilesRecursive2(rootDir).filter((full) => full.endsWith(`-${sessionId}.jsonl`)).filter((full) => {
836
877
  try {
837
878
  return fs.statSync(full).isFile();