crewx 0.8.1 → 0.8.2-rc.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.
Files changed (125) hide show
  1. package/README.md +268 -268
  2. package/bin/cli-commands.js +34 -0
  3. package/bin/crewx-lib.js +213 -108
  4. package/bin/crewx-ui.js +83 -83
  5. package/bin/crewx.js +219 -147
  6. package/bin/launcher-flags.js +29 -0
  7. package/bin/package.json +1 -1
  8. package/dist/assets/MarketPage-DptjaFpT.js +36 -0
  9. package/dist/assets/{PromptTab-DVKc7hJY.js → PromptTab-DZha2_v1.js} +1 -1
  10. package/dist/assets/{_baseUniq-wjlVo2E6.js → _baseUniq-jd6NubI3.js} +1 -1
  11. package/dist/assets/{arc-BfPgRtzW.js → arc-C2te3-8P.js} +1 -1
  12. package/dist/assets/{architectureDiagram-Q4EWVU46-ewcueFAG.js → architectureDiagram-Q4EWVU46-CbIQua02.js} +1 -1
  13. package/dist/assets/{blockDiagram-DXYQGD6D-TxlbbvKn.js → blockDiagram-DXYQGD6D-Cg7qkpSM.js} +1 -1
  14. package/dist/assets/{c4Diagram-AHTNJAMY-C1lT_bl_.js → c4Diagram-AHTNJAMY-BkffDY1F.js} +1 -1
  15. package/dist/assets/channel-beae0DeI.js +1 -0
  16. package/dist/assets/chatgpt-logo-dark.svg +15 -15
  17. package/dist/assets/chatgpt-logo.svg +15 -15
  18. package/dist/assets/{chunk-4BX2VUAB-C41j2mCL.js → chunk-4BX2VUAB-BxHe9wPE.js} +1 -1
  19. package/dist/assets/{chunk-4TB4RGXK-HNNsUbz0.js → chunk-4TB4RGXK--f1tN90O.js} +1 -1
  20. package/dist/assets/{chunk-55IACEB6-qtCgO0r2.js → chunk-55IACEB6-B5QXfPXQ.js} +1 -1
  21. package/dist/assets/{chunk-EDXVE4YY-BSnDYtsd.js → chunk-EDXVE4YY-DqKhblOg.js} +1 -1
  22. package/dist/assets/{chunk-FMBD7UC4-DyHRLQqX.js → chunk-FMBD7UC4-DZ1w_G02.js} +1 -1
  23. package/dist/assets/{chunk-OYMX7WX6-CCjfi6WS.js → chunk-OYMX7WX6-BqAgQpv8.js} +1 -1
  24. package/dist/assets/{chunk-QZHKN3VN-COLty8kd.js → chunk-QZHKN3VN-DPqnGqVi.js} +1 -1
  25. package/dist/assets/{chunk-YZCP3GAM-CHUUnGeN.js → chunk-YZCP3GAM-x-ZYSQLd.js} +1 -1
  26. package/dist/assets/classDiagram-6PBFFD2Q-BquWrs1y.js +1 -0
  27. package/dist/assets/classDiagram-v2-HSJHXN6E-BquWrs1y.js +1 -0
  28. package/dist/assets/clone-C9wSPtDN.js +1 -0
  29. package/dist/assets/{cose-bilkent-S5V4N54A-CSip-V2g.js → cose-bilkent-S5V4N54A-4VCNRP-E.js} +1 -1
  30. package/dist/assets/{dagre-KV5264BT-DkdpnWhv.js → dagre-KV5264BT-DucVi1QS.js} +1 -1
  31. package/dist/assets/{diagram-5BDNPKRD-PH4qc6PV.js → diagram-5BDNPKRD-ChdRA8bE.js} +1 -1
  32. package/dist/assets/{diagram-G4DWMVQ6-Cg5xZcjx.js → diagram-G4DWMVQ6-B1-97yHr.js} +1 -1
  33. package/dist/assets/{diagram-MMDJMWI5-soKmeTCW.js → diagram-MMDJMWI5-CQs3cO7G.js} +1 -1
  34. package/dist/assets/{diagram-TYMM5635-Daq5Mihu.js → diagram-TYMM5635-CuNCxDfO.js} +1 -1
  35. package/dist/assets/{erDiagram-SMLLAGMA-kr2OtY0Y.js → erDiagram-SMLLAGMA-DdR8v8g-.js} +1 -1
  36. package/dist/assets/{flowDiagram-DWJPFMVM-DQZCb8gm.js → flowDiagram-DWJPFMVM-Dt02upId.js} +1 -1
  37. package/dist/assets/{ganttDiagram-T4ZO3ILL-BHkn485T.js → ganttDiagram-T4ZO3ILL-cG_k9VOa.js} +1 -1
  38. package/dist/assets/{gitGraphDiagram-UUTBAWPF-FaCyYFmC.js → gitGraphDiagram-UUTBAWPF-Dz1DjhKq.js} +1 -1
  39. package/dist/assets/{graph-BVJlrP6V.js → graph-CF2NtM33.js} +1 -1
  40. package/dist/assets/{infoDiagram-42DDH7IO-DJOWkKdM.js → infoDiagram-42DDH7IO-tQWKrYM6.js} +1 -1
  41. package/dist/assets/{ishikawaDiagram-UXIWVN3A-VfpvNaIf.js → ishikawaDiagram-UXIWVN3A-CLuUMkF0.js} +1 -1
  42. package/dist/assets/{journeyDiagram-VCZTEJTY-CPzsak-v.js → journeyDiagram-VCZTEJTY-a6JenLCk.js} +1 -1
  43. package/dist/assets/{kanban-definition-6JOO6SKY-DFqLDBU0.js → kanban-definition-6JOO6SKY-rqOxTzYb.js} +1 -1
  44. package/dist/assets/{layout-CCSbNPHm.js → layout-Dvic1Hpy.js} +1 -1
  45. package/dist/assets/{linear-C4T7PCKE.js → linear-CfMV1S6a.js} +1 -1
  46. package/dist/assets/main-05K4ggqd.css +10 -0
  47. package/dist/assets/main-CCM1gtr8.js +1165 -0
  48. package/dist/assets/{min-CGQNEYGh.js → min-GpF3DZux.js} +1 -1
  49. package/dist/assets/{mindmap-definition-QFDTVHPH-AuU1EqwS.js → mindmap-definition-QFDTVHPH-Cg80z0Jx.js} +1 -1
  50. package/dist/assets/{pieDiagram-DEJITSTG-CopkCZwp.js → pieDiagram-DEJITSTG-BrtK7lAq.js} +1 -1
  51. package/dist/assets/{quadrantDiagram-34T5L4WZ-lMKrSv_t.js → quadrantDiagram-34T5L4WZ-BL2txAAS.js} +1 -1
  52. package/dist/assets/{requirementDiagram-MS252O5E-dWUpHOFb.js → requirementDiagram-MS252O5E-Co3wpBnu.js} +1 -1
  53. package/dist/assets/{sankeyDiagram-XADWPNL6-C8UQx9Bb.js → sankeyDiagram-XADWPNL6-B4KJXdQ4.js} +1 -1
  54. package/dist/assets/{sequenceDiagram-FGHM5R23-CUVNIItJ.js → sequenceDiagram-FGHM5R23-xs5OuzvV.js} +1 -1
  55. package/dist/assets/{stateDiagram-FHFEXIEX-Ct0GamGl.js → stateDiagram-FHFEXIEX-bbEP20JD.js} +1 -1
  56. package/dist/assets/stateDiagram-v2-QKLJ7IA2-XYh9U1A7.js +1 -0
  57. package/dist/assets/{timeline-definition-GMOUNBTQ-ul8Po7f7.js → timeline-definition-GMOUNBTQ-CTc2wVwC.js} +1 -1
  58. package/dist/assets/{vennDiagram-DHZGUBPP-B4AOWQnP.js → vennDiagram-DHZGUBPP-oJpXott7.js} +1 -1
  59. package/dist/assets/{wardley-RL74JXVD-Dr7Wp3AJ.js → wardley-RL74JXVD-aTmOXkKh.js} +1 -1
  60. package/dist/assets/{wardleyDiagram-NUSXRM2D-Ck70faXX.js → wardleyDiagram-NUSXRM2D-C83SOkig.js} +1 -1
  61. package/dist/assets/{xychartDiagram-5P7HB3ND-Bsy5-cNt.js → xychartDiagram-5P7HB3ND-BScws0tF.js} +1 -1
  62. package/dist/index.html +13 -13
  63. package/dist-electron/main.js +153 -116
  64. package/dist-electron/overlay.js +102 -65
  65. package/dist-electron/package.json +1 -0
  66. package/dist-electron/preload.js +8 -8
  67. package/dist-server/bootstrap/crewx-server.js +19 -10
  68. package/dist-server/domain/agent/agent.service.js +12 -0
  69. package/dist-server/domain/agent/dto/update-agent.dto.js +20 -1
  70. package/dist-server/domain/mcp/crewx-tool.factory.js +44 -113
  71. package/dist-server/domain/mcp/mcp.module.js +2 -0
  72. package/dist-server/domain/mcp/mcp.service.js +37 -12
  73. package/dist-server/domain/message/message.service.js +21 -13
  74. package/dist-server/domain/skill/skill.service.js +63 -43
  75. package/dist-server/domain/task/task.module.js +2 -0
  76. package/dist-server/domain/task/task.service.js +17 -10
  77. package/dist-server/domain/thread/dto/update-thread.dto.js +23 -0
  78. package/dist-server/domain/thread/thread.controller.js +16 -0
  79. package/dist-server/domain/thread/thread.service.js +9 -0
  80. package/dist-server/main.js +1 -1
  81. package/dist-server/modules/crewx.module.js +16 -1
  82. package/dist-server/repository/box.repository.js +20 -20
  83. package/dist-server/repository/project.repository.js +13 -13
  84. package/dist-server/repository/request-log.repository.js +10 -10
  85. package/dist-server/repository/task.repository.js +72 -72
  86. package/dist-server/repository/thread.repository.js +78 -58
  87. package/package.json +6 -6
  88. package/packages/cli/dist/bootstrap/crewx-cli.js +12 -0
  89. package/packages/cli/dist/commands/agent.js +23 -23
  90. package/packages/cli/dist/commands/init.js +19 -19
  91. package/packages/cli/dist/commands/parse-common-flags.d.ts +19 -3
  92. package/packages/cli/dist/commands/parse-common-flags.js +46 -6
  93. package/packages/cli/dist/commands/registry.d.ts +13 -0
  94. package/packages/cli/dist/commands/registry.js +29 -0
  95. package/packages/cli/dist/commands/task-db.js +7 -7
  96. package/packages/cli/dist/examples/deny-secrets-plugin.d.ts +22 -0
  97. package/packages/cli/dist/examples/deny-secrets-plugin.js +40 -0
  98. package/packages/cli/dist/main.js +134 -68
  99. package/packages/cli/dist/plugins/examples/echo-hook.d.ts +24 -0
  100. package/packages/cli/dist/plugins/examples/echo-hook.js +60 -0
  101. package/packages/cli/dist/plugins/examples/verify-echo-hook.d.ts +8 -0
  102. package/packages/cli/dist/plugins/examples/verify-echo-hook.js +47 -0
  103. package/packages/cli/dist/plugins/sqlite-tracing.d.ts +11 -0
  104. package/packages/cli/dist/plugins/sqlite-tracing.js +19 -0
  105. package/packages/cli/dist/schema/tasks.d.ts +7 -0
  106. package/packages/cli/dist/schema/tasks.js +48 -0
  107. package/packages/cli/package.json +52 -52
  108. package/scripts/analyze-task-logs.mjs +569 -0
  109. package/scripts/build-manual.mjs +266 -266
  110. package/scripts/emit-dist-server-package-json.mjs +7 -7
  111. package/scripts/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  112. package/scripts/postinstall.mjs +44 -44
  113. package/scripts/smoke-tarball.mjs +285 -285
  114. package/scripts/snapshot-msg-list.sh +52 -52
  115. package/server.js +167 -164
  116. package/dist/assets/MarketPage-Dwsg6K-B.js +0 -31
  117. package/dist/assets/channel-BP4PNMmz.js +0 -1
  118. package/dist/assets/classDiagram-6PBFFD2Q-Upr3UAcM.js +0 -1
  119. package/dist/assets/classDiagram-v2-HSJHXN6E-Upr3UAcM.js +0 -1
  120. package/dist/assets/clone-B8BP7ReZ.js +0 -1
  121. package/dist/assets/main-CELBpK6r.js +0 -1166
  122. package/dist/assets/main-CmP-VosD.css +0 -10
  123. package/dist/assets/stateDiagram-v2-QKLJ7IA2-CFBLQDEx.js +0 -1
  124. package/dist-server/domain/task/dto/project-usage.dto.js +0 -38
  125. package/dist-server/domain/thread/dto/send-message.dto.js +0 -10
