@c15t/cli 2.0.0-rc.6 → 2.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 +8 -8
- package/dist/145.mjs +1223 -332
- package/dist/generate-files.mjs +64 -90
- package/dist-types/auth/base-url.d.ts +2 -2
- package/dist-types/auth/config-store.d.ts +2 -2
- package/dist-types/auth/types.d.ts +1 -1
- package/dist-types/commands/generate/prompts/mode-select.d.ts +1 -1
- package/dist-types/commands/generate/templates/css.d.ts +10 -11
- package/dist-types/commands/generate/templates/shared/components.d.ts +1 -1
- package/dist-types/commands/generate/templates/shared/framework-config.d.ts +0 -4
- package/dist-types/commands/index.d.ts +1 -1
- package/dist-types/commands/instances/index.d.ts +13 -8
- package/dist-types/commands/self-host/migrate/migrator-result.d.ts +1 -1
- package/dist-types/commands/self-host/migrate/orm-result.d.ts +1 -1
- package/dist-types/commands/self-host/migrate/read-config.d.ts +1 -1
- package/dist-types/commands/shared/stylesheets.d.ts +19 -0
- package/dist-types/constants.d.ts +19 -7
- package/dist-types/context/types.d.ts +3 -1
- package/dist-types/control-plane/client.d.ts +6 -6
- package/dist-types/control-plane/types.d.ts +5 -5
- package/dist-types/core/errors.d.ts +10 -10
- package/dist-types/core/telemetry.d.ts +2 -77
- package/dist-types/machines/generate/actors/prompts.d.ts +1 -1
- package/dist-types/machines/generate/machine.d.ts +7 -7
- package/dist-types/machines/generate/types.d.ts +2 -2
- package/dist-types/types.d.ts +8 -18
- package/dist-types/utils/logger.d.ts +7 -6
- package/dist-types/utils/telemetry.d.ts +61 -117
- package/dist-types/utils/validation.d.ts +2 -2
- package/package.json +9 -9
- package/readme.json +2 -2
package/dist/145.mjs
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import "dotenv/config";
|
|
2
2
|
import open_0 from "open";
|
|
3
3
|
import picocolors from "picocolors";
|
|
4
|
-
import promises, { readdir, writeFile } from "node:fs/promises";
|
|
5
|
-
import node_path, { extname, join as external_node_path_join } from "node:path";
|
|
4
|
+
import promises, { readFile, readdir, writeFile } from "node:fs/promises";
|
|
5
|
+
import node_path, { dirname, extname, join as external_node_path_join, relative, resolve as external_node_path_resolve } from "node:path";
|
|
6
6
|
import { Node, Project, SyntaxKind } from "ts-morph";
|
|
7
|
-
import { existsSync as external_node_fs_existsSync } from "node:fs";
|
|
7
|
+
import node_fs, { existsSync as external_node_fs_existsSync } from "node:fs";
|
|
8
8
|
import { assign as external_xstate_assign, createActor, fromPromise, setup } from "xstate";
|
|
9
9
|
import node_crypto from "node:crypto";
|
|
10
10
|
import node_os from "node:os";
|
|
11
|
-
import {
|
|
11
|
+
import { createLogger, initLogger } from "evlog";
|
|
12
|
+
import { createDrainPipeline } from "evlog/pipeline";
|
|
12
13
|
import { spawn as external_node_child_process_spawn } from "node:child_process";
|
|
13
14
|
import { once } from "node:events";
|
|
14
|
-
import { createLogger } from "@c15t/logger";
|
|
15
|
+
import { createLogger as logger_createLogger } from "@c15t/logger";
|
|
15
16
|
import { migrator } from "@c15t/backend/db/migrator";
|
|
16
17
|
import { DB } from "@c15t/backend/db/schema";
|
|
17
18
|
import { loadConfig as external_c12_loadConfig } from "c12";
|
|
@@ -83,14 +84,17 @@ __webpack_require__.r(config_store_namespaceObject);
|
|
|
83
84
|
__webpack_require__.d(config_store_namespaceObject, {
|
|
84
85
|
getAccessToken: ()=>config_store_getAccessToken,
|
|
85
86
|
IM: ()=>getAuthState,
|
|
87
|
+
qs: ()=>getSelectedInstanceId,
|
|
88
|
+
M3: ()=>isLoggedIn,
|
|
86
89
|
_g: ()=>setSelectedInstanceId,
|
|
87
90
|
Ex: ()=>storeTokens
|
|
88
91
|
});
|
|
89
92
|
function showHelpMenu(context, version, commands, flags) {
|
|
90
93
|
const { logger } = context;
|
|
91
94
|
logger.debug('Displaying help menu using command and flag structures.');
|
|
92
|
-
const
|
|
93
|
-
const
|
|
95
|
+
const visibleCommands = commands.filter((cmd)=>!cmd.hidden);
|
|
96
|
+
const commandColumnWidth = Math.max(...visibleCommands.map((cmd)=>cmd.name.length), 10) + 2;
|
|
97
|
+
const commandLines = visibleCommands.map((cmd)=>` ${cmd.name.padEnd(commandColumnWidth)}${cmd.description}`).join('\n');
|
|
94
98
|
const flagDisplays = flags.map((flag)=>{
|
|
95
99
|
const names = flag.names.join(', ');
|
|
96
100
|
const valuePlaceholder = flag.expectsValue ? ' <value>' : '';
|
|
@@ -111,7 +115,7 @@ ${optionLines}
|
|
|
111
115
|
|
|
112
116
|
Run a command directly (e.g., ${picocolors.cyan('c15t setup')}) or select one interactively when no command is provided.
|
|
113
117
|
|
|
114
|
-
For more help, visit: https://
|
|
118
|
+
For more help, visit: https://c15t.com`;
|
|
115
119
|
logger.debug('Help menu content generated.');
|
|
116
120
|
logger.note(helpContent, 'Usage');
|
|
117
121
|
}
|
|
@@ -351,6 +355,191 @@ async function runActiveUiApiCodemod(options) {
|
|
|
351
355
|
errors
|
|
352
356
|
};
|
|
353
357
|
}
|
|
358
|
+
const CSS_ENTRYPOINT_CANDIDATES = [
|
|
359
|
+
'app/globals.css',
|
|
360
|
+
'src/app/globals.css',
|
|
361
|
+
'app/global.css',
|
|
362
|
+
'src/app/global.css',
|
|
363
|
+
'styles/globals.css',
|
|
364
|
+
'src/styles/globals.css',
|
|
365
|
+
'styles/global.css',
|
|
366
|
+
'src/styles/global.css',
|
|
367
|
+
'src/index.css',
|
|
368
|
+
'src/styles.css',
|
|
369
|
+
'src/style.css',
|
|
370
|
+
'styles.css',
|
|
371
|
+
'app.css',
|
|
372
|
+
'src/App.css'
|
|
373
|
+
];
|
|
374
|
+
const LOCAL_CSS_IMPORT_RE = /^\s*import(?:\s+[^'"]+\s+from\s+)?['"]([^'"]+\.css)['"];\s*$/gm;
|
|
375
|
+
const TAILWIND_V4_IMPORT_RE = /^\s*@import\s+['"]tailwindcss['"];\s*$/;
|
|
376
|
+
const TAILWIND_COMPONENTS_RE = /^\s*@tailwind\s+components\s*;\s*$/;
|
|
377
|
+
const TAILWIND_UTILITIES_RE = /^\s*@tailwind\s+utilities\s*;\s*$/;
|
|
378
|
+
function normalizePath(projectRoot, filePath) {
|
|
379
|
+
if (filePath.startsWith(projectRoot)) return filePath;
|
|
380
|
+
return external_node_path_resolve(projectRoot, filePath);
|
|
381
|
+
}
|
|
382
|
+
function dedupePaths(paths) {
|
|
383
|
+
return [
|
|
384
|
+
...new Set(paths)
|
|
385
|
+
];
|
|
386
|
+
}
|
|
387
|
+
function isNonModuleLocalCssImport(moduleSpecifier) {
|
|
388
|
+
return moduleSpecifier.startsWith('.') && moduleSpecifier.endsWith('.css') && !moduleSpecifier.endsWith('.module.css');
|
|
389
|
+
}
|
|
390
|
+
function getManagedPackages(packageName) {
|
|
391
|
+
if ('@c15t/react' === packageName || '@c15t/nextjs' === packageName) return [
|
|
392
|
+
'@c15t/react',
|
|
393
|
+
'@c15t/nextjs'
|
|
394
|
+
];
|
|
395
|
+
return [
|
|
396
|
+
'@c15t/ui'
|
|
397
|
+
];
|
|
398
|
+
}
|
|
399
|
+
function getImportVariants(packageName, kind) {
|
|
400
|
+
if ('base' === kind) return [
|
|
401
|
+
`${packageName}/styles.css`,
|
|
402
|
+
`${packageName}/styles.tw3.css`
|
|
403
|
+
];
|
|
404
|
+
return [
|
|
405
|
+
`${packageName}/iab/styles.css`,
|
|
406
|
+
`${packageName}/iab/styles.tw3.css`
|
|
407
|
+
];
|
|
408
|
+
}
|
|
409
|
+
function getDesiredImportPath(packageName, kind, tailwindVersion) {
|
|
410
|
+
const suffix = isTailwindV3(tailwindVersion) ? 'styles.tw3.css' : 'styles.css';
|
|
411
|
+
return 'base' === kind ? `${packageName}/${suffix}` : `${packageName}/iab/${suffix}`;
|
|
412
|
+
}
|
|
413
|
+
function getDesiredImports(packageName, tailwindVersion, includeBase, includeIab) {
|
|
414
|
+
const imports = [];
|
|
415
|
+
if (includeBase) imports.push(getDesiredImportPath(packageName, 'base', tailwindVersion));
|
|
416
|
+
if (includeIab) imports.push(getDesiredImportPath(packageName, 'iab', tailwindVersion));
|
|
417
|
+
return imports;
|
|
418
|
+
}
|
|
419
|
+
function getFrameworkImportRegex(packageNames) {
|
|
420
|
+
const escapedPackages = packageNames.map((packageName)=>packageName.replace('/', '\\/')).join('|');
|
|
421
|
+
return new RegExp(`^\\s*@import\\s+['"](?:${escapedPackages})(?:\\/iab)?\\/styles(?:\\.tw3)?\\.css['"];\\s*$`);
|
|
422
|
+
}
|
|
423
|
+
function findTopInsertionLineIndex(lines) {
|
|
424
|
+
let index = 0;
|
|
425
|
+
if (lines[index]?.trim().startsWith('/*')) {
|
|
426
|
+
while(index < lines.length){
|
|
427
|
+
const line = lines[index];
|
|
428
|
+
index += 1;
|
|
429
|
+
if (line?.includes('*/')) break;
|
|
430
|
+
}
|
|
431
|
+
while(index < lines.length && lines[index]?.trim() === '')index += 1;
|
|
432
|
+
}
|
|
433
|
+
return index;
|
|
434
|
+
}
|
|
435
|
+
function insertImportsIntoCssContent(content, desiredImports, tailwindVersion, managedPackages) {
|
|
436
|
+
const normalizedContent = content.replace(/\r\n/g, '\n');
|
|
437
|
+
const hadTrailingNewline = normalizedContent.endsWith('\n');
|
|
438
|
+
const body = hadTrailingNewline ? normalizedContent.slice(0, -1) : normalizedContent;
|
|
439
|
+
const lines = body.length > 0 ? body.split('\n') : [];
|
|
440
|
+
const importRegex = getFrameworkImportRegex(managedPackages);
|
|
441
|
+
const filteredLines = lines.filter((line)=>!importRegex.test(line));
|
|
442
|
+
const importLines = desiredImports.map((importPath)=>`@import "${importPath}";`);
|
|
443
|
+
let insertionIndex = findTopInsertionLineIndex(filteredLines);
|
|
444
|
+
if (isTailwindV3(tailwindVersion)) {
|
|
445
|
+
const componentsIndex = filteredLines.findIndex((line)=>TAILWIND_COMPONENTS_RE.test(line));
|
|
446
|
+
if (componentsIndex >= 0) insertionIndex = componentsIndex + 1;
|
|
447
|
+
else {
|
|
448
|
+
const utilitiesIndex = filteredLines.findIndex((line)=>TAILWIND_UTILITIES_RE.test(line));
|
|
449
|
+
if (utilitiesIndex >= 0) insertionIndex = utilitiesIndex;
|
|
450
|
+
}
|
|
451
|
+
} else {
|
|
452
|
+
const tailwindImportIndex = filteredLines.findIndex((line)=>TAILWIND_V4_IMPORT_RE.test(line));
|
|
453
|
+
if (tailwindImportIndex >= 0) insertionIndex = tailwindImportIndex + 1;
|
|
454
|
+
}
|
|
455
|
+
const nextLines = [
|
|
456
|
+
...filteredLines.slice(0, insertionIndex),
|
|
457
|
+
...importLines,
|
|
458
|
+
...filteredLines.slice(insertionIndex)
|
|
459
|
+
];
|
|
460
|
+
let nextContent = nextLines.join('\n');
|
|
461
|
+
if (hadTrailingNewline) nextContent += '\n';
|
|
462
|
+
if (content.includes('\r\n')) nextContent = nextContent.replace(/\n/g, '\r\n');
|
|
463
|
+
return nextContent;
|
|
464
|
+
}
|
|
465
|
+
function describeImportChange(content, packageName, desiredImportPath) {
|
|
466
|
+
const kind = desiredImportPath.includes('/iab/') ? 'iab' : 'base';
|
|
467
|
+
const variants = getImportVariants(packageName, kind);
|
|
468
|
+
const alternateVariant = variants.find((variant)=>variant !== desiredImportPath && content.includes(variant));
|
|
469
|
+
if (alternateVariant) return `replaced @import "${alternateVariant}"; with @import "${desiredImportPath}";`;
|
|
470
|
+
if (content.includes(desiredImportPath)) return `normalized @import "${desiredImportPath}";`;
|
|
471
|
+
return `added @import "${desiredImportPath}";`;
|
|
472
|
+
}
|
|
473
|
+
async function resolveCssEntrypoint({ projectRoot, entrypointPath }) {
|
|
474
|
+
const searchedPaths = [];
|
|
475
|
+
if (entrypointPath) {
|
|
476
|
+
const resolvedEntrypointPath = normalizePath(projectRoot, entrypointPath);
|
|
477
|
+
if (external_node_fs_existsSync(resolvedEntrypointPath)) {
|
|
478
|
+
const entrypointContent = await readFile(resolvedEntrypointPath, 'utf-8');
|
|
479
|
+
for (const match of entrypointContent.matchAll(LOCAL_CSS_IMPORT_RE)){
|
|
480
|
+
const moduleSpecifier = match[1];
|
|
481
|
+
if (!moduleSpecifier || !isNonModuleLocalCssImport(moduleSpecifier)) continue;
|
|
482
|
+
const candidatePath = external_node_path_resolve(dirname(resolvedEntrypointPath), moduleSpecifier);
|
|
483
|
+
searchedPaths.push(candidatePath);
|
|
484
|
+
if (external_node_fs_existsSync(candidatePath)) return {
|
|
485
|
+
filePath: candidatePath,
|
|
486
|
+
searchedPaths: dedupePaths(searchedPaths)
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
for (const candidate of CSS_ENTRYPOINT_CANDIDATES){
|
|
492
|
+
const candidatePath = external_node_path_join(projectRoot, candidate);
|
|
493
|
+
searchedPaths.push(candidatePath);
|
|
494
|
+
if (external_node_fs_existsSync(candidatePath)) return {
|
|
495
|
+
filePath: candidatePath,
|
|
496
|
+
searchedPaths: dedupePaths(searchedPaths)
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
return {
|
|
500
|
+
filePath: null,
|
|
501
|
+
searchedPaths: dedupePaths(searchedPaths)
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
function formatSearchedCssPaths(projectRoot, searchedPaths) {
|
|
505
|
+
return searchedPaths.map((filePath)=>relative(projectRoot, filePath) || '.').join(', ');
|
|
506
|
+
}
|
|
507
|
+
function isTailwindV3(version) {
|
|
508
|
+
return null != version && /^(?:\^|~)?3/.test(version);
|
|
509
|
+
}
|
|
510
|
+
async function ensureGlobalCssStylesheetImports(options) {
|
|
511
|
+
const desiredImports = getDesiredImports(options.packageName, options.tailwindVersion, options.includeBase, options.includeIab);
|
|
512
|
+
if (0 === desiredImports.length) return {
|
|
513
|
+
updated: false,
|
|
514
|
+
filePath: null,
|
|
515
|
+
searchedPaths: [],
|
|
516
|
+
changes: []
|
|
517
|
+
};
|
|
518
|
+
const { filePath, searchedPaths } = await resolveCssEntrypoint(options);
|
|
519
|
+
if (!filePath) return {
|
|
520
|
+
updated: false,
|
|
521
|
+
filePath: null,
|
|
522
|
+
searchedPaths,
|
|
523
|
+
changes: []
|
|
524
|
+
};
|
|
525
|
+
const content = await readFile(filePath, 'utf-8');
|
|
526
|
+
const managedPackages = getManagedPackages(options.packageName);
|
|
527
|
+
const nextContent = insertImportsIntoCssContent(content, desiredImports, options.tailwindVersion, managedPackages);
|
|
528
|
+
if (nextContent === content) return {
|
|
529
|
+
updated: false,
|
|
530
|
+
filePath,
|
|
531
|
+
searchedPaths,
|
|
532
|
+
changes: []
|
|
533
|
+
};
|
|
534
|
+
if (!options.dryRun) await writeFile(filePath, nextContent, 'utf-8');
|
|
535
|
+
const changes = desiredImports.map((importPath)=>describeImportChange(content, options.packageName, importPath));
|
|
536
|
+
return {
|
|
537
|
+
updated: true,
|
|
538
|
+
filePath,
|
|
539
|
+
searchedPaths,
|
|
540
|
+
changes
|
|
541
|
+
};
|
|
542
|
+
}
|
|
354
543
|
const add_stylesheet_imports_SUPPORTED_EXTENSIONS = new Set([
|
|
355
544
|
'.ts',
|
|
356
545
|
'.tsx',
|
|
@@ -400,6 +589,17 @@ const NEXTJS_ENTRYPOINTS = [
|
|
|
400
589
|
'pages/_app.tsx',
|
|
401
590
|
'pages/_app.jsx'
|
|
402
591
|
];
|
|
592
|
+
async function detectTailwindVersion(projectRoot) {
|
|
593
|
+
const packageJsonPath = external_node_path_join(projectRoot, 'package.json');
|
|
594
|
+
if (!external_node_fs_existsSync(packageJsonPath)) return null;
|
|
595
|
+
try {
|
|
596
|
+
const content = await readFile(packageJsonPath, 'utf-8');
|
|
597
|
+
const parsed = JSON.parse(content);
|
|
598
|
+
return parsed.dependencies?.tailwindcss ?? parsed.devDependencies?.tailwindcss ?? null;
|
|
599
|
+
} catch {
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
403
603
|
async function add_stylesheet_imports_collectSourceFiles(rootDir) {
|
|
404
604
|
const files = [];
|
|
405
605
|
async function walk(currentDir) {
|
|
@@ -492,23 +692,19 @@ function findEntrypoint(projectRoot, framework) {
|
|
|
492
692
|
}
|
|
493
693
|
return null;
|
|
494
694
|
}
|
|
495
|
-
|
|
695
|
+
const FRAMEWORK_STYLESHEET_IMPORT_RE = /^@c15t\/(?:react|nextjs)(?:\/iab)?\/styles(?:\.tw3)?\.css$/;
|
|
696
|
+
function removeFrameworkStylesheetImports(project, filePath) {
|
|
496
697
|
const sourceFile = project.addSourceFileAtPathIfExists(filePath);
|
|
497
|
-
if (!sourceFile) return
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
if (importDeclarations.length > 0) {
|
|
508
|
-
const lastImport = importDeclarations[importDeclarations.length - 1];
|
|
509
|
-
if (lastImport) sourceFile.insertStatements(lastImport.getChildIndex() + 1, `import '${cssImportPath}';`);
|
|
510
|
-
} else sourceFile.insertStatements(0, `import '${cssImportPath}';`);
|
|
511
|
-
return true;
|
|
698
|
+
if (!sourceFile) return [];
|
|
699
|
+
const removedImports = [];
|
|
700
|
+
for (const importDeclaration of sourceFile.getImportDeclarations()){
|
|
701
|
+
const moduleSpecifier = importDeclaration.getModuleSpecifierValue();
|
|
702
|
+
if (FRAMEWORK_STYLESHEET_IMPORT_RE.test(moduleSpecifier)) {
|
|
703
|
+
removedImports.push(moduleSpecifier);
|
|
704
|
+
importDeclaration.remove();
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
return removedImports;
|
|
512
708
|
}
|
|
513
709
|
async function runAddStylesheetImportsCodemod(options) {
|
|
514
710
|
const project = new Project({
|
|
@@ -550,40 +746,42 @@ async function runAddStylesheetImportsCodemod(options) {
|
|
|
550
746
|
};
|
|
551
747
|
}
|
|
552
748
|
const pkg = 'nextjs' === detection.framework ? '@c15t/nextjs' : '@c15t/react';
|
|
553
|
-
const
|
|
554
|
-
const summaries = [];
|
|
749
|
+
const tailwindVersion = await detectTailwindVersion(options.projectRoot);
|
|
555
750
|
try {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
}
|
|
751
|
+
const stylesheetResult = await ensureGlobalCssStylesheetImports({
|
|
752
|
+
projectRoot: options.projectRoot,
|
|
753
|
+
packageName: pkg,
|
|
754
|
+
tailwindVersion,
|
|
755
|
+
entrypointPath: entrypoint,
|
|
756
|
+
includeBase: detection.usesStyledUi || detection.usesIabUi,
|
|
757
|
+
includeIab: detection.usesIabUi,
|
|
758
|
+
dryRun: options.dryRun
|
|
759
|
+
});
|
|
760
|
+
if (!stylesheetResult.filePath) {
|
|
761
|
+
errors.push({
|
|
762
|
+
filePath: options.projectRoot,
|
|
763
|
+
error: `No suitable global CSS entrypoint found. Checked: ${formatSearchedCssPaths(options.projectRoot, stylesheetResult.searchedPaths)}`
|
|
764
|
+
});
|
|
765
|
+
return {
|
|
766
|
+
totalFiles: filePaths.length,
|
|
767
|
+
changedFiles,
|
|
768
|
+
errors
|
|
769
|
+
};
|
|
575
770
|
}
|
|
576
|
-
if (
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
771
|
+
if (stylesheetResult.updated) changedFiles.push({
|
|
772
|
+
filePath: stylesheetResult.filePath,
|
|
773
|
+
operations: stylesheetResult.changes.length,
|
|
774
|
+
summaries: stylesheetResult.changes
|
|
775
|
+
});
|
|
776
|
+
for (const filePath of filePaths){
|
|
777
|
+
const removedImports = removeFrameworkStylesheetImports(project, filePath);
|
|
778
|
+
if (0 !== removedImports.length) changedFiles.push({
|
|
779
|
+
filePath,
|
|
780
|
+
operations: removedImports.length,
|
|
781
|
+
summaries: removedImports.map((importPath)=>`removed JS import '${importPath}'`)
|
|
581
782
|
});
|
|
582
|
-
if (!options.dryRun) {
|
|
583
|
-
const sourceFile = project.getSourceFile(entrypoint);
|
|
584
|
-
if (sourceFile) await sourceFile.save();
|
|
585
|
-
}
|
|
586
783
|
}
|
|
784
|
+
if (!options.dryRun) await project.save();
|
|
587
785
|
} catch (error) {
|
|
588
786
|
errors.push({
|
|
589
787
|
filePath: entrypoint,
|
|
@@ -2113,8 +2311,8 @@ const codemods = [
|
|
|
2113
2311
|
},
|
|
2114
2312
|
{
|
|
2115
2313
|
id: 'add-stylesheet-imports',
|
|
2116
|
-
label: '
|
|
2117
|
-
hint: '
|
|
2314
|
+
label: 'configure global CSS for prebuilt UI',
|
|
2315
|
+
hint: 'Moves styled c15t imports into the app CSS entrypoint, including Tailwind 3 and IAB variants when needed.',
|
|
2118
2316
|
run: async (context, dryRun)=>{
|
|
2119
2317
|
const { projectRoot } = context;
|
|
2120
2318
|
const result = await runAddStylesheetImportsCodemod({
|
|
@@ -2173,7 +2371,95 @@ const codemodsCommand = {
|
|
|
2173
2371
|
description: 'Run project codemods (for example translations -> i18n migration).',
|
|
2174
2372
|
action: runCodemods
|
|
2175
2373
|
};
|
|
2176
|
-
const
|
|
2374
|
+
const constants_URLS = {
|
|
2375
|
+
CONSENT_IO: 'https://inth.com',
|
|
2376
|
+
TELEMETRY: 'https://telemetry.c15t.com/c15t/v1/logs',
|
|
2377
|
+
DOCS: 'https://c15t.com/docs',
|
|
2378
|
+
GITHUB: 'https://github.com/c15t/c15t',
|
|
2379
|
+
DISCORD: 'https://c15t.com/discord',
|
|
2380
|
+
API_DOCS: 'https://c15t.com/docs/api',
|
|
2381
|
+
CLI_DOCS: 'https://c15t.com/docs/cli',
|
|
2382
|
+
CHANGELOG: 'https://c15t.com/changelog'
|
|
2383
|
+
};
|
|
2384
|
+
const PATHS = {
|
|
2385
|
+
CONFIG_DIR: '.c15t',
|
|
2386
|
+
CONFIG_FILE: 'config.json',
|
|
2387
|
+
TELEMETRY_STATE_FILE: 'telemetry.json',
|
|
2388
|
+
TELEMETRY_QUEUE_FILE: 'telemetry-queue.json',
|
|
2389
|
+
PROJECT_CONFIG: 'c15t.config.ts',
|
|
2390
|
+
PROJECT_CONFIG_JS: 'c15t.config.js',
|
|
2391
|
+
ENV_FILE: '.env',
|
|
2392
|
+
ENV_LOCAL: '.env.local'
|
|
2393
|
+
};
|
|
2394
|
+
const constants_REGEX = {
|
|
2395
|
+
URL: /^https?:\/\/.+/,
|
|
2396
|
+
C15T_URL: /^https:\/\/[\w-]+\.(?:c15t\.dev|inth\.app)$/,
|
|
2397
|
+
DYNAMIC_SEGMENT: /\[[\w-]+\]/,
|
|
2398
|
+
SEMVER: /^\d+\.\d+\.\d+(-[\w.]+)?$/,
|
|
2399
|
+
PACKAGE_NAME: /^(@[\w-]+\/)?[\w-]+$/
|
|
2400
|
+
};
|
|
2401
|
+
const CLI_INFO = {
|
|
2402
|
+
NAME: 'c15t',
|
|
2403
|
+
BIN: 'c15t',
|
|
2404
|
+
CONTROL_PLANE_CLIENT_NAME: 'c15t-cli',
|
|
2405
|
+
VERSION: '2.0.0'
|
|
2406
|
+
};
|
|
2407
|
+
const TIMEOUTS = {
|
|
2408
|
+
DEVICE_FLOW_POLL_INTERVAL: 5,
|
|
2409
|
+
DEVICE_FLOW_EXPIRY: 900,
|
|
2410
|
+
HTTP_REQUEST: 10000,
|
|
2411
|
+
CONTROL_PLANE_CONNECTION: 30000
|
|
2412
|
+
};
|
|
2413
|
+
const ENV_VARS = {
|
|
2414
|
+
V2: 'V2',
|
|
2415
|
+
TELEMETRY_DISABLED: 'C15T_TELEMETRY_DISABLED',
|
|
2416
|
+
TELEMETRY_ENDPOINT: 'C15T_TELEMETRY_ENDPOINT',
|
|
2417
|
+
TELEMETRY_WRITE_KEY: 'C15T_TELEMETRY_WRITE_KEY',
|
|
2418
|
+
TELEMETRY_ORG_ID: 'C15T_TELEMETRY_ORG_ID',
|
|
2419
|
+
CONSENT_URL: 'CONSENT_URL',
|
|
2420
|
+
BACKEND_URL: 'C15T_URL',
|
|
2421
|
+
API_KEY: 'C15T_API_KEY',
|
|
2422
|
+
DEBUG: 'C15T_DEBUG'
|
|
2423
|
+
};
|
|
2424
|
+
const STORAGE_MODES = {
|
|
2425
|
+
HOSTED: 'hosted',
|
|
2426
|
+
C15T: 'c15t',
|
|
2427
|
+
OFFLINE: 'offline',
|
|
2428
|
+
SELF_HOSTED: 'self-hosted',
|
|
2429
|
+
CUSTOM: 'custom'
|
|
2430
|
+
};
|
|
2431
|
+
const LAYOUT_PATTERNS = [
|
|
2432
|
+
'app/layout.tsx',
|
|
2433
|
+
'app/layout.ts',
|
|
2434
|
+
'app/layout.jsx',
|
|
2435
|
+
'app/layout.js',
|
|
2436
|
+
'src/app/layout.tsx',
|
|
2437
|
+
'src/app/layout.ts',
|
|
2438
|
+
'src/app/layout.jsx',
|
|
2439
|
+
'src/app/layout.js',
|
|
2440
|
+
'app/*/layout.tsx',
|
|
2441
|
+
'app/*/layout.ts',
|
|
2442
|
+
'app/*/layout.jsx',
|
|
2443
|
+
'app/*/layout.js',
|
|
2444
|
+
'src/app/*/layout.tsx',
|
|
2445
|
+
'src/app/*/layout.ts',
|
|
2446
|
+
'src/app/*/layout.jsx',
|
|
2447
|
+
'src/app/*/layout.js',
|
|
2448
|
+
'app/*/*/layout.tsx',
|
|
2449
|
+
'app/*/*/layout.ts',
|
|
2450
|
+
'src/app/*/*/layout.tsx',
|
|
2451
|
+
'src/app/*/*/layout.ts'
|
|
2452
|
+
];
|
|
2453
|
+
const PAGES_APP_PATTERNS = [
|
|
2454
|
+
'pages/_app.tsx',
|
|
2455
|
+
'pages/_app.ts',
|
|
2456
|
+
'pages/_app.jsx',
|
|
2457
|
+
'pages/_app.js',
|
|
2458
|
+
'src/pages/_app.tsx',
|
|
2459
|
+
'src/pages/_app.ts',
|
|
2460
|
+
'src/pages/_app.jsx',
|
|
2461
|
+
'src/pages/_app.js'
|
|
2462
|
+
];
|
|
2177
2463
|
const TelemetryEventName = {
|
|
2178
2464
|
CLI_INVOKED: 'cli.invoked',
|
|
2179
2465
|
CLI_COMPLETED: 'cli.completed',
|
|
@@ -2191,6 +2477,7 @@ const TelemetryEventName = {
|
|
|
2191
2477
|
HELP_DISPLAYED: 'help.displayed',
|
|
2192
2478
|
VERSION_DISPLAYED: 'version.displayed',
|
|
2193
2479
|
ONBOARDING_STARTED: 'onboarding.started',
|
|
2480
|
+
ONBOARDING_STAGE: 'onboarding.stage',
|
|
2194
2481
|
ONBOARDING_COMPLETED: 'onboarding.completed',
|
|
2195
2482
|
ONBOARDING_EXITED: 'onboarding.exited',
|
|
2196
2483
|
ONBOARDING_STORAGE_MODE_SELECTED: 'onboarding.storage_mode_selected',
|
|
@@ -2201,6 +2488,13 @@ const TelemetryEventName = {
|
|
|
2201
2488
|
ONBOARDING_DEPENDENCIES_CHOICE: 'onboarding.dependencies_choice',
|
|
2202
2489
|
ONBOARDING_DEPENDENCIES_INSTALLED: 'onboarding.dependencies_installed',
|
|
2203
2490
|
ONBOARDING_GITHUB_STAR: 'onboarding.github_star',
|
|
2491
|
+
AUTH_LOGIN_STARTED: 'auth.login.started',
|
|
2492
|
+
AUTH_LOGIN_SUCCEEDED: 'auth.login.succeeded',
|
|
2493
|
+
AUTH_LOGIN_FAILED: 'auth.login.failed',
|
|
2494
|
+
AUTH_LOGOUT: 'auth.logout',
|
|
2495
|
+
PROJECTS_LISTED: 'projects.listed',
|
|
2496
|
+
PROJECT_SELECTED: 'project.selected',
|
|
2497
|
+
PROJECT_CREATED: 'project.created',
|
|
2204
2498
|
ERROR_OCCURRED: 'error.occurred',
|
|
2205
2499
|
MIGRATION_STARTED: 'migration.started',
|
|
2206
2500
|
MIGRATION_PLANNED: 'migration.planned',
|
|
@@ -2218,178 +2512,408 @@ const TelemetryEventName = {
|
|
|
2218
2512
|
CLI_STATE_CANCELLED: 'cli.state.cancelled',
|
|
2219
2513
|
CLI_STATE_COMPLETE: 'cli.state.complete'
|
|
2220
2514
|
};
|
|
2515
|
+
const DEFAULT_QUEUE_LIMIT = 250;
|
|
2516
|
+
const DEFAULT_TIMEOUT_MS = 3000;
|
|
2517
|
+
const DEFAULT_BATCH_SIZE = 20;
|
|
2518
|
+
const DEFAULT_BATCH_INTERVAL_MS = 1000;
|
|
2519
|
+
const DEFAULT_MAX_BUFFER_SIZE = 250;
|
|
2520
|
+
const MAX_DEPTH = 5;
|
|
2521
|
+
const MAX_ARRAY_LENGTH = 20;
|
|
2522
|
+
const MAX_OBJECT_KEYS = 50;
|
|
2523
|
+
const MAX_STRING_LENGTH = 500;
|
|
2524
|
+
const RESERVED_TOP_LEVEL_KEYS = new Set([
|
|
2525
|
+
'event',
|
|
2526
|
+
'installId',
|
|
2527
|
+
'sessionId',
|
|
2528
|
+
'commandRunId',
|
|
2529
|
+
'sequence',
|
|
2530
|
+
'source'
|
|
2531
|
+
]);
|
|
2532
|
+
const SENSITIVE_KEY_PATTERN = /(^|[-_])(token|secret|password|authorization|cookie|api[-_]?key|access[-_]?token|refresh[-_]?token|config)([-_]|$)/i;
|
|
2533
|
+
const SECRET_VALUE_PATTERN = /^(Bearer\s+[A-Za-z0-9._-]+|[A-Za-z0-9+/=_-]{80,})$/;
|
|
2221
2534
|
class Telemetry {
|
|
2222
|
-
|
|
2223
|
-
|
|
2535
|
+
endpoint;
|
|
2536
|
+
fetchImpl;
|
|
2537
|
+
queuePath;
|
|
2538
|
+
statePath;
|
|
2539
|
+
headers;
|
|
2224
2540
|
defaultProperties;
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2541
|
+
sessionId = node_crypto.randomUUID();
|
|
2542
|
+
installId;
|
|
2543
|
+
isFirstRun;
|
|
2544
|
+
drain;
|
|
2545
|
+
storageDir;
|
|
2228
2546
|
logger;
|
|
2547
|
+
disabled;
|
|
2548
|
+
debug;
|
|
2549
|
+
sequence = 0;
|
|
2550
|
+
activeCommandName;
|
|
2551
|
+
activeCommandRunId;
|
|
2552
|
+
flushPromise = null;
|
|
2553
|
+
queueReplayPromise = Promise.resolve();
|
|
2554
|
+
queueWritePromise = Promise.resolve();
|
|
2229
2555
|
constructor(options){
|
|
2230
|
-
const envDisabled = '1' === process.env[
|
|
2231
|
-
|
|
2232
|
-
this.disabled = options?.disabled ?? envDisabled ?? !hasValidApiKey;
|
|
2233
|
-
this.defaultProperties = options?.defaultProperties ?? {};
|
|
2234
|
-
this.logger = options?.logger;
|
|
2556
|
+
const envDisabled = '1' === process.env[ENV_VARS.TELEMETRY_DISABLED] || process.env[ENV_VARS.TELEMETRY_DISABLED]?.toLowerCase() === 'true';
|
|
2557
|
+
this.disabled = options?.disabled ?? envDisabled ?? false;
|
|
2235
2558
|
this.debug = options?.debug ?? false;
|
|
2236
|
-
this.
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2559
|
+
this.logger = options?.logger;
|
|
2560
|
+
this.defaultProperties = this.sanitizeProperties(options?.defaultProperties ?? {});
|
|
2561
|
+
this.endpoint = options?.endpoint ?? process.env[ENV_VARS.TELEMETRY_ENDPOINT] ?? constants_URLS.TELEMETRY;
|
|
2562
|
+
this.fetchImpl = options?.fetch ?? fetch;
|
|
2563
|
+
this.storageDir = options?.storageDir ?? node_path.join(node_os.homedir(), PATHS.CONFIG_DIR);
|
|
2564
|
+
this.statePath = node_path.join(this.storageDir, PATHS.TELEMETRY_STATE_FILE);
|
|
2565
|
+
this.queuePath = node_path.join(this.storageDir, PATHS.TELEMETRY_QUEUE_FILE);
|
|
2566
|
+
this.headers = this.buildHeaders(options?.headers);
|
|
2567
|
+
const identity = this.loadOrCreateInstallIdentity();
|
|
2568
|
+
this.installId = identity.installId;
|
|
2569
|
+
this.isFirstRun = identity.isFirstRun;
|
|
2570
|
+
const userDrainOptions = options?.drainOptions;
|
|
2571
|
+
const onDropped = userDrainOptions?.onDropped;
|
|
2572
|
+
const pipeline = createDrainPipeline({
|
|
2573
|
+
batch: {
|
|
2574
|
+
size: userDrainOptions?.batch?.size ?? DEFAULT_BATCH_SIZE,
|
|
2575
|
+
intervalMs: userDrainOptions?.batch?.intervalMs ?? DEFAULT_BATCH_INTERVAL_MS
|
|
2576
|
+
},
|
|
2577
|
+
retry: {
|
|
2578
|
+
maxAttempts: userDrainOptions?.retry?.maxAttempts ?? 2,
|
|
2579
|
+
backoff: userDrainOptions?.retry?.backoff ?? 'fixed',
|
|
2580
|
+
initialDelayMs: userDrainOptions?.retry?.initialDelayMs ?? 250,
|
|
2581
|
+
maxDelayMs: userDrainOptions?.retry?.maxDelayMs ?? 1000
|
|
2582
|
+
},
|
|
2583
|
+
maxBufferSize: userDrainOptions?.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE,
|
|
2584
|
+
onDropped: (events, error)=>{
|
|
2585
|
+
onDropped?.(events, error);
|
|
2586
|
+
this.persistDroppedEvents(events, error);
|
|
2587
|
+
}
|
|
2588
|
+
});
|
|
2589
|
+
this.drain = pipeline(async (batch)=>{
|
|
2590
|
+
await this.sendBatch(batch.map((item)=>item.event));
|
|
2591
|
+
});
|
|
2592
|
+
this.applyLoggerConfig();
|
|
2593
|
+
this.queueReplayPromise = this.flushQueuedEvents();
|
|
2245
2594
|
}
|
|
2246
2595
|
trackEvent(eventName, properties = {}) {
|
|
2247
|
-
if (this.disabled
|
|
2248
|
-
if (this.debug) this.logDebug(`Telemetry event skipped (${eventName}):
|
|
2249
|
-
return;
|
|
2250
|
-
}
|
|
2251
|
-
try {
|
|
2252
|
-
const safeProperties = {};
|
|
2253
|
-
for (const [key, value] of Object.entries(properties))if ('config' !== key && void 0 !== value) safeProperties[key] = value;
|
|
2254
|
-
if (this.debug) this.logDebug(`Sending telemetry event: ${eventName}`);
|
|
2255
|
-
this.client.capture({
|
|
2256
|
-
distinctId: this.distinctId,
|
|
2257
|
-
event: eventName,
|
|
2258
|
-
properties: {
|
|
2259
|
-
...this.defaultProperties,
|
|
2260
|
-
...safeProperties,
|
|
2261
|
-
timestamp: new Date().toISOString()
|
|
2262
|
-
}
|
|
2263
|
-
});
|
|
2264
|
-
this.client.flush();
|
|
2265
|
-
} catch (error) {
|
|
2266
|
-
if (this.debug) this.logDebug(`Error sending telemetry event ${eventName}:`, error);
|
|
2267
|
-
}
|
|
2268
|
-
}
|
|
2269
|
-
trackEventSync(eventName, properties = {}) {
|
|
2270
|
-
if (this.disabled || !this.client) {
|
|
2271
|
-
if (this.debug) this.logDebug('Telemetry disabled or client not initialized');
|
|
2596
|
+
if (this.disabled) {
|
|
2597
|
+
if (this.debug) this.logDebug(`Telemetry event skipped (${eventName}): telemetry disabled`);
|
|
2272
2598
|
return;
|
|
2273
2599
|
}
|
|
2274
|
-
const safeProperties = {};
|
|
2275
|
-
for (const [key, value] of Object.entries(properties))if ('config' !== key && void 0 !== value) safeProperties[key] = value;
|
|
2276
|
-
if (this.debug) this.logDebug(`Sending telemetry event: ${eventName}`);
|
|
2277
2600
|
try {
|
|
2278
|
-
this.
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
...this.defaultProperties,
|
|
2283
|
-
...safeProperties,
|
|
2284
|
-
timestamp: new Date().toISOString()
|
|
2285
|
-
}
|
|
2286
|
-
});
|
|
2287
|
-
this.client.flush();
|
|
2288
|
-
if (this.debug) this.logDebug(`Flushed telemetry event: ${eventName}`);
|
|
2601
|
+
const log = createLogger(this.buildBaseContext(eventName));
|
|
2602
|
+
log.set(this.sanitizeProperties(properties));
|
|
2603
|
+
log.emit();
|
|
2604
|
+
if (this.debug) this.logDebug(`Queued telemetry event: ${eventName}`);
|
|
2289
2605
|
} catch (error) {
|
|
2290
|
-
if (this.debug) this.logDebug(`
|
|
2606
|
+
if (this.debug) this.logDebug(`Failed to queue telemetry event ${eventName}:`, error);
|
|
2291
2607
|
}
|
|
2292
2608
|
}
|
|
2293
2609
|
trackCommand(command, args = [], flags = {}) {
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2610
|
+
this.activeCommandName = command;
|
|
2611
|
+
this.activeCommandRunId = node_crypto.randomUUID();
|
|
2612
|
+
const safeFlags = this.sanitizeProperties(flags);
|
|
2613
|
+
const safeArgs = this.sanitizeValue(args);
|
|
2297
2614
|
this.trackEvent(TelemetryEventName.COMMAND_EXECUTED, {
|
|
2298
2615
|
command,
|
|
2299
|
-
|
|
2300
|
-
|
|
2616
|
+
commandRunId: this.activeCommandRunId,
|
|
2617
|
+
args: safeArgs,
|
|
2618
|
+
argsCount: args.length,
|
|
2619
|
+
flags: safeFlags,
|
|
2620
|
+
flagCount: Object.keys(safeFlags).length,
|
|
2621
|
+
flagNames: Object.keys(safeFlags).sort(),
|
|
2622
|
+
subcommand: 'string' == typeof safeArgs[0] ? safeArgs[0] : void 0
|
|
2301
2623
|
});
|
|
2302
2624
|
}
|
|
2303
2625
|
trackError(error, command) {
|
|
2304
|
-
if (this.disabled
|
|
2305
|
-
this.
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2626
|
+
if (this.disabled) return;
|
|
2627
|
+
const safeCommand = command ?? this.activeCommandName;
|
|
2628
|
+
const safeError = this.sanitizeError(error);
|
|
2629
|
+
const errorMetadata = this.buildErrorMetadata(error);
|
|
2630
|
+
try {
|
|
2631
|
+
const log = createLogger(this.buildBaseContext(TelemetryEventName.ERROR_OCCURRED));
|
|
2632
|
+
log.error(safeError, {
|
|
2633
|
+
command: safeCommand,
|
|
2634
|
+
commandRunId: this.activeCommandRunId,
|
|
2635
|
+
failure: errorMetadata
|
|
2636
|
+
});
|
|
2637
|
+
log.emit();
|
|
2638
|
+
if (this.debug) this.logDebug(`Queued telemetry error event: ${safeCommand ?? 'unknown-command'}`);
|
|
2639
|
+
} catch (trackingError) {
|
|
2640
|
+
if (this.debug) this.logDebug('Failed to queue telemetry error event:', trackingError);
|
|
2641
|
+
}
|
|
2311
2642
|
}
|
|
2312
|
-
|
|
2313
|
-
this.disabled
|
|
2643
|
+
flushSync() {
|
|
2644
|
+
if (this.disabled) return;
|
|
2645
|
+
this.flushPromise = this.flushAll();
|
|
2314
2646
|
}
|
|
2315
|
-
|
|
2316
|
-
this.disabled
|
|
2317
|
-
|
|
2647
|
+
async shutdown() {
|
|
2648
|
+
if (this.disabled) return;
|
|
2649
|
+
await (this.flushPromise ?? this.flushAll());
|
|
2318
2650
|
}
|
|
2319
2651
|
isDisabled() {
|
|
2320
2652
|
return this.disabled;
|
|
2321
2653
|
}
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2654
|
+
disable() {
|
|
2655
|
+
this.disabled = true;
|
|
2656
|
+
this.applyLoggerConfig();
|
|
2657
|
+
}
|
|
2658
|
+
enable() {
|
|
2659
|
+
this.disabled = false;
|
|
2660
|
+
this.applyLoggerConfig();
|
|
2661
|
+
this.queueReplayPromise = this.flushQueuedEvents();
|
|
2327
2662
|
}
|
|
2328
2663
|
setLogger(logger) {
|
|
2329
2664
|
this.logger = logger;
|
|
2330
2665
|
}
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2666
|
+
applyLoggerConfig() {
|
|
2667
|
+
const cliVersion = this.readString(this.defaultProperties.cliVersion);
|
|
2668
|
+
initLogger({
|
|
2669
|
+
enabled: !this.disabled,
|
|
2670
|
+
silent: true,
|
|
2671
|
+
pretty: false,
|
|
2672
|
+
stringify: false,
|
|
2673
|
+
_suppressDrainWarning: this.disabled,
|
|
2674
|
+
env: {
|
|
2675
|
+
service: 'c15t-cli',
|
|
2676
|
+
environment: this.getEnvironmentName(),
|
|
2677
|
+
version: cliVersion
|
|
2678
|
+
},
|
|
2679
|
+
drain: this.disabled ? void 0 : this.drain
|
|
2680
|
+
});
|
|
2334
2681
|
}
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2682
|
+
buildBaseContext(eventName) {
|
|
2683
|
+
this.sequence += 1;
|
|
2684
|
+
return {
|
|
2685
|
+
event: eventName,
|
|
2686
|
+
source: 'c15t-cli',
|
|
2687
|
+
installId: this.installId,
|
|
2688
|
+
sessionId: this.sessionId,
|
|
2689
|
+
commandRunId: this.activeCommandRunId,
|
|
2690
|
+
command: this.activeCommandName,
|
|
2691
|
+
sequence: this.sequence,
|
|
2692
|
+
firstRun: this.isFirstRun,
|
|
2693
|
+
interactive: Boolean(process.stdin.isTTY && process.stdout.isTTY),
|
|
2694
|
+
tty: Boolean(process.stdout.isTTY),
|
|
2695
|
+
ci: this.isCi(),
|
|
2696
|
+
platform: process.platform,
|
|
2697
|
+
arch: process.arch,
|
|
2698
|
+
nodeVersion: process.version,
|
|
2699
|
+
...this.defaultProperties
|
|
2700
|
+
};
|
|
2701
|
+
}
|
|
2702
|
+
buildHeaders(overrides) {
|
|
2703
|
+
const writeKey = process.env[ENV_VARS.TELEMETRY_WRITE_KEY];
|
|
2704
|
+
const orgId = process.env[ENV_VARS.TELEMETRY_ORG_ID];
|
|
2705
|
+
return {
|
|
2706
|
+
'Content-Type': 'application/json',
|
|
2707
|
+
...writeKey ? {
|
|
2708
|
+
Authorization: `Bearer ${writeKey}`
|
|
2709
|
+
} : {},
|
|
2710
|
+
...orgId ? {
|
|
2711
|
+
'X-Axiom-Org-Id': orgId
|
|
2712
|
+
} : {},
|
|
2713
|
+
...overrides
|
|
2714
|
+
};
|
|
2715
|
+
}
|
|
2716
|
+
async flushAll() {
|
|
2717
|
+
try {
|
|
2718
|
+
await this.queueReplayPromise;
|
|
2719
|
+
await this.drain.flush();
|
|
2720
|
+
await this.queueWritePromise;
|
|
2721
|
+
await this.flushQueuedEvents();
|
|
2722
|
+
if (this.debug) this.logDebug('Flushed telemetry events');
|
|
2723
|
+
} catch (error) {
|
|
2724
|
+
if (this.debug) this.logDebug('Telemetry flush failed:', error);
|
|
2725
|
+
} finally{
|
|
2726
|
+
this.flushPromise = null;
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
async sendBatch(events) {
|
|
2730
|
+
if (0 === events.length) return;
|
|
2731
|
+
const controller = new AbortController();
|
|
2732
|
+
const timeout = setTimeout(()=>controller.abort(), DEFAULT_TIMEOUT_MS);
|
|
2733
|
+
try {
|
|
2734
|
+
const response = await this.fetchImpl(this.endpoint, {
|
|
2735
|
+
method: 'POST',
|
|
2736
|
+
headers: this.headers,
|
|
2737
|
+
body: JSON.stringify(events),
|
|
2738
|
+
signal: controller.signal
|
|
2739
|
+
});
|
|
2740
|
+
if (!response.ok) throw new Error(`Telemetry ingest failed with status ${response.status}`);
|
|
2741
|
+
} finally{
|
|
2742
|
+
clearTimeout(timeout);
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
async persistDroppedEvents(events, error) {
|
|
2746
|
+
this.queueWritePromise = this.queueWritePromise.then(async ()=>{
|
|
2346
2747
|
try {
|
|
2347
|
-
const
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
if (this.debug) this.logDebug(
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
if (this.debug) this.logDebug('PostHog client initialized in', initTime, 'ms');
|
|
2357
|
-
} catch (error) {
|
|
2358
|
-
this.disabled = true;
|
|
2359
|
-
const errorDetails = error instanceof Error ? {
|
|
2360
|
-
message: error.message,
|
|
2361
|
-
name: error.name,
|
|
2362
|
-
stack: error.stack
|
|
2363
|
-
} : {
|
|
2364
|
-
rawError: String(error)
|
|
2365
|
-
};
|
|
2366
|
-
if (this.debug) this.logDebug('Telemetry disabled due to initialization error:', JSON.stringify(errorDetails, null, 2));
|
|
2367
|
-
try {
|
|
2368
|
-
if (this.debug) this.logDebug('Attempting fallback PostHog initialization');
|
|
2369
|
-
this.client = new PostHog(this.apiKey);
|
|
2370
|
-
this.disabled = false;
|
|
2371
|
-
if (this.debug) this.logDebug('PostHog client initialized using fallback method');
|
|
2372
|
-
} catch (fallbackError) {
|
|
2373
|
-
this.logDebug('Fallback initialization also failed:', fallbackError instanceof Error ? fallbackError.message : String(fallbackError));
|
|
2374
|
-
}
|
|
2748
|
+
const existing = await this.readQueuedEvents();
|
|
2749
|
+
const next = [
|
|
2750
|
+
...existing,
|
|
2751
|
+
...events.map((item)=>item.event)
|
|
2752
|
+
].slice(-DEFAULT_QUEUE_LIMIT);
|
|
2753
|
+
await this.writeQueuedEvents(next);
|
|
2754
|
+
if (this.debug) this.logDebug(`Persisted ${events.length} dropped telemetry event(s) to disk`, error);
|
|
2755
|
+
} catch (queueError) {
|
|
2756
|
+
if (this.debug) this.logDebug('Failed to persist dropped telemetry events:', queueError);
|
|
2375
2757
|
}
|
|
2758
|
+
});
|
|
2759
|
+
await this.queueWritePromise;
|
|
2760
|
+
}
|
|
2761
|
+
async flushQueuedEvents() {
|
|
2762
|
+
if (this.disabled) return;
|
|
2763
|
+
try {
|
|
2764
|
+
const queuedEvents = await this.readQueuedEvents();
|
|
2765
|
+
if (0 === queuedEvents.length) return;
|
|
2766
|
+
await this.sendBatch(queuedEvents);
|
|
2767
|
+
await promises.unlink(this.queuePath).catch(()=>void 0);
|
|
2768
|
+
if (this.debug) this.logDebug(`Replayed ${queuedEvents.length} queued telemetry event(s)`);
|
|
2769
|
+
} catch (error) {
|
|
2770
|
+
if (this.debug) this.logDebug('Failed to replay queued telemetry events:', error);
|
|
2376
2771
|
}
|
|
2377
2772
|
}
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2773
|
+
async readQueuedEvents() {
|
|
2774
|
+
try {
|
|
2775
|
+
const content = await promises.readFile(this.queuePath, 'utf-8');
|
|
2776
|
+
const parsed = JSON.parse(content);
|
|
2777
|
+
if (!Array.isArray(parsed)) return [];
|
|
2778
|
+
return parsed.filter((item)=>'object' == typeof item && null !== item);
|
|
2779
|
+
} catch {
|
|
2780
|
+
return [];
|
|
2781
|
+
}
|
|
2381
2782
|
}
|
|
2382
|
-
|
|
2383
|
-
|
|
2783
|
+
async writeQueuedEvents(events) {
|
|
2784
|
+
await promises.mkdir(this.storageDir, {
|
|
2785
|
+
recursive: true
|
|
2786
|
+
});
|
|
2787
|
+
await promises.writeFile(this.queuePath, JSON.stringify(events, null, 2), {
|
|
2788
|
+
mode: 384
|
|
2789
|
+
});
|
|
2790
|
+
}
|
|
2791
|
+
loadOrCreateInstallIdentity() {
|
|
2792
|
+
try {
|
|
2793
|
+
node_fs.mkdirSync(this.storageDir, {
|
|
2794
|
+
recursive: true
|
|
2795
|
+
});
|
|
2796
|
+
if (node_fs.existsSync(this.statePath)) {
|
|
2797
|
+
const content = node_fs.readFileSync(this.statePath, 'utf-8');
|
|
2798
|
+
const parsed = JSON.parse(content);
|
|
2799
|
+
if ('string' == typeof parsed.installId && parsed.installId.length > 0) return {
|
|
2800
|
+
installId: parsed.installId,
|
|
2801
|
+
isFirstRun: false
|
|
2802
|
+
};
|
|
2803
|
+
}
|
|
2804
|
+
} catch (error) {
|
|
2805
|
+
if (this.debug) this.logDebug('Failed to read telemetry state file:', error);
|
|
2806
|
+
}
|
|
2807
|
+
const installId = node_crypto.randomUUID();
|
|
2384
2808
|
try {
|
|
2385
|
-
this.
|
|
2386
|
-
|
|
2809
|
+
node_fs.writeFileSync(this.statePath, JSON.stringify({
|
|
2810
|
+
installId,
|
|
2811
|
+
createdAt: new Date().toISOString()
|
|
2812
|
+
}, null, 2), {
|
|
2813
|
+
mode: 384
|
|
2814
|
+
});
|
|
2387
2815
|
} catch (error) {
|
|
2388
|
-
if (this.debug) this.logDebug(
|
|
2816
|
+
if (this.debug) this.logDebug('Failed to persist telemetry install ID:', error);
|
|
2817
|
+
}
|
|
2818
|
+
return {
|
|
2819
|
+
installId,
|
|
2820
|
+
isFirstRun: true
|
|
2821
|
+
};
|
|
2822
|
+
}
|
|
2823
|
+
buildErrorMetadata(error) {
|
|
2824
|
+
const eventError = error;
|
|
2825
|
+
return this.sanitizeProperties({
|
|
2826
|
+
name: eventError.name,
|
|
2827
|
+
message: eventError.message,
|
|
2828
|
+
code: 'string' == typeof eventError.code || 'number' == typeof eventError.code ? eventError.code : void 0,
|
|
2829
|
+
status: 'number' == typeof eventError.status ? eventError.status : void 0,
|
|
2830
|
+
statusCode: 'number' == typeof eventError.statusCode ? eventError.statusCode : void 0,
|
|
2831
|
+
cause: eventError.cause instanceof Error ? eventError.cause.message : 'string' == typeof eventError.cause ? eventError.cause : void 0
|
|
2832
|
+
});
|
|
2833
|
+
}
|
|
2834
|
+
sanitizeError(error) {
|
|
2835
|
+
const safeError = new Error(error.message);
|
|
2836
|
+
safeError.name = error.name;
|
|
2837
|
+
if ('development' === process.env.NODE_ENV) safeError.stack = error.stack;
|
|
2838
|
+
else delete safeError.stack;
|
|
2839
|
+
const sourceError = error;
|
|
2840
|
+
const targetError = safeError;
|
|
2841
|
+
if ('number' == typeof sourceError.status) targetError.status = sourceError.status;
|
|
2842
|
+
if ('number' == typeof sourceError.statusCode) targetError.statusCode = sourceError.statusCode;
|
|
2843
|
+
if ('string' == typeof sourceError.statusText) targetError.statusText = sourceError.statusText;
|
|
2844
|
+
if ('string' == typeof sourceError.statusMessage) targetError.statusMessage = sourceError.statusMessage;
|
|
2845
|
+
if (sourceError.cause instanceof Error) targetError.cause = sourceError.cause.message;
|
|
2846
|
+
else if ('string' == typeof sourceError.cause) targetError.cause = sourceError.cause;
|
|
2847
|
+
return safeError;
|
|
2848
|
+
}
|
|
2849
|
+
sanitizeProperties(properties) {
|
|
2850
|
+
const sanitized = {};
|
|
2851
|
+
for (const [key, value] of Object.entries(properties))if (!RESERVED_TOP_LEVEL_KEYS.has(key)) {
|
|
2852
|
+
if (void 0 !== value) {
|
|
2853
|
+
if (SENSITIVE_KEY_PATTERN.test(key)) {
|
|
2854
|
+
sanitized[key] = '[redacted]';
|
|
2855
|
+
continue;
|
|
2856
|
+
}
|
|
2857
|
+
sanitized[key] = this.sanitizeValue(value, 0, key);
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
return sanitized;
|
|
2861
|
+
}
|
|
2862
|
+
sanitizeValue(value, depth = 0, keyHint) {
|
|
2863
|
+
if (depth >= MAX_DEPTH) return '[truncated]';
|
|
2864
|
+
if (null === value) return null;
|
|
2865
|
+
if ('string' == typeof value || 'number' == typeof value || 'boolean' == typeof value) return this.sanitizePrimitive(value, keyHint);
|
|
2866
|
+
if (value instanceof Date) return value.toISOString();
|
|
2867
|
+
if (value instanceof Error) return this.sanitizeProperties(this.buildErrorMetadata(value));
|
|
2868
|
+
if (Array.isArray(value)) return value.slice(0, MAX_ARRAY_LENGTH).map((item)=>this.sanitizeValue(item, depth + 1));
|
|
2869
|
+
if ('object' == typeof value) {
|
|
2870
|
+
const objectValue = value;
|
|
2871
|
+
const sanitizedObject = {};
|
|
2872
|
+
for (const key of Object.keys(objectValue).slice(0, MAX_OBJECT_KEYS)){
|
|
2873
|
+
const nextValue = objectValue[key];
|
|
2874
|
+
if (void 0 !== nextValue) {
|
|
2875
|
+
if (SENSITIVE_KEY_PATTERN.test(key)) {
|
|
2876
|
+
sanitizedObject[key] = '[redacted]';
|
|
2877
|
+
continue;
|
|
2878
|
+
}
|
|
2879
|
+
sanitizedObject[key] = this.sanitizeValue(nextValue, depth + 1, key);
|
|
2880
|
+
}
|
|
2881
|
+
}
|
|
2882
|
+
return sanitizedObject;
|
|
2389
2883
|
}
|
|
2884
|
+
return String(value);
|
|
2885
|
+
}
|
|
2886
|
+
sanitizePrimitive(value, keyHint) {
|
|
2887
|
+
if ('string' != typeof value) return value;
|
|
2888
|
+
if (keyHint && SENSITIVE_KEY_PATTERN.test(keyHint)) return '[redacted]';
|
|
2889
|
+
if (SECRET_VALUE_PATTERN.test(value)) return '[redacted]';
|
|
2890
|
+
if (node_path.isAbsolute(value)) return '[absolute-path]';
|
|
2891
|
+
if (value.startsWith('http://') || value.startsWith('https://')) try {
|
|
2892
|
+
const parsed = new URL(value);
|
|
2893
|
+
parsed.username = '';
|
|
2894
|
+
parsed.password = '';
|
|
2895
|
+
parsed.search = '';
|
|
2896
|
+
parsed.hash = '';
|
|
2897
|
+
value = parsed.toString();
|
|
2898
|
+
} catch {}
|
|
2899
|
+
if (value.length > MAX_STRING_LENGTH) return `${value.slice(0, MAX_STRING_LENGTH)}...`;
|
|
2900
|
+
return value;
|
|
2901
|
+
}
|
|
2902
|
+
getEnvironmentName() {
|
|
2903
|
+
return process.env.NODE_ENV ?? (this.isCi() ? 'production' : 'development');
|
|
2904
|
+
}
|
|
2905
|
+
isCi() {
|
|
2906
|
+
return Boolean(process.env.CI || process.env.GITHUB_ACTIONS || process.env.BUILDKITE || process.env.VERCEL);
|
|
2907
|
+
}
|
|
2908
|
+
readString(value) {
|
|
2909
|
+
return 'string' == typeof value ? value : void 0;
|
|
2910
|
+
}
|
|
2911
|
+
logDebug(message, ...args) {
|
|
2912
|
+
if (this.logger) this.logger.debug(message, ...args);
|
|
2913
|
+
else console.debug(message, ...args);
|
|
2390
2914
|
}
|
|
2391
2915
|
}
|
|
2392
|
-
function
|
|
2916
|
+
function telemetry_createTelemetry(options) {
|
|
2393
2917
|
return new Telemetry(options);
|
|
2394
2918
|
}
|
|
2395
2919
|
const DEFAULT_PERSIST_FILENAME = '.c15t-state.json';
|
|
@@ -2484,6 +3008,86 @@ function createPersistenceSubscriber(machineId, persistPath, options = {}) {
|
|
|
2484
3008
|
});
|
|
2485
3009
|
};
|
|
2486
3010
|
}
|
|
3011
|
+
const GENERATE_STAGE_NAMES = {
|
|
3012
|
+
preflight: 'preflight',
|
|
3013
|
+
preflightError: 'preflight',
|
|
3014
|
+
modeSelection: 'mode_selection',
|
|
3015
|
+
hostedMode: 'hosted_mode',
|
|
3016
|
+
offlineMode: 'offline_mode',
|
|
3017
|
+
customMode: 'custom_mode',
|
|
3018
|
+
backendOptions: 'backend_options',
|
|
3019
|
+
frontendOptions: 'frontend_options',
|
|
3020
|
+
scriptsOptions: "scripts_options",
|
|
3021
|
+
fileGeneration: 'file_generation',
|
|
3022
|
+
dependencyCheck: 'dependency_check',
|
|
3023
|
+
dependencyConfirm: 'dependency_confirm',
|
|
3024
|
+
dependencyInstall: 'dependency_install',
|
|
3025
|
+
summary: 'summary',
|
|
3026
|
+
skillsInstall: 'skills_install',
|
|
3027
|
+
githubStar: 'github_star',
|
|
3028
|
+
cancelling: 'cancelling',
|
|
3029
|
+
cleanup: 'cleanup',
|
|
3030
|
+
complete: 'complete',
|
|
3031
|
+
error: 'error',
|
|
3032
|
+
exited: 'exited',
|
|
3033
|
+
cancelled: 'cancelled'
|
|
3034
|
+
};
|
|
3035
|
+
function normalizeGenerateStageName(state) {
|
|
3036
|
+
return GENERATE_STAGE_NAMES[state] ?? state.replace(/[A-Z]/g, (char)=>`_${char.toLowerCase()}`);
|
|
3037
|
+
}
|
|
3038
|
+
function getGenerateContext(snapshot) {
|
|
3039
|
+
return snapshot.context;
|
|
3040
|
+
}
|
|
3041
|
+
function normalizeCancelReason(reason) {
|
|
3042
|
+
if (!reason) return 'user_cancelled';
|
|
3043
|
+
const normalized = reason.toLowerCase();
|
|
3044
|
+
if (normalized.includes('signal')) return 'signal_interrupted';
|
|
3045
|
+
if (normalized.includes('mode selection')) return 'mode_selection_cancelled';
|
|
3046
|
+
if (normalized.includes('hosted setup')) return 'hosted_setup_cancelled';
|
|
3047
|
+
if (normalized.includes('backend options')) return 'backend_options_cancelled';
|
|
3048
|
+
if (normalized.includes('frontend options')) return 'frontend_options_cancelled';
|
|
3049
|
+
if (normalized.includes("scripts option")) return "scripts_options_cancelled";
|
|
3050
|
+
if (normalized.includes('dependency')) return 'dependency_install_cancelled';
|
|
3051
|
+
if (normalized.includes('prompt cancelled at stage:')) return normalized.replace('prompt cancelled at stage:', '').trim().replace(/\s+/g, '_').concat('_cancelled');
|
|
3052
|
+
return 'user_cancelled';
|
|
3053
|
+
}
|
|
3054
|
+
function getStageReason(fromState, toState, context) {
|
|
3055
|
+
if ('preflightError' === toState) return 'preflight_failed';
|
|
3056
|
+
if ('cancelling' === toState || 'cancelled' === toState || 'exited' === toState) return normalizeCancelReason(context?.cancelReason);
|
|
3057
|
+
if ('dependencyInstall' === fromState && context?.installSucceeded === false) return 'dependency_install_failed';
|
|
3058
|
+
if ('error' === toState) {
|
|
3059
|
+
const lastError = context?.errors?.[context.errors.length - 1];
|
|
3060
|
+
if (lastError?.state === 'fileGeneration') return 'file_generation_failed';
|
|
3061
|
+
if (lastError?.error?.name === 'PromptCancelledError') return normalizeCancelReason(lastError.error.message);
|
|
3062
|
+
if (lastError?.state) return `${normalizeGenerateStageName(lastError.state)}_failed`;
|
|
3063
|
+
return 'machine_error';
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
function getStageResult(fromState, toState, context) {
|
|
3067
|
+
if ('preflightError' === toState || 'error' === toState) return 'failed';
|
|
3068
|
+
if ('cancelling' === toState || 'cancelled' === toState || 'exited' === toState) return 'cancelled';
|
|
3069
|
+
if ('dependencyInstall' === fromState && context?.installSucceeded === false) return 'failed';
|
|
3070
|
+
return 'completed';
|
|
3071
|
+
}
|
|
3072
|
+
function buildGenerateStageTelemetry(fromState, toState, durationMs, snapshot) {
|
|
3073
|
+
const context = getGenerateContext(snapshot);
|
|
3074
|
+
return {
|
|
3075
|
+
stage: normalizeGenerateStageName(fromState),
|
|
3076
|
+
nextStage: normalizeGenerateStageName(toState),
|
|
3077
|
+
durationMs,
|
|
3078
|
+
result: getStageResult(fromState, toState, context),
|
|
3079
|
+
reason: getStageReason(fromState, toState, context),
|
|
3080
|
+
selectedMode: context?.selectedMode ?? void 0,
|
|
3081
|
+
hostedProvider: context?.hostedProvider ?? void 0,
|
|
3082
|
+
dependencyCount: context?.dependenciesToAdd?.length ?? 0,
|
|
3083
|
+
filesCreatedCount: context?.filesCreated?.length ?? 0,
|
|
3084
|
+
filesModifiedCount: context?.filesModified?.length ?? 0,
|
|
3085
|
+
installConfirmed: context?.installConfirmed ?? void 0,
|
|
3086
|
+
installAttempted: context?.installAttempted ?? void 0,
|
|
3087
|
+
installSucceeded: context?.installSucceeded ?? void 0,
|
|
3088
|
+
errorsCount: context?.errors?.length ?? 0
|
|
3089
|
+
};
|
|
3090
|
+
}
|
|
2487
3091
|
function createTelemetrySubscriber(config) {
|
|
2488
3092
|
const { telemetry, machineId, skipStates = [] } = config;
|
|
2489
3093
|
let lastState = null;
|
|
@@ -2502,6 +3106,7 @@ function createTelemetrySubscriber(config) {
|
|
|
2502
3106
|
toState: currentState,
|
|
2503
3107
|
duration
|
|
2504
3108
|
});
|
|
3109
|
+
if ('generate' === machineId) telemetry.trackEvent(TelemetryEventName.ONBOARDING_STAGE, buildGenerateStageTelemetry(lastState, currentState, duration, snapshot));
|
|
2505
3110
|
}
|
|
2506
3111
|
stateHistory.push({
|
|
2507
3112
|
state: currentState,
|
|
@@ -2556,89 +3161,6 @@ function createDebugSubscriber(machineId, logger) {
|
|
|
2556
3161
|
}
|
|
2557
3162
|
};
|
|
2558
3163
|
}
|
|
2559
|
-
const constants_URLS = {
|
|
2560
|
-
CONSENT_IO: 'https://consent.io',
|
|
2561
|
-
DOCS: 'https://v2.c15t.com/docs',
|
|
2562
|
-
GITHUB: 'https://github.com/c15t/c15t',
|
|
2563
|
-
DISCORD: 'https://v2.c15t.com/discord',
|
|
2564
|
-
API_DOCS: 'https://v2.c15t.com/docs/api',
|
|
2565
|
-
CLI_DOCS: 'https://v2.c15t.com/docs/cli',
|
|
2566
|
-
CHANGELOG: 'https://v2.c15t.com/changelog'
|
|
2567
|
-
};
|
|
2568
|
-
const PATHS = {
|
|
2569
|
-
CONFIG_DIR: '.c15t',
|
|
2570
|
-
CONFIG_FILE: 'config.json',
|
|
2571
|
-
PROJECT_CONFIG: 'c15t.config.ts',
|
|
2572
|
-
PROJECT_CONFIG_JS: 'c15t.config.js',
|
|
2573
|
-
ENV_FILE: '.env',
|
|
2574
|
-
ENV_LOCAL: '.env.local'
|
|
2575
|
-
};
|
|
2576
|
-
const constants_REGEX = {
|
|
2577
|
-
URL: /^https?:\/\/.+/,
|
|
2578
|
-
C15T_URL: /^https:\/\/[\w-]+\.c15t\.dev$/,
|
|
2579
|
-
DYNAMIC_SEGMENT: /\[[\w-]+\]/,
|
|
2580
|
-
SEMVER: /^\d+\.\d+\.\d+(-[\w.]+)?$/,
|
|
2581
|
-
PACKAGE_NAME: /^(@[\w-]+\/)?[\w-]+$/
|
|
2582
|
-
};
|
|
2583
|
-
const CLI_INFO = {
|
|
2584
|
-
NAME: 'c15t',
|
|
2585
|
-
BIN: 'c15t',
|
|
2586
|
-
CONTROL_PLANE_CLIENT_NAME: 'c15t-cli',
|
|
2587
|
-
VERSION: '2.0.0'
|
|
2588
|
-
};
|
|
2589
|
-
const TIMEOUTS = {
|
|
2590
|
-
DEVICE_FLOW_POLL_INTERVAL: 5,
|
|
2591
|
-
DEVICE_FLOW_EXPIRY: 900,
|
|
2592
|
-
HTTP_REQUEST: 10000,
|
|
2593
|
-
CONTROL_PLANE_CONNECTION: 30000
|
|
2594
|
-
};
|
|
2595
|
-
const ENV_VARS = {
|
|
2596
|
-
V2: 'V2',
|
|
2597
|
-
TELEMETRY_DISABLED: 'C15T_TELEMETRY_DISABLED',
|
|
2598
|
-
CONSENT_URL: 'CONSENT_URL',
|
|
2599
|
-
BACKEND_URL: 'C15T_URL',
|
|
2600
|
-
API_KEY: 'C15T_API_KEY',
|
|
2601
|
-
DEBUG: 'C15T_DEBUG'
|
|
2602
|
-
};
|
|
2603
|
-
const STORAGE_MODES = {
|
|
2604
|
-
HOSTED: 'hosted',
|
|
2605
|
-
C15T: 'c15t',
|
|
2606
|
-
OFFLINE: 'offline',
|
|
2607
|
-
SELF_HOSTED: 'self-hosted',
|
|
2608
|
-
CUSTOM: 'custom'
|
|
2609
|
-
};
|
|
2610
|
-
const LAYOUT_PATTERNS = [
|
|
2611
|
-
'app/layout.tsx',
|
|
2612
|
-
'app/layout.ts',
|
|
2613
|
-
'app/layout.jsx',
|
|
2614
|
-
'app/layout.js',
|
|
2615
|
-
'src/app/layout.tsx',
|
|
2616
|
-
'src/app/layout.ts',
|
|
2617
|
-
'src/app/layout.jsx',
|
|
2618
|
-
'src/app/layout.js',
|
|
2619
|
-
'app/*/layout.tsx',
|
|
2620
|
-
'app/*/layout.ts',
|
|
2621
|
-
'app/*/layout.jsx',
|
|
2622
|
-
'app/*/layout.js',
|
|
2623
|
-
'src/app/*/layout.tsx',
|
|
2624
|
-
'src/app/*/layout.ts',
|
|
2625
|
-
'src/app/*/layout.jsx',
|
|
2626
|
-
'src/app/*/layout.js',
|
|
2627
|
-
'app/*/*/layout.tsx',
|
|
2628
|
-
'app/*/*/layout.ts',
|
|
2629
|
-
'src/app/*/*/layout.tsx',
|
|
2630
|
-
'src/app/*/*/layout.ts'
|
|
2631
|
-
];
|
|
2632
|
-
const PAGES_APP_PATTERNS = [
|
|
2633
|
-
'pages/_app.tsx',
|
|
2634
|
-
'pages/_app.ts',
|
|
2635
|
-
'pages/_app.jsx',
|
|
2636
|
-
'pages/_app.js',
|
|
2637
|
-
'src/pages/_app.tsx',
|
|
2638
|
-
'src/pages/_app.ts',
|
|
2639
|
-
'src/pages/_app.jsx',
|
|
2640
|
-
'src/pages/_app.js'
|
|
2641
|
-
];
|
|
2642
3164
|
const ERROR_CATALOG = {
|
|
2643
3165
|
AUTH_FAILED: {
|
|
2644
3166
|
code: 'AUTH_FAILED',
|
|
@@ -2715,7 +3237,7 @@ const ERROR_CATALOG = {
|
|
|
2715
3237
|
},
|
|
2716
3238
|
CONTROL_PLANE_CONNECTION_FAILED: {
|
|
2717
3239
|
code: 'CONTROL_PLANE_CONNECTION_FAILED',
|
|
2718
|
-
message: 'Could not connect to
|
|
3240
|
+
message: 'Could not connect to inth.com',
|
|
2719
3241
|
hint: `Check if ${constants_URLS.CONSENT_IO} is accessible`
|
|
2720
3242
|
},
|
|
2721
3243
|
API_ERROR: {
|
|
@@ -2726,17 +3248,17 @@ const ERROR_CATALOG = {
|
|
|
2726
3248
|
URL_INVALID: {
|
|
2727
3249
|
code: 'URL_INVALID',
|
|
2728
3250
|
message: 'Invalid URL format',
|
|
2729
|
-
hint: 'Expected format: https://your-
|
|
3251
|
+
hint: 'Expected format: https://your-project.inth.app'
|
|
2730
3252
|
},
|
|
2731
3253
|
INSTANCE_NOT_FOUND: {
|
|
2732
3254
|
code: 'INSTANCE_NOT_FOUND',
|
|
2733
|
-
message: '
|
|
2734
|
-
hint: 'Run `c15t
|
|
3255
|
+
message: 'Project not found',
|
|
3256
|
+
hint: 'Run `c15t projects list` to see available projects'
|
|
2735
3257
|
},
|
|
2736
3258
|
INSTANCE_NAME_INVALID: {
|
|
2737
3259
|
code: 'INSTANCE_NAME_INVALID',
|
|
2738
|
-
message: 'Invalid
|
|
2739
|
-
hint: '
|
|
3260
|
+
message: 'Invalid project slug',
|
|
3261
|
+
hint: 'Project slugs must be alphanumeric with hyphens'
|
|
2740
3262
|
},
|
|
2741
3263
|
FILE_NOT_FOUND: {
|
|
2742
3264
|
code: 'FILE_NOT_FOUND',
|
|
@@ -3255,11 +3777,19 @@ async function getAuthState() {
|
|
|
3255
3777
|
isExpired: isTokenExpired(config)
|
|
3256
3778
|
};
|
|
3257
3779
|
}
|
|
3780
|
+
async function isLoggedIn() {
|
|
3781
|
+
const state = await getAuthState();
|
|
3782
|
+
return state.isLoggedIn && !state.isExpired;
|
|
3783
|
+
}
|
|
3258
3784
|
async function config_store_getAccessToken() {
|
|
3259
3785
|
const config = await loadConfig();
|
|
3260
3786
|
if (!config || isTokenExpired(config)) return null;
|
|
3261
3787
|
return config.accessToken;
|
|
3262
3788
|
}
|
|
3789
|
+
async function getSelectedInstanceId() {
|
|
3790
|
+
const config = await loadConfig();
|
|
3791
|
+
return config?.selectedInstanceId || null;
|
|
3792
|
+
}
|
|
3263
3793
|
async function setSelectedInstanceId(instanceId) {
|
|
3264
3794
|
await updateConfig({
|
|
3265
3795
|
selectedInstanceId: instanceId
|
|
@@ -3508,7 +4038,7 @@ function sleep(ms) {
|
|
|
3508
4038
|
async function getDevToolsOption({ context, handleCancel, onCancel }) {
|
|
3509
4039
|
const isReactProject = '@c15t/react' === context.framework.pkg || '@c15t/nextjs' === context.framework.pkg;
|
|
3510
4040
|
context.logger.info("c15t DevTools helps you inspect consent state, scripts, and location overrides during development.");
|
|
3511
|
-
context.logger.info('Learn more: https://
|
|
4041
|
+
context.logger.info('Learn more: https://c15t.com/docs/dev-tools/overview');
|
|
3512
4042
|
const enableDevTools = await __rspack_external__clack_prompts_3cae1695.select({
|
|
3513
4043
|
message: 'Install and enable c15t DevTools?',
|
|
3514
4044
|
options: [
|
|
@@ -3539,7 +4069,7 @@ async function getSSROption({ context, handleCancel, onCancel }) {
|
|
|
3539
4069
|
context.logger.info('SSR consent prefetch starts data loading on the server for faster banner visibility.');
|
|
3540
4070
|
context.logger.info('Tradeoff: this uses Next.js headers() and makes the route dynamic (not fully static).');
|
|
3541
4071
|
context.logger.info('On slow backends or cross-region setups, SSR can increase TTFB. Measure both TTFB and banner visibility.');
|
|
3542
|
-
context.logger.info('Learn more: https://
|
|
4072
|
+
context.logger.info('Learn more: https://c15t.com/docs/frameworks/nextjs/ssr');
|
|
3543
4073
|
const enableSSR = await __rspack_external__clack_prompts_3cae1695.select({
|
|
3544
4074
|
message: 'Enable SSR consent prefetch? (faster first banner visibility, dynamic route)',
|
|
3545
4075
|
options: [
|
|
@@ -3668,7 +4198,7 @@ class ControlPlaneClient {
|
|
|
3668
4198
|
const instances = await this.listInstances();
|
|
3669
4199
|
const instance = instances.find((item)=>item.id === id);
|
|
3670
4200
|
if (!instance) throw new CliError('INSTANCE_NOT_FOUND', {
|
|
3671
|
-
details: `
|
|
4201
|
+
details: `Project not found: ${id}`
|
|
3672
4202
|
});
|
|
3673
4203
|
return instance;
|
|
3674
4204
|
}
|
|
@@ -3762,8 +4292,8 @@ function createValidator(validate, errorMessage) {
|
|
|
3762
4292
|
};
|
|
3763
4293
|
}
|
|
3764
4294
|
createValidator(isValidUrl, 'Please enter a valid URL (e.g., https://example.com)');
|
|
3765
|
-
createValidator(isValidC15tUrl, 'Please enter a valid
|
|
3766
|
-
const validateInstanceName = createValidator(isValidInstanceName, '
|
|
4295
|
+
createValidator(isValidC15tUrl, 'Please enter a valid hosted URL (e.g., https://my-app.inth.app)');
|
|
4296
|
+
const validateInstanceName = createValidator(isValidInstanceName, 'Project slug must be 3-63 lowercase alphanumeric characters with hyphens');
|
|
3767
4297
|
createValidator((value)=>value.trim().length > 0, 'This field is required');
|
|
3768
4298
|
function isCancel(value) {
|
|
3769
4299
|
return __rspack_external__clack_prompts_3cae1695.isCancel(value);
|
|
@@ -3795,7 +4325,7 @@ const modeSelectionActor = fromPromise(async ({ input })=>{
|
|
|
3795
4325
|
{
|
|
3796
4326
|
value: 'hosted',
|
|
3797
4327
|
label: 'Hosted',
|
|
3798
|
-
hint: '
|
|
4328
|
+
hint: 'inth.com or self-hosted backend URL'
|
|
3799
4329
|
},
|
|
3800
4330
|
{
|
|
3801
4331
|
value: 'offline',
|
|
@@ -3913,7 +4443,7 @@ async function createInstanceInteractively(client, cliContext) {
|
|
|
3913
4443
|
})),
|
|
3914
4444
|
initialValue: organizations[0]?.organizationSlug
|
|
3915
4445
|
});
|
|
3916
|
-
if (isCancel(orgSelection)) throw new PromptCancelledError('
|
|
4446
|
+
if (isCancel(orgSelection)) throw new PromptCancelledError('project_create_org_slug');
|
|
3917
4447
|
const v2Regions = regions.filter((region)=>'v2' === region.family);
|
|
3918
4448
|
if (0 === v2Regions.length) throw new CliError('API_ERROR', {
|
|
3919
4449
|
details: 'No v2 provisioning regions available'
|
|
@@ -3927,15 +4457,15 @@ async function createInstanceInteractively(client, cliContext) {
|
|
|
3927
4457
|
})),
|
|
3928
4458
|
initialValue: v2Regions.find((region)=>'us-east-1' === region.id)?.id
|
|
3929
4459
|
});
|
|
3930
|
-
if (isCancel(regionSelection)) throw new PromptCancelledError('
|
|
4460
|
+
if (isCancel(regionSelection)) throw new PromptCancelledError('project_create_region');
|
|
3931
4461
|
const slugInput = await __rspack_external__clack_prompts_3cae1695.text({
|
|
3932
|
-
message: 'New
|
|
4462
|
+
message: 'New project slug:',
|
|
3933
4463
|
placeholder: 'my-app',
|
|
3934
4464
|
validate: (value)=>validateInstanceName(value?.trim() ?? '')
|
|
3935
4465
|
});
|
|
3936
|
-
if (isCancel(slugInput)) throw new PromptCancelledError('
|
|
4466
|
+
if (isCancel(slugInput)) throw new PromptCancelledError('project_create_name');
|
|
3937
4467
|
const slug = slugInput.trim();
|
|
3938
|
-
const createSpinner = createTaskSpinner(`Creating
|
|
4468
|
+
const createSpinner = createTaskSpinner(`Creating project "${slug}"...`);
|
|
3939
4469
|
createSpinner.start();
|
|
3940
4470
|
try {
|
|
3941
4471
|
const instance = await client.createInstance({
|
|
@@ -3945,17 +4475,17 @@ async function createInstanceInteractively(client, cliContext) {
|
|
|
3945
4475
|
region: regionSelection
|
|
3946
4476
|
}
|
|
3947
4477
|
});
|
|
3948
|
-
createSpinner.success('
|
|
3949
|
-
cliContext.logger.info('Created as a v2 development
|
|
4478
|
+
createSpinner.success('Project created');
|
|
4479
|
+
cliContext.logger.info('Created as a v2 development project. Enable production mode in the dashboard when you are ready.');
|
|
3950
4480
|
return instance;
|
|
3951
4481
|
} catch (error) {
|
|
3952
|
-
createSpinner.error('Failed to create
|
|
4482
|
+
createSpinner.error('Failed to create project');
|
|
3953
4483
|
throw error;
|
|
3954
4484
|
}
|
|
3955
4485
|
}
|
|
3956
4486
|
async function selectOrCreateInstance(cliContext) {
|
|
3957
4487
|
const baseUrl = getControlPlaneBaseUrl();
|
|
3958
|
-
const listSpinner = createTaskSpinner('Fetching your
|
|
4488
|
+
const listSpinner = createTaskSpinner('Fetching your inth.com projects...');
|
|
3959
4489
|
listSpinner.start();
|
|
3960
4490
|
const client = await createControlPlaneClientFromConfig(baseUrl);
|
|
3961
4491
|
if (!client) {
|
|
@@ -3966,11 +4496,11 @@ async function selectOrCreateInstance(cliContext) {
|
|
|
3966
4496
|
const instances = await client.listInstances();
|
|
3967
4497
|
listSpinner.stop();
|
|
3968
4498
|
if (0 === instances.length) {
|
|
3969
|
-
cliContext.logger.info('No
|
|
4499
|
+
cliContext.logger.info('No projects found. Creating a new project for this local project.');
|
|
3970
4500
|
return await createInstanceInteractively(client, cliContext);
|
|
3971
4501
|
}
|
|
3972
4502
|
const selectedId = await __rspack_external__clack_prompts_3cae1695.select({
|
|
3973
|
-
message: 'Select
|
|
4503
|
+
message: 'Select a project to use:',
|
|
3974
4504
|
options: [
|
|
3975
4505
|
...instances.map((instance)=>({
|
|
3976
4506
|
value: instance.id,
|
|
@@ -3979,12 +4509,12 @@ async function selectOrCreateInstance(cliContext) {
|
|
|
3979
4509
|
})),
|
|
3980
4510
|
{
|
|
3981
4511
|
value: '__create__',
|
|
3982
|
-
label: 'Create new
|
|
3983
|
-
hint: 'Provision a new
|
|
4512
|
+
label: 'Create new project',
|
|
4513
|
+
hint: 'Provision a new inth.com project now'
|
|
3984
4514
|
}
|
|
3985
4515
|
]
|
|
3986
4516
|
});
|
|
3987
|
-
if (isCancel(selectedId)) throw new PromptCancelledError('
|
|
4517
|
+
if (isCancel(selectedId)) throw new PromptCancelledError('project_select');
|
|
3988
4518
|
if ('__create__' === selectedId) return await createInstanceInteractively(client, cliContext);
|
|
3989
4519
|
const selected = instances.find((instance)=>instance.id === selectedId);
|
|
3990
4520
|
if (!selected) throw new CliError('INSTANCE_NOT_FOUND');
|
|
@@ -4004,8 +4534,8 @@ const hostedModeActor = fromPromise(async ({ input })=>{
|
|
|
4004
4534
|
message: 'Choose your hosted backend option:',
|
|
4005
4535
|
options: [
|
|
4006
4536
|
{
|
|
4007
|
-
value: '
|
|
4008
|
-
label: '
|
|
4537
|
+
value: 'inth.com',
|
|
4538
|
+
label: 'inth.com (Recommended)',
|
|
4009
4539
|
hint: 'Managed infrastucture'
|
|
4010
4540
|
},
|
|
4011
4541
|
{
|
|
@@ -4014,7 +4544,7 @@ const hostedModeActor = fromPromise(async ({ input })=>{
|
|
|
4014
4544
|
hint: 'Use your own deployed c15t backend'
|
|
4015
4545
|
}
|
|
4016
4546
|
],
|
|
4017
|
-
initialValue: '
|
|
4547
|
+
initialValue: 'inth.com'
|
|
4018
4548
|
});
|
|
4019
4549
|
if (isCancel(providerSelection)) throw new PromptCancelledError('hosted_provider');
|
|
4020
4550
|
provider = providerSelection;
|
|
@@ -4032,29 +4562,28 @@ const hostedModeActor = fromPromise(async ({ input })=>{
|
|
|
4032
4562
|
};
|
|
4033
4563
|
}
|
|
4034
4564
|
if (!isV2ModeEnabled()) {
|
|
4035
|
-
cliContext.logger.info('consent.io sign-in is currently disabled. Set V2=1 to enable sign-in and instance selection.');
|
|
4036
4565
|
const url = await promptBackendURL({
|
|
4037
|
-
message: 'Enter your
|
|
4038
|
-
placeholder: 'https://your-
|
|
4566
|
+
message: 'Enter your inth.com project URL:',
|
|
4567
|
+
placeholder: 'https://your-project.inth.app',
|
|
4039
4568
|
initialURL,
|
|
4040
4569
|
stage: 'consent_manual_url'
|
|
4041
4570
|
});
|
|
4042
4571
|
return {
|
|
4043
4572
|
url,
|
|
4044
|
-
provider: '
|
|
4573
|
+
provider: 'inth.com'
|
|
4045
4574
|
};
|
|
4046
4575
|
}
|
|
4047
4576
|
const setupMethod = await __rspack_external__clack_prompts_3cae1695.select({
|
|
4048
|
-
message: 'How do you want to configure
|
|
4577
|
+
message: 'How do you want to configure inth.com?',
|
|
4049
4578
|
options: [
|
|
4050
4579
|
{
|
|
4051
4580
|
value: 'sign-in',
|
|
4052
|
-
label: 'Sign in and pick
|
|
4053
|
-
hint: 'List existing
|
|
4581
|
+
label: 'Sign in and pick a project',
|
|
4582
|
+
hint: 'List existing projects or create a new one'
|
|
4054
4583
|
},
|
|
4055
4584
|
{
|
|
4056
4585
|
value: 'manual-url',
|
|
4057
|
-
label: 'Enter
|
|
4586
|
+
label: 'Enter project URL manually',
|
|
4058
4587
|
hint: 'Use an existing backend URL'
|
|
4059
4588
|
}
|
|
4060
4589
|
],
|
|
@@ -4063,23 +4592,23 @@ const hostedModeActor = fromPromise(async ({ input })=>{
|
|
|
4063
4592
|
if (isCancel(setupMethod)) throw new PromptCancelledError('consent_setup_method');
|
|
4064
4593
|
if ('manual-url' === setupMethod) {
|
|
4065
4594
|
const url = await promptBackendURL({
|
|
4066
|
-
message: 'Enter your
|
|
4067
|
-
placeholder: 'https://your-
|
|
4595
|
+
message: 'Enter your inth.com project URL:',
|
|
4596
|
+
placeholder: 'https://your-project.inth.app',
|
|
4068
4597
|
initialURL,
|
|
4069
4598
|
stage: 'consent_manual_url'
|
|
4070
4599
|
});
|
|
4071
4600
|
return {
|
|
4072
4601
|
url,
|
|
4073
|
-
provider: '
|
|
4602
|
+
provider: 'inth.com'
|
|
4074
4603
|
};
|
|
4075
4604
|
}
|
|
4076
4605
|
await runConsentLogin(cliContext);
|
|
4077
4606
|
const instance = await selectOrCreateInstance(cliContext);
|
|
4078
4607
|
await setSelectedInstanceId(instance.id);
|
|
4079
|
-
cliContext.logger.info(`Using
|
|
4608
|
+
cliContext.logger.info(`Using project ${picocolors.cyan(instance.name)} (${picocolors.dim(instance.id)})`);
|
|
4080
4609
|
return {
|
|
4081
4610
|
url: instance.url,
|
|
4082
|
-
provider: '
|
|
4611
|
+
provider: 'inth.com'
|
|
4083
4612
|
};
|
|
4084
4613
|
});
|
|
4085
4614
|
const backendOptionsActor = fromPromise(async ({ input })=>{
|
|
@@ -4093,7 +4622,7 @@ const backendOptionsActor = fromPromise(async ({ input })=>{
|
|
|
4093
4622
|
if ('@c15t/nextjs' === cliContext.framework.pkg) {
|
|
4094
4623
|
cliContext.logger.info('Learn more about Next.js Rewrites: https://nextjs.org/docs/app/api-reference/config/next-config-js/rewrites');
|
|
4095
4624
|
const proxyResult = await __rspack_external__clack_prompts_3cae1695.confirm({
|
|
4096
|
-
message: 'Proxy requests to your
|
|
4625
|
+
message: 'Proxy requests to your project with Next.js Rewrites? (Recommended)',
|
|
4097
4626
|
initialValue: true
|
|
4098
4627
|
});
|
|
4099
4628
|
if (isCancel(proxyResult)) throw new PromptCancelledError('proxy_nextjs');
|
|
@@ -4130,7 +4659,7 @@ const frontendOptionsActor = fromPromise(async ({ input })=>{
|
|
|
4130
4659
|
});
|
|
4131
4660
|
}
|
|
4132
4661
|
cliContext.logger.info('Choose how you want your consent UI components generated.');
|
|
4133
|
-
cliContext.logger.info('Learn more: https://
|
|
4662
|
+
cliContext.logger.info('Learn more: https://c15t.com/docs/frameworks/nextjs/customization');
|
|
4134
4663
|
const styleResult = await __rspack_external__clack_prompts_3cae1695.select({
|
|
4135
4664
|
message: 'UI component style:',
|
|
4136
4665
|
options: [
|
|
@@ -4536,7 +5065,7 @@ function normalizeSelectedMode(mode) {
|
|
|
4536
5065
|
}
|
|
4537
5066
|
function getHostedProviderFromMode(mode) {
|
|
4538
5067
|
if ('self-hosted' === mode) return 'self-hosted';
|
|
4539
|
-
if ('c15t' === mode) return '
|
|
5068
|
+
if ('c15t' === mode) return 'inth.com';
|
|
4540
5069
|
return null;
|
|
4541
5070
|
}
|
|
4542
5071
|
const generateMachine = setup({
|
|
@@ -4931,7 +5460,7 @@ const generateMachine = setup({
|
|
|
4931
5460
|
const { logger, packageManager } = context.cliContext;
|
|
4932
5461
|
if ('hosted' === context.selectedMode && 'self-hosted' === context.hostedProvider) {
|
|
4933
5462
|
logger.info('Setup your backend with the c15t docs:');
|
|
4934
|
-
logger.info('https://
|
|
5463
|
+
logger.info('https://c15t.com/docs/self-host/v2');
|
|
4935
5464
|
} else if ('custom' === context.selectedMode) logger.info('Configuration Complete! Implement your custom endpoint handlers.');
|
|
4936
5465
|
if (context.installConfirmed && !context.installSucceeded) logger.warn('Dependency installation failed. Please check errors and install manually.');
|
|
4937
5466
|
else if (!context.installConfirmed && context.dependenciesToAdd.length > 0) {
|
|
@@ -5033,6 +5562,29 @@ const generateMachine = setup({
|
|
|
5033
5562
|
}
|
|
5034
5563
|
}
|
|
5035
5564
|
});
|
|
5565
|
+
function getSetupTrigger(modeArg, resumed) {
|
|
5566
|
+
if (resumed) return 'resume';
|
|
5567
|
+
if (modeArg) return 'arg';
|
|
5568
|
+
return 'interactive';
|
|
5569
|
+
}
|
|
5570
|
+
function normalizeSetupReason(finalState, finalContext) {
|
|
5571
|
+
if ('complete' === finalState) return;
|
|
5572
|
+
if ('preflightError' === finalState || !finalContext.preflightPassed && finalContext.preflightChecks.some((check)=>'fail' === check.status)) return 'preflight_failed';
|
|
5573
|
+
if ('exited' === finalState || 'cancelled' === finalState) {
|
|
5574
|
+
const reason = finalContext.cancelReason?.toLowerCase();
|
|
5575
|
+
if (!reason) return 'user_cancelled';
|
|
5576
|
+
if (reason.includes('signal')) return 'signal_interrupted';
|
|
5577
|
+
if (reason.includes('mode selection')) return 'mode_selection_cancelled';
|
|
5578
|
+
if (reason.includes('hosted setup')) return 'hosted_setup_cancelled';
|
|
5579
|
+
if (reason.includes('backend options')) return 'backend_options_cancelled';
|
|
5580
|
+
if (reason.includes('frontend options')) return 'frontend_options_cancelled';
|
|
5581
|
+
if (reason.includes("scripts option")) return "scripts_options_cancelled";
|
|
5582
|
+
return 'user_cancelled';
|
|
5583
|
+
}
|
|
5584
|
+
const lastError = finalContext.errors[finalContext.errors.length - 1];
|
|
5585
|
+
if (lastError?.state) return `${lastError.state}_failed`;
|
|
5586
|
+
return 'machine_error';
|
|
5587
|
+
}
|
|
5036
5588
|
async function runGenerateMachine(options) {
|
|
5037
5589
|
const { context: cliContext, modeArg, resume = false, debug = false, persist = true } = options;
|
|
5038
5590
|
const { logger, telemetry } = cliContext;
|
|
@@ -5069,7 +5621,9 @@ async function runGenerateMachine(options) {
|
|
|
5069
5621
|
const combinedSubscriber = combineSubscribers(...subscribers);
|
|
5070
5622
|
actor.subscribe((snapshot)=>combinedSubscriber(snapshot));
|
|
5071
5623
|
telemetry.trackEvent(TelemetryEventName.ONBOARDING_STARTED, {
|
|
5072
|
-
resumed: resume && void 0 !== snapshot
|
|
5624
|
+
resumed: resume && void 0 !== snapshot,
|
|
5625
|
+
trigger: getSetupTrigger(modeArg, resume && void 0 !== snapshot),
|
|
5626
|
+
requestedMode: modeArg ?? void 0
|
|
5073
5627
|
});
|
|
5074
5628
|
telemetry.flushSync();
|
|
5075
5629
|
actor.start();
|
|
@@ -5085,11 +5639,28 @@ async function runGenerateMachine(options) {
|
|
|
5085
5639
|
const duration = Date.now() - startTime;
|
|
5086
5640
|
clearSnapshot(persistPath).catch(()=>{});
|
|
5087
5641
|
const success = 'complete' === finalState;
|
|
5642
|
+
const durationMs = duration;
|
|
5643
|
+
const reason = normalizeSetupReason(finalState, finalContext);
|
|
5644
|
+
const result = success ? 'success' : 'exited' === finalState ? 'cancelled' : 'failed';
|
|
5088
5645
|
telemetry.trackEvent(TelemetryEventName.ONBOARDING_COMPLETED, {
|
|
5089
5646
|
success,
|
|
5647
|
+
result,
|
|
5648
|
+
reason,
|
|
5649
|
+
trigger: getSetupTrigger(modeArg, resume && void 0 !== snapshot),
|
|
5650
|
+
resumed: resume && void 0 !== snapshot,
|
|
5090
5651
|
selectedMode: finalContext.selectedMode ?? void 0,
|
|
5652
|
+
hostedProvider: finalContext.hostedProvider ?? void 0,
|
|
5091
5653
|
installDependencies: finalContext.installSucceeded,
|
|
5654
|
+
installConfirmed: finalContext.installConfirmed,
|
|
5655
|
+
installAttempted: finalContext.installAttempted,
|
|
5656
|
+
installSucceeded: finalContext.installSucceeded,
|
|
5657
|
+
dependencyCount: finalContext.dependenciesToAdd.length,
|
|
5658
|
+
filesCreatedCount: finalContext.filesCreated.length,
|
|
5659
|
+
filesModifiedCount: finalContext.filesModified.length,
|
|
5660
|
+
errorsCount: finalContext.errors.length,
|
|
5661
|
+
cancelReason: finalContext.cancelReason ?? void 0,
|
|
5092
5662
|
duration,
|
|
5663
|
+
durationMs,
|
|
5093
5664
|
finalState
|
|
5094
5665
|
});
|
|
5095
5666
|
resolve({
|
|
@@ -5139,6 +5710,294 @@ async function generate(context, mode) {
|
|
|
5139
5710
|
];
|
|
5140
5711
|
return generateAction(context);
|
|
5141
5712
|
}
|
|
5713
|
+
function instances_formatInstanceLabel(instance) {
|
|
5714
|
+
if (instance.organizationSlug) return `${instance.organizationSlug}/${instance.name}`;
|
|
5715
|
+
return instance.name;
|
|
5716
|
+
}
|
|
5717
|
+
function instances_formatInstanceRegion(instance) {
|
|
5718
|
+
return `(${instance.region ?? 'unknown'})`;
|
|
5719
|
+
}
|
|
5720
|
+
async function requireAuth(context) {
|
|
5721
|
+
if (!await isLoggedIn()) {
|
|
5722
|
+
context.logger.error('You must be logged in to manage projects');
|
|
5723
|
+
context.logger.message(`Run ${picocolors.cyan('c15t login')} to authenticate`);
|
|
5724
|
+
throw new CliError('AUTH_NOT_LOGGED_IN');
|
|
5725
|
+
}
|
|
5726
|
+
}
|
|
5727
|
+
async function listAction(context) {
|
|
5728
|
+
const { logger, telemetry } = context;
|
|
5729
|
+
const baseUrl = getControlPlaneBaseUrl();
|
|
5730
|
+
await requireAuth(context);
|
|
5731
|
+
const spinner = createTaskSpinner('Fetching projects...');
|
|
5732
|
+
spinner.start();
|
|
5733
|
+
try {
|
|
5734
|
+
const client = await createControlPlaneClientFromConfig(baseUrl);
|
|
5735
|
+
if (!client) {
|
|
5736
|
+
spinner.stop();
|
|
5737
|
+
throw new CliError('AUTH_NOT_LOGGED_IN');
|
|
5738
|
+
}
|
|
5739
|
+
const instances = await client.listInstances();
|
|
5740
|
+
await client.close();
|
|
5741
|
+
spinner.stop();
|
|
5742
|
+
telemetry.trackEvent(TelemetryEventName.PROJECTS_LISTED, {
|
|
5743
|
+
count: instances.length
|
|
5744
|
+
});
|
|
5745
|
+
if (0 === instances.length) {
|
|
5746
|
+
logger.message('');
|
|
5747
|
+
logger.message('No projects found.');
|
|
5748
|
+
logger.message('');
|
|
5749
|
+
logger.message(`Run ${picocolors.cyan('c15t projects create')} to create one`);
|
|
5750
|
+
return;
|
|
5751
|
+
}
|
|
5752
|
+
const selectedId = await getSelectedInstanceId();
|
|
5753
|
+
logger.message('');
|
|
5754
|
+
logger.message(picocolors.bold('Your projects:'));
|
|
5755
|
+
logger.message('');
|
|
5756
|
+
for (const instance of instances){
|
|
5757
|
+
const isSelected = instance.id === selectedId;
|
|
5758
|
+
const status = getStatusColor(instance.status);
|
|
5759
|
+
const marker = isSelected ? picocolors.green('▸ ') : ' ';
|
|
5760
|
+
const label = instances_formatInstanceLabel(instance);
|
|
5761
|
+
logger.message(`${marker}${picocolors.bold(label)} ${picocolors.dim(`(${instance.id})`)}`);
|
|
5762
|
+
logger.message(` Region: ${picocolors.cyan(instances_formatInstanceRegion(instance))}`);
|
|
5763
|
+
logger.message(` Status: ${status}`);
|
|
5764
|
+
logger.message('');
|
|
5765
|
+
}
|
|
5766
|
+
if (selectedId) logger.message(picocolors.dim('▸ indicates the currently selected project'));
|
|
5767
|
+
} catch (error) {
|
|
5768
|
+
spinner.stop();
|
|
5769
|
+
throw error;
|
|
5770
|
+
}
|
|
5771
|
+
}
|
|
5772
|
+
async function selectAction(context) {
|
|
5773
|
+
const { logger, telemetry, commandArgs } = context;
|
|
5774
|
+
const baseUrl = getControlPlaneBaseUrl();
|
|
5775
|
+
await requireAuth(context);
|
|
5776
|
+
const spinner = createTaskSpinner('Fetching projects...');
|
|
5777
|
+
spinner.start();
|
|
5778
|
+
try {
|
|
5779
|
+
const client = await createControlPlaneClientFromConfig(baseUrl);
|
|
5780
|
+
if (!client) {
|
|
5781
|
+
spinner.stop();
|
|
5782
|
+
throw new CliError('AUTH_NOT_LOGGED_IN');
|
|
5783
|
+
}
|
|
5784
|
+
const instances = await client.listInstances();
|
|
5785
|
+
await client.close();
|
|
5786
|
+
spinner.stop();
|
|
5787
|
+
if (0 === instances.length) {
|
|
5788
|
+
logger.message('No projects found.');
|
|
5789
|
+
logger.message(`Run ${picocolors.cyan('c15t projects create')} to create one`);
|
|
5790
|
+
return;
|
|
5791
|
+
}
|
|
5792
|
+
let selectedInstance;
|
|
5793
|
+
if (commandArgs.length > 0) {
|
|
5794
|
+
const query = commandArgs[0];
|
|
5795
|
+
const found = instances.find((i)=>i.id === query || i.name === query || instances_formatInstanceLabel(i) === query);
|
|
5796
|
+
if (!found) throw new CliError('INSTANCE_NOT_FOUND', {
|
|
5797
|
+
details: `No project found with ID, name, or org/name: ${query}`
|
|
5798
|
+
});
|
|
5799
|
+
selectedInstance = found;
|
|
5800
|
+
} else {
|
|
5801
|
+
const currentId = await getSelectedInstanceId();
|
|
5802
|
+
const result = await __rspack_external__clack_prompts_3cae1695.select({
|
|
5803
|
+
message: 'Select a project:',
|
|
5804
|
+
options: instances.map((instance)=>({
|
|
5805
|
+
value: instance.id,
|
|
5806
|
+
label: instances_formatInstanceLabel(instance),
|
|
5807
|
+
hint: instance.id === currentId ? `(currently selected) • ${instances_formatInstanceRegion(instance)}` : instances_formatInstanceRegion(instance)
|
|
5808
|
+
}))
|
|
5809
|
+
});
|
|
5810
|
+
if (__rspack_external__clack_prompts_3cae1695.isCancel(result)) return void logger.info('Selection cancelled');
|
|
5811
|
+
selectedInstance = instances.find((i)=>i.id === result);
|
|
5812
|
+
}
|
|
5813
|
+
await setSelectedInstanceId(selectedInstance.id);
|
|
5814
|
+
telemetry.trackEvent(TelemetryEventName.PROJECT_SELECTED, {
|
|
5815
|
+
projectId: selectedInstance.id
|
|
5816
|
+
});
|
|
5817
|
+
logger.success(`Selected project: ${picocolors.cyan(instances_formatInstanceLabel(selectedInstance))}`);
|
|
5818
|
+
logger.message(`Region: ${instances_formatInstanceRegion(selectedInstance)}`);
|
|
5819
|
+
} catch (error) {
|
|
5820
|
+
spinner.stop();
|
|
5821
|
+
throw error;
|
|
5822
|
+
}
|
|
5823
|
+
}
|
|
5824
|
+
async function createAction(context) {
|
|
5825
|
+
const { logger, telemetry, commandArgs } = context;
|
|
5826
|
+
const baseUrl = getControlPlaneBaseUrl();
|
|
5827
|
+
await requireAuth(context);
|
|
5828
|
+
const client = await createControlPlaneClientFromConfig(baseUrl);
|
|
5829
|
+
if (!client) throw new CliError('AUTH_NOT_LOGGED_IN');
|
|
5830
|
+
try {
|
|
5831
|
+
const preloadSpinner = createTaskSpinner('Loading organizations and regions...');
|
|
5832
|
+
preloadSpinner.start();
|
|
5833
|
+
let organizations;
|
|
5834
|
+
let regions;
|
|
5835
|
+
try {
|
|
5836
|
+
[organizations, regions] = await Promise.all([
|
|
5837
|
+
client.listOrganizations(),
|
|
5838
|
+
client.listRegions()
|
|
5839
|
+
]);
|
|
5840
|
+
} finally{
|
|
5841
|
+
preloadSpinner.stop();
|
|
5842
|
+
}
|
|
5843
|
+
if (0 === organizations.length) throw new CliError('API_ERROR', {
|
|
5844
|
+
details: 'No organizations available for this account'
|
|
5845
|
+
});
|
|
5846
|
+
if (0 === regions.length) throw new CliError('API_ERROR', {
|
|
5847
|
+
details: 'No provisioning regions available'
|
|
5848
|
+
});
|
|
5849
|
+
let name;
|
|
5850
|
+
if (commandArgs.length > 0) {
|
|
5851
|
+
const providedName = commandArgs[0];
|
|
5852
|
+
if (!providedName) throw new CliError('INSTANCE_NAME_INVALID', {
|
|
5853
|
+
details: 'Project slug is required'
|
|
5854
|
+
});
|
|
5855
|
+
const error = validateInstanceName(providedName);
|
|
5856
|
+
if (error) throw new CliError('INSTANCE_NAME_INVALID', {
|
|
5857
|
+
details: error
|
|
5858
|
+
});
|
|
5859
|
+
name = providedName;
|
|
5860
|
+
} else {
|
|
5861
|
+
const result = await __rspack_external__clack_prompts_3cae1695.text({
|
|
5862
|
+
message: 'Project slug:',
|
|
5863
|
+
placeholder: 'my-app',
|
|
5864
|
+
validate: (value)=>validateInstanceName(value?.trim() ?? '')
|
|
5865
|
+
});
|
|
5866
|
+
if (__rspack_external__clack_prompts_3cae1695.isCancel(result)) return void logger.info('Creation cancelled');
|
|
5867
|
+
name = result;
|
|
5868
|
+
}
|
|
5869
|
+
name = name.trim();
|
|
5870
|
+
const nameValidationError = validateInstanceName(name);
|
|
5871
|
+
if (nameValidationError) throw new CliError('INSTANCE_NAME_INVALID', {
|
|
5872
|
+
details: nameValidationError
|
|
5873
|
+
});
|
|
5874
|
+
const orgSelection = await __rspack_external__clack_prompts_3cae1695.select({
|
|
5875
|
+
message: 'Select organization:',
|
|
5876
|
+
options: organizations.map((org)=>({
|
|
5877
|
+
value: org.organizationSlug,
|
|
5878
|
+
label: org.organizationName,
|
|
5879
|
+
hint: `${org.organizationSlug} • ${org.role}`
|
|
5880
|
+
})),
|
|
5881
|
+
initialValue: organizations[0]?.organizationSlug
|
|
5882
|
+
});
|
|
5883
|
+
if (__rspack_external__clack_prompts_3cae1695.isCancel(orgSelection)) return void logger.info('Creation cancelled');
|
|
5884
|
+
const v2Regions = regions.filter((region)=>'v2' === region.family);
|
|
5885
|
+
if (0 === v2Regions.length) throw new CliError('API_ERROR', {
|
|
5886
|
+
details: 'No v2 provisioning regions available'
|
|
5887
|
+
});
|
|
5888
|
+
const regionSelection = await __rspack_external__clack_prompts_3cae1695.select({
|
|
5889
|
+
message: 'Select V2 region:',
|
|
5890
|
+
options: v2Regions.map((region)=>({
|
|
5891
|
+
value: region.id,
|
|
5892
|
+
label: region.id,
|
|
5893
|
+
hint: region.label
|
|
5894
|
+
})),
|
|
5895
|
+
initialValue: v2Regions.find((region)=>'us-east-1' === region.id)?.id
|
|
5896
|
+
});
|
|
5897
|
+
if (__rspack_external__clack_prompts_3cae1695.isCancel(regionSelection)) return void logger.info('Creation cancelled');
|
|
5898
|
+
const spinner = createTaskSpinner(`Creating project "${name}"...`);
|
|
5899
|
+
spinner.start();
|
|
5900
|
+
let instance;
|
|
5901
|
+
try {
|
|
5902
|
+
instance = await client.createInstance({
|
|
5903
|
+
name,
|
|
5904
|
+
config: {
|
|
5905
|
+
organizationSlug: orgSelection,
|
|
5906
|
+
region: regionSelection
|
|
5907
|
+
}
|
|
5908
|
+
});
|
|
5909
|
+
spinner.success('Project created');
|
|
5910
|
+
} catch (error) {
|
|
5911
|
+
spinner.error('Failed to create project');
|
|
5912
|
+
throw error;
|
|
5913
|
+
}
|
|
5914
|
+
telemetry.trackEvent(TelemetryEventName.PROJECT_CREATED, {
|
|
5915
|
+
projectId: instance.id
|
|
5916
|
+
});
|
|
5917
|
+
logger.message('');
|
|
5918
|
+
logger.message(`Name: ${picocolors.bold(instance.name)}`);
|
|
5919
|
+
logger.message(`ID: ${picocolors.dim(instance.id)}`);
|
|
5920
|
+
logger.message(`URL: ${picocolors.cyan(instance.url)}`);
|
|
5921
|
+
logger.message('');
|
|
5922
|
+
logger.info('Created as a v2 development project. Enable production mode in the dashboard when you are ready.');
|
|
5923
|
+
const shouldSelect = await __rspack_external__clack_prompts_3cae1695.confirm({
|
|
5924
|
+
message: 'Would you like to use this project for your project?',
|
|
5925
|
+
initialValue: true
|
|
5926
|
+
});
|
|
5927
|
+
if (shouldSelect && !__rspack_external__clack_prompts_3cae1695.isCancel(shouldSelect)) {
|
|
5928
|
+
await setSelectedInstanceId(instance.id);
|
|
5929
|
+
logger.info('Project selected');
|
|
5930
|
+
}
|
|
5931
|
+
} finally{
|
|
5932
|
+
await client.close();
|
|
5933
|
+
}
|
|
5934
|
+
}
|
|
5935
|
+
function getStatusColor(status) {
|
|
5936
|
+
switch(status){
|
|
5937
|
+
case 'active':
|
|
5938
|
+
return picocolors.green('active');
|
|
5939
|
+
case 'inactive':
|
|
5940
|
+
return picocolors.yellow('inactive');
|
|
5941
|
+
case 'pending':
|
|
5942
|
+
return picocolors.blue('pending');
|
|
5943
|
+
default:
|
|
5944
|
+
return status;
|
|
5945
|
+
}
|
|
5946
|
+
}
|
|
5947
|
+
async function projectsAction(context) {
|
|
5948
|
+
const { commandArgs } = context;
|
|
5949
|
+
const subcommand = commandArgs[0];
|
|
5950
|
+
switch(subcommand){
|
|
5951
|
+
case 'list':
|
|
5952
|
+
context.commandArgs = commandArgs.slice(1);
|
|
5953
|
+
return listAction(context);
|
|
5954
|
+
case 'select':
|
|
5955
|
+
context.commandArgs = commandArgs.slice(1);
|
|
5956
|
+
return selectAction(context);
|
|
5957
|
+
case 'create':
|
|
5958
|
+
context.commandArgs = commandArgs.slice(1);
|
|
5959
|
+
return createAction(context);
|
|
5960
|
+
default:
|
|
5961
|
+
return listAction(context);
|
|
5962
|
+
}
|
|
5963
|
+
}
|
|
5964
|
+
const projectsCommand = {
|
|
5965
|
+
name: 'projects',
|
|
5966
|
+
label: 'Projects',
|
|
5967
|
+
hint: 'Manage your c15t projects',
|
|
5968
|
+
description: 'List, select, and create c15t projects',
|
|
5969
|
+
action: projectsAction,
|
|
5970
|
+
subcommands: [
|
|
5971
|
+
{
|
|
5972
|
+
name: 'list',
|
|
5973
|
+
label: 'List',
|
|
5974
|
+
hint: 'List all projects',
|
|
5975
|
+
description: 'List all c15t projects for your account',
|
|
5976
|
+
action: listAction
|
|
5977
|
+
},
|
|
5978
|
+
{
|
|
5979
|
+
name: 'select',
|
|
5980
|
+
label: 'Select',
|
|
5981
|
+
hint: 'Select a project',
|
|
5982
|
+
description: 'Select a project for your local project',
|
|
5983
|
+
action: selectAction
|
|
5984
|
+
},
|
|
5985
|
+
{
|
|
5986
|
+
name: 'create',
|
|
5987
|
+
label: 'Create',
|
|
5988
|
+
hint: 'Create a new project',
|
|
5989
|
+
description: 'Create a new c15t project',
|
|
5990
|
+
action: createAction
|
|
5991
|
+
}
|
|
5992
|
+
]
|
|
5993
|
+
};
|
|
5994
|
+
({
|
|
5995
|
+
...projectsCommand,
|
|
5996
|
+
name: 'instances',
|
|
5997
|
+
label: 'Instances',
|
|
5998
|
+
description: 'Alias for `c15t projects`',
|
|
5999
|
+
hidden: true
|
|
6000
|
+
});
|
|
5142
6001
|
const validLogLevels = [
|
|
5143
6002
|
'error',
|
|
5144
6003
|
'warn',
|
|
@@ -5194,7 +6053,7 @@ const logger_logMessage = (logLevel, message, ...args)=>{
|
|
|
5194
6053
|
}
|
|
5195
6054
|
};
|
|
5196
6055
|
const logger_createCliLogger = (level)=>{
|
|
5197
|
-
const baseLogger =
|
|
6056
|
+
const baseLogger = logger_createLogger({
|
|
5198
6057
|
level,
|
|
5199
6058
|
appName: 'c15t',
|
|
5200
6059
|
log: (logLevel, message, ...args)=>{
|
|
@@ -5205,23 +6064,26 @@ const logger_createCliLogger = (level)=>{
|
|
|
5205
6064
|
extendedLogger.message = (message)=>{
|
|
5206
6065
|
__rspack_external__clack_prompts_3cae1695.log.message(message);
|
|
5207
6066
|
};
|
|
5208
|
-
extendedLogger.note = (
|
|
5209
|
-
|
|
5210
|
-
const title = args.length > 0 && 'string' == typeof args[0] ? args[0] : void 0;
|
|
5211
|
-
__rspack_external__clack_prompts_3cae1695.note(messageStr, title, {
|
|
6067
|
+
extendedLogger.note = (content, title)=>{
|
|
6068
|
+
__rspack_external__clack_prompts_3cae1695.note(content, title, {
|
|
5212
6069
|
format: (line)=>line
|
|
5213
6070
|
});
|
|
5214
6071
|
};
|
|
5215
|
-
extendedLogger.success = (message
|
|
5216
|
-
logger_logMessage('success', message
|
|
6072
|
+
extendedLogger.success = (message)=>{
|
|
6073
|
+
logger_logMessage('success', message);
|
|
5217
6074
|
};
|
|
5218
|
-
extendedLogger.failed = (message
|
|
5219
|
-
logger_logMessage('failed', message
|
|
6075
|
+
extendedLogger.failed = (message)=>{
|
|
6076
|
+
logger_logMessage('failed', message);
|
|
5220
6077
|
process.exit(0);
|
|
5221
6078
|
};
|
|
5222
6079
|
extendedLogger.outro = (message)=>{
|
|
5223
6080
|
__rspack_external__clack_prompts_3cae1695.outro(message);
|
|
5224
6081
|
};
|
|
6082
|
+
extendedLogger.step = (current, total, label)=>{
|
|
6083
|
+
const filled = picocolors.green('█'.repeat(current));
|
|
6084
|
+
const empty = picocolors.dim('░'.repeat(total - current));
|
|
6085
|
+
__rspack_external__clack_prompts_3cae1695.log.step(`[${filled}${empty}] Step ${current}/${total}: ${label}`);
|
|
6086
|
+
};
|
|
5225
6087
|
return extendedLogger;
|
|
5226
6088
|
};
|
|
5227
6089
|
async function addAndInstallDependenciesViaPM(projectRoot, dependencies, packageManager) {
|
|
@@ -6279,10 +7141,13 @@ async function createCliContext(rawArgs, cwd, commands) {
|
|
|
6279
7141
|
const telemetryDisabled = true === parsedFlags['no-telemetry'];
|
|
6280
7142
|
const telemetryDebug = true === parsedFlags['telemetry-debug'];
|
|
6281
7143
|
try {
|
|
6282
|
-
context.telemetry =
|
|
7144
|
+
context.telemetry = telemetry_createTelemetry({
|
|
6283
7145
|
disabled: telemetryDisabled,
|
|
6284
7146
|
debug: telemetryDebug,
|
|
6285
7147
|
defaultProperties: {
|
|
7148
|
+
entryCommand: commandName ?? 'interactive',
|
|
7149
|
+
commandArgsCount: commandArgs.length,
|
|
7150
|
+
enabledFlags: Object.entries(parsedFlags).filter(([, value])=>false !== value && void 0 !== value).map(([key])=>key).sort(),
|
|
6286
7151
|
cliVersion: context.fs.getPackageInfo().version,
|
|
6287
7152
|
framework: context.framework.framework ?? 'unknown',
|
|
6288
7153
|
frameworkVersion: context.framework.frameworkVersion ?? 'unknown',
|
|
@@ -6297,9 +7162,20 @@ async function createCliContext(rawArgs, cwd, commands) {
|
|
|
6297
7162
|
if (telemetryDisabled) logger.debug('Telemetry is disabled by user preference');
|
|
6298
7163
|
else if (telemetryDebug) logger.debug('Telemetry initialized with debug mode enabled');
|
|
6299
7164
|
else logger.debug('Telemetry initialized');
|
|
7165
|
+
context.telemetry.trackEvent(TelemetryEventName.CLI_ENVIRONMENT_DETECTED, {
|
|
7166
|
+
command: commandName ?? 'interactive',
|
|
7167
|
+
projectRootChanged: context.projectRoot !== cwd,
|
|
7168
|
+
framework: context.framework.framework ?? 'unknown',
|
|
7169
|
+
frameworkVersion: context.framework.frameworkVersion ?? 'unknown',
|
|
7170
|
+
packageManager: context.packageManager.name,
|
|
7171
|
+
packageManagerVersion: context.packageManager.version ?? 'unknown',
|
|
7172
|
+
hasReact: context.framework.hasReact,
|
|
7173
|
+
reactVersion: context.framework.reactVersion ?? 'unknown',
|
|
7174
|
+
tailwindVersion: context.framework.tailwindVersion ?? 'unknown'
|
|
7175
|
+
});
|
|
6300
7176
|
} catch {
|
|
6301
7177
|
logger.warn('Failed to initialize telemetry, continuing with telemetry disabled');
|
|
6302
|
-
context.telemetry =
|
|
7178
|
+
context.telemetry = telemetry_createTelemetry({
|
|
6303
7179
|
disabled: true,
|
|
6304
7180
|
logger: context.logger
|
|
6305
7181
|
});
|
|
@@ -6363,6 +7239,21 @@ const src_commands = [
|
|
|
6363
7239
|
await open_0(constants_URLS.GITHUB);
|
|
6364
7240
|
logger.success('Thank you for your support!');
|
|
6365
7241
|
}
|
|
7242
|
+
},
|
|
7243
|
+
{
|
|
7244
|
+
name: 'projects',
|
|
7245
|
+
label: 'Projects',
|
|
7246
|
+
hint: 'Manage your c15t projects',
|
|
7247
|
+
description: 'List, select, and create c15t projects.',
|
|
7248
|
+
action: (context)=>projectsAction(context)
|
|
7249
|
+
},
|
|
7250
|
+
{
|
|
7251
|
+
name: 'instances',
|
|
7252
|
+
label: 'Instances',
|
|
7253
|
+
hint: 'Alias for `projects`',
|
|
7254
|
+
description: 'Alias for `c15t projects`.',
|
|
7255
|
+
action: (context)=>projectsAction(context),
|
|
7256
|
+
hidden: true
|
|
6366
7257
|
}
|
|
6367
7258
|
];
|
|
6368
7259
|
async function main() {
|
|
@@ -6438,7 +7329,7 @@ flag or set ${picocolors.cyan('C15T_TELEMETRY_DISABLED=1')} in your environment.
|
|
|
6438
7329
|
} else {
|
|
6439
7330
|
logger.debug('No command specified, entering interactive selection.');
|
|
6440
7331
|
telemetry.trackEvent(TelemetryEventName.INTERACTIVE_MENU_OPENED, {});
|
|
6441
|
-
const promptOptions = src_commands.map((cmd)=>({
|
|
7332
|
+
const promptOptions = src_commands.filter((cmd)=>!cmd.hidden).map((cmd)=>({
|
|
6442
7333
|
value: cmd.name,
|
|
6443
7334
|
label: cmd.label,
|
|
6444
7335
|
hint: cmd.hint
|
|
@@ -6506,4 +7397,4 @@ flag or set ${picocolors.cyan('C15T_TELEMETRY_DISABLED=1')} in your environment.
|
|
|
6506
7397
|
await telemetry.shutdown();
|
|
6507
7398
|
}
|
|
6508
7399
|
main();
|
|
6509
|
-
export { __webpack_require__temp as __webpack_require__, LAYOUT_PATTERNS, PAGES_APP_PATTERNS, STORAGE_MODES, config_store_getAccessToken as getAccessToken, constants_REGEX, getAuthState, logger_formatLogMessage, main, setSelectedInstanceId, storeTokens };
|
|
7400
|
+
export { __webpack_require__temp as __webpack_require__, LAYOUT_PATTERNS, PAGES_APP_PATTERNS, STORAGE_MODES, config_store_getAccessToken as getAccessToken, constants_REGEX, ensureGlobalCssStylesheetImports, formatSearchedCssPaths, getAuthState, getSelectedInstanceId, isLoggedIn, logger_formatLogMessage, main, setSelectedInstanceId, storeTokens };
|