happy-coder 0.1.9 → 0.1.10

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.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-fXgEaaqP.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-DnQGY77F.mjs';
2
2
  import 'axios';
3
3
  import 'chalk';
4
4
  import 'fs';
@@ -193,11 +193,11 @@ const UpdateSessionBodySchema = z.z.object({
193
193
  sid: z.z.string(),
194
194
  metadata: z.z.object({
195
195
  version: z.z.number(),
196
- metadata: z.z.string()
196
+ value: z.z.string()
197
197
  }).nullish(),
198
198
  agentState: z.z.object({
199
199
  version: z.z.number(),
200
- agentState: z.z.string()
200
+ value: z.z.string()
201
201
  }).nullish()
202
202
  });
203
203
  z.z.object({
@@ -236,7 +236,7 @@ z.z.object({
236
236
  agentStateVersion: z.z.number()
237
237
  })
238
238
  });
239
- const UserMessageSchema$1 = z.z.object({
239
+ const UserMessageSchema = z.z.object({
240
240
  role: z.z.literal("user"),
241
241
  content: z.z.object({
242
242
  type: z.z.literal("text"),
@@ -254,7 +254,7 @@ const AgentMessageSchema = z.z.object({
254
254
  data: z.z.any()
255
255
  })
256
256
  });
257
- z.z.union([UserMessageSchema$1, AgentMessageSchema]);
257
+ z.z.union([UserMessageSchema, AgentMessageSchema]);
258
258
 
259
259
  function encodeBase64(buffer, variant = "base64") {
260
260
  if (variant === "base64url") {
@@ -393,7 +393,7 @@ class ApiSessionClient extends node_events.EventEmitter {
393
393
  if (data.body.t === "new-message" && data.body.message.content.t === "encrypted") {
394
394
  const body = decrypt(decodeBase64(data.body.message.content.c), this.secret);
395
395
  exports.logger.debugLargeJson("[SOCKET] [UPDATE] Received update:", body);
396
- const userResult = UserMessageSchema$1.safeParse(body);
396
+ const userResult = UserMessageSchema.safeParse(body);
397
397
  if (userResult.success) {
398
398
  if (this.pendingMessageCallback) {
399
399
  this.pendingMessageCallback(userResult.data);
@@ -405,11 +405,11 @@ class ApiSessionClient extends node_events.EventEmitter {
405
405
  }
406
406
  } else if (data.body.t === "update-session") {
407
407
  if (data.body.metadata && data.body.metadata.version > this.metadataVersion) {
408
- this.metadata = decrypt(decodeBase64(data.body.metadata.metadata), this.secret);
408
+ this.metadata = decrypt(decodeBase64(data.body.metadata.value), this.secret);
409
409
  this.metadataVersion = data.body.metadata.version;
410
410
  }
411
411
  if (data.body.agentState && data.body.agentState.version > this.agentStateVersion) {
412
- this.agentState = data.body.agentState.agentState ? decrypt(decodeBase64(data.body.agentState.agentState), this.secret) : null;
412
+ this.agentState = data.body.agentState.value ? decrypt(decodeBase64(data.body.agentState.value), this.secret) : null;
413
413
  this.agentStateVersion = data.body.agentState.version;
414
414
  }
415
415
  }
@@ -431,7 +431,7 @@ class ApiSessionClient extends node_events.EventEmitter {
431
431
  */
432
432
  sendClaudeSessionMessage(body) {
433
433
  let content;
434
- if (body.type === "user" && typeof body.message.content === "string") {
434
+ if (body.type === "user" && typeof body.message.content === "string" && body.isSidechain !== true && body.isMeta !== true) {
435
435
  content = {
436
436
  role: "user",
437
437
  content: {
@@ -462,6 +462,15 @@ class ApiSessionClient extends node_events.EventEmitter {
462
462
  exports.logger.debug("[SOCKET] Failed to send usage data:", error);
463
463
  }
464
464
  }
465
+ if (body.type === "summary" && "summary" in body && "leafUuid" in body) {
466
+ this.updateMetadata((metadata) => ({
467
+ ...metadata,
468
+ summary: {
469
+ text: body.summary,
470
+ updatedAt: Date.now()
471
+ }
472
+ }));
473
+ }
465
474
  }
466
475
  /**
467
476
  * Send a ping message to keep the connection alive
@@ -767,97 +776,45 @@ const UsageSchema = z.z.object({
767
776
  cache_read_input_tokens: z.z.number().int().nonnegative().optional(),
768
777
  output_tokens: z.z.number().int().nonnegative(),
769
778
  service_tier: z.z.string().optional()
770
- });
771
- const TextContentSchema = z.z.object({
772
- type: z.z.literal("text"),
773
- text: z.z.string()
774
- });
775
- const ThinkingContentSchema = z.z.object({
776
- type: z.z.literal("thinking"),
777
- thinking: z.z.string(),
778
- signature: z.z.string()
779
- });
780
- const ToolUseContentSchema = z.z.object({
781
- type: z.z.literal("tool_use"),
782
- id: z.z.string(),
783
- name: z.z.string(),
784
- input: z.z.unknown()
785
- // Tool-specific input parameters
786
- });
787
- const ToolResultContentSchema = z.z.object({
788
- tool_use_id: z.z.string(),
789
- type: z.z.literal("tool_result"),
790
- content: z.z.union([
791
- z.z.string(),
792
- // For simple string responses
793
- z.z.array(TextContentSchema)
794
- // For structured content blocks (typically text)
795
- ]),
796
- is_error: z.z.boolean().optional()
797
- });
798
- const ContentSchema = z.z.union([
799
- TextContentSchema,
800
- ThinkingContentSchema,
801
- ToolUseContentSchema,
802
- ToolResultContentSchema
803
- ]);
804
- const UserMessageSchema = z.z.object({
805
- role: z.z.literal("user"),
806
- content: z.z.union([
807
- z.z.string(),
808
- // Simple string content
809
- z.z.array(z.z.union([ToolResultContentSchema, TextContentSchema]))
810
- ])
811
- });
812
- const AssistantMessageSchema = z.z.object({
813
- id: z.z.string(),
814
- type: z.z.literal("message"),
815
- role: z.z.literal("assistant"),
816
- model: z.z.string(),
817
- content: z.z.array(ContentSchema),
818
- stop_reason: z.z.string().nullable(),
819
- stop_sequence: z.z.string().nullable(),
820
- usage: UsageSchema
821
- });
822
- const BaseEntrySchema = z.z.object({
823
- cwd: z.z.string(),
824
- sessionId: z.z.string(),
825
- version: z.z.string(),
826
- uuid: z.z.string(),
827
- timestamp: z.z.string().datetime(),
828
- parent_tool_use_id: z.z.string().nullable().optional()
829
- });
830
- const SummaryEntrySchema = z.z.object({
831
- type: z.z.literal("summary"),
832
- summary: z.z.string(),
833
- leafUuid: z.z.string()
834
- });
835
- const UserEntrySchema = BaseEntrySchema.extend({
836
- type: z.z.literal("user"),
837
- message: UserMessageSchema,
838
- isMeta: z.z.boolean().optional(),
839
- toolUseResult: z.z.unknown().optional()
840
- // Present when user responds to tool use
841
- });
842
- const AssistantEntrySchema = BaseEntrySchema.extend({
843
- type: z.z.literal("assistant"),
844
- message: AssistantMessageSchema,
845
- requestId: z.z.string().optional()
846
- });
847
- const SystemEntrySchema = BaseEntrySchema.extend({
848
- type: z.z.literal("system"),
849
- content: z.z.string(),
850
- isMeta: z.z.boolean().optional(),
851
- level: z.z.string().optional(),
852
- parentUuid: z.z.string().optional(),
853
- isSidechain: z.z.boolean().optional(),
854
- userType: z.z.string().optional()
855
- });
779
+ }).passthrough();
856
780
  const RawJSONLinesSchema = z.z.discriminatedUnion("type", [
857
- UserEntrySchema,
858
- AssistantEntrySchema,
859
- SummaryEntrySchema,
860
- SystemEntrySchema
781
+ // User message - validates uuid and message.content
782
+ z.z.object({
783
+ type: z.z.literal("user"),
784
+ isSidechain: z.z.boolean().optional(),
785
+ isMeta: z.z.boolean().optional(),
786
+ uuid: z.z.string(),
787
+ // Used in getMessageKey()
788
+ message: z.z.object({
789
+ content: z.z.union([z.z.string(), z.z.any()])
790
+ // Used in sessionScanner.ts
791
+ }).passthrough()
792
+ }).passthrough(),
793
+ // Assistant message - validates message object with usage and content
794
+ z.z.object({
795
+ type: z.z.literal("assistant"),
796
+ message: z.z.object({
797
+ // Entire message used in getMessageKey()
798
+ usage: UsageSchema.optional(),
799
+ // Used in apiSession.ts
800
+ content: z.z.any()
801
+ // Used in tests
802
+ }).passthrough()
803
+ }).passthrough(),
804
+ // Summary message - validates summary and leafUuid
805
+ z.z.object({
806
+ type: z.z.literal("summary"),
807
+ summary: z.z.string(),
808
+ // Used in apiSession.ts
809
+ leafUuid: z.z.string()
810
+ // Used in getMessageKey()
811
+ }).passthrough(),
812
+ // System message - validates uuid
813
+ z.z.object({
814
+ type: z.z.literal("system"),
815
+ uuid: z.z.string()
816
+ // Used in getMessageKey()
817
+ }).passthrough()
861
818
  ]);
862
819
 
863
820
  exports.ApiClient = ApiClient;
@@ -865,8 +822,10 @@ exports.ApiSessionClient = ApiSessionClient;
865
822
  exports.RawJSONLinesSchema = RawJSONLinesSchema;
866
823
  exports.backoff = backoff;
867
824
  exports.decodeBase64 = decodeBase64;
825
+ exports.decrypt = decrypt;
868
826
  exports.delay = delay;
869
827
  exports.encodeBase64 = encodeBase64;
870
828
  exports.encodeBase64Url = encodeBase64Url;
829
+ exports.encrypt = encrypt;
871
830
  exports.initLoggerWithGlobalConfiguration = initLoggerWithGlobalConfiguration;
872
831
  exports.initializeConfiguration = initializeConfiguration;
@@ -191,11 +191,11 @@ const UpdateSessionBodySchema = z.object({
191
191
  sid: z.string(),
192
192
  metadata: z.object({
193
193
  version: z.number(),
194
- metadata: z.string()
194
+ value: z.string()
195
195
  }).nullish(),
196
196
  agentState: z.object({
197
197
  version: z.number(),
198
- agentState: z.string()
198
+ value: z.string()
199
199
  }).nullish()
200
200
  });
201
201
  z.object({
@@ -234,7 +234,7 @@ z.object({
234
234
  agentStateVersion: z.number()
235
235
  })
236
236
  });
237
- const UserMessageSchema$1 = z.object({
237
+ const UserMessageSchema = z.object({
238
238
  role: z.literal("user"),
239
239
  content: z.object({
240
240
  type: z.literal("text"),
@@ -252,7 +252,7 @@ const AgentMessageSchema = z.object({
252
252
  data: z.any()
253
253
  })
254
254
  });
255
- z.union([UserMessageSchema$1, AgentMessageSchema]);
255
+ z.union([UserMessageSchema, AgentMessageSchema]);
256
256
 
257
257
  function encodeBase64(buffer, variant = "base64") {
258
258
  if (variant === "base64url") {
@@ -391,7 +391,7 @@ class ApiSessionClient extends EventEmitter {
391
391
  if (data.body.t === "new-message" && data.body.message.content.t === "encrypted") {
392
392
  const body = decrypt(decodeBase64(data.body.message.content.c), this.secret);
393
393
  logger.debugLargeJson("[SOCKET] [UPDATE] Received update:", body);
394
- const userResult = UserMessageSchema$1.safeParse(body);
394
+ const userResult = UserMessageSchema.safeParse(body);
395
395
  if (userResult.success) {
396
396
  if (this.pendingMessageCallback) {
397
397
  this.pendingMessageCallback(userResult.data);
@@ -403,11 +403,11 @@ class ApiSessionClient extends EventEmitter {
403
403
  }
404
404
  } else if (data.body.t === "update-session") {
405
405
  if (data.body.metadata && data.body.metadata.version > this.metadataVersion) {
406
- this.metadata = decrypt(decodeBase64(data.body.metadata.metadata), this.secret);
406
+ this.metadata = decrypt(decodeBase64(data.body.metadata.value), this.secret);
407
407
  this.metadataVersion = data.body.metadata.version;
408
408
  }
409
409
  if (data.body.agentState && data.body.agentState.version > this.agentStateVersion) {
410
- this.agentState = data.body.agentState.agentState ? decrypt(decodeBase64(data.body.agentState.agentState), this.secret) : null;
410
+ this.agentState = data.body.agentState.value ? decrypt(decodeBase64(data.body.agentState.value), this.secret) : null;
411
411
  this.agentStateVersion = data.body.agentState.version;
412
412
  }
413
413
  }
@@ -429,7 +429,7 @@ class ApiSessionClient extends EventEmitter {
429
429
  */
430
430
  sendClaudeSessionMessage(body) {
431
431
  let content;
432
- if (body.type === "user" && typeof body.message.content === "string") {
432
+ if (body.type === "user" && typeof body.message.content === "string" && body.isSidechain !== true && body.isMeta !== true) {
433
433
  content = {
434
434
  role: "user",
435
435
  content: {
@@ -460,6 +460,15 @@ class ApiSessionClient extends EventEmitter {
460
460
  logger.debug("[SOCKET] Failed to send usage data:", error);
461
461
  }
462
462
  }
463
+ if (body.type === "summary" && "summary" in body && "leafUuid" in body) {
464
+ this.updateMetadata((metadata) => ({
465
+ ...metadata,
466
+ summary: {
467
+ text: body.summary,
468
+ updatedAt: Date.now()
469
+ }
470
+ }));
471
+ }
463
472
  }
464
473
  /**
465
474
  * Send a ping message to keep the connection alive
@@ -765,97 +774,45 @@ const UsageSchema = z.object({
765
774
  cache_read_input_tokens: z.number().int().nonnegative().optional(),
766
775
  output_tokens: z.number().int().nonnegative(),
767
776
  service_tier: z.string().optional()
768
- });
769
- const TextContentSchema = z.object({
770
- type: z.literal("text"),
771
- text: z.string()
772
- });
773
- const ThinkingContentSchema = z.object({
774
- type: z.literal("thinking"),
775
- thinking: z.string(),
776
- signature: z.string()
777
- });
778
- const ToolUseContentSchema = z.object({
779
- type: z.literal("tool_use"),
780
- id: z.string(),
781
- name: z.string(),
782
- input: z.unknown()
783
- // Tool-specific input parameters
784
- });
785
- const ToolResultContentSchema = z.object({
786
- tool_use_id: z.string(),
787
- type: z.literal("tool_result"),
788
- content: z.union([
789
- z.string(),
790
- // For simple string responses
791
- z.array(TextContentSchema)
792
- // For structured content blocks (typically text)
793
- ]),
794
- is_error: z.boolean().optional()
795
- });
796
- const ContentSchema = z.union([
797
- TextContentSchema,
798
- ThinkingContentSchema,
799
- ToolUseContentSchema,
800
- ToolResultContentSchema
801
- ]);
802
- const UserMessageSchema = z.object({
803
- role: z.literal("user"),
804
- content: z.union([
805
- z.string(),
806
- // Simple string content
807
- z.array(z.union([ToolResultContentSchema, TextContentSchema]))
808
- ])
809
- });
810
- const AssistantMessageSchema = z.object({
811
- id: z.string(),
812
- type: z.literal("message"),
813
- role: z.literal("assistant"),
814
- model: z.string(),
815
- content: z.array(ContentSchema),
816
- stop_reason: z.string().nullable(),
817
- stop_sequence: z.string().nullable(),
818
- usage: UsageSchema
819
- });
820
- const BaseEntrySchema = z.object({
821
- cwd: z.string(),
822
- sessionId: z.string(),
823
- version: z.string(),
824
- uuid: z.string(),
825
- timestamp: z.string().datetime(),
826
- parent_tool_use_id: z.string().nullable().optional()
827
- });
828
- const SummaryEntrySchema = z.object({
829
- type: z.literal("summary"),
830
- summary: z.string(),
831
- leafUuid: z.string()
832
- });
833
- const UserEntrySchema = BaseEntrySchema.extend({
834
- type: z.literal("user"),
835
- message: UserMessageSchema,
836
- isMeta: z.boolean().optional(),
837
- toolUseResult: z.unknown().optional()
838
- // Present when user responds to tool use
839
- });
840
- const AssistantEntrySchema = BaseEntrySchema.extend({
841
- type: z.literal("assistant"),
842
- message: AssistantMessageSchema,
843
- requestId: z.string().optional()
844
- });
845
- const SystemEntrySchema = BaseEntrySchema.extend({
846
- type: z.literal("system"),
847
- content: z.string(),
848
- isMeta: z.boolean().optional(),
849
- level: z.string().optional(),
850
- parentUuid: z.string().optional(),
851
- isSidechain: z.boolean().optional(),
852
- userType: z.string().optional()
853
- });
777
+ }).passthrough();
854
778
  const RawJSONLinesSchema = z.discriminatedUnion("type", [
855
- UserEntrySchema,
856
- AssistantEntrySchema,
857
- SummaryEntrySchema,
858
- SystemEntrySchema
779
+ // User message - validates uuid and message.content
780
+ z.object({
781
+ type: z.literal("user"),
782
+ isSidechain: z.boolean().optional(),
783
+ isMeta: z.boolean().optional(),
784
+ uuid: z.string(),
785
+ // Used in getMessageKey()
786
+ message: z.object({
787
+ content: z.union([z.string(), z.any()])
788
+ // Used in sessionScanner.ts
789
+ }).passthrough()
790
+ }).passthrough(),
791
+ // Assistant message - validates message object with usage and content
792
+ z.object({
793
+ type: z.literal("assistant"),
794
+ message: z.object({
795
+ // Entire message used in getMessageKey()
796
+ usage: UsageSchema.optional(),
797
+ // Used in apiSession.ts
798
+ content: z.any()
799
+ // Used in tests
800
+ }).passthrough()
801
+ }).passthrough(),
802
+ // Summary message - validates summary and leafUuid
803
+ z.object({
804
+ type: z.literal("summary"),
805
+ summary: z.string(),
806
+ // Used in apiSession.ts
807
+ leafUuid: z.string()
808
+ // Used in getMessageKey()
809
+ }).passthrough(),
810
+ // System message - validates uuid
811
+ z.object({
812
+ type: z.literal("system"),
813
+ uuid: z.string()
814
+ // Used in getMessageKey()
815
+ }).passthrough()
859
816
  ]);
860
817
 
861
- export { ApiClient as A, RawJSONLinesSchema as R, ApiSessionClient as a, initializeConfiguration as b, configuration as c, backoff as d, encodeBase64 as e, encodeBase64Url as f, decodeBase64 as g, delay as h, initLoggerWithGlobalConfiguration as i, logger as l };
818
+ export { ApiClient as A, RawJSONLinesSchema as R, ApiSessionClient as a, initializeConfiguration as b, configuration as c, backoff as d, encodeBase64 as e, encodeBase64Url as f, decodeBase64 as g, delay as h, initLoggerWithGlobalConfiguration as i, encrypt as j, decrypt as k, logger as l };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "happy-coder",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Claude Code session sharing CLI",
5
5
  "author": "Kirill Dubovitskiy",
6
6
  "license": "MIT",
@@ -1,109 +0,0 @@
1
- 'use strict';
2
-
3
- var types = require('./types-mykDX2xe.cjs');
4
- var fs = require('fs');
5
- var child_process = require('child_process');
6
- require('axios');
7
- require('chalk');
8
- require('node:os');
9
- require('node:path');
10
- require('node:fs/promises');
11
- require('node:fs');
12
- require('node:events');
13
- require('socket.io-client');
14
- require('zod');
15
- require('node:crypto');
16
- require('tweetnacl');
17
- require('expo-server-sdk');
18
-
19
- function trimIdent(text) {
20
- const lines = text.split("\n");
21
- while (lines.length > 0 && lines[0].trim() === "") {
22
- lines.shift();
23
- }
24
- while (lines.length > 0 && lines[lines.length - 1].trim() === "") {
25
- lines.pop();
26
- }
27
- const minSpaces = lines.reduce((min, line) => {
28
- if (line.trim() === "") {
29
- return min;
30
- }
31
- const leadingSpaces = line.match(/^\s*/)[0].length;
32
- return Math.min(min, leadingSpaces);
33
- }, Infinity);
34
- const trimmedLines = lines.map((line) => line.slice(minSpaces));
35
- return trimmedLines.join("\n");
36
- }
37
-
38
- const PLIST_LABEL = "com.happy-cli.daemon";
39
- const PLIST_FILE = `/Library/LaunchDaemons/${PLIST_LABEL}.plist`;
40
- async function install$1() {
41
- try {
42
- if (fs.existsSync(PLIST_FILE)) {
43
- types.logger.info("Daemon plist already exists. Uninstalling first...");
44
- child_process.execSync(`launchctl unload ${PLIST_FILE}`, { stdio: "inherit" });
45
- }
46
- const happyPath = process.argv[0];
47
- const scriptPath = process.argv[1];
48
- const plistContent = trimIdent(`
49
- <?xml version="1.0" encoding="UTF-8"?>
50
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
51
- <plist version="1.0">
52
- <dict>
53
- <key>Label</key>
54
- <string>${PLIST_LABEL}</string>
55
-
56
- <key>ProgramArguments</key>
57
- <array>
58
- <string>${happyPath}</string>
59
- <string>${scriptPath}</string>
60
- <string>daemon</string>
61
- </array>
62
-
63
- <key>EnvironmentVariables</key>
64
- <dict>
65
- <key>HAPPY_DAEMON_MODE</key>
66
- <string>true</string>
67
- </dict>
68
-
69
- <key>RunAtLoad</key>
70
- <true/>
71
-
72
- <key>KeepAlive</key>
73
- <true/>
74
-
75
- <key>StandardErrorPath</key>
76
- <string>/var/log/happy-cli-daemon.err</string>
77
-
78
- <key>StandardOutPath</key>
79
- <string>/var/log/happy-cli-daemon.log</string>
80
-
81
- <key>WorkingDirectory</key>
82
- <string>/tmp</string>
83
- </dict>
84
- </plist>
85
- `);
86
- fs.writeFileSync(PLIST_FILE, plistContent);
87
- fs.chmodSync(PLIST_FILE, 420);
88
- types.logger.info(`Created daemon plist at ${PLIST_FILE}`);
89
- child_process.execSync(`launchctl load ${PLIST_FILE}`, { stdio: "inherit" });
90
- types.logger.info("Daemon installed and started successfully");
91
- types.logger.info("Check logs at /var/log/happy-cli-daemon.log");
92
- } catch (error) {
93
- types.logger.debug("Failed to install daemon:", error);
94
- throw error;
95
- }
96
- }
97
-
98
- async function install() {
99
- if (process.platform !== "darwin") {
100
- throw new Error("Daemon installation is currently only supported on macOS");
101
- }
102
- if (process.getuid && process.getuid() !== 0) {
103
- throw new Error("Daemon installation requires sudo privileges. Please run with sudo.");
104
- }
105
- types.logger.info("Installing Happy CLI daemon for macOS...");
106
- await install$1();
107
- }
108
-
109
- exports.install = install;
@@ -1,107 +0,0 @@
1
- import { l as logger } from './types-fXgEaaqP.mjs';
2
- import { existsSync, writeFileSync, chmodSync } from 'fs';
3
- import { execSync } from 'child_process';
4
- import 'axios';
5
- import 'chalk';
6
- import 'node:os';
7
- import 'node:path';
8
- import 'node:fs/promises';
9
- import 'node:fs';
10
- import 'node:events';
11
- import 'socket.io-client';
12
- import 'zod';
13
- import 'node:crypto';
14
- import 'tweetnacl';
15
- import 'expo-server-sdk';
16
-
17
- function trimIdent(text) {
18
- const lines = text.split("\n");
19
- while (lines.length > 0 && lines[0].trim() === "") {
20
- lines.shift();
21
- }
22
- while (lines.length > 0 && lines[lines.length - 1].trim() === "") {
23
- lines.pop();
24
- }
25
- const minSpaces = lines.reduce((min, line) => {
26
- if (line.trim() === "") {
27
- return min;
28
- }
29
- const leadingSpaces = line.match(/^\s*/)[0].length;
30
- return Math.min(min, leadingSpaces);
31
- }, Infinity);
32
- const trimmedLines = lines.map((line) => line.slice(minSpaces));
33
- return trimmedLines.join("\n");
34
- }
35
-
36
- const PLIST_LABEL = "com.happy-cli.daemon";
37
- const PLIST_FILE = `/Library/LaunchDaemons/${PLIST_LABEL}.plist`;
38
- async function install$1() {
39
- try {
40
- if (existsSync(PLIST_FILE)) {
41
- logger.info("Daemon plist already exists. Uninstalling first...");
42
- execSync(`launchctl unload ${PLIST_FILE}`, { stdio: "inherit" });
43
- }
44
- const happyPath = process.argv[0];
45
- const scriptPath = process.argv[1];
46
- const plistContent = trimIdent(`
47
- <?xml version="1.0" encoding="UTF-8"?>
48
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
49
- <plist version="1.0">
50
- <dict>
51
- <key>Label</key>
52
- <string>${PLIST_LABEL}</string>
53
-
54
- <key>ProgramArguments</key>
55
- <array>
56
- <string>${happyPath}</string>
57
- <string>${scriptPath}</string>
58
- <string>daemon</string>
59
- </array>
60
-
61
- <key>EnvironmentVariables</key>
62
- <dict>
63
- <key>HAPPY_DAEMON_MODE</key>
64
- <string>true</string>
65
- </dict>
66
-
67
- <key>RunAtLoad</key>
68
- <true/>
69
-
70
- <key>KeepAlive</key>
71
- <true/>
72
-
73
- <key>StandardErrorPath</key>
74
- <string>/var/log/happy-cli-daemon.err</string>
75
-
76
- <key>StandardOutPath</key>
77
- <string>/var/log/happy-cli-daemon.log</string>
78
-
79
- <key>WorkingDirectory</key>
80
- <string>/tmp</string>
81
- </dict>
82
- </plist>
83
- `);
84
- writeFileSync(PLIST_FILE, plistContent);
85
- chmodSync(PLIST_FILE, 420);
86
- logger.info(`Created daemon plist at ${PLIST_FILE}`);
87
- execSync(`launchctl load ${PLIST_FILE}`, { stdio: "inherit" });
88
- logger.info("Daemon installed and started successfully");
89
- logger.info("Check logs at /var/log/happy-cli-daemon.log");
90
- } catch (error) {
91
- logger.debug("Failed to install daemon:", error);
92
- throw error;
93
- }
94
- }
95
-
96
- async function install() {
97
- if (process.platform !== "darwin") {
98
- throw new Error("Daemon installation is currently only supported on macOS");
99
- }
100
- if (process.getuid && process.getuid() !== 0) {
101
- throw new Error("Daemon installation requires sudo privileges. Please run with sudo.");
102
- }
103
- logger.info("Installing Happy CLI daemon for macOS...");
104
- await install$1();
105
- }
106
-
107
- export { install };