package/bin/crewx-lib.js CHANGED
@@ -1,108 +1,213 @@
1
- /**
2
- * crewx launcher library — testable pure-logic functions extracted from crewx.js.
3
- */
4
-
5
- /**
6
- * Parse --port option from extra CLI args and validate the value.
7
- *
8
- * @param {string[]} extraArgs - remaining args after route resolution (e.g. ['--port', '3000'])
9
- * @returns {{ port: number } | { error: string } | null}
10
- * - `{ port }` when a valid port is found
11
- * - `{ error }` when --port is present but the value is invalid
12
- * - `null` when --port is not specified
13
- */
14
- export function parseWebPort(extraArgs) {
15
- const portIdx = extraArgs.indexOf('--port');
16
- if (portIdx === -1) return null;
17
- const portVal = Number(extraArgs[portIdx + 1]);
18
- if (!portVal || portVal < 1 || portVal > 65535) {
19
- return { error: 'Invalid port number. Use 1-65535.' };
20
- }
21
- return { port: portVal };
22
- }
23
-
24
- /**
25
- * Determine the route based on CLI arguments.
26
- * UI flags (--web, --no-open, --overlay, --electron, --desktop) are checked first.
27
- * Any other argument routes to CLI. No arguments → web default (with browser auto-open).
28
- *
29
- * @param {string[]} args - process.argv.slice(2)
30
- * @returns {{ route: string, routeArgs: string[] }}
31
- */
32
- export function resolveRoute(args) {
33
- const firstArg = args[0];
34
-
35
- if (firstArg === '--web') return { route: 'web', routeArgs: args.slice(1) };
36
- if (firstArg === '--no-open') return { route: 'web', routeArgs: args.slice(1) };
37
- if (firstArg === '--overlay') return { route: 'overlay', routeArgs: args.slice(1) };
38
- if (firstArg === '--electron') return { route: 'electron-explicit', routeArgs: args.slice(1) };
39
- if (firstArg === '--desktop') return { route: 'electron-explicit', routeArgs: args.slice(1) };
40
- if (firstArg !== undefined) return { route: 'cli', routeArgs: args };
41
- return { route: 'web-default', routeArgs: args };
42
- }
43
-
44
- /**
45
- * Forward signals to a child process for clean shutdown.
46
- *
47
- * Uses an `exited` flag (set by the child's 'exit' event) instead of
48
- * `child.killed`, because `child.killed` becomes `true` immediately after
49
- * calling `child.kill()` even if the child process is still running.
50
- * This ensures the SIGKILL escalation timer can actually fire.
51
- *
52
- * On Windows, SIGTERM is equivalent to an immediate kill.
53
- *
54
- * @param {import('child_process').ChildProcess} child
55
- * @param {{ escalateKill?: boolean, detached?: boolean }} options
56
- */
57
- export function setupSignalForwarding(child, { escalateKill = false, detached = false } = {}) {
58
- let exited = false;
59
- let escalationScheduled = false;
60
-
61
- child.on('exit', () => {
62
- exited = true;
63
- });
64
-
65
- // When detached, the child runs in its own process group.
66
- // Use process.kill(-pid) to kill the entire group (Electron + GPU helpers).
67
- // When not detached, use child.kill() for single-process kill.
68
- const killChild = (signal) => {
69
- try {
70
- if (detached) {
71
- process.kill(-child.pid, signal);
72
- } else {
73
- child.kill(signal);
74
- }
75
- } catch {
76
- // Process or process group already terminated
77
- }
78
- };
79
-
80
- process.on('SIGINT', () => {
81
- if (!exited) {
82
- killChild('SIGTERM');
83
- if (escalateKill && !escalationScheduled) {
84
- escalationScheduled = true;
85
- setTimeout(() => {
86
- if (!exited) killChild('SIGKILL');
87
- }, 3000).unref();
88
- }
89
- }
90
- });
91
-
92
- process.on('SIGTERM', () => {
93
- if (!exited) killChild('SIGTERM');
94
- });
95
-
96
- process.on('SIGHUP', () => {
97
- if (!exited) killChild('SIGHUP');
98
- });
99
-
100
- // Clean up the child process group when the parent exits unexpectedly
101
- if (detached) {
102
- process.on('exit', () => {
103
- if (!exited) {
104
- try { process.kill(-child.pid, 'SIGKILL'); } catch {}
105
- }
106
- });
107
- }
108
- }
1
+ /**
2
+ * crewx launcher library — testable pure-logic functions extracted from crewx.js.
3
+ */
4
+ import { CLI_SUBCOMMANDS } from './cli-commands.js';
5
+ import { buildHintMessage } from './launcher-flags.js';
6
+
7
+ /**
8
+ * Parse serve-related flags: --port, --token, --token-file, --no-open.
9
+ * Strict mode: unknown --xxx tokens { error } instead of silent absorption.
10
+ *
11
+ * @param {string[]} args
12
+ * @returns {{ port?: number, token?: string, tokenFile?: string, noOpen: boolean } | { error: string }}
13
+ */
14
+ export function parseServeFlags(args) {
15
+ let port = undefined;
16
+ let token = undefined;
17
+ let tokenFile = undefined;
18
+ let noOpen = false;
19
+
20
+ const consumed = new Set();
21
+
22
+ for (let i = 0; i < args.length; i++) {
23
+ const arg = args[i];
24
+
25
+ if (arg === '--port') {
26
+ const val = Number(args[i + 1]);
27
+ if (!val || val < 1 || val > 65535) {
28
+ return { error: 'Invalid port number. Use 1-65535.' };
29
+ }
30
+ port = val;
31
+ consumed.add(i);
32
+ consumed.add(i + 1);
33
+ i++;
34
+ } else if (arg?.startsWith('--port=')) {
35
+ const val = Number(arg.slice('--port='.length));
36
+ if (!val || val < 1 || val > 65535) {
37
+ return { error: 'Invalid port number. Use 1-65535.' };
38
+ }
39
+ port = val;
40
+ consumed.add(i);
41
+ } else if (arg === '--token') {
42
+ token = args[i + 1];
43
+ consumed.add(i);
44
+ consumed.add(i + 1);
45
+ i++;
46
+ } else if (arg?.startsWith('--token=')) {
47
+ token = arg.slice('--token='.length);
48
+ consumed.add(i);
49
+ } else if (arg === '--token-file') {
50
+ tokenFile = args[i + 1];
51
+ consumed.add(i);
52
+ consumed.add(i + 1);
53
+ i++;
54
+ } else if (arg?.startsWith('--token-file=')) {
55
+ tokenFile = arg.slice('--token-file='.length);
56
+ consumed.add(i);
57
+ } else if (arg === '--no-open') {
58
+ noOpen = true;
59
+ consumed.add(i);
60
+ } else if (arg === '--help' || arg === '-h') {
61
+ return { help: true };
62
+ }
63
+ }
64
+
65
+ // Strict: reject unconsumed --xxx flags
66
+ for (let i = 0; i < args.length; i++) {
67
+ if (consumed.has(i)) continue;
68
+ const arg = args[i];
69
+ if (arg?.startsWith('--')) {
70
+ return { error: `Unknown option for "crewx serve": ${arg}` };
71
+ }
72
+ }
73
+
74
+ return { port, token, tokenFile, noOpen };
75
+ }
76
+
77
+ /**
78
+ * Parse --port option from extra CLI args.
79
+ * Backward-compatible thin wrapper; kept for existing callers.
80
+ *
81
+ * @param {string[]} extraArgs
82
+ * @returns {{ port: number } | { error: string } | null}
83
+ */
84
+ export function parseWebPort(extraArgs) {
85
+ const portIdx = extraArgs.indexOf('--port');
86
+ if (portIdx === -1) return null;
87
+ const portVal = Number(extraArgs[portIdx + 1]);
88
+ if (!portVal || portVal < 1 || portVal > 65535) {
89
+ return { error: 'Invalid port number. Use 1-65535.' };
90
+ }
91
+ return { port: portVal };
92
+ }
93
+
94
+ /**
95
+ * Determine the route based on CLI arguments.
96
+ *
97
+ * Priority order:
98
+ * 1. --help/-h, --version/-v → CLI (global options handled by CLI engine)
99
+ * 2. Known CLI subcommand → CLI
100
+ * 3. 'serve' verb → serve
101
+ * 4. 'electron' verb → electron
102
+ * 5. No arguments → web-default (browser auto-open)
103
+ * 6. Unknown first argument → error with hint
104
+ *
105
+ * @param {string[]} args - process.argv.slice(2)
106
+ * @returns {{ route: string, routeArgs: string[], error?: string }}
107
+ */
108
+ export function resolveRoute(args) {
109
+ const firstArg = args[0];
110
+
111
+ // 1. Global options → CLI engine handles them
112
+ if (firstArg === '--help' || firstArg === '-h') {
113
+ return { route: 'cli', routeArgs: args };
114
+ }
115
+ if (firstArg === '--version' || firstArg === '-v') {
116
+ return { route: 'cli', routeArgs: args };
117
+ }
118
+
119
+ // 2. CLI subcommand whitelist → CLI engine
120
+ if (firstArg !== undefined && CLI_SUBCOMMANDS.has(firstArg)) {
121
+ return { route: 'cli', routeArgs: args };
122
+ }
123
+
124
+ // 3. New verb: serve
125
+ if (firstArg === 'serve') {
126
+ return { route: 'serve', routeArgs: args.slice(1) };
127
+ }
128
+
129
+ // 4. New verb: electron
130
+ if (firstArg === 'electron') {
131
+ return { route: 'electron', routeArgs: args.slice(1) };
132
+ }
133
+
134
+ // 5. No arguments → web default
135
+ if (firstArg === undefined) {
136
+ return { route: 'web-default', routeArgs: [] };
137
+ }
138
+
139
+ // 6. Unknown → error with hint
140
+ const hint = buildHintMessage(firstArg);
141
+ const hintSuffix = hint ? `\n ${hint}` : '\n Run `crewx --help` for usage.';
142
+ return {
143
+ route: 'error',
144
+ error: `Unknown command or option: "${firstArg}"${hintSuffix}`,
145
+ routeArgs: args,
146
+ };
147
+ }
148
+
149
+ /**
150
+ * Forward signals to a child process for clean shutdown.
151
+ *
152
+ * Uses an `exited` flag (set by the child's 'exit' event) instead of
153
+ * `child.killed`, because `child.killed` becomes `true` immediately after
154
+ * calling `child.kill()` — even if the child process is still running.
155
+ * This ensures the SIGKILL escalation timer can actually fire.
156
+ *
157
+ * On Windows, SIGTERM is equivalent to an immediate kill.
158
+ *
159
+ * @param {import('child_process').ChildProcess} child
160
+ * @param {{ escalateKill?: boolean, detached?: boolean }} options
161
+ */
162
+ export function setupSignalForwarding(child, { escalateKill = false, detached = false } = {}) {
163
+ let exited = false;
164
+ let escalationScheduled = false;
165
+
166
+ child.on('exit', () => {
167
+ exited = true;
168
+ });
169
+
170
+ // When detached, the child runs in its own process group.
171
+ // Use process.kill(-pid) to kill the entire group (Electron + GPU helpers).
172
+ // When not detached, use child.kill() for single-process kill.
173
+ const killChild = (signal) => {
174
+ try {
175
+ if (detached) {
176
+ process.kill(-child.pid, signal);
177
+ } else {
178
+ child.kill(signal);
179
+ }
180
+ } catch {
181
+ // Process or process group already terminated
182
+ }
183
+ };
184
+
185
+ process.on('SIGINT', () => {
186
+ if (!exited) {
187
+ killChild('SIGTERM');
188
+ if (escalateKill && !escalationScheduled) {
189
+ escalationScheduled = true;
190
+ setTimeout(() => {
191
+ if (!exited) killChild('SIGKILL');
192
+ }, 3000).unref();
193
+ }
194
+ }
195
+ });
196
+
197
+ process.on('SIGTERM', () => {
198
+ if (!exited) killChild('SIGTERM');
199
+ });
200
+
201
+ process.on('SIGHUP', () => {
202
+ if (!exited) killChild('SIGHUP');
203
+ });
204
+
205
+ // Clean up the child process group when the parent exits unexpectedly
206
+ if (detached) {
207
+ process.on('exit', () => {
208
+ if (!exited) {
209
+ try { process.kill(-child.pid, 'SIGKILL'); } catch {}
210
+ }
211
+ });
212
+ }
213
+ }
package/bin/crewx-ui.js CHANGED
@@ -1,83 +1,83 @@
1
- #!/usr/bin/env node
2
- /**
3
- * crewx-ui launcher
4
- *
5
- * Usage:
6
- * npx crewx-ui → NestJS web server (default)
7
- * npx crewx-ui --electron → Electron dashboard (main window)
8
- * npx crewx-ui --overlay → Electron floating overlay
9
- */
10
- import { spawn } from 'child_process';
11
- import { fileURLToPath } from 'url';
12
- import { dirname, join } from 'path';
13
- import { createRequire } from 'module';
14
-
15
- const __filename = fileURLToPath(import.meta.url);
16
- const __dirname = dirname(__filename);
17
-
18
- const args = process.argv.slice(2);
19
- const isElectron = args.includes('--electron');
20
- const isOverlay = args.includes('--overlay');
21
-
22
- function getElectronSpawnEnv() {
23
- const env = { ...process.env };
24
- delete env.ELECTRON_RUN_AS_NODE;
25
- env.NODE_ENV = 'production'; // Force production — prevents users from bypassing FREE limits via env
26
- return env;
27
- }
28
-
29
- if (isElectron || isOverlay) {
30
- // Electron mode: resolve binary path via require('electron')
31
- const require = createRequire(import.meta.url);
32
-
33
- let electronPath;
34
- try {
35
- electronPath = require('electron');
36
- } catch {
37
- console.error('');
38
- console.error(' ❌ Electron is not installed.');
39
- console.error('');
40
- console.error(' To use --electron or --overlay mode, install Electron:');
41
- console.error('');
42
- console.error(' npm install -g electron');
43
- console.error(' # or, in this project:');
44
- console.error(' npm install --save-dev electron');
45
- console.error('');
46
- process.exit(1);
47
- }
48
-
49
- const entryFile = isOverlay
50
- ? join(__dirname, '..', 'dist-electron', 'overlay.js')
51
- : join(__dirname, '..', 'dist-electron', 'main.js');
52
-
53
- if (process.env.ELECTRON_RUN_AS_NODE) {
54
- console.warn(
55
- '[crewx-ui] Ignoring ELECTRON_RUN_AS_NODE for Electron UI launch (it forces Electron into Node mode).',
56
- );
57
- }
58
-
59
- const child = spawn(String(electronPath), [entryFile], {
60
- stdio: 'inherit',
61
- env: getElectronSpawnEnv(),
62
- });
63
-
64
- child.on('exit', (code) => process.exit(code ?? 0));
65
- child.on('error', (err) => {
66
- console.error('Failed to launch Electron:', err);
67
- process.exit(1);
68
- });
69
- } else {
70
- // Default: delegate to server.js (NestJS web server)
71
- const serverScript = join(__dirname, '..', 'server.js');
72
-
73
- const child = spawn(process.execPath, [serverScript, ...args], {
74
- stdio: 'inherit',
75
- env: { ...process.env, NODE_ENV: 'production' }, // Force production — prevents users from bypassing FREE limits via env
76
- });
77
-
78
- child.on('exit', (code) => process.exit(code ?? 0));
79
- child.on('error', (err) => {
80
- console.error('Failed to start server:', err);
81
- process.exit(1);
82
- });
83
- }
1
+ #!/usr/bin/env node
2
+ /**
3
+ * crewx-ui launcher
4
+ *
5
+ * Usage:
6
+ * npx crewx-ui → NestJS web server (default)
7
+ * npx crewx-ui --electron → Electron dashboard (main window)
8
+ * npx crewx-ui --overlay → Electron floating overlay
9
+ */
10
+ import { spawn } from 'child_process';
11
+ import { fileURLToPath } from 'url';
12
+ import { dirname, join } from 'path';
13
+ import { createRequire } from 'module';
14
+
15
+ const __filename = fileURLToPath(import.meta.url);
16
+ const __dirname = dirname(__filename);
17
+
18
+ const args = process.argv.slice(2);
19
+ const isElectron = args.includes('--electron');
20
+ const isOverlay = args.includes('--overlay');
21
+
22
+ function getElectronSpawnEnv() {
23
+ const env = { ...process.env };
24
+ delete env.ELECTRON_RUN_AS_NODE;
25
+ env.NODE_ENV = 'production'; // Force production — prevents users from bypassing FREE limits via env
26
+ return env;
27
+ }
28
+
29
+ if (isElectron || isOverlay) {
30
+ // Electron mode: resolve binary path via require('electron')
31
+ const require = createRequire(import.meta.url);
32
+
33
+ let electronPath;
34
+ try {
35
+ electronPath = require('electron');
36
+ } catch {
37
+ console.error('');
38
+ console.error(' ❌ Electron is not installed.');
39
+ console.error('');
40
+ console.error(' To use --electron or --overlay mode, install Electron:');
41
+ console.error('');
42
+ console.error(' npm install -g electron');
43
+ console.error(' # or, in this project:');
44
+ console.error(' npm install --save-dev electron');
45
+ console.error('');
46
+ process.exit(1);
47
+ }
48
+
49
+ const entryFile = isOverlay
50
+ ? join(__dirname, '..', 'dist-electron', 'overlay.js')
51
+ : join(__dirname, '..', 'dist-electron', 'main.js');
52
+
53
+ if (process.env.ELECTRON_RUN_AS_NODE) {
54
+ console.warn(
55
+ '[crewx-ui] Ignoring ELECTRON_RUN_AS_NODE for Electron UI launch (it forces Electron into Node mode).',
56
+ );
57
+ }
58
+
59
+ const child = spawn(String(electronPath), [entryFile], {
60
+ stdio: 'inherit',
61
+ env: getElectronSpawnEnv(),
62
+ });
63
+
64
+ child.on('exit', (code) => process.exit(code ?? 0));
65
+ child.on('error', (err) => {
66
+ console.error('Failed to launch Electron:', err);
67
+ process.exit(1);
68
+ });
69
+ } else {
70
+ // Default: delegate to server.js (NestJS web server)
71
+ const serverScript = join(__dirname, '..', 'server.js');
72
+
73
+ const child = spawn(process.execPath, [serverScript, ...args], {
74
+ stdio: 'inherit',
75
+ env: { ...process.env, NODE_ENV: 'production' }, // Force production — prevents users from bypassing FREE limits via env
76
+ });
77
+
78
+ child.on('exit', (code) => process.exit(code ?? 0));
79
+ child.on('error', (err) => {
80
+ console.error('Failed to start server:', err);
81
+ process.exit(1);
82
+ });
83
+ }