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
|
@@ -1,285 +1,285 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* smoke-tarball.mjs — Tarball-isolated smoke test for crewx CLI.
|
|
4
|
-
*
|
|
5
|
-
* Packs the current project into a tarball, installs it in a temp directory
|
|
6
|
-
* (no global install), and runs a suite of smoke checks against the binary.
|
|
7
|
-
* This avoids workspace symlink false-positives that npm install -g . can produce.
|
|
8
|
-
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* node scripts/smoke-tarball.mjs [--version <expected>] [--skip-web]
|
|
11
|
-
*
|
|
12
|
-
* Flags:
|
|
13
|
-
* --version <ver> Expected crewx --version output. Defaults to package.json version.
|
|
14
|
-
* --skip-web Skip the NestJS web server startup check (useful in CI).
|
|
15
|
-
* --timeout <ms> Per-command timeout in ms. Default: 15000.
|
|
16
|
-
*
|
|
17
|
-
* Exit code: 0 = ALL PASS, 1 = one or more FAILs.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import { execFileSync, spawnSync } from 'child_process';
|
|
21
|
-
import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from 'fs';
|
|
22
|
-
import { join, resolve } from 'path';
|
|
23
|
-
import { tmpdir } from 'os';
|
|
24
|
-
import { createRequire } from 'module';
|
|
25
|
-
|
|
26
|
-
const require = createRequire(import.meta.url);
|
|
27
|
-
const rootPkg = require('../package.json');
|
|
28
|
-
|
|
29
|
-
// ── CLI args ──────────────────────────────────────────────────────────────────
|
|
30
|
-
const args = process.argv.slice(2);
|
|
31
|
-
const getArg = (flag) => {
|
|
32
|
-
const i = args.indexOf(flag);
|
|
33
|
-
return i !== -1 ? args[i + 1] : null;
|
|
34
|
-
};
|
|
35
|
-
const hasFlag = (flag) => args.includes(flag);
|
|
36
|
-
|
|
37
|
-
const expectedVersion = getArg('--version') ?? rootPkg.version;
|
|
38
|
-
const skipWeb = hasFlag('--skip-web');
|
|
39
|
-
const cmdTimeout = parseInt(getArg('--timeout') ?? '15000', 10);
|
|
40
|
-
|
|
41
|
-
// ── Result tracking ───────────────────────────────────────────────────────────
|
|
42
|
-
const results = [];
|
|
43
|
-
let anyFail = false;
|
|
44
|
-
|
|
45
|
-
function pass(label) {
|
|
46
|
-
results.push({ label, status: 'PASS' });
|
|
47
|
-
console.log(` ✓ PASS ${label}`);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function fail(label, detail = '') {
|
|
51
|
-
results.push({ label, status: 'FAIL', detail });
|
|
52
|
-
console.error(` ✗ FAIL ${label}${detail ? ': ' + detail : ''}`);
|
|
53
|
-
anyFail = true;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function run(cmd, cmdArgs, opts = {}) {
|
|
57
|
-
const result = spawnSync(cmd, cmdArgs, {
|
|
58
|
-
timeout: opts.timeout ?? cmdTimeout,
|
|
59
|
-
encoding: 'utf8',
|
|
60
|
-
cwd: opts.cwd,
|
|
61
|
-
env: { ...process.env, ...opts.env },
|
|
62
|
-
});
|
|
63
|
-
return {
|
|
64
|
-
stdout: result.stdout ?? '',
|
|
65
|
-
stderr: result.stderr ?? '',
|
|
66
|
-
status: result.status ?? -1,
|
|
67
|
-
timedOut: result.signal === 'SIGTERM',
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// ── Step 1: Pack ──────────────────────────────────────────────────────────────
|
|
72
|
-
const packDest = tmpdir();
|
|
73
|
-
console.log('\n[1/3] Packing tarball...');
|
|
74
|
-
let tarball;
|
|
75
|
-
try {
|
|
76
|
-
const out = execFileSync('pnpm', ['pack', '--pack-destination', packDest], {
|
|
77
|
-
cwd: resolve('.'),
|
|
78
|
-
encoding: 'utf8',
|
|
79
|
-
}).trim();
|
|
80
|
-
tarball = out.split('\n').pop().trim();
|
|
81
|
-
console.log(` Tarball: ${tarball}`);
|
|
82
|
-
} catch (e) {
|
|
83
|
-
console.error('FATAL: pnpm pack failed:', e.message);
|
|
84
|
-
process.exit(1);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// ── Step 2: Install in temp dir ───────────────────────────────────────────────
|
|
88
|
-
const installDir = mkdtempSync(join(tmpdir(), 'crewx-smoke-install-'));
|
|
89
|
-
console.log(`\n[2/3] Installing into ${installDir}...`);
|
|
90
|
-
try {
|
|
91
|
-
execFileSync('npm', ['install', tarball], {
|
|
92
|
-
cwd: installDir,
|
|
93
|
-
encoding: 'utf8',
|
|
94
|
-
stdio: 'inherit',
|
|
95
|
-
});
|
|
96
|
-
} catch (e) {
|
|
97
|
-
console.error('FATAL: npm install failed:', e.message);
|
|
98
|
-
rmSync(installDir, { recursive: true, force: true });
|
|
99
|
-
process.exit(1);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const binDir = join(installDir, 'node_modules', '.bin');
|
|
103
|
-
const env = { ...process.env, PATH: `${binDir}:${process.env.PATH}` };
|
|
104
|
-
|
|
105
|
-
// ── Step 3: Test dir (clean workspace) ────────────────────────────────────────
|
|
106
|
-
const testDir = mkdtempSync(join(tmpdir(), 'crewx-smoke-test-'));
|
|
107
|
-
mkdirSync(join(testDir, '.git')); // minimal git marker
|
|
108
|
-
|
|
109
|
-
// Minimal crewx.yaml for agent tests
|
|
110
|
-
writeFileSync(join(testDir, 'crewx.yaml'), `
|
|
111
|
-
agents:
|
|
112
|
-
- id: crewx
|
|
113
|
-
name: "CrewX Assistant"
|
|
114
|
-
inline:
|
|
115
|
-
provider: "cli/claude"
|
|
116
|
-
prompt: "You are a test agent. Reply with OK only."
|
|
117
|
-
`.trim());
|
|
118
|
-
|
|
119
|
-
console.log(`\n[3/3] Running smoke checks from ${testDir}...\n`);
|
|
120
|
-
|
|
121
|
-
// ── Check 1: version ──────────────────────────────────────────────────────────
|
|
122
|
-
{
|
|
123
|
-
const r = run('crewx', ['--version'], { cwd: testDir, env });
|
|
124
|
-
const actual = r.stdout.trim();
|
|
125
|
-
if (actual === expectedVersion) {
|
|
126
|
-
pass(`crewx --version == ${expectedVersion}`);
|
|
127
|
-
} else {
|
|
128
|
-
fail('crewx --version', `expected "${expectedVersion}", got "${actual}"`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// ── Check 2: help ─────────────────────────────────────────────────────────────
|
|
133
|
-
{
|
|
134
|
-
const r = run('crewx', ['--help'], { cwd: testDir, env });
|
|
135
|
-
if (r.status === 0 && r.stdout.includes(expectedVersion)) {
|
|
136
|
-
pass('crewx --help shows correct version');
|
|
137
|
-
} else {
|
|
138
|
-
fail('crewx --help', `exit=${r.status}, version in output: ${r.stdout.includes(expectedVersion)}`);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// ── Check 3: agent list ───────────────────────────────────────────────────────
|
|
143
|
-
{
|
|
144
|
-
const r = run('crewx', ['agent', 'list'], { cwd: testDir, env });
|
|
145
|
-
if (r.status === 0) {
|
|
146
|
-
pass('crewx agent list');
|
|
147
|
-
} else {
|
|
148
|
-
fail('crewx agent list', r.stderr.slice(0, 200));
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// ── Check 4: skill list ───────────────────────────────────────────────────────
|
|
153
|
-
{
|
|
154
|
-
const r = run('crewx', ['skill', 'list'], { cwd: testDir, env });
|
|
155
|
-
if (r.status === 0 || r.stdout.includes('No skills')) {
|
|
156
|
-
pass('crewx skill list');
|
|
157
|
-
} else {
|
|
158
|
-
fail('crewx skill list', r.stderr.slice(0, 200));
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// ── Check 5: memory --help ────────────────────────────────────────────────────
|
|
163
|
-
{
|
|
164
|
-
const r = run('crewx', ['memory', '--help'], { cwd: testDir, env });
|
|
165
|
-
if (r.status === 0) {
|
|
166
|
-
pass('crewx memory --help');
|
|
167
|
-
} else {
|
|
168
|
-
fail('crewx memory --help', r.stderr.slice(0, 200));
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// ── Check 6: workflow --help ──────────────────────────────────────────────────
|
|
173
|
-
{
|
|
174
|
-
const r = run('crewx', ['workflow', '--help'], { cwd: testDir, env });
|
|
175
|
-
if (r.status === 0) {
|
|
176
|
-
pass('crewx workflow --help');
|
|
177
|
-
} else {
|
|
178
|
-
fail('crewx workflow --help', r.stderr.slice(0, 200));
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// ── Check 7: search ───────────────────────────────────────────────────────────
|
|
183
|
-
{
|
|
184
|
-
const r = run('crewx', ['search', 'test', '.'], { cwd: testDir, env, timeout: 10000 });
|
|
185
|
-
const noError = !r.stderr.includes('ERR_MODULE_NOT_FOUND') && !r.stderr.includes('is not a function');
|
|
186
|
-
if (noError) {
|
|
187
|
-
pass('crewx search (no crash)');
|
|
188
|
-
} else {
|
|
189
|
-
fail('crewx search', r.stderr.slice(0, 300));
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// ── Check 8: crewx q "hi" (SDK loading check) ────────────────────────────────
|
|
194
|
-
{
|
|
195
|
-
const r = run('crewx', ['q', 'hi'], { cwd: testDir, env, timeout: 15000 });
|
|
196
|
-
const combined = r.stdout + r.stderr;
|
|
197
|
-
const sdkErrors = ['ERR_MODULE_NOT_FOUND', 'is not a function', 'Cannot find module'];
|
|
198
|
-
const hasError = sdkErrors.some(e => combined.includes(e));
|
|
199
|
-
if (!hasError) {
|
|
200
|
-
pass('crewx q "hi" (no SDK import errors)');
|
|
201
|
-
} else {
|
|
202
|
-
const found = sdkErrors.find(e => combined.includes(e));
|
|
203
|
-
fail('crewx q "hi"', `SDK error: ${found}`);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// ── Check 9: crewx web startup ────────────────────────────────────────────────
|
|
208
|
-
if (!skipWeb) {
|
|
209
|
-
const r = run('crewx', [], { cwd: testDir, env, timeout: 10000 });
|
|
210
|
-
const combined = r.stdout + r.stderr;
|
|
211
|
-
const hasModuleError = combined.includes('ERR_MODULE_NOT_FOUND');
|
|
212
|
-
if (!hasModuleError) {
|
|
213
|
-
pass('crewx (web) startup — no ERR_MODULE_NOT_FOUND');
|
|
214
|
-
} else {
|
|
215
|
-
fail('crewx (web) startup', 'ERR_MODULE_NOT_FOUND detected');
|
|
216
|
-
}
|
|
217
|
-
} else {
|
|
218
|
-
console.log(' - SKIP crewx web startup (--skip-web)');
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// ── Check 10: SDK templates load (CJS require) ───────────────────────────────
|
|
222
|
-
{
|
|
223
|
-
const sdkDir = join(installDir, 'node_modules', '@crewx', 'sdk');
|
|
224
|
-
const r = run('node', ['-e', `
|
|
225
|
-
const { existsSync, readdirSync } = require('fs');
|
|
226
|
-
const { join, dirname } = require('path');
|
|
227
|
-
const sdkMain = require.resolve('${sdkDir.replace(/\\/g, '/')}/dist/index.js');
|
|
228
|
-
const distDir = dirname(sdkMain);
|
|
229
|
-
const templatesPath = join(distDir, '../templates/agents');
|
|
230
|
-
if (!existsSync(templatesPath)) { process.stdout.write('FAIL: ' + templatesPath + '\\n'); process.exit(1); }
|
|
231
|
-
const files = readdirSync(templatesPath);
|
|
232
|
-
if (!files.includes('default.yaml')) { process.stdout.write('FAIL: default.yaml missing\\n'); process.exit(1); }
|
|
233
|
-
process.stdout.write('OK: ' + templatesPath + '\\n');
|
|
234
|
-
`], { cwd: testDir, env, timeout: 5000 });
|
|
235
|
-
if (r.status === 0 && r.stdout.includes('OK:')) {
|
|
236
|
-
pass('SDK templates load (CJS) — dist/index.js → ../templates/agents ✓');
|
|
237
|
-
} else {
|
|
238
|
-
fail('SDK templates load (CJS)', r.stderr.slice(0, 200) || r.stdout.slice(0, 200));
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// ── Check 11: SDK templates load (ESM import — same dist/index.js via exports) ─
|
|
243
|
-
{
|
|
244
|
-
const sdkDir = join(installDir, 'node_modules', '@crewx', 'sdk');
|
|
245
|
-
const r = run('node', ['--input-type=module', '--eval', `
|
|
246
|
-
import { existsSync, readdirSync } from 'fs';
|
|
247
|
-
import { join, dirname } from 'path';
|
|
248
|
-
import { createRequire } from 'module';
|
|
249
|
-
const require = createRequire(import.meta.url);
|
|
250
|
-
// "import" condition in package.json exports also points to dist/index.js (CJS)
|
|
251
|
-
const sdkMain = require.resolve('${sdkDir.replace(/\\/g, '/')}/dist/index.js');
|
|
252
|
-
const distDir = dirname(sdkMain);
|
|
253
|
-
const templatesPath = join(distDir, '../templates/agents');
|
|
254
|
-
if (!existsSync(templatesPath)) { process.stdout.write('FAIL: ' + templatesPath + '\\n'); process.exit(1); }
|
|
255
|
-
const files = readdirSync(templatesPath);
|
|
256
|
-
if (!files.includes('default.yaml')) { process.stdout.write('FAIL: default.yaml missing\\n'); process.exit(1); }
|
|
257
|
-
process.stdout.write('OK: ' + templatesPath + '\\n');
|
|
258
|
-
`], { cwd: testDir, env, timeout: 5000 });
|
|
259
|
-
if (r.status === 0 && r.stdout.includes('OK:')) {
|
|
260
|
-
pass('SDK templates load (ESM) — dist/index.js → ../templates/agents ✓');
|
|
261
|
-
} else {
|
|
262
|
-
fail('SDK templates load (ESM)', r.stderr.slice(0, 200) || r.stdout.slice(0, 200));
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// ── Cleanup ───────────────────────────────────────────────────────────────────
|
|
267
|
-
rmSync(testDir, { recursive: true, force: true });
|
|
268
|
-
rmSync(installDir, { recursive: true, force: true });
|
|
269
|
-
try { rmSync(tarball, { force: true }); } catch {}
|
|
270
|
-
|
|
271
|
-
// ── Summary ───────────────────────────────────────────────────────────────────
|
|
272
|
-
console.log('\n' + '─'.repeat(60));
|
|
273
|
-
const passed = results.filter(r => r.status === 'PASS').length;
|
|
274
|
-
const failed = results.filter(r => r.status === 'FAIL').length;
|
|
275
|
-
|
|
276
|
-
if (!anyFail) {
|
|
277
|
-
console.log(`✅ ALL PASS (${passed}/${results.length})`);
|
|
278
|
-
} else {
|
|
279
|
-
console.log(`❌ ${failed} FAIL / ${passed} PASS`);
|
|
280
|
-
results.filter(r => r.status === 'FAIL').forEach(r => {
|
|
281
|
-
console.error(` FAIL: ${r.label}${r.detail ? ' — ' + r.detail : ''}`);
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
process.exit(anyFail ? 1 : 0);
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* smoke-tarball.mjs — Tarball-isolated smoke test for crewx CLI.
|
|
4
|
+
*
|
|
5
|
+
* Packs the current project into a tarball, installs it in a temp directory
|
|
6
|
+
* (no global install), and runs a suite of smoke checks against the binary.
|
|
7
|
+
* This avoids workspace symlink false-positives that npm install -g . can produce.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node scripts/smoke-tarball.mjs [--version <expected>] [--skip-web]
|
|
11
|
+
*
|
|
12
|
+
* Flags:
|
|
13
|
+
* --version <ver> Expected crewx --version output. Defaults to package.json version.
|
|
14
|
+
* --skip-web Skip the NestJS web server startup check (useful in CI).
|
|
15
|
+
* --timeout <ms> Per-command timeout in ms. Default: 15000.
|
|
16
|
+
*
|
|
17
|
+
* Exit code: 0 = ALL PASS, 1 = one or more FAILs.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { execFileSync, spawnSync } from 'child_process';
|
|
21
|
+
import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from 'fs';
|
|
22
|
+
import { join, resolve } from 'path';
|
|
23
|
+
import { tmpdir } from 'os';
|
|
24
|
+
import { createRequire } from 'module';
|
|
25
|
+
|
|
26
|
+
const require = createRequire(import.meta.url);
|
|
27
|
+
const rootPkg = require('../package.json');
|
|
28
|
+
|
|
29
|
+
// ── CLI args ──────────────────────────────────────────────────────────────────
|
|
30
|
+
const args = process.argv.slice(2);
|
|
31
|
+
const getArg = (flag) => {
|
|
32
|
+
const i = args.indexOf(flag);
|
|
33
|
+
return i !== -1 ? args[i + 1] : null;
|
|
34
|
+
};
|
|
35
|
+
const hasFlag = (flag) => args.includes(flag);
|
|
36
|
+
|
|
37
|
+
const expectedVersion = getArg('--version') ?? rootPkg.version;
|
|
38
|
+
const skipWeb = hasFlag('--skip-web');
|
|
39
|
+
const cmdTimeout = parseInt(getArg('--timeout') ?? '15000', 10);
|
|
40
|
+
|
|
41
|
+
// ── Result tracking ───────────────────────────────────────────────────────────
|
|
42
|
+
const results = [];
|
|
43
|
+
let anyFail = false;
|
|
44
|
+
|
|
45
|
+
function pass(label) {
|
|
46
|
+
results.push({ label, status: 'PASS' });
|
|
47
|
+
console.log(` ✓ PASS ${label}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function fail(label, detail = '') {
|
|
51
|
+
results.push({ label, status: 'FAIL', detail });
|
|
52
|
+
console.error(` ✗ FAIL ${label}${detail ? ': ' + detail : ''}`);
|
|
53
|
+
anyFail = true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function run(cmd, cmdArgs, opts = {}) {
|
|
57
|
+
const result = spawnSync(cmd, cmdArgs, {
|
|
58
|
+
timeout: opts.timeout ?? cmdTimeout,
|
|
59
|
+
encoding: 'utf8',
|
|
60
|
+
cwd: opts.cwd,
|
|
61
|
+
env: { ...process.env, ...opts.env },
|
|
62
|
+
});
|
|
63
|
+
return {
|
|
64
|
+
stdout: result.stdout ?? '',
|
|
65
|
+
stderr: result.stderr ?? '',
|
|
66
|
+
status: result.status ?? -1,
|
|
67
|
+
timedOut: result.signal === 'SIGTERM',
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ── Step 1: Pack ──────────────────────────────────────────────────────────────
|
|
72
|
+
const packDest = tmpdir();
|
|
73
|
+
console.log('\n[1/3] Packing tarball...');
|
|
74
|
+
let tarball;
|
|
75
|
+
try {
|
|
76
|
+
const out = execFileSync('pnpm', ['pack', '--pack-destination', packDest], {
|
|
77
|
+
cwd: resolve('.'),
|
|
78
|
+
encoding: 'utf8',
|
|
79
|
+
}).trim();
|
|
80
|
+
tarball = out.split('\n').pop().trim();
|
|
81
|
+
console.log(` Tarball: ${tarball}`);
|
|
82
|
+
} catch (e) {
|
|
83
|
+
console.error('FATAL: pnpm pack failed:', e.message);
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ── Step 2: Install in temp dir ───────────────────────────────────────────────
|
|
88
|
+
const installDir = mkdtempSync(join(tmpdir(), 'crewx-smoke-install-'));
|
|
89
|
+
console.log(`\n[2/3] Installing into ${installDir}...`);
|
|
90
|
+
try {
|
|
91
|
+
execFileSync('npm', ['install', tarball], {
|
|
92
|
+
cwd: installDir,
|
|
93
|
+
encoding: 'utf8',
|
|
94
|
+
stdio: 'inherit',
|
|
95
|
+
});
|
|
96
|
+
} catch (e) {
|
|
97
|
+
console.error('FATAL: npm install failed:', e.message);
|
|
98
|
+
rmSync(installDir, { recursive: true, force: true });
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const binDir = join(installDir, 'node_modules', '.bin');
|
|
103
|
+
const env = { ...process.env, PATH: `${binDir}:${process.env.PATH}` };
|
|
104
|
+
|
|
105
|
+
// ── Step 3: Test dir (clean workspace) ────────────────────────────────────────
|
|
106
|
+
const testDir = mkdtempSync(join(tmpdir(), 'crewx-smoke-test-'));
|
|
107
|
+
mkdirSync(join(testDir, '.git')); // minimal git marker
|
|
108
|
+
|
|
109
|
+
// Minimal crewx.yaml for agent tests
|
|
110
|
+
writeFileSync(join(testDir, 'crewx.yaml'), `
|
|
111
|
+
agents:
|
|
112
|
+
- id: crewx
|
|
113
|
+
name: "CrewX Assistant"
|
|
114
|
+
inline:
|
|
115
|
+
provider: "cli/claude"
|
|
116
|
+
prompt: "You are a test agent. Reply with OK only."
|
|
117
|
+
`.trim());
|
|
118
|
+
|
|
119
|
+
console.log(`\n[3/3] Running smoke checks from ${testDir}...\n`);
|
|
120
|
+
|
|
121
|
+
// ── Check 1: version ──────────────────────────────────────────────────────────
|
|
122
|
+
{
|
|
123
|
+
const r = run('crewx', ['--version'], { cwd: testDir, env });
|
|
124
|
+
const actual = r.stdout.trim();
|
|
125
|
+
if (actual === expectedVersion) {
|
|
126
|
+
pass(`crewx --version == ${expectedVersion}`);
|
|
127
|
+
} else {
|
|
128
|
+
fail('crewx --version', `expected "${expectedVersion}", got "${actual}"`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ── Check 2: help ─────────────────────────────────────────────────────────────
|
|
133
|
+
{
|
|
134
|
+
const r = run('crewx', ['--help'], { cwd: testDir, env });
|
|
135
|
+
if (r.status === 0 && r.stdout.includes(expectedVersion)) {
|
|
136
|
+
pass('crewx --help shows correct version');
|
|
137
|
+
} else {
|
|
138
|
+
fail('crewx --help', `exit=${r.status}, version in output: ${r.stdout.includes(expectedVersion)}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ── Check 3: agent list ───────────────────────────────────────────────────────
|
|
143
|
+
{
|
|
144
|
+
const r = run('crewx', ['agent', 'list'], { cwd: testDir, env });
|
|
145
|
+
if (r.status === 0) {
|
|
146
|
+
pass('crewx agent list');
|
|
147
|
+
} else {
|
|
148
|
+
fail('crewx agent list', r.stderr.slice(0, 200));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ── Check 4: skill list ───────────────────────────────────────────────────────
|
|
153
|
+
{
|
|
154
|
+
const r = run('crewx', ['skill', 'list'], { cwd: testDir, env });
|
|
155
|
+
if (r.status === 0 || r.stdout.includes('No skills')) {
|
|
156
|
+
pass('crewx skill list');
|
|
157
|
+
} else {
|
|
158
|
+
fail('crewx skill list', r.stderr.slice(0, 200));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ── Check 5: memory --help ────────────────────────────────────────────────────
|
|
163
|
+
{
|
|
164
|
+
const r = run('crewx', ['memory', '--help'], { cwd: testDir, env });
|
|
165
|
+
if (r.status === 0) {
|
|
166
|
+
pass('crewx memory --help');
|
|
167
|
+
} else {
|
|
168
|
+
fail('crewx memory --help', r.stderr.slice(0, 200));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ── Check 6: workflow --help ──────────────────────────────────────────────────
|
|
173
|
+
{
|
|
174
|
+
const r = run('crewx', ['workflow', '--help'], { cwd: testDir, env });
|
|
175
|
+
if (r.status === 0) {
|
|
176
|
+
pass('crewx workflow --help');
|
|
177
|
+
} else {
|
|
178
|
+
fail('crewx workflow --help', r.stderr.slice(0, 200));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ── Check 7: search ───────────────────────────────────────────────────────────
|
|
183
|
+
{
|
|
184
|
+
const r = run('crewx', ['search', 'test', '.'], { cwd: testDir, env, timeout: 10000 });
|
|
185
|
+
const noError = !r.stderr.includes('ERR_MODULE_NOT_FOUND') && !r.stderr.includes('is not a function');
|
|
186
|
+
if (noError) {
|
|
187
|
+
pass('crewx search (no crash)');
|
|
188
|
+
} else {
|
|
189
|
+
fail('crewx search', r.stderr.slice(0, 300));
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ── Check 8: crewx q "hi" (SDK loading check) ────────────────────────────────
|
|
194
|
+
{
|
|
195
|
+
const r = run('crewx', ['q', 'hi'], { cwd: testDir, env, timeout: 15000 });
|
|
196
|
+
const combined = r.stdout + r.stderr;
|
|
197
|
+
const sdkErrors = ['ERR_MODULE_NOT_FOUND', 'is not a function', 'Cannot find module'];
|
|
198
|
+
const hasError = sdkErrors.some(e => combined.includes(e));
|
|
199
|
+
if (!hasError) {
|
|
200
|
+
pass('crewx q "hi" (no SDK import errors)');
|
|
201
|
+
} else {
|
|
202
|
+
const found = sdkErrors.find(e => combined.includes(e));
|
|
203
|
+
fail('crewx q "hi"', `SDK error: ${found}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ── Check 9: crewx web startup ────────────────────────────────────────────────
|
|
208
|
+
if (!skipWeb) {
|
|
209
|
+
const r = run('crewx', [], { cwd: testDir, env, timeout: 10000 });
|
|
210
|
+
const combined = r.stdout + r.stderr;
|
|
211
|
+
const hasModuleError = combined.includes('ERR_MODULE_NOT_FOUND');
|
|
212
|
+
if (!hasModuleError) {
|
|
213
|
+
pass('crewx (web) startup — no ERR_MODULE_NOT_FOUND');
|
|
214
|
+
} else {
|
|
215
|
+
fail('crewx (web) startup', 'ERR_MODULE_NOT_FOUND detected');
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
console.log(' - SKIP crewx web startup (--skip-web)');
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ── Check 10: SDK templates load (CJS require) ───────────────────────────────
|
|
222
|
+
{
|
|
223
|
+
const sdkDir = join(installDir, 'node_modules', '@crewx', 'sdk');
|
|
224
|
+
const r = run('node', ['-e', `
|
|
225
|
+
const { existsSync, readdirSync } = require('fs');
|
|
226
|
+
const { join, dirname } = require('path');
|
|
227
|
+
const sdkMain = require.resolve('${sdkDir.replace(/\\/g, '/')}/dist/index.js');
|
|
228
|
+
const distDir = dirname(sdkMain);
|
|
229
|
+
const templatesPath = join(distDir, '../templates/agents');
|
|
230
|
+
if (!existsSync(templatesPath)) { process.stdout.write('FAIL: ' + templatesPath + '\\n'); process.exit(1); }
|
|
231
|
+
const files = readdirSync(templatesPath);
|
|
232
|
+
if (!files.includes('default.yaml')) { process.stdout.write('FAIL: default.yaml missing\\n'); process.exit(1); }
|
|
233
|
+
process.stdout.write('OK: ' + templatesPath + '\\n');
|
|
234
|
+
`], { cwd: testDir, env, timeout: 5000 });
|
|
235
|
+
if (r.status === 0 && r.stdout.includes('OK:')) {
|
|
236
|
+
pass('SDK templates load (CJS) — dist/index.js → ../templates/agents ✓');
|
|
237
|
+
} else {
|
|
238
|
+
fail('SDK templates load (CJS)', r.stderr.slice(0, 200) || r.stdout.slice(0, 200));
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// ── Check 11: SDK templates load (ESM import — same dist/index.js via exports) ─
|
|
243
|
+
{
|
|
244
|
+
const sdkDir = join(installDir, 'node_modules', '@crewx', 'sdk');
|
|
245
|
+
const r = run('node', ['--input-type=module', '--eval', `
|
|
246
|
+
import { existsSync, readdirSync } from 'fs';
|
|
247
|
+
import { join, dirname } from 'path';
|
|
248
|
+
import { createRequire } from 'module';
|
|
249
|
+
const require = createRequire(import.meta.url);
|
|
250
|
+
// "import" condition in package.json exports also points to dist/index.js (CJS)
|
|
251
|
+
const sdkMain = require.resolve('${sdkDir.replace(/\\/g, '/')}/dist/index.js');
|
|
252
|
+
const distDir = dirname(sdkMain);
|
|
253
|
+
const templatesPath = join(distDir, '../templates/agents');
|
|
254
|
+
if (!existsSync(templatesPath)) { process.stdout.write('FAIL: ' + templatesPath + '\\n'); process.exit(1); }
|
|
255
|
+
const files = readdirSync(templatesPath);
|
|
256
|
+
if (!files.includes('default.yaml')) { process.stdout.write('FAIL: default.yaml missing\\n'); process.exit(1); }
|
|
257
|
+
process.stdout.write('OK: ' + templatesPath + '\\n');
|
|
258
|
+
`], { cwd: testDir, env, timeout: 5000 });
|
|
259
|
+
if (r.status === 0 && r.stdout.includes('OK:')) {
|
|
260
|
+
pass('SDK templates load (ESM) — dist/index.js → ../templates/agents ✓');
|
|
261
|
+
} else {
|
|
262
|
+
fail('SDK templates load (ESM)', r.stderr.slice(0, 200) || r.stdout.slice(0, 200));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// ── Cleanup ───────────────────────────────────────────────────────────────────
|
|
267
|
+
rmSync(testDir, { recursive: true, force: true });
|
|
268
|
+
rmSync(installDir, { recursive: true, force: true });
|
|
269
|
+
try { rmSync(tarball, { force: true }); } catch {}
|
|
270
|
+
|
|
271
|
+
// ── Summary ───────────────────────────────────────────────────────────────────
|
|
272
|
+
console.log('\n' + '─'.repeat(60));
|
|
273
|
+
const passed = results.filter(r => r.status === 'PASS').length;
|
|
274
|
+
const failed = results.filter(r => r.status === 'FAIL').length;
|
|
275
|
+
|
|
276
|
+
if (!anyFail) {
|
|
277
|
+
console.log(`✅ ALL PASS (${passed}/${results.length})`);
|
|
278
|
+
} else {
|
|
279
|
+
console.log(`❌ ${failed} FAIL / ${passed} PASS`);
|
|
280
|
+
results.filter(r => r.status === 'FAIL').forEach(r => {
|
|
281
|
+
console.error(` FAIL: ${r.label}${r.detail ? ' — ' + r.detail : ''}`);
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
process.exit(anyFail ? 1 : 0);
|