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.
- package/README.md +268 -268
- package/bin/cli-commands.js +34 -0
- package/bin/crewx-lib.js +213 -108
- package/bin/crewx-ui.js +83 -83
- package/bin/crewx.js +219 -147
- package/bin/launcher-flags.js +29 -0
- package/bin/package.json +1 -1
- package/dist/assets/MarketPage-DptjaFpT.js +36 -0
- package/dist/assets/{PromptTab-DVKc7hJY.js → PromptTab-DZha2_v1.js} +1 -1
- package/dist/assets/{_baseUniq-wjlVo2E6.js → _baseUniq-jd6NubI3.js} +1 -1
- package/dist/assets/{arc-BfPgRtzW.js → arc-C2te3-8P.js} +1 -1
- package/dist/assets/{architectureDiagram-Q4EWVU46-ewcueFAG.js → architectureDiagram-Q4EWVU46-CbIQua02.js} +1 -1
- package/dist/assets/{blockDiagram-DXYQGD6D-TxlbbvKn.js → blockDiagram-DXYQGD6D-Cg7qkpSM.js} +1 -1
- package/dist/assets/{c4Diagram-AHTNJAMY-C1lT_bl_.js → c4Diagram-AHTNJAMY-BkffDY1F.js} +1 -1
- package/dist/assets/channel-beae0DeI.js +1 -0
- package/dist/assets/chatgpt-logo-dark.svg +15 -15
- package/dist/assets/chatgpt-logo.svg +15 -15
- package/dist/assets/{chunk-4BX2VUAB-C41j2mCL.js → chunk-4BX2VUAB-BxHe9wPE.js} +1 -1
- package/dist/assets/{chunk-4TB4RGXK-HNNsUbz0.js → chunk-4TB4RGXK--f1tN90O.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-qtCgO0r2.js → chunk-55IACEB6-B5QXfPXQ.js} +1 -1
- package/dist/assets/{chunk-EDXVE4YY-BSnDYtsd.js → chunk-EDXVE4YY-DqKhblOg.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-DyHRLQqX.js → chunk-FMBD7UC4-DZ1w_G02.js} +1 -1
- package/dist/assets/{chunk-OYMX7WX6-CCjfi6WS.js → chunk-OYMX7WX6-BqAgQpv8.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-COLty8kd.js → chunk-QZHKN3VN-DPqnGqVi.js} +1 -1
- package/dist/assets/{chunk-YZCP3GAM-CHUUnGeN.js → chunk-YZCP3GAM-x-ZYSQLd.js} +1 -1
- package/dist/assets/classDiagram-6PBFFD2Q-BquWrs1y.js +1 -0
- package/dist/assets/classDiagram-v2-HSJHXN6E-BquWrs1y.js +1 -0
- package/dist/assets/clone-C9wSPtDN.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-CSip-V2g.js → cose-bilkent-S5V4N54A-4VCNRP-E.js} +1 -1
- package/dist/assets/{dagre-KV5264BT-DkdpnWhv.js → dagre-KV5264BT-DucVi1QS.js} +1 -1
- package/dist/assets/{diagram-5BDNPKRD-PH4qc6PV.js → diagram-5BDNPKRD-ChdRA8bE.js} +1 -1
- package/dist/assets/{diagram-G4DWMVQ6-Cg5xZcjx.js → diagram-G4DWMVQ6-B1-97yHr.js} +1 -1
- package/dist/assets/{diagram-MMDJMWI5-soKmeTCW.js → diagram-MMDJMWI5-CQs3cO7G.js} +1 -1
- package/dist/assets/{diagram-TYMM5635-Daq5Mihu.js → diagram-TYMM5635-CuNCxDfO.js} +1 -1
- package/dist/assets/{erDiagram-SMLLAGMA-kr2OtY0Y.js → erDiagram-SMLLAGMA-DdR8v8g-.js} +1 -1
- package/dist/assets/{flowDiagram-DWJPFMVM-DQZCb8gm.js → flowDiagram-DWJPFMVM-Dt02upId.js} +1 -1
- package/dist/assets/{ganttDiagram-T4ZO3ILL-BHkn485T.js → ganttDiagram-T4ZO3ILL-cG_k9VOa.js} +1 -1
- package/dist/assets/{gitGraphDiagram-UUTBAWPF-FaCyYFmC.js → gitGraphDiagram-UUTBAWPF-Dz1DjhKq.js} +1 -1
- package/dist/assets/{graph-BVJlrP6V.js → graph-CF2NtM33.js} +1 -1
- package/dist/assets/{infoDiagram-42DDH7IO-DJOWkKdM.js → infoDiagram-42DDH7IO-tQWKrYM6.js} +1 -1
- package/dist/assets/{ishikawaDiagram-UXIWVN3A-VfpvNaIf.js → ishikawaDiagram-UXIWVN3A-CLuUMkF0.js} +1 -1
- package/dist/assets/{journeyDiagram-VCZTEJTY-CPzsak-v.js → journeyDiagram-VCZTEJTY-a6JenLCk.js} +1 -1
- package/dist/assets/{kanban-definition-6JOO6SKY-DFqLDBU0.js → kanban-definition-6JOO6SKY-rqOxTzYb.js} +1 -1
- package/dist/assets/{layout-CCSbNPHm.js → layout-Dvic1Hpy.js} +1 -1
- package/dist/assets/{linear-C4T7PCKE.js → linear-CfMV1S6a.js} +1 -1
- package/dist/assets/main-05K4ggqd.css +10 -0
- package/dist/assets/main-CCM1gtr8.js +1165 -0
- package/dist/assets/{min-CGQNEYGh.js → min-GpF3DZux.js} +1 -1
- package/dist/assets/{mindmap-definition-QFDTVHPH-AuU1EqwS.js → mindmap-definition-QFDTVHPH-Cg80z0Jx.js} +1 -1
- package/dist/assets/{pieDiagram-DEJITSTG-CopkCZwp.js → pieDiagram-DEJITSTG-BrtK7lAq.js} +1 -1
- package/dist/assets/{quadrantDiagram-34T5L4WZ-lMKrSv_t.js → quadrantDiagram-34T5L4WZ-BL2txAAS.js} +1 -1
- package/dist/assets/{requirementDiagram-MS252O5E-dWUpHOFb.js → requirementDiagram-MS252O5E-Co3wpBnu.js} +1 -1
- package/dist/assets/{sankeyDiagram-XADWPNL6-C8UQx9Bb.js → sankeyDiagram-XADWPNL6-B4KJXdQ4.js} +1 -1
- package/dist/assets/{sequenceDiagram-FGHM5R23-CUVNIItJ.js → sequenceDiagram-FGHM5R23-xs5OuzvV.js} +1 -1
- package/dist/assets/{stateDiagram-FHFEXIEX-Ct0GamGl.js → stateDiagram-FHFEXIEX-bbEP20JD.js} +1 -1
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-XYh9U1A7.js +1 -0
- package/dist/assets/{timeline-definition-GMOUNBTQ-ul8Po7f7.js → timeline-definition-GMOUNBTQ-CTc2wVwC.js} +1 -1
- package/dist/assets/{vennDiagram-DHZGUBPP-B4AOWQnP.js → vennDiagram-DHZGUBPP-oJpXott7.js} +1 -1
- package/dist/assets/{wardley-RL74JXVD-Dr7Wp3AJ.js → wardley-RL74JXVD-aTmOXkKh.js} +1 -1
- package/dist/assets/{wardleyDiagram-NUSXRM2D-Ck70faXX.js → wardleyDiagram-NUSXRM2D-C83SOkig.js} +1 -1
- package/dist/assets/{xychartDiagram-5P7HB3ND-Bsy5-cNt.js → xychartDiagram-5P7HB3ND-BScws0tF.js} +1 -1
- package/dist/index.html +13 -13
- package/dist-electron/main.js +153 -116
- package/dist-electron/overlay.js +102 -65
- package/dist-electron/package.json +1 -0
- package/dist-electron/preload.js +8 -8
- package/dist-server/bootstrap/crewx-server.js +19 -10
- package/dist-server/domain/agent/agent.service.js +12 -0
- package/dist-server/domain/agent/dto/update-agent.dto.js +20 -1
- package/dist-server/domain/mcp/crewx-tool.factory.js +44 -113
- package/dist-server/domain/mcp/mcp.module.js +2 -0
- package/dist-server/domain/mcp/mcp.service.js +37 -12
- package/dist-server/domain/message/message.service.js +21 -13
- package/dist-server/domain/skill/skill.service.js +63 -43
- package/dist-server/domain/task/task.module.js +2 -0
- package/dist-server/domain/task/task.service.js +17 -10
- package/dist-server/domain/thread/dto/update-thread.dto.js +23 -0
- package/dist-server/domain/thread/thread.controller.js +16 -0
- package/dist-server/domain/thread/thread.service.js +9 -0
- package/dist-server/main.js +1 -1
- package/dist-server/modules/crewx.module.js +16 -1
- package/dist-server/repository/box.repository.js +20 -20
- package/dist-server/repository/project.repository.js +13 -13
- package/dist-server/repository/request-log.repository.js +10 -10
- package/dist-server/repository/task.repository.js +72 -72
- package/dist-server/repository/thread.repository.js +78 -58
- package/package.json +6 -6
- package/packages/cli/dist/bootstrap/crewx-cli.js +12 -0
- package/packages/cli/dist/commands/agent.js +23 -23
- package/packages/cli/dist/commands/init.js +19 -19
- package/packages/cli/dist/commands/parse-common-flags.d.ts +19 -3
- package/packages/cli/dist/commands/parse-common-flags.js +46 -6
- package/packages/cli/dist/commands/registry.d.ts +13 -0
- package/packages/cli/dist/commands/registry.js +29 -0
- package/packages/cli/dist/commands/task-db.js +7 -7
- package/packages/cli/dist/examples/deny-secrets-plugin.d.ts +22 -0
- package/packages/cli/dist/examples/deny-secrets-plugin.js +40 -0
- package/packages/cli/dist/main.js +134 -68
- package/packages/cli/dist/plugins/examples/echo-hook.d.ts +24 -0
- package/packages/cli/dist/plugins/examples/echo-hook.js +60 -0
- package/packages/cli/dist/plugins/examples/verify-echo-hook.d.ts +8 -0
- package/packages/cli/dist/plugins/examples/verify-echo-hook.js +47 -0
- package/packages/cli/dist/plugins/sqlite-tracing.d.ts +11 -0
- package/packages/cli/dist/plugins/sqlite-tracing.js +19 -0
- package/packages/cli/dist/schema/tasks.d.ts +7 -0
- package/packages/cli/dist/schema/tasks.js +48 -0
- package/packages/cli/package.json +52 -52
- package/scripts/analyze-task-logs.mjs +569 -0
- package/scripts/build-manual.mjs +266 -266
- package/scripts/emit-dist-server-package-json.mjs +7 -7
- package/scripts/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/scripts/postinstall.mjs +44 -44
- package/scripts/smoke-tarball.mjs +285 -285
- package/scripts/snapshot-msg-list.sh +52 -52
- package/server.js +167 -164
- package/dist/assets/MarketPage-Dwsg6K-B.js +0 -31
- package/dist/assets/channel-BP4PNMmz.js +0 -1
- package/dist/assets/classDiagram-6PBFFD2Q-Upr3UAcM.js +0 -1
- package/dist/assets/classDiagram-v2-HSJHXN6E-Upr3UAcM.js +0 -1
- package/dist/assets/clone-B8BP7ReZ.js +0 -1
- package/dist/assets/main-CELBpK6r.js +0 -1166
- package/dist/assets/main-CmP-VosD.css +0 -10
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-CFBLQDEx.js +0 -1
- package/dist-server/domain/task/dto/project-usage.dto.js +0 -38
- 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
|
-
|
|
7
|
-
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*/
|
|
14
|
-
export function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
+
}
|