cbrowser 5.2.0 → 6.0.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/README.md +80 -3
- package/dist/browser.d.ts +24 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +206 -0
- package/dist/browser.js.map +1 -1
- package/dist/cli.js +404 -13
- package/dist/cli.js.map +1 -1
- package/dist/personas.d.ts +31 -1
- package/dist/personas.d.ts.map +1 -1
- package/dist/personas.js +349 -2
- package/dist/personas.js.map +1 -1
- package/dist/types.d.ts +36 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -13,8 +13,8 @@ const mcp_server_js_1 = require("./mcp-server.js");
|
|
|
13
13
|
function showHelp() {
|
|
14
14
|
console.log(`
|
|
15
15
|
╔══════════════════════════════════════════════════════════════════════════════╗
|
|
16
|
-
║ CBrowser CLI
|
|
17
|
-
║ AI-powered browser automation with
|
|
16
|
+
║ CBrowser CLI v6.0.0 ║
|
|
17
|
+
║ AI-powered browser automation with multi-persona comparison ║
|
|
18
18
|
╚══════════════════════════════════════════════════════════════════════════════╝
|
|
19
19
|
|
|
20
20
|
NAVIGATION
|
|
@@ -36,8 +36,31 @@ AUTONOMOUS JOURNEYS
|
|
|
36
36
|
--goal <goal> What to accomplish
|
|
37
37
|
--record-video Record journey as video
|
|
38
38
|
|
|
39
|
+
MULTI-PERSONA COMPARISON (v6.0.0)
|
|
40
|
+
compare-personas Compare multiple personas on the same journey
|
|
41
|
+
--start <url> Starting URL (required)
|
|
42
|
+
--goal <goal> What to accomplish (required)
|
|
43
|
+
--personas <list> Comma-separated persona names
|
|
44
|
+
--concurrency <n> Max parallel browsers (default: 3)
|
|
45
|
+
--output <file> Save JSON report to file
|
|
46
|
+
--html Generate HTML report
|
|
47
|
+
Examples:
|
|
48
|
+
cbrowser compare-personas --start "https://example.com" \\
|
|
49
|
+
--goal "Complete checkout" \\
|
|
50
|
+
--personas power-user,first-timer,elderly-user,mobile-user
|
|
51
|
+
|
|
39
52
|
PERSONAS
|
|
40
|
-
persona list List
|
|
53
|
+
persona list List all personas (built-in + custom)
|
|
54
|
+
persona create "<desc>" Create persona from natural language description
|
|
55
|
+
--name <name> Persona name (default: generated from description)
|
|
56
|
+
Examples:
|
|
57
|
+
cbrowser persona create "impatient developer who hates slow UIs"
|
|
58
|
+
cbrowser persona create "elderly user new to computers" --name grandma
|
|
59
|
+
cbrowser persona create "distracted teen on mobile phone"
|
|
60
|
+
persona show <name> Show detailed persona configuration
|
|
61
|
+
persona delete <name> Delete a custom persona
|
|
62
|
+
persona export <name> Export persona to JSON file
|
|
63
|
+
persona import <file> Import persona from JSON file
|
|
41
64
|
|
|
42
65
|
SESSION MANAGEMENT
|
|
43
66
|
session save <name> Save browser session (cookies, storage, URL)
|
|
@@ -276,6 +299,138 @@ function formatBytes(bytes) {
|
|
|
276
299
|
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
277
300
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
278
301
|
}
|
|
302
|
+
function generateHtmlReport(comparison) {
|
|
303
|
+
const rows = comparison.personas.map((p) => `
|
|
304
|
+
<tr class="${p.success ? 'success' : 'failure'}">
|
|
305
|
+
<td><strong>${p.persona}</strong><br><small>${p.description}</small></td>
|
|
306
|
+
<td>${p.success ? '✓' : '✗'}</td>
|
|
307
|
+
<td>${(p.totalTime / 1000).toFixed(1)}s</td>
|
|
308
|
+
<td>${p.stepCount}</td>
|
|
309
|
+
<td>${p.frictionCount}</td>
|
|
310
|
+
<td>${p.techLevel}</td>
|
|
311
|
+
<td>${p.device}</td>
|
|
312
|
+
<td><small>${p.frictionPoints.join('<br>') || '-'}</small></td>
|
|
313
|
+
</tr>
|
|
314
|
+
`).join('');
|
|
315
|
+
return `<!DOCTYPE html>
|
|
316
|
+
<html lang="en">
|
|
317
|
+
<head>
|
|
318
|
+
<meta charset="UTF-8">
|
|
319
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
320
|
+
<title>Persona Comparison Report</title>
|
|
321
|
+
<style>
|
|
322
|
+
* { box-sizing: border-box; }
|
|
323
|
+
body {
|
|
324
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
325
|
+
max-width: 1400px;
|
|
326
|
+
margin: 0 auto;
|
|
327
|
+
padding: 2rem;
|
|
328
|
+
background: #f5f5f5;
|
|
329
|
+
}
|
|
330
|
+
h1 { color: #1a1a1a; border-bottom: 3px solid #3b82f6; padding-bottom: 0.5rem; }
|
|
331
|
+
.meta { background: white; padding: 1rem; border-radius: 8px; margin-bottom: 1rem; }
|
|
332
|
+
.meta p { margin: 0.25rem 0; }
|
|
333
|
+
table {
|
|
334
|
+
width: 100%;
|
|
335
|
+
border-collapse: collapse;
|
|
336
|
+
background: white;
|
|
337
|
+
border-radius: 8px;
|
|
338
|
+
overflow: hidden;
|
|
339
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
340
|
+
}
|
|
341
|
+
th, td {
|
|
342
|
+
padding: 1rem;
|
|
343
|
+
text-align: left;
|
|
344
|
+
border-bottom: 1px solid #eee;
|
|
345
|
+
}
|
|
346
|
+
th { background: #1a1a1a; color: white; }
|
|
347
|
+
tr.success { background: #ecfdf5; }
|
|
348
|
+
tr.failure { background: #fef2f2; }
|
|
349
|
+
.summary {
|
|
350
|
+
display: grid;
|
|
351
|
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
352
|
+
gap: 1rem;
|
|
353
|
+
margin: 1rem 0;
|
|
354
|
+
}
|
|
355
|
+
.stat {
|
|
356
|
+
background: white;
|
|
357
|
+
padding: 1rem;
|
|
358
|
+
border-radius: 8px;
|
|
359
|
+
text-align: center;
|
|
360
|
+
}
|
|
361
|
+
.stat .value { font-size: 2rem; font-weight: bold; color: #3b82f6; }
|
|
362
|
+
.stat .label { color: #666; font-size: 0.875rem; }
|
|
363
|
+
.recommendations {
|
|
364
|
+
background: #fffbeb;
|
|
365
|
+
border: 1px solid #fbbf24;
|
|
366
|
+
border-radius: 8px;
|
|
367
|
+
padding: 1rem;
|
|
368
|
+
margin-top: 1rem;
|
|
369
|
+
}
|
|
370
|
+
.recommendations h3 { margin-top: 0; }
|
|
371
|
+
.recommendations ul { margin: 0; padding-left: 1.5rem; }
|
|
372
|
+
</style>
|
|
373
|
+
</head>
|
|
374
|
+
<body>
|
|
375
|
+
<h1>🎭 Multi-Persona Comparison Report</h1>
|
|
376
|
+
|
|
377
|
+
<div class="meta">
|
|
378
|
+
<p><strong>URL:</strong> ${comparison.url}</p>
|
|
379
|
+
<p><strong>Goal:</strong> ${comparison.goal}</p>
|
|
380
|
+
<p><strong>Timestamp:</strong> ${comparison.timestamp}</p>
|
|
381
|
+
<p><strong>Total Duration:</strong> ${(comparison.duration / 1000).toFixed(1)}s</p>
|
|
382
|
+
</div>
|
|
383
|
+
|
|
384
|
+
<div class="summary">
|
|
385
|
+
<div class="stat">
|
|
386
|
+
<div class="value">${comparison.summary.successCount}/${comparison.summary.totalPersonas}</div>
|
|
387
|
+
<div class="label">Success Rate</div>
|
|
388
|
+
</div>
|
|
389
|
+
<div class="stat">
|
|
390
|
+
<div class="value">${(comparison.summary.avgCompletionTime / 1000).toFixed(1)}s</div>
|
|
391
|
+
<div class="label">Avg Completion Time</div>
|
|
392
|
+
</div>
|
|
393
|
+
<div class="stat">
|
|
394
|
+
<div class="value">${comparison.summary.fastestPersona}</div>
|
|
395
|
+
<div class="label">Fastest</div>
|
|
396
|
+
</div>
|
|
397
|
+
<div class="stat">
|
|
398
|
+
<div class="value">${comparison.summary.mostFriction}</div>
|
|
399
|
+
<div class="label">Most Friction</div>
|
|
400
|
+
</div>
|
|
401
|
+
</div>
|
|
402
|
+
|
|
403
|
+
<table>
|
|
404
|
+
<thead>
|
|
405
|
+
<tr>
|
|
406
|
+
<th>Persona</th>
|
|
407
|
+
<th>Success</th>
|
|
408
|
+
<th>Time</th>
|
|
409
|
+
<th>Steps</th>
|
|
410
|
+
<th>Friction</th>
|
|
411
|
+
<th>Tech Level</th>
|
|
412
|
+
<th>Device</th>
|
|
413
|
+
<th>Issues</th>
|
|
414
|
+
</tr>
|
|
415
|
+
</thead>
|
|
416
|
+
<tbody>
|
|
417
|
+
${rows}
|
|
418
|
+
</tbody>
|
|
419
|
+
</table>
|
|
420
|
+
|
|
421
|
+
<div class="recommendations">
|
|
422
|
+
<h3>💡 Recommendations</h3>
|
|
423
|
+
<ul>
|
|
424
|
+
${comparison.recommendations.map((r) => `<li>${r}</li>`).join('')}
|
|
425
|
+
</ul>
|
|
426
|
+
</div>
|
|
427
|
+
|
|
428
|
+
<p style="color: #999; text-align: center; margin-top: 2rem;">
|
|
429
|
+
Generated by CBrowser v6.0.0 - Multi-Persona Comparison
|
|
430
|
+
</p>
|
|
431
|
+
</body>
|
|
432
|
+
</html>`;
|
|
433
|
+
}
|
|
279
434
|
function parseGeoLocation(location) {
|
|
280
435
|
// Check if it's a preset
|
|
281
436
|
if (types_js_1.LOCATION_PRESETS[location]) {
|
|
@@ -671,19 +826,255 @@ async function main() {
|
|
|
671
826
|
}
|
|
672
827
|
break;
|
|
673
828
|
}
|
|
829
|
+
// =========================================================================
|
|
830
|
+
// Tier 6: Multi-Persona Comparison (v6.0.0)
|
|
831
|
+
// =========================================================================
|
|
832
|
+
case "compare-personas": {
|
|
833
|
+
const startUrl = options.start;
|
|
834
|
+
const goal = options.goal;
|
|
835
|
+
const personaList = options.personas;
|
|
836
|
+
if (!startUrl) {
|
|
837
|
+
console.error("Error: --start URL required");
|
|
838
|
+
process.exit(1);
|
|
839
|
+
}
|
|
840
|
+
if (!goal) {
|
|
841
|
+
console.error("Error: --goal required");
|
|
842
|
+
process.exit(1);
|
|
843
|
+
}
|
|
844
|
+
// Default to comparing all built-in personas if none specified
|
|
845
|
+
const personaNames = personaList
|
|
846
|
+
? personaList.split(",").map((p) => p.trim())
|
|
847
|
+
: Object.keys(personas_js_1.BUILTIN_PERSONAS);
|
|
848
|
+
const concurrency = options.concurrency
|
|
849
|
+
? parseInt(options.concurrency)
|
|
850
|
+
: 3;
|
|
851
|
+
const comparison = await (0, browser_js_1.comparePersonas)({
|
|
852
|
+
startUrl,
|
|
853
|
+
goal,
|
|
854
|
+
personas: personaNames,
|
|
855
|
+
maxConcurrency: concurrency,
|
|
856
|
+
headless,
|
|
857
|
+
});
|
|
858
|
+
// Print formatted report
|
|
859
|
+
const report = (0, browser_js_1.formatComparisonReport)(comparison);
|
|
860
|
+
console.log(report);
|
|
861
|
+
// Save JSON output if requested
|
|
862
|
+
if (options.output) {
|
|
863
|
+
const fs = await import("fs");
|
|
864
|
+
fs.writeFileSync(options.output, JSON.stringify(comparison, null, 2));
|
|
865
|
+
console.log(`\n📄 JSON report saved: ${options.output}`);
|
|
866
|
+
}
|
|
867
|
+
// Generate HTML report if requested
|
|
868
|
+
if (options.html) {
|
|
869
|
+
const fs = await import("fs");
|
|
870
|
+
const htmlReport = generateHtmlReport(comparison);
|
|
871
|
+
const htmlPath = options.output?.replace(".json", ".html") || "comparison-report.html";
|
|
872
|
+
fs.writeFileSync(htmlPath, htmlReport);
|
|
873
|
+
console.log(`\n🌐 HTML report saved: ${htmlPath}`);
|
|
874
|
+
}
|
|
875
|
+
// Exit with error if any personas failed
|
|
876
|
+
if (comparison.summary.failureCount > 0) {
|
|
877
|
+
process.exit(1);
|
|
878
|
+
}
|
|
879
|
+
break;
|
|
880
|
+
}
|
|
674
881
|
case "persona": {
|
|
675
882
|
const subcommand = args[0];
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
console.log(
|
|
681
|
-
|
|
682
|
-
|
|
883
|
+
switch (subcommand) {
|
|
884
|
+
case "list": {
|
|
885
|
+
console.log("\n📋 Available Personas:\n");
|
|
886
|
+
// Built-in personas
|
|
887
|
+
console.log(" Built-in:");
|
|
888
|
+
for (const [name, persona] of Object.entries(personas_js_1.BUILTIN_PERSONAS)) {
|
|
889
|
+
console.log(` ${name}`);
|
|
890
|
+
console.log(` ${persona.description}`);
|
|
891
|
+
console.log(` Tech: ${persona.demographics.tech_level} | Device: ${persona.demographics.device}`);
|
|
892
|
+
console.log("");
|
|
893
|
+
}
|
|
894
|
+
// Custom personas
|
|
895
|
+
const customPersonas = (0, personas_js_1.loadCustomPersonas)();
|
|
896
|
+
const customNames = Object.keys(customPersonas);
|
|
897
|
+
if (customNames.length > 0) {
|
|
898
|
+
console.log(" Custom:");
|
|
899
|
+
for (const [name, persona] of Object.entries(customPersonas)) {
|
|
900
|
+
console.log(` ${name}`);
|
|
901
|
+
console.log(` ${persona.description}`);
|
|
902
|
+
console.log(` Tech: ${persona.demographics.tech_level} | Device: ${persona.demographics.device}`);
|
|
903
|
+
console.log("");
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
break;
|
|
683
907
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
908
|
+
case "create": {
|
|
909
|
+
const description = args.slice(1).join(" ");
|
|
910
|
+
if (!description) {
|
|
911
|
+
console.error("Usage: cbrowser persona create \"<description>\" [--name <name>]");
|
|
912
|
+
console.error("\nExamples:");
|
|
913
|
+
console.error(" cbrowser persona create \"impatient developer who hates slow UIs\"");
|
|
914
|
+
console.error(" cbrowser persona create \"elderly user new to computers\" --name grandma");
|
|
915
|
+
console.error(" cbrowser persona create \"distracted teen on their phone\"");
|
|
916
|
+
process.exit(1);
|
|
917
|
+
}
|
|
918
|
+
// Generate name from description if not provided
|
|
919
|
+
let personaName = options.name;
|
|
920
|
+
if (!personaName) {
|
|
921
|
+
// Create a slug from first few words
|
|
922
|
+
personaName = description
|
|
923
|
+
.toLowerCase()
|
|
924
|
+
.replace(/[^a-z0-9\s]/g, "")
|
|
925
|
+
.split(/\s+/)
|
|
926
|
+
.slice(0, 3)
|
|
927
|
+
.join("-");
|
|
928
|
+
}
|
|
929
|
+
// Check if trying to overwrite built-in
|
|
930
|
+
if ((0, personas_js_1.isBuiltinPersona)(personaName)) {
|
|
931
|
+
console.error(`Error: Cannot overwrite built-in persona "${personaName}"`);
|
|
932
|
+
console.error("Use a different name with --name <name>");
|
|
933
|
+
process.exit(1);
|
|
934
|
+
}
|
|
935
|
+
console.log(`\n🤖 Generating persona from description...\n`);
|
|
936
|
+
console.log(` "${description}"\n`);
|
|
937
|
+
const persona = (0, personas_js_1.generatePersonaFromDescription)(personaName, description);
|
|
938
|
+
// Display the generated persona
|
|
939
|
+
console.log(`━━━ Generated Persona: ${persona.name} ━━━\n`);
|
|
940
|
+
console.log(`Description: ${persona.description}`);
|
|
941
|
+
console.log(`\nDemographics:`);
|
|
942
|
+
console.log(` Age Range: ${persona.demographics.age_range}`);
|
|
943
|
+
console.log(` Tech Level: ${persona.demographics.tech_level}`);
|
|
944
|
+
console.log(` Device: ${persona.demographics.device}`);
|
|
945
|
+
console.log(`\nTiming:`);
|
|
946
|
+
console.log(` Reaction Time: ${persona.humanBehavior?.timing.reactionTime.min}-${persona.humanBehavior?.timing.reactionTime.max}ms`);
|
|
947
|
+
console.log(` Click Delay: ${persona.humanBehavior?.timing.clickDelay.min}-${persona.humanBehavior?.timing.clickDelay.max}ms`);
|
|
948
|
+
console.log(` Type Speed: ${persona.humanBehavior?.timing.typeSpeed.min}-${persona.humanBehavior?.timing.typeSpeed.max}ms/char`);
|
|
949
|
+
console.log(` Reading Speed: ${persona.humanBehavior?.timing.readingSpeed} wpm`);
|
|
950
|
+
console.log(`\nError Rates:`);
|
|
951
|
+
console.log(` Misclick: ${((persona.humanBehavior?.errors.misClickRate || 0) * 100).toFixed(0)}%`);
|
|
952
|
+
console.log(` Accidental Double-click: ${((persona.humanBehavior?.errors.doubleClickAccidental || 0) * 100).toFixed(0)}%`);
|
|
953
|
+
console.log(` Typo: ${((persona.humanBehavior?.errors.typoRate || 0) * 100).toFixed(0)}%`);
|
|
954
|
+
console.log(`\nMouse Behavior:`);
|
|
955
|
+
console.log(` Speed: ${persona.humanBehavior?.mouse.speed}`);
|
|
956
|
+
console.log(` Curvature: ${persona.humanBehavior?.mouse.curvature}`);
|
|
957
|
+
console.log(` Jitter: ${persona.humanBehavior?.mouse.jitter}px`);
|
|
958
|
+
console.log(`\nAttention:`);
|
|
959
|
+
console.log(` Pattern: ${persona.humanBehavior?.attention.pattern}`);
|
|
960
|
+
console.log(` Scroll: ${persona.humanBehavior?.attention.scrollBehavior}`);
|
|
961
|
+
console.log(` Focus: ${persona.humanBehavior?.attention.focusAreas.join(", ")}`);
|
|
962
|
+
console.log(` Distraction Rate: ${((persona.humanBehavior?.attention.distractionRate || 0) * 100).toFixed(0)}%`);
|
|
963
|
+
console.log(`\nViewport: ${persona.context?.viewport?.[0]}x${persona.context?.viewport?.[1]}`);
|
|
964
|
+
if (Object.keys(persona.behaviors).length > 0) {
|
|
965
|
+
console.log(`\nBehaviors: ${Object.keys(persona.behaviors).join(", ")}`);
|
|
966
|
+
}
|
|
967
|
+
// Save the persona
|
|
968
|
+
const filepath = (0, personas_js_1.saveCustomPersona)(persona);
|
|
969
|
+
console.log(`\n✓ Persona saved: ${filepath}`);
|
|
970
|
+
console.log(`\nUse with: cbrowser journey ${personaName} --start <url> --goal "<goal>"`);
|
|
971
|
+
break;
|
|
972
|
+
}
|
|
973
|
+
case "show": {
|
|
974
|
+
const name = args[1];
|
|
975
|
+
if (!name) {
|
|
976
|
+
console.error("Usage: cbrowser persona show <name>");
|
|
977
|
+
process.exit(1);
|
|
978
|
+
}
|
|
979
|
+
// Check built-in first, then custom
|
|
980
|
+
let persona = personas_js_1.BUILTIN_PERSONAS[name];
|
|
981
|
+
let isCustom = false;
|
|
982
|
+
if (!persona) {
|
|
983
|
+
const customPersonas = (0, personas_js_1.loadCustomPersonas)();
|
|
984
|
+
persona = customPersonas[name];
|
|
985
|
+
isCustom = true;
|
|
986
|
+
}
|
|
987
|
+
if (!persona) {
|
|
988
|
+
console.error(`Persona not found: ${name}`);
|
|
989
|
+
console.error("Run 'cbrowser persona list' to see available personas");
|
|
990
|
+
process.exit(1);
|
|
991
|
+
}
|
|
992
|
+
console.log(`\n━━━ Persona: ${persona.name} ${isCustom ? "(custom)" : "(built-in)"} ━━━\n`);
|
|
993
|
+
console.log(JSON.stringify(persona, null, 2));
|
|
994
|
+
break;
|
|
995
|
+
}
|
|
996
|
+
case "delete": {
|
|
997
|
+
const name = args[1];
|
|
998
|
+
if (!name) {
|
|
999
|
+
console.error("Usage: cbrowser persona delete <name>");
|
|
1000
|
+
process.exit(1);
|
|
1001
|
+
}
|
|
1002
|
+
if ((0, personas_js_1.isBuiltinPersona)(name)) {
|
|
1003
|
+
console.error(`Cannot delete built-in persona: ${name}`);
|
|
1004
|
+
process.exit(1);
|
|
1005
|
+
}
|
|
1006
|
+
const deleted = (0, personas_js_1.deleteCustomPersona)(name);
|
|
1007
|
+
if (deleted) {
|
|
1008
|
+
console.log(`✓ Persona deleted: ${name}`);
|
|
1009
|
+
}
|
|
1010
|
+
else {
|
|
1011
|
+
console.error(`Custom persona not found: ${name}`);
|
|
1012
|
+
process.exit(1);
|
|
1013
|
+
}
|
|
1014
|
+
break;
|
|
1015
|
+
}
|
|
1016
|
+
case "export": {
|
|
1017
|
+
const name = args[1];
|
|
1018
|
+
if (!name) {
|
|
1019
|
+
console.error("Usage: cbrowser persona export <name>");
|
|
1020
|
+
process.exit(1);
|
|
1021
|
+
}
|
|
1022
|
+
// Get persona (built-in or custom)
|
|
1023
|
+
let persona = personas_js_1.BUILTIN_PERSONAS[name];
|
|
1024
|
+
if (!persona) {
|
|
1025
|
+
const customPersonas = (0, personas_js_1.loadCustomPersonas)();
|
|
1026
|
+
persona = customPersonas[name];
|
|
1027
|
+
}
|
|
1028
|
+
if (!persona) {
|
|
1029
|
+
console.error(`Persona not found: ${name}`);
|
|
1030
|
+
process.exit(1);
|
|
1031
|
+
}
|
|
1032
|
+
const fs = await import("fs");
|
|
1033
|
+
const filename = `${name}.persona.json`;
|
|
1034
|
+
fs.writeFileSync(filename, JSON.stringify(persona, null, 2));
|
|
1035
|
+
console.log(`✓ Exported to: ${filename}`);
|
|
1036
|
+
break;
|
|
1037
|
+
}
|
|
1038
|
+
case "import": {
|
|
1039
|
+
const file = args[1];
|
|
1040
|
+
if (!file) {
|
|
1041
|
+
console.error("Usage: cbrowser persona import <file>");
|
|
1042
|
+
process.exit(1);
|
|
1043
|
+
}
|
|
1044
|
+
const fs = await import("fs");
|
|
1045
|
+
if (!fs.existsSync(file)) {
|
|
1046
|
+
console.error(`File not found: ${file}`);
|
|
1047
|
+
process.exit(1);
|
|
1048
|
+
}
|
|
1049
|
+
try {
|
|
1050
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
1051
|
+
const persona = JSON.parse(content);
|
|
1052
|
+
if (!persona.name || !persona.description) {
|
|
1053
|
+
console.error("Invalid persona file: missing name or description");
|
|
1054
|
+
process.exit(1);
|
|
1055
|
+
}
|
|
1056
|
+
if ((0, personas_js_1.isBuiltinPersona)(persona.name)) {
|
|
1057
|
+
console.error(`Cannot import: "${persona.name}" is a built-in persona name`);
|
|
1058
|
+
console.error("Edit the JSON file to change the name");
|
|
1059
|
+
process.exit(1);
|
|
1060
|
+
}
|
|
1061
|
+
const filepath = (0, personas_js_1.saveCustomPersona)(persona);
|
|
1062
|
+
console.log(`✓ Imported persona: ${persona.name}`);
|
|
1063
|
+
console.log(` Saved to: ${filepath}`);
|
|
1064
|
+
}
|
|
1065
|
+
catch (e) {
|
|
1066
|
+
console.error(`Failed to import: ${e.message}`);
|
|
1067
|
+
process.exit(1);
|
|
1068
|
+
}
|
|
1069
|
+
break;
|
|
1070
|
+
}
|
|
1071
|
+
default:
|
|
1072
|
+
console.error("Usage: cbrowser persona [list|create|show|delete|export|import]");
|
|
1073
|
+
console.error("\nExamples:");
|
|
1074
|
+
console.error(" cbrowser persona list");
|
|
1075
|
+
console.error(" cbrowser persona create \"impatient developer\" --name dev-persona");
|
|
1076
|
+
console.error(" cbrowser persona show power-user");
|
|
1077
|
+
console.error(" cbrowser persona delete my-custom-persona");
|
|
687
1078
|
}
|
|
688
1079
|
break;
|
|
689
1080
|
}
|