humanbehavior-js 0.7.0 → 0.8.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/packages/browser/dist/cjs/index.js +6 -6
- package/packages/browser/dist/cjs/index.js.map +1 -1
- package/packages/browser/dist/esm/index.js +4 -4
- package/packages/browser/dist/esm/index.js.map +1 -1
- package/packages/browser/dist/index.min.js +1 -1
- package/packages/browser/dist/index.min.js.map +1 -1
- package/packages/core/dist/index.js +1 -1
- package/packages/core/dist/index.js.map +1 -1
- package/packages/core/dist/index.mjs +1 -1
- package/packages/core/dist/index.mjs.map +1 -1
- package/packages/core/dist/tracker.d.ts +12 -0
- package/packages/core/dist/tracker.d.ts.map +1 -1
- package/packages/react/dist/index.js +1 -1
- package/packages/react/dist/index.js.map +1 -1
- package/packages/react/dist/index.mjs +1 -1
- package/packages/react/dist/index.mjs.map +1 -1
- package/packages/wizard/dist/agent/claude-agent-installer.d.ts +17 -0
- package/packages/wizard/dist/agent/claude-agent-installer.d.ts.map +1 -0
- package/packages/wizard/dist/ai/ai-install-wizard.d.ts +2 -2
- package/packages/wizard/dist/ai/ai-install-wizard.d.ts.map +1 -1
- package/packages/wizard/dist/ai/manual-framework-wizard.d.ts +2 -2
- package/packages/wizard/dist/ai/manual-framework-wizard.d.ts.map +1 -1
- package/packages/wizard/dist/cli/ai-auto-install.d.ts +6 -0
- package/packages/wizard/dist/cli/ai-auto-install.d.ts.map +1 -1
- package/packages/wizard/dist/cli/ai-auto-install.js +641 -81
- package/packages/wizard/dist/cli/ai-auto-install.js.map +1 -1
- package/packages/wizard/dist/cli/auto-install.d.ts +4 -0
- package/packages/wizard/dist/cli/auto-install.d.ts.map +1 -1
- package/packages/wizard/dist/cli/auto-install.js +314 -56
- package/packages/wizard/dist/cli/auto-install.js.map +1 -1
- package/packages/wizard/dist/core/install-wizard.d.ts +51 -3
- package/packages/wizard/dist/core/install-wizard.d.ts.map +1 -1
- package/packages/wizard/dist/index.d.ts +2 -0
- package/packages/wizard/dist/index.d.ts.map +1 -1
- package/packages/wizard/dist/index.js +1 -1
- package/packages/wizard/dist/index.js.map +1 -1
- package/packages/wizard/dist/index.mjs +1 -1
- package/packages/wizard/dist/index.mjs.map +1 -1
|
@@ -9,9 +9,12 @@
|
|
|
9
9
|
*/
|
|
10
10
|
interface CLIOptions {
|
|
11
11
|
apiKey?: string;
|
|
12
|
+
apiKeyFile?: string;
|
|
13
|
+
reportJson?: string;
|
|
12
14
|
projectPath?: string;
|
|
13
15
|
yes?: boolean;
|
|
14
16
|
dryRun?: boolean;
|
|
17
|
+
upgrade?: boolean;
|
|
15
18
|
}
|
|
16
19
|
declare class AutoInstallCLI {
|
|
17
20
|
private options;
|
|
@@ -20,6 +23,7 @@ declare class AutoInstallCLI {
|
|
|
20
23
|
private getApiKey;
|
|
21
24
|
private confirmInstallation;
|
|
22
25
|
private displayResults;
|
|
26
|
+
private writeReport;
|
|
23
27
|
}
|
|
24
28
|
export { AutoInstallCLI };
|
|
25
29
|
//# sourceMappingURL=auto-install.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auto-install.d.ts","sourceRoot":"","sources":["../../src/cli/auto-install.ts"],"names":[],"mappings":";AAEA;;;;;;;GAOG;AAOH,UAAU,UAAU;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"auto-install.d.ts","sourceRoot":"","sources":["../../src/cli/auto-install.ts"],"names":[],"mappings":";AAEA;;;;;;;GAOG;AAOH,UAAU,UAAU;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,cAAM,cAAc;IAClB,OAAO,CAAC,OAAO,CAAa;gBAEhB,OAAO,EAAE,UAAU;IAIzB,GAAG;YA6CK,SAAS;YA8BT,mBAAmB;IAQjC,OAAO,CAAC,cAAc;IA+CtB,OAAO,CAAC,WAAW;CAoBpB;AAkFD,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -10,12 +10,18 @@ import * as clack from '@clack/prompts';
|
|
|
10
10
|
* This wizard automatically detects the user's framework and modifies their codebase
|
|
11
11
|
* to integrate the SDK with minimal user intervention.
|
|
12
12
|
*/
|
|
13
|
+
const MIN_SUPPORTED_SDK_VERSION = '0.7.0';
|
|
14
|
+
const TESTED_SDK_VERSION = '0.7.0';
|
|
13
15
|
class AutoInstallationWizard {
|
|
14
|
-
constructor(apiKey, projectRoot = process.cwd()) {
|
|
16
|
+
constructor(apiKey, projectRoot = process.cwd(), options = {}) {
|
|
15
17
|
this.framework = null;
|
|
16
18
|
this.manualNotes = [];
|
|
17
19
|
this.apiKey = apiKey;
|
|
18
|
-
this.projectRoot = projectRoot;
|
|
20
|
+
this.projectRoot = path.resolve(projectRoot);
|
|
21
|
+
this.options = options;
|
|
22
|
+
}
|
|
23
|
+
setAgentPlan(agentPlan) {
|
|
24
|
+
this.options.agentPlan = agentPlan;
|
|
19
25
|
}
|
|
20
26
|
/**
|
|
21
27
|
* Simple version comparison utility
|
|
@@ -46,11 +52,13 @@ class AutoInstallationWizard {
|
|
|
46
52
|
try {
|
|
47
53
|
// Step 1: Detect framework
|
|
48
54
|
this.framework = await this.detectFramework();
|
|
49
|
-
// Step 2:
|
|
50
|
-
await this.installPackage();
|
|
55
|
+
// Step 2: Plan and optionally install package
|
|
56
|
+
const installPlan = await this.installPackage();
|
|
51
57
|
// Step 3: Generate and apply code modifications
|
|
52
58
|
const modifications = await this.generateModifications();
|
|
53
|
-
|
|
59
|
+
if (!this.options.dryRun) {
|
|
60
|
+
await this.applyModifications(modifications);
|
|
61
|
+
}
|
|
54
62
|
// Step 4: Generate next steps
|
|
55
63
|
const nextSteps = this.generateNextSteps();
|
|
56
64
|
return {
|
|
@@ -58,7 +66,10 @@ class AutoInstallationWizard {
|
|
|
58
66
|
framework: this.framework,
|
|
59
67
|
modifications,
|
|
60
68
|
errors: [],
|
|
61
|
-
nextSteps
|
|
69
|
+
nextSteps,
|
|
70
|
+
dryRun: this.options.dryRun,
|
|
71
|
+
installPlan,
|
|
72
|
+
agentPlan: this.options.agentPlan
|
|
62
73
|
};
|
|
63
74
|
}
|
|
64
75
|
catch (error) {
|
|
@@ -67,7 +78,9 @@ class AutoInstallationWizard {
|
|
|
67
78
|
framework: this.framework || { name: 'unknown', type: 'vanilla' },
|
|
68
79
|
modifications: [],
|
|
69
80
|
errors: [error instanceof Error ? error.message : 'Unknown error'],
|
|
70
|
-
nextSteps: []
|
|
81
|
+
nextSteps: [],
|
|
82
|
+
dryRun: this.options.dryRun,
|
|
83
|
+
agentPlan: this.options.agentPlan
|
|
71
84
|
};
|
|
72
85
|
}
|
|
73
86
|
}
|
|
@@ -243,38 +256,212 @@ class AutoInstallationWizard {
|
|
|
243
256
|
else if (dependencies.rollup) {
|
|
244
257
|
framework.bundler = 'rollup';
|
|
245
258
|
}
|
|
246
|
-
|
|
247
|
-
if (fs.existsSync(path.join(this.projectRoot, 'yarn.lock'))) {
|
|
248
|
-
framework.packageManager = 'yarn';
|
|
249
|
-
}
|
|
250
|
-
else if (fs.existsSync(path.join(this.projectRoot, 'pnpm-lock.yaml'))) {
|
|
251
|
-
framework.packageManager = 'pnpm';
|
|
252
|
-
}
|
|
253
|
-
else {
|
|
254
|
-
framework.packageManager = 'npm';
|
|
255
|
-
}
|
|
259
|
+
framework.packageManager = this.detectPackageManager(this.projectRoot);
|
|
256
260
|
return framework;
|
|
257
261
|
}
|
|
258
262
|
/**
|
|
259
263
|
* Install the SDK package with latest version range
|
|
260
264
|
*/
|
|
261
265
|
async installPackage() {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
: this.framework?.packageManager === 'pnpm'
|
|
266
|
-
? 'pnpm add humanbehavior-js@latest'
|
|
267
|
-
: 'npm install humanbehavior-js@latest';
|
|
268
|
-
// Add legacy peer deps flag for npm to handle dependency conflicts
|
|
269
|
-
if (this.framework?.packageManager !== 'yarn' && this.framework?.packageManager !== 'pnpm') {
|
|
270
|
-
command += ' --legacy-peer-deps';
|
|
266
|
+
const plan = this.planPackageInstall();
|
|
267
|
+
if (!plan.shouldInstall || this.options.skipInstall || this.options.dryRun) {
|
|
268
|
+
return plan;
|
|
271
269
|
}
|
|
272
270
|
try {
|
|
273
|
-
execSync(command, { cwd:
|
|
271
|
+
execSync(plan.command, { cwd: plan.cwd, stdio: 'inherit' });
|
|
274
272
|
}
|
|
275
273
|
catch (error) {
|
|
276
274
|
throw new Error(`Failed to install humanbehavior-js: ${error}`);
|
|
277
275
|
}
|
|
276
|
+
return plan;
|
|
277
|
+
}
|
|
278
|
+
planPackageInstall() {
|
|
279
|
+
const packageManager = this.framework?.packageManager || this.detectPackageManager(this.projectRoot);
|
|
280
|
+
const workspaceRoot = this.findWorkspaceRoot(this.projectRoot);
|
|
281
|
+
const packageJson = this.readPackageJson(this.projectRoot);
|
|
282
|
+
const packageName = packageJson?.name;
|
|
283
|
+
const installedVersion = this.getInstalledSDKVersion(packageJson);
|
|
284
|
+
const alreadyInstalled = installedVersion !== undefined;
|
|
285
|
+
const versionStatus = this.getSDKVersionStatus(installedVersion);
|
|
286
|
+
const isWorkspace = workspaceRoot !== this.projectRoot;
|
|
287
|
+
const targetRef = packageName || path.relative(workspaceRoot, this.projectRoot);
|
|
288
|
+
const sdkPackage = 'humanbehavior-js@latest';
|
|
289
|
+
const shouldInstall = !alreadyInstalled ||
|
|
290
|
+
(versionStatus === 'stale' && this.options.upgrade === true);
|
|
291
|
+
const command = this.buildInstallCommand(packageManager, sdkPackage, {
|
|
292
|
+
isWorkspace,
|
|
293
|
+
targetRef
|
|
294
|
+
});
|
|
295
|
+
return {
|
|
296
|
+
packageName: 'humanbehavior-js',
|
|
297
|
+
packageManager,
|
|
298
|
+
command,
|
|
299
|
+
cwd: isWorkspace ? workspaceRoot : this.projectRoot,
|
|
300
|
+
targetPackagePath: this.projectRoot,
|
|
301
|
+
workspaceRoot,
|
|
302
|
+
isWorkspace,
|
|
303
|
+
alreadyInstalled,
|
|
304
|
+
installedVersion,
|
|
305
|
+
minimumSupportedVersion: MIN_SUPPORTED_SDK_VERSION,
|
|
306
|
+
testedVersion: TESTED_SDK_VERSION,
|
|
307
|
+
versionStatus,
|
|
308
|
+
shouldInstall,
|
|
309
|
+
reason: this.getInstallPlanReason({
|
|
310
|
+
alreadyInstalled,
|
|
311
|
+
installedVersion,
|
|
312
|
+
versionStatus,
|
|
313
|
+
shouldInstall,
|
|
314
|
+
isWorkspace,
|
|
315
|
+
targetRef
|
|
316
|
+
})
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
getInstallPlanReason(options) {
|
|
320
|
+
if (options.alreadyInstalled) {
|
|
321
|
+
switch (options.versionStatus) {
|
|
322
|
+
case 'compatible':
|
|
323
|
+
return `humanbehavior-js is already compatible (${options.installedVersion})`;
|
|
324
|
+
case 'stale':
|
|
325
|
+
return options.shouldInstall
|
|
326
|
+
? `humanbehavior-js ${options.installedVersion} is below ${MIN_SUPPORTED_SDK_VERSION}; upgrade requested`
|
|
327
|
+
: `humanbehavior-js ${options.installedVersion} is below ${MIN_SUPPORTED_SDK_VERSION}; rerun with --upgrade to update`;
|
|
328
|
+
case 'newer':
|
|
329
|
+
return `humanbehavior-js ${options.installedVersion} is newer than tested ${TESTED_SDK_VERSION}; keeping existing version`;
|
|
330
|
+
case 'unknown':
|
|
331
|
+
return `humanbehavior-js is already declared with a non-semver range (${options.installedVersion}); keeping existing version`;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return options.isWorkspace
|
|
335
|
+
? `Install scoped to workspace package ${options.targetRef}`
|
|
336
|
+
: 'Install in selected project package';
|
|
337
|
+
}
|
|
338
|
+
buildInstallCommand(packageManager, sdkPackage, options) {
|
|
339
|
+
if (options.isWorkspace) {
|
|
340
|
+
switch (packageManager) {
|
|
341
|
+
case 'pnpm':
|
|
342
|
+
return `pnpm --filter ${this.shellQuote(options.targetRef)} add ${sdkPackage}`;
|
|
343
|
+
case 'yarn':
|
|
344
|
+
return `yarn workspace ${this.shellQuote(options.targetRef)} add ${sdkPackage}`;
|
|
345
|
+
case 'bun':
|
|
346
|
+
return `bun add ${sdkPackage} --cwd ${this.shellQuote(options.targetRef)}`;
|
|
347
|
+
case 'npm':
|
|
348
|
+
default:
|
|
349
|
+
return `npm install ${sdkPackage} --workspace ${this.shellQuote(options.targetRef)} --legacy-peer-deps`;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
switch (packageManager) {
|
|
353
|
+
case 'pnpm':
|
|
354
|
+
return `pnpm add ${sdkPackage}`;
|
|
355
|
+
case 'yarn':
|
|
356
|
+
return `yarn add ${sdkPackage}`;
|
|
357
|
+
case 'bun':
|
|
358
|
+
return `bun add ${sdkPackage}`;
|
|
359
|
+
case 'npm':
|
|
360
|
+
default:
|
|
361
|
+
return `npm install ${sdkPackage} --legacy-peer-deps`;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
detectPackageManager(startDir) {
|
|
365
|
+
const root = this.findWorkspaceRoot(startDir);
|
|
366
|
+
const lockfileChecks = [
|
|
367
|
+
['pnpm-lock.yaml', 'pnpm'],
|
|
368
|
+
['yarn.lock', 'yarn'],
|
|
369
|
+
['bun.lockb', 'bun'],
|
|
370
|
+
['bun.lock', 'bun'],
|
|
371
|
+
['package-lock.json', 'npm'],
|
|
372
|
+
['npm-shrinkwrap.json', 'npm']
|
|
373
|
+
];
|
|
374
|
+
for (const dir of [startDir, root]) {
|
|
375
|
+
for (const [lockfile, packageManager] of lockfileChecks) {
|
|
376
|
+
if (fs.existsSync(path.join(dir, lockfile))) {
|
|
377
|
+
return packageManager;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
const packageJson = this.readPackageJson(root) || this.readPackageJson(startDir);
|
|
382
|
+
const packageManager = packageJson?.packageManager;
|
|
383
|
+
if (typeof packageManager === 'string') {
|
|
384
|
+
if (packageManager.startsWith('pnpm@'))
|
|
385
|
+
return 'pnpm';
|
|
386
|
+
if (packageManager.startsWith('yarn@'))
|
|
387
|
+
return 'yarn';
|
|
388
|
+
if (packageManager.startsWith('bun@'))
|
|
389
|
+
return 'bun';
|
|
390
|
+
if (packageManager.startsWith('npm@'))
|
|
391
|
+
return 'npm';
|
|
392
|
+
}
|
|
393
|
+
return 'npm';
|
|
394
|
+
}
|
|
395
|
+
findWorkspaceRoot(startDir) {
|
|
396
|
+
let current = path.resolve(startDir);
|
|
397
|
+
let bestRoot = current;
|
|
398
|
+
while (true) {
|
|
399
|
+
const packageJson = this.readPackageJson(current);
|
|
400
|
+
const hasWorkspacePackageJson = !!packageJson &&
|
|
401
|
+
(Array.isArray(packageJson.workspaces) ||
|
|
402
|
+
(packageJson.workspaces && Array.isArray(packageJson.workspaces.packages)));
|
|
403
|
+
const hasPnpmWorkspace = fs.existsSync(path.join(current, 'pnpm-workspace.yaml'));
|
|
404
|
+
if (hasWorkspacePackageJson || hasPnpmWorkspace) {
|
|
405
|
+
bestRoot = current;
|
|
406
|
+
}
|
|
407
|
+
const parent = path.dirname(current);
|
|
408
|
+
if (parent === current)
|
|
409
|
+
break;
|
|
410
|
+
current = parent;
|
|
411
|
+
}
|
|
412
|
+
return bestRoot;
|
|
413
|
+
}
|
|
414
|
+
readPackageJson(dir) {
|
|
415
|
+
const packageJsonPath = path.join(dir, 'package.json');
|
|
416
|
+
if (!fs.existsSync(packageJsonPath))
|
|
417
|
+
return null;
|
|
418
|
+
try {
|
|
419
|
+
return JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
getInstalledSDKVersion(packageJson) {
|
|
426
|
+
if (!packageJson)
|
|
427
|
+
return undefined;
|
|
428
|
+
const dependencyFields = [
|
|
429
|
+
'dependencies',
|
|
430
|
+
'devDependencies',
|
|
431
|
+
'peerDependencies',
|
|
432
|
+
'optionalDependencies'
|
|
433
|
+
];
|
|
434
|
+
for (const field of dependencyFields) {
|
|
435
|
+
const version = packageJson[field]?.['humanbehavior-js'];
|
|
436
|
+
if (typeof version === 'string') {
|
|
437
|
+
return version;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return undefined;
|
|
441
|
+
}
|
|
442
|
+
getSDKVersionStatus(installedVersion) {
|
|
443
|
+
if (!installedVersion)
|
|
444
|
+
return 'not-installed';
|
|
445
|
+
const normalized = this.extractSemver(installedVersion);
|
|
446
|
+
if (!normalized)
|
|
447
|
+
return 'unknown';
|
|
448
|
+
if (this.compareVersions(normalized, MIN_SUPPORTED_SDK_VERSION) < 0) {
|
|
449
|
+
return 'stale';
|
|
450
|
+
}
|
|
451
|
+
if (this.compareVersions(normalized, TESTED_SDK_VERSION) > 0) {
|
|
452
|
+
return 'newer';
|
|
453
|
+
}
|
|
454
|
+
return 'compatible';
|
|
455
|
+
}
|
|
456
|
+
extractSemver(versionRange) {
|
|
457
|
+
const match = versionRange.match(/\d+\.\d+\.\d+/);
|
|
458
|
+
return match ? match[0] : null;
|
|
459
|
+
}
|
|
460
|
+
shellQuote(value) {
|
|
461
|
+
if (/^[A-Za-z0-9_@./:-]+$/.test(value)) {
|
|
462
|
+
return value;
|
|
463
|
+
}
|
|
464
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
278
465
|
}
|
|
279
466
|
/**
|
|
280
467
|
* Generate code modifications based on framework
|
|
@@ -601,20 +788,14 @@ export function useHumanBehavior() {
|
|
|
601
788
|
return { tracker: null }
|
|
602
789
|
}
|
|
603
790
|
`;
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
action: 'create',
|
|
612
|
-
content: composableContent,
|
|
613
|
-
description: 'Created Vue composable useHumanBehavior'
|
|
614
|
-
});
|
|
615
|
-
}
|
|
791
|
+
if (!fs.existsSync(composablePath)) {
|
|
792
|
+
modifications.push({
|
|
793
|
+
filePath: composablePath,
|
|
794
|
+
action: 'create',
|
|
795
|
+
content: composableContent,
|
|
796
|
+
description: 'Created Vue composable useHumanBehavior'
|
|
797
|
+
});
|
|
616
798
|
}
|
|
617
|
-
catch { }
|
|
618
799
|
if (mainFile) {
|
|
619
800
|
const content = fs.readFileSync(mainFile, 'utf8');
|
|
620
801
|
const modifiedContent = this.injectVuePlugin(content);
|
|
@@ -667,9 +848,6 @@ export class HumanBehavior {
|
|
|
667
848
|
}
|
|
668
849
|
}
|
|
669
850
|
`;
|
|
670
|
-
if (!fs.existsSync(serviceDir)) {
|
|
671
|
-
fs.mkdirSync(serviceDir, { recursive: true });
|
|
672
|
-
}
|
|
673
851
|
if (!fs.existsSync(servicePath)) {
|
|
674
852
|
modifications.push({
|
|
675
853
|
filePath: servicePath,
|
|
@@ -681,11 +859,6 @@ export class HumanBehavior {
|
|
|
681
859
|
// Handle Angular environment files (proper Angular way)
|
|
682
860
|
const envFile = path.join(this.projectRoot, 'src', 'environments', 'environment.ts');
|
|
683
861
|
const envProdFile = path.join(this.projectRoot, 'src', 'environments', 'environment.prod.ts');
|
|
684
|
-
// Create environments directory if it doesn't exist
|
|
685
|
-
const envDir = path.dirname(envFile);
|
|
686
|
-
if (!fs.existsSync(envDir)) {
|
|
687
|
-
fs.mkdirSync(envDir, { recursive: true });
|
|
688
|
-
}
|
|
689
862
|
// Create or update development environment
|
|
690
863
|
if (fs.existsSync(envFile)) {
|
|
691
864
|
const content = fs.readFileSync(envFile, 'utf8');
|
|
@@ -868,8 +1041,15 @@ export const onClientEntry = () => {
|
|
|
868
1041
|
* Apply modifications to the codebase
|
|
869
1042
|
*/
|
|
870
1043
|
async applyModifications(modifications) {
|
|
871
|
-
|
|
872
|
-
|
|
1044
|
+
const snapshots = modifications.map((modification) => ({
|
|
1045
|
+
filePath: modification.filePath,
|
|
1046
|
+
existed: fs.existsSync(modification.filePath),
|
|
1047
|
+
content: fs.existsSync(modification.filePath)
|
|
1048
|
+
? fs.readFileSync(modification.filePath, 'utf8')
|
|
1049
|
+
: null
|
|
1050
|
+
}));
|
|
1051
|
+
try {
|
|
1052
|
+
for (const modification of modifications) {
|
|
873
1053
|
const dir = path.dirname(modification.filePath);
|
|
874
1054
|
if (!fs.existsSync(dir)) {
|
|
875
1055
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -886,8 +1066,24 @@ export const onClientEntry = () => {
|
|
|
886
1066
|
break;
|
|
887
1067
|
}
|
|
888
1068
|
}
|
|
889
|
-
|
|
890
|
-
|
|
1069
|
+
}
|
|
1070
|
+
catch (error) {
|
|
1071
|
+
this.rollbackModifications(snapshots);
|
|
1072
|
+
throw new Error(`Failed to apply modifications; rolled back file changes: ${error}`);
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
rollbackModifications(snapshots) {
|
|
1076
|
+
for (const snapshot of snapshots.reverse()) {
|
|
1077
|
+
try {
|
|
1078
|
+
if (snapshot.existed && snapshot.content !== null) {
|
|
1079
|
+
fs.writeFileSync(snapshot.filePath, snapshot.content);
|
|
1080
|
+
}
|
|
1081
|
+
else if (!snapshot.existed && fs.existsSync(snapshot.filePath)) {
|
|
1082
|
+
fs.rmSync(snapshot.filePath, { force: true });
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
catch {
|
|
1086
|
+
// Best-effort rollback. The original apply error is more actionable.
|
|
891
1087
|
}
|
|
892
1088
|
}
|
|
893
1089
|
}
|
|
@@ -895,6 +1091,13 @@ export const onClientEntry = () => {
|
|
|
895
1091
|
* Generate next steps for the user
|
|
896
1092
|
*/
|
|
897
1093
|
generateNextSteps() {
|
|
1094
|
+
if (this.options.dryRun) {
|
|
1095
|
+
return [
|
|
1096
|
+
'Dry run complete. No packages were installed and no files were changed.',
|
|
1097
|
+
'Review the planned install command and file modifications.',
|
|
1098
|
+
'Run again without --dry-run to apply these changes.'
|
|
1099
|
+
];
|
|
1100
|
+
}
|
|
898
1101
|
const steps = [
|
|
899
1102
|
'✅ SDK installed and configured automatically!',
|
|
900
1103
|
'🚀 Your app is now tracking user behavior',
|
|
@@ -1463,11 +1666,15 @@ class AutoInstallCLI {
|
|
|
1463
1666
|
// Run installation
|
|
1464
1667
|
const spinner = clack.spinner();
|
|
1465
1668
|
spinner.start('🔍 Detecting framework and applying integration...');
|
|
1466
|
-
const wizard = new AutoInstallationWizard(apiKey, projectPath
|
|
1669
|
+
const wizard = new AutoInstallationWizard(apiKey, projectPath, {
|
|
1670
|
+
dryRun: this.options.dryRun,
|
|
1671
|
+
upgrade: this.options.upgrade
|
|
1672
|
+
});
|
|
1467
1673
|
const result = await wizard.install();
|
|
1468
|
-
spinner.stop('Installation complete!');
|
|
1674
|
+
spinner.stop(this.options.dryRun ? 'Dry run complete!' : 'Installation complete!');
|
|
1469
1675
|
// Display results
|
|
1470
1676
|
this.displayResults(result);
|
|
1677
|
+
this.writeReport(result);
|
|
1471
1678
|
}
|
|
1472
1679
|
catch (error) {
|
|
1473
1680
|
clack.cancel(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
@@ -1478,6 +1685,12 @@ class AutoInstallCLI {
|
|
|
1478
1685
|
if (this.options.apiKey) {
|
|
1479
1686
|
return this.options.apiKey;
|
|
1480
1687
|
}
|
|
1688
|
+
if (this.options.apiKeyFile) {
|
|
1689
|
+
return fs.readFileSync(this.options.apiKeyFile, 'utf8').trim();
|
|
1690
|
+
}
|
|
1691
|
+
if (process.env.HB_API_KEY) {
|
|
1692
|
+
return process.env.HB_API_KEY;
|
|
1693
|
+
}
|
|
1481
1694
|
const apiKey = await clack.text({
|
|
1482
1695
|
message: 'Enter your HumanBehavior API key:',
|
|
1483
1696
|
placeholder: 'hb_...',
|
|
@@ -1500,13 +1713,27 @@ class AutoInstallCLI {
|
|
|
1500
1713
|
}
|
|
1501
1714
|
displayResults(result) {
|
|
1502
1715
|
if (result.success) {
|
|
1503
|
-
clack.outro(
|
|
1716
|
+
clack.outro(result.dryRun
|
|
1717
|
+
? 'Dry run completed successfully. No packages were installed and no files were changed.'
|
|
1718
|
+
: '🎉 Installation completed successfully!');
|
|
1504
1719
|
// Display framework info
|
|
1505
1720
|
clack.note(`Framework detected: ${result.framework.name} (${result.framework.type})`, 'Framework Info');
|
|
1506
1721
|
// Display modifications
|
|
1722
|
+
if (result.installPlan) {
|
|
1723
|
+
clack.note([
|
|
1724
|
+
`Command: ${result.installPlan.command}`,
|
|
1725
|
+
`Run from: ${result.installPlan.cwd}`,
|
|
1726
|
+
`Target package: ${result.installPlan.targetPackagePath}`,
|
|
1727
|
+
`Workspace: ${result.installPlan.isWorkspace ? 'yes' : 'no'}`,
|
|
1728
|
+
`Version status: ${result.installPlan.versionStatus}`,
|
|
1729
|
+
result.installPlan.alreadyInstalled
|
|
1730
|
+
? `Already installed: ${result.installPlan.installedVersion}`
|
|
1731
|
+
: `Reason: ${result.installPlan.reason}`
|
|
1732
|
+
].join('\n'), result.dryRun ? 'Planned Package Install' : 'Package Install');
|
|
1733
|
+
}
|
|
1507
1734
|
if (result.modifications && result.modifications.length > 0) {
|
|
1508
1735
|
const modifications = result.modifications.map((mod) => `${mod.action}: ${mod.filePath} - ${mod.description}`);
|
|
1509
|
-
clack.note(modifications.join('\n'), 'Files Modified');
|
|
1736
|
+
clack.note(modifications.join('\n'), result.dryRun ? 'Planned File Changes' : 'Files Modified');
|
|
1510
1737
|
}
|
|
1511
1738
|
// Display next steps
|
|
1512
1739
|
if (result.nextSteps && result.nextSteps.length > 0) {
|
|
@@ -1520,6 +1747,25 @@ class AutoInstallCLI {
|
|
|
1520
1747
|
}
|
|
1521
1748
|
}
|
|
1522
1749
|
}
|
|
1750
|
+
writeReport(result) {
|
|
1751
|
+
if (!this.options.reportJson)
|
|
1752
|
+
return;
|
|
1753
|
+
const report = {
|
|
1754
|
+
schemaVersion: 1,
|
|
1755
|
+
status: result.success ? (result.dryRun ? 'dry-run' : 'completed') : 'failed',
|
|
1756
|
+
framework: result.framework,
|
|
1757
|
+
dryRun: result.dryRun === true,
|
|
1758
|
+
installPlan: result.installPlan,
|
|
1759
|
+
modifications: (result.modifications || []).map((mod) => ({
|
|
1760
|
+
filePath: mod.filePath,
|
|
1761
|
+
action: mod.action,
|
|
1762
|
+
description: mod.description
|
|
1763
|
+
})),
|
|
1764
|
+
errors: result.errors || [],
|
|
1765
|
+
nextSteps: result.nextSteps || []
|
|
1766
|
+
};
|
|
1767
|
+
fs.writeFileSync(this.options.reportJson, JSON.stringify(report, null, 2));
|
|
1768
|
+
}
|
|
1523
1769
|
}
|
|
1524
1770
|
function parseArgs() {
|
|
1525
1771
|
const args = process.argv.slice(2);
|
|
@@ -1539,10 +1785,19 @@ function parseArgs() {
|
|
|
1539
1785
|
case '--dry-run':
|
|
1540
1786
|
options.dryRun = true;
|
|
1541
1787
|
break;
|
|
1788
|
+
case '--upgrade':
|
|
1789
|
+
options.upgrade = true;
|
|
1790
|
+
break;
|
|
1542
1791
|
case '--project':
|
|
1543
1792
|
case '-p':
|
|
1544
1793
|
options.projectPath = args[++i];
|
|
1545
1794
|
break;
|
|
1795
|
+
case '--api-key-file':
|
|
1796
|
+
options.apiKeyFile = args[++i];
|
|
1797
|
+
break;
|
|
1798
|
+
case '--report-json':
|
|
1799
|
+
options.reportJson = args[++i];
|
|
1800
|
+
break;
|
|
1546
1801
|
default:
|
|
1547
1802
|
if (!options.apiKey && !arg.startsWith('-')) {
|
|
1548
1803
|
options.apiKey = arg;
|
|
@@ -1564,6 +1819,9 @@ Options:
|
|
|
1564
1819
|
-h, --help Show this help message
|
|
1565
1820
|
-y, --yes Skip all prompts and use defaults
|
|
1566
1821
|
--dry-run Show what would be changed without making changes
|
|
1822
|
+
--upgrade Upgrade stale existing SDK versions
|
|
1823
|
+
--api-key-file <path> Read API key from a local file
|
|
1824
|
+
--report-json <path> Write a structured install report without file contents
|
|
1567
1825
|
-p, --project <path> Specify project directory
|
|
1568
1826
|
|
|
1569
1827
|
Examples:
|