workos 0.4.5 → 0.5.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/dist/bin.js +32 -3
- package/dist/bin.js.map +1 -1
- package/dist/commands/doctor.d.ts +10 -0
- package/dist/commands/doctor.js +30 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/doctor/checks/connectivity.d.ts +2 -0
- package/dist/doctor/checks/connectivity.js +35 -0
- package/dist/doctor/checks/connectivity.js.map +1 -0
- package/dist/doctor/checks/dashboard.d.ts +3 -0
- package/dist/doctor/checks/dashboard.js +123 -0
- package/dist/doctor/checks/dashboard.js.map +1 -0
- package/dist/doctor/checks/environment.d.ts +2 -0
- package/dist/doctor/checks/environment.js +68 -0
- package/dist/doctor/checks/environment.js.map +1 -0
- package/dist/doctor/checks/framework.d.ts +2 -0
- package/dist/doctor/checks/framework.js +75 -0
- package/dist/doctor/checks/framework.js.map +1 -0
- package/dist/doctor/checks/runtime.d.ts +2 -0
- package/dist/doctor/checks/runtime.js +20 -0
- package/dist/doctor/checks/runtime.js.map +1 -0
- package/dist/doctor/checks/sdk.d.ts +2 -0
- package/dist/doctor/checks/sdk.js +111 -0
- package/dist/doctor/checks/sdk.js.map +1 -0
- package/dist/doctor/clipboard.d.ts +1 -0
- package/dist/doctor/clipboard.js +43 -0
- package/dist/doctor/clipboard.js.map +1 -0
- package/dist/doctor/index.d.ts +6 -0
- package/dist/doctor/index.js +94 -0
- package/dist/doctor/index.js.map +1 -0
- package/dist/doctor/issues.d.ts +58 -0
- package/dist/doctor/issues.js +134 -0
- package/dist/doctor/issues.js.map +1 -0
- package/dist/doctor/json-output.d.ts +2 -0
- package/dist/doctor/json-output.js +4 -0
- package/dist/doctor/json-output.js.map +1 -0
- package/dist/doctor/output.d.ts +5 -0
- package/dist/doctor/output.js +149 -0
- package/dist/doctor/output.js.map +1 -0
- package/dist/doctor/types.d.ts +105 -0
- package/dist/doctor/types.js +2 -0
- package/dist/doctor/types.js.map +1 -0
- package/dist/integrations/dotnet/index.d.ts +8 -0
- package/dist/integrations/dotnet/index.js +163 -0
- package/dist/integrations/dotnet/index.js.map +1 -0
- package/dist/integrations/elixir/index.d.ts +8 -0
- package/dist/integrations/elixir/index.js +152 -0
- package/dist/integrations/elixir/index.js.map +1 -0
- package/dist/integrations/go/index.d.ts +11 -0
- package/dist/integrations/go/index.js +220 -0
- package/dist/integrations/go/index.js.map +1 -0
- package/dist/integrations/kotlin/index.d.ts +4 -0
- package/dist/integrations/kotlin/index.js +53 -0
- package/dist/integrations/kotlin/index.js.map +1 -0
- package/dist/integrations/nextjs/index.d.ts +4 -0
- package/dist/integrations/nextjs/index.js +90 -0
- package/dist/integrations/nextjs/index.js.map +1 -0
- package/dist/integrations/nextjs/utils.d.ts +8 -0
- package/dist/integrations/nextjs/utils.js +53 -0
- package/dist/integrations/nextjs/utils.js.map +1 -0
- package/dist/integrations/node/index.d.ts +4 -0
- package/dist/integrations/node/index.js +52 -0
- package/dist/integrations/node/index.js.map +1 -0
- package/dist/integrations/php/index.d.ts +4 -0
- package/dist/integrations/php/index.js +51 -0
- package/dist/integrations/php/index.js.map +1 -0
- package/dist/integrations/php-laravel/index.d.ts +4 -0
- package/dist/integrations/php-laravel/index.js +51 -0
- package/dist/integrations/php-laravel/index.js.map +1 -0
- package/dist/integrations/python/index.d.ts +9 -0
- package/dist/integrations/python/index.js +254 -0
- package/dist/integrations/python/index.js.map +1 -0
- package/dist/integrations/react/index.d.ts +4 -0
- package/dist/integrations/react/index.js +49 -0
- package/dist/integrations/react/index.js.map +1 -0
- package/dist/integrations/react-router/index.d.ts +4 -0
- package/dist/integrations/react-router/index.js +94 -0
- package/dist/integrations/react-router/index.js.map +1 -0
- package/dist/integrations/react-router/utils.d.ts +10 -0
- package/dist/integrations/react-router/utils.js +146 -0
- package/dist/integrations/react-router/utils.js.map +1 -0
- package/dist/integrations/ruby/index.d.ts +8 -0
- package/dist/integrations/ruby/index.js +142 -0
- package/dist/integrations/ruby/index.js.map +1 -0
- package/dist/integrations/sveltekit/index.d.ts +4 -0
- package/dist/integrations/sveltekit/index.js +50 -0
- package/dist/integrations/sveltekit/index.js.map +1 -0
- package/dist/integrations/tanstack-start/index.d.ts +4 -0
- package/dist/integrations/tanstack-start/index.js +51 -0
- package/dist/integrations/tanstack-start/index.js.map +1 -0
- package/dist/integrations/vanilla-js/index.d.ts +4 -0
- package/dist/integrations/vanilla-js/index.js +49 -0
- package/dist/integrations/vanilla-js/index.js.map +1 -0
- package/dist/lib/agent-interface.js +66 -1
- package/dist/lib/agent-interface.js.map +1 -1
- package/dist/lib/config.d.ts +32 -58
- package/dist/lib/config.js +19 -70
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/constants.d.ts +17 -14
- package/dist/lib/constants.js +12 -31
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/framework-config.d.ts +13 -4
- package/dist/lib/framework-config.js.map +1 -1
- package/dist/lib/language-detection.d.ts +20 -0
- package/dist/lib/language-detection.js +96 -0
- package/dist/lib/language-detection.js.map +1 -0
- package/dist/lib/port-detection.js +4 -2
- package/dist/lib/port-detection.js.map +1 -1
- package/dist/lib/registry.d.ts +43 -0
- package/dist/lib/registry.js +96 -0
- package/dist/lib/registry.js.map +1 -0
- package/dist/lib/run-with-core.js +70 -26
- package/dist/lib/run-with-core.js.map +1 -1
- package/dist/nextjs/nextjs-installer-agent.d.ts +3 -4
- package/dist/nextjs/nextjs-installer-agent.js +3 -94
- package/dist/nextjs/nextjs-installer-agent.js.map +1 -1
- package/dist/nextjs/utils.d.ts +4 -8
- package/dist/nextjs/utils.js +4 -52
- package/dist/nextjs/utils.js.map +1 -1
- package/dist/react/react-installer-agent.d.ts +4 -2
- package/dist/react/react-installer-agent.js +4 -46
- package/dist/react/react-installer-agent.js.map +1 -1
- package/dist/react-router/react-router-installer-agent.d.ts +2 -4
- package/dist/react-router/react-router-installer-agent.js +2 -100
- package/dist/react-router/react-router-installer-agent.js.map +1 -1
- package/dist/react-router/utils.d.ts +2 -17
- package/dist/react-router/utils.js +2 -207
- package/dist/react-router/utils.js.map +1 -1
- package/dist/tanstack-start/tanstack-start-installer-agent.d.ts +4 -2
- package/dist/tanstack-start/tanstack-start-installer-agent.js +4 -48
- package/dist/tanstack-start/tanstack-start-installer-agent.js.map +1 -1
- package/dist/vanilla-js/vanilla-js-installer-agent.d.ts +4 -2
- package/dist/vanilla-js/vanilla-js-installer-agent.js +4 -46
- package/dist/vanilla-js/vanilla-js-installer-agent.js.map +1 -1
- package/package.json +6 -5
- package/skills/workos-authkit-sveltekit/SKILL.md +160 -0
- package/skills/workos-dotnet/SKILL.md +163 -0
- package/skills/workos-elixir/SKILL.md +194 -0
- package/skills/workos-go/SKILL.md +191 -0
- package/skills/workos-kotlin/SKILL.md +161 -0
- package/skills/workos-node/SKILL.md +164 -0
- package/skills/workos-php/SKILL.md +127 -0
- package/skills/workos-php-laravel/SKILL.md +147 -0
- package/skills/workos-python/SKILL.md +159 -0
- package/skills/workos-ruby/SKILL.md +163 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Integration } from './constants.js';
|
|
2
1
|
import type { InstallerOptions } from '../utils/types.js';
|
|
2
|
+
import type { Language } from './language-detection.js';
|
|
3
3
|
/**
|
|
4
4
|
* Configuration interface for framework-specific agent integrations.
|
|
5
5
|
* Each framework exports a FrameworkConfig that the universal runner uses.
|
|
@@ -18,8 +18,8 @@ export interface FrameworkConfig {
|
|
|
18
18
|
export interface FrameworkMetadata {
|
|
19
19
|
/** Display name (e.g., "Next.js", "React") */
|
|
20
20
|
name: string;
|
|
21
|
-
/** Integration
|
|
22
|
-
integration:
|
|
21
|
+
/** Integration identifier (e.g., 'nextjs', 'python'). String, not enum — auto-discovered from registry. */
|
|
22
|
+
integration: string;
|
|
23
23
|
/** URL to framework-specific WorkOS AuthKit docs */
|
|
24
24
|
docsUrl: string;
|
|
25
25
|
/**
|
|
@@ -36,9 +36,18 @@ export interface FrameworkMetadata {
|
|
|
36
36
|
/**
|
|
37
37
|
* Name of the framework-specific skill for agent integration.
|
|
38
38
|
* Skills are located in .claude/skills/{skillName}/SKILL.md
|
|
39
|
-
* Will be populated per-framework in Phase 3.
|
|
40
39
|
*/
|
|
41
40
|
skillName?: string;
|
|
41
|
+
/** Language ecosystem this integration belongs to */
|
|
42
|
+
language: Language;
|
|
43
|
+
/** Stability tier: 'stable' for tested integrations, 'experimental' for new ones */
|
|
44
|
+
stability: 'stable' | 'experimental';
|
|
45
|
+
/** Detection priority — higher numbers are checked first */
|
|
46
|
+
priority: number;
|
|
47
|
+
/** Default package manager command (e.g., 'pip', 'gem', 'go'). Optional for JS integrations. */
|
|
48
|
+
packageManager?: string;
|
|
49
|
+
/** Primary manifest file (e.g., 'pyproject.toml', 'Gemfile'). Optional for JS integrations. */
|
|
50
|
+
manifestFile?: string;
|
|
42
51
|
}
|
|
43
52
|
/**
|
|
44
53
|
* Framework detection and version handling
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"framework-config.js","sourceRoot":"","sources":["../../src/lib/framework-config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"framework-config.js","sourceRoot":"","sources":["../../src/lib/framework-config.ts"],"names":[],"mappings":"AAuIA;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,aAAqB;IACrD,OAAO,kBAAkB,aAAa,4BAA4B,CAAC;AACrE,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,iFAAiF,CAAC","sourcesContent":["import type { InstallerOptions } from '../utils/types.js';\nimport type { Language } from './language-detection.js';\n\n/**\n * Configuration interface for framework-specific agent integrations.\n * Each framework exports a FrameworkConfig that the universal runner uses.\n */\nexport interface FrameworkConfig {\n metadata: FrameworkMetadata;\n detection: FrameworkDetection;\n environment: EnvironmentConfig;\n analytics: AnalyticsConfig;\n prompts: PromptConfig;\n ui: UIConfig;\n}\n\n/**\n * Basic framework information and documentation\n */\nexport interface FrameworkMetadata {\n /** Display name (e.g., \"Next.js\", \"React\") */\n name: string;\n\n /** Integration identifier (e.g., 'nextjs', 'python'). String, not enum — auto-discovered from registry. */\n integration: string;\n\n /** URL to framework-specific WorkOS AuthKit docs */\n docsUrl: string;\n\n /**\n * Optional URL to docs for users with unsupported framework versions.\n * If not provided, defaults to docsUrl.\n */\n unsupportedVersionDocsUrl?: string;\n\n /**\n * Optional function to gather framework-specific context before agent runs.\n * For Next.js: detects router type\n * For React Native: detects Expo vs bare\n */\n gatherContext?: (options: InstallerOptions) => Promise<Record<string, any>>;\n\n /**\n * Name of the framework-specific skill for agent integration.\n * Skills are located in .claude/skills/{skillName}/SKILL.md\n */\n skillName?: string;\n\n /** Language ecosystem this integration belongs to */\n language: Language;\n\n /** Stability tier: 'stable' for tested integrations, 'experimental' for new ones */\n stability: 'stable' | 'experimental';\n\n /** Detection priority — higher numbers are checked first */\n priority: number;\n\n /** Default package manager command (e.g., 'pip', 'gem', 'go'). Optional for JS integrations. */\n packageManager?: string;\n\n /** Primary manifest file (e.g., 'pyproject.toml', 'Gemfile'). Optional for JS integrations. */\n manifestFile?: string;\n}\n\n/**\n * Framework detection and version handling\n */\nexport interface FrameworkDetection {\n /** Package name to check in package.json (e.g., \"next\", \"react\") */\n packageName: string;\n\n /** Human-readable name for error messages (e.g., \"Next.js\") */\n packageDisplayName: string;\n\n /** Extract version from package.json */\n getVersion: (packageJson: any) => string | undefined;\n\n /** Optional: Convert version to analytics bucket (e.g., \"15.x\") */\n getVersionBucket?: (version: string) => string;\n}\n\n/**\n * Environment variable configuration\n */\nexport interface EnvironmentConfig {\n /** Whether to upload env vars to hosting providers post-agent */\n uploadToHosting: boolean;\n\n /** Whether this framework requires API key (false for client-only SDKs) */\n requiresApiKey: boolean;\n\n /**\n * Build the environment variables object for this framework.\n * Returns the exact variable names and values to upload to hosting providers.\n */\n getEnvVars: (apiKey: string, clientId: string) => Record<string, string>;\n}\n\n/**\n * Analytics configuration\n */\nexport interface AnalyticsConfig {\n /** Generate tags from context (e.g., { 'nextjs-version': '15.x', 'router': 'app' }) */\n getTags: (context: any) => Record<string, any>;\n\n /** Optional: Additional event properties */\n getEventProperties?: (context: any) => Record<string, any>;\n}\n\n/**\n * Prompt configuration\n */\nexport interface PromptConfig {\n /**\n * Optional: Additional context lines to append to base prompt\n * For Next.js: \"- Router: app\"\n * For React Native: \"- Platform: Expo\"\n */\n getAdditionalContextLines?: (context: any) => string[];\n}\n\n/**\n * UI messaging configuration\n */\nexport interface UIConfig {\n /** Success message when agent completes */\n successMessage: string;\n\n /** Generate \"What the agent did\" bullets from context */\n getOutroChanges: (context: any) => string[];\n\n /** Generate \"Next steps\" bullets from context */\n getOutroNextSteps: (context: any) => string[];\n}\n\n/**\n * Generate welcome message from framework name\n */\nexport function getWelcomeMessage(frameworkName: string): string {\n return `WorkOS AuthKit ${frameworkName} installer (agent-powered)`;\n}\n\n/**\n * Shared spinner message for all frameworks\n */\nexport const SPINNER_MESSAGE = 'Setting up WorkOS AuthKit with login, authentication, and session management...';\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported programming languages for framework detection.
|
|
3
|
+
*/
|
|
4
|
+
export type Language = 'javascript' | 'python' | 'ruby' | 'php' | 'go' | 'kotlin' | 'dotnet' | 'elixir';
|
|
5
|
+
export interface LanguageSignal {
|
|
6
|
+
language: Language;
|
|
7
|
+
confidence: number;
|
|
8
|
+
manifestFile: string;
|
|
9
|
+
}
|
|
10
|
+
export interface LanguageDetectionResult {
|
|
11
|
+
primary: Language;
|
|
12
|
+
signals: LanguageSignal[];
|
|
13
|
+
ambiguous: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Detect the primary programming language of a project.
|
|
17
|
+
* Runs all detectors and returns the highest-priority match.
|
|
18
|
+
* Sets `ambiguous: true` if multiple non-JS languages are detected.
|
|
19
|
+
*/
|
|
20
|
+
export declare function detectLanguage(cwd: string): LanguageDetectionResult | undefined;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
function fileExists(cwd, filename) {
|
|
4
|
+
const fullPath = join(cwd, filename);
|
|
5
|
+
return { found: existsSync(fullPath), manifestFile: filename };
|
|
6
|
+
}
|
|
7
|
+
function globExists(cwd, pattern) {
|
|
8
|
+
// Simple glob for *.ext patterns in the root directory
|
|
9
|
+
const ext = pattern.replace('*', '');
|
|
10
|
+
try {
|
|
11
|
+
const files = readdirSync(cwd);
|
|
12
|
+
const match = files.find((f) => f.endsWith(ext));
|
|
13
|
+
return { found: !!match, manifestFile: match || pattern };
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return { found: false, manifestFile: pattern };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function detectPython(cwd) {
|
|
20
|
+
for (const file of ['pyproject.toml', 'requirements.txt', 'setup.py', 'Pipfile']) {
|
|
21
|
+
if (existsSync(join(cwd, file))) {
|
|
22
|
+
return { found: true, manifestFile: file };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return { found: false, manifestFile: 'pyproject.toml' };
|
|
26
|
+
}
|
|
27
|
+
function detectKotlin(cwd) {
|
|
28
|
+
const ktsPath = join(cwd, 'build.gradle.kts');
|
|
29
|
+
if (existsSync(ktsPath)) {
|
|
30
|
+
try {
|
|
31
|
+
const content = readFileSync(ktsPath, 'utf-8');
|
|
32
|
+
if (/org\.jetbrains\.kotlin/.test(content) || /kotlin\(/.test(content)) {
|
|
33
|
+
return { found: true, manifestFile: 'build.gradle.kts' };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Can't read file
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Also check build.gradle (Groovy DSL)
|
|
41
|
+
const gradlePath = join(cwd, 'build.gradle');
|
|
42
|
+
if (existsSync(gradlePath)) {
|
|
43
|
+
try {
|
|
44
|
+
const content = readFileSync(gradlePath, 'utf-8');
|
|
45
|
+
if (/kotlin/.test(content)) {
|
|
46
|
+
return { found: true, manifestFile: 'build.gradle' };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Can't read file
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { found: false, manifestFile: 'build.gradle.kts' };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Language detectors ordered by specificity.
|
|
57
|
+
* More specific languages are checked first.
|
|
58
|
+
* JavaScript is last because many non-JS projects also have package.json.
|
|
59
|
+
*/
|
|
60
|
+
const LANGUAGE_DETECTORS = [
|
|
61
|
+
{ language: 'elixir', detect: (cwd) => fileExists(cwd, 'mix.exs') },
|
|
62
|
+
{ language: 'go', detect: (cwd) => fileExists(cwd, 'go.mod') },
|
|
63
|
+
{ language: 'dotnet', detect: (cwd) => globExists(cwd, '*.csproj') },
|
|
64
|
+
{ language: 'kotlin', detect: detectKotlin },
|
|
65
|
+
{ language: 'ruby', detect: (cwd) => fileExists(cwd, 'Gemfile') },
|
|
66
|
+
{ language: 'php', detect: (cwd) => fileExists(cwd, 'composer.json') },
|
|
67
|
+
{ language: 'python', detect: detectPython },
|
|
68
|
+
{ language: 'javascript', detect: (cwd) => fileExists(cwd, 'package.json') },
|
|
69
|
+
];
|
|
70
|
+
/**
|
|
71
|
+
* Detect the primary programming language of a project.
|
|
72
|
+
* Runs all detectors and returns the highest-priority match.
|
|
73
|
+
* Sets `ambiguous: true` if multiple non-JS languages are detected.
|
|
74
|
+
*/
|
|
75
|
+
export function detectLanguage(cwd) {
|
|
76
|
+
const signals = [];
|
|
77
|
+
for (const detector of LANGUAGE_DETECTORS) {
|
|
78
|
+
const result = detector.detect(cwd);
|
|
79
|
+
if (result.found) {
|
|
80
|
+
signals.push({
|
|
81
|
+
language: detector.language,
|
|
82
|
+
confidence: 1.0,
|
|
83
|
+
manifestFile: result.manifestFile,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (signals.length === 0) {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
const primary = signals[0].language;
|
|
91
|
+
// Ambiguous if multiple non-JS languages detected
|
|
92
|
+
const nonJsSignals = signals.filter((s) => s.language !== 'javascript');
|
|
93
|
+
const ambiguous = nonJsSignals.length > 1;
|
|
94
|
+
return { primary, signals, ambiguous };
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=language-detection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"language-detection.js","sourceRoot":"","sources":["../../src/lib/language-detection.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync, readdirSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/**\n * Supported programming languages for framework detection.\n */\nexport type Language = 'javascript' | 'python' | 'ruby' | 'php' | 'go' | 'kotlin' | 'dotnet' | 'elixir';\n\nexport interface LanguageSignal {\n language: Language;\n confidence: number; // 0-1\n manifestFile: string;\n}\n\nexport interface LanguageDetectionResult {\n primary: Language;\n signals: LanguageSignal[];\n ambiguous: boolean;\n}\n\nfunction fileExists(cwd: string, filename: string): { found: boolean; manifestFile: string } {\n const fullPath = join(cwd, filename);\n return { found: existsSync(fullPath), manifestFile: filename };\n}\n\nfunction globExists(cwd: string, pattern: string): { found: boolean; manifestFile: string } {\n // Simple glob for *.ext patterns in the root directory\n const ext = pattern.replace('*', '');\n try {\n const files = readdirSync(cwd);\n const match = files.find((f) => f.endsWith(ext));\n return { found: !!match, manifestFile: match || pattern };\n } catch {\n return { found: false, manifestFile: pattern };\n }\n}\n\nfunction detectPython(cwd: string): { found: boolean; manifestFile: string } {\n for (const file of ['pyproject.toml', 'requirements.txt', 'setup.py', 'Pipfile']) {\n if (existsSync(join(cwd, file))) {\n return { found: true, manifestFile: file };\n }\n }\n return { found: false, manifestFile: 'pyproject.toml' };\n}\n\nfunction detectKotlin(cwd: string): { found: boolean; manifestFile: string } {\n const ktsPath = join(cwd, 'build.gradle.kts');\n if (existsSync(ktsPath)) {\n try {\n const content = readFileSync(ktsPath, 'utf-8');\n if (/org\\.jetbrains\\.kotlin/.test(content) || /kotlin\\(/.test(content)) {\n return { found: true, manifestFile: 'build.gradle.kts' };\n }\n } catch {\n // Can't read file\n }\n }\n\n // Also check build.gradle (Groovy DSL)\n const gradlePath = join(cwd, 'build.gradle');\n if (existsSync(gradlePath)) {\n try {\n const content = readFileSync(gradlePath, 'utf-8');\n if (/kotlin/.test(content)) {\n return { found: true, manifestFile: 'build.gradle' };\n }\n } catch {\n // Can't read file\n }\n }\n\n return { found: false, manifestFile: 'build.gradle.kts' };\n}\n\n/**\n * Language detectors ordered by specificity.\n * More specific languages are checked first.\n * JavaScript is last because many non-JS projects also have package.json.\n */\nconst LANGUAGE_DETECTORS: Array<{\n language: Language;\n detect: (cwd: string) => { found: boolean; manifestFile: string };\n}> = [\n { language: 'elixir', detect: (cwd) => fileExists(cwd, 'mix.exs') },\n { language: 'go', detect: (cwd) => fileExists(cwd, 'go.mod') },\n { language: 'dotnet', detect: (cwd) => globExists(cwd, '*.csproj') },\n { language: 'kotlin', detect: detectKotlin },\n { language: 'ruby', detect: (cwd) => fileExists(cwd, 'Gemfile') },\n { language: 'php', detect: (cwd) => fileExists(cwd, 'composer.json') },\n { language: 'python', detect: detectPython },\n { language: 'javascript', detect: (cwd) => fileExists(cwd, 'package.json') },\n];\n\n/**\n * Detect the primary programming language of a project.\n * Runs all detectors and returns the highest-priority match.\n * Sets `ambiguous: true` if multiple non-JS languages are detected.\n */\nexport function detectLanguage(cwd: string): LanguageDetectionResult | undefined {\n const signals: LanguageSignal[] = [];\n\n for (const detector of LANGUAGE_DETECTORS) {\n const result = detector.detect(cwd);\n if (result.found) {\n signals.push({\n language: detector.language,\n confidence: 1.0,\n manifestFile: result.manifestFile,\n });\n }\n }\n\n if (signals.length === 0) {\n return undefined;\n }\n\n const primary = signals[0].language;\n\n // Ambiguous if multiple non-JS languages detected\n const nonJsSignals = signals.filter((s) => s.language !== 'javascript');\n const ambiguous = nonJsSignals.length > 1;\n\n return { primary, signals, ambiguous };\n}\n"]}
|
|
@@ -9,13 +9,15 @@ const INTEGRATION_TO_SETTINGS_KEY = {
|
|
|
9
9
|
'react-router': 'reactRouter',
|
|
10
10
|
'vanilla-js': 'vanillaJs',
|
|
11
11
|
};
|
|
12
|
+
const DEFAULT_PORT = 3000;
|
|
13
|
+
const DEFAULT_CALLBACK_PATH = '/auth/callback';
|
|
12
14
|
function getDefaultPort(integration) {
|
|
13
15
|
const settingsKey = INTEGRATION_TO_SETTINGS_KEY[integration];
|
|
14
|
-
return settings.frameworks[settingsKey]
|
|
16
|
+
return settings.frameworks[settingsKey]?.port ?? DEFAULT_PORT;
|
|
15
17
|
}
|
|
16
18
|
export function getCallbackPath(integration) {
|
|
17
19
|
const settingsKey = INTEGRATION_TO_SETTINGS_KEY[integration];
|
|
18
|
-
return settings.frameworks[settingsKey]
|
|
20
|
+
return settings.frameworks[settingsKey]?.callbackPath ?? DEFAULT_CALLBACK_PATH;
|
|
19
21
|
}
|
|
20
22
|
/**
|
|
21
23
|
* Parse port from Vite config file.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"port-detection.js","sourceRoot":"","sources":["../../src/lib/port-detection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,MAAM,QAAQ,GAAG,SAAS,EAAE,CAAC;AAE7B,MAAM,2BAA2B,GAAgC;IAC/D,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,OAAO;IACd,gBAAgB,EAAE,eAAe;IACjC,cAAc,EAAE,aAAa;IAC7B,YAAY,EAAE,WAAW;CAC1B,CAAC;AAEF,SAAS,cAAc,CAAC,WAAwB;IAC9C,MAAM,WAAW,GAAG,2BAA2B,CAAC,WAAW,CAAC,CAAC;IAC7D,OAAO,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"port-detection.js","sourceRoot":"","sources":["../../src/lib/port-detection.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'node:fs';\nimport { join } from 'node:path';\nimport type { Integration } from './constants.js';\nimport { getConfig } from './settings.js';\n\nconst settings = getConfig();\n\nconst INTEGRATION_TO_SETTINGS_KEY: Record<Integration, string> = {\n nextjs: 'nextjs',\n react: 'react',\n 'tanstack-start': 'tanstackStart',\n 'react-router': 'reactRouter',\n 'vanilla-js': 'vanillaJs',\n};\n\nconst DEFAULT_PORT = 3000;\nconst DEFAULT_CALLBACK_PATH = '/auth/callback';\n\nfunction getDefaultPort(integration: Integration): number {\n const settingsKey = INTEGRATION_TO_SETTINGS_KEY[integration];\n return settings.frameworks[settingsKey]?.port ?? DEFAULT_PORT;\n}\n\nexport function getCallbackPath(integration: Integration): string {\n const settingsKey = INTEGRATION_TO_SETTINGS_KEY[integration];\n return settings.frameworks[settingsKey]?.callbackPath ?? DEFAULT_CALLBACK_PATH;\n}\n\n/**\n * Parse port from Vite config file.\n * Looks for server.port in vite.config.{ts,js,mjs}\n */\nfunction parseViteConfigPort(configPath: string): number | null {\n try {\n const content = fs.readFileSync(configPath, 'utf-8');\n // Match: port: 3000 or port: \"3000\" or port: '3000'\n const portMatch = content.match(/port\\s*:\\s*['\"]?(\\d+)['\"]?/);\n if (portMatch) {\n return parseInt(portMatch[1], 10);\n }\n } catch {\n // File doesn't exist or can't be read\n }\n return null;\n}\n\n/**\n * Parse port from Next.js package.json scripts.\n * Next.js uses: \"dev\": \"next dev -p 4000\" or --port 4000\n */\nfunction parseNextConfigPort(installDir: string): number | null {\n try {\n const packageJsonPath = join(installDir, 'package.json');\n const content = fs.readFileSync(packageJsonPath, 'utf-8');\n const packageJson = JSON.parse(content);\n\n const devScript = packageJson.scripts?.dev || '';\n // Match: -p 4000, --port 4000, --port=4000\n const portMatch = devScript.match(/-p\\s+(\\d+)|--port[=\\s]+(\\d+)/);\n if (portMatch) {\n return parseInt(portMatch[1] || portMatch[2], 10);\n }\n } catch {\n // Can't read package.json\n }\n return null;\n}\n\n/**\n * Parse port from TanStack Start app.config.ts.\n * Uses Vinxi: server: { port: N }\n */\nfunction parseTanStackPort(installDir: string): number | null {\n const configPaths = [join(installDir, 'app.config.ts'), join(installDir, 'app.config.js')];\n\n for (const configPath of configPaths) {\n try {\n const content = fs.readFileSync(configPath, 'utf-8');\n // Match server config with port\n const portMatch = content.match(/server\\s*:\\s*\\{[^}]*port\\s*:\\s*(\\d+)/);\n if (portMatch) {\n return parseInt(portMatch[1], 10);\n }\n } catch {\n // Config file doesn't exist\n }\n }\n return null;\n}\n\n/**\n * Detect the dev server port for a framework.\n * Checks config files first, falls back to framework default.\n */\nexport function detectPort(integration: Integration, installDir: string): number {\n let detectedPort: number | null = null;\n\n switch (integration) {\n case 'nextjs':\n detectedPort = parseNextConfigPort(installDir);\n break;\n\n case 'tanstack-start':\n detectedPort = parseTanStackPort(installDir);\n break;\n\n case 'react':\n case 'react-router':\n case 'vanilla-js': {\n // Vite-based frameworks\n const viteConfigs = [\n join(installDir, 'vite.config.ts'),\n join(installDir, 'vite.config.js'),\n join(installDir, 'vite.config.mjs'),\n ];\n for (const configPath of viteConfigs) {\n detectedPort = parseViteConfigPort(configPath);\n if (detectedPort) break;\n }\n break;\n }\n }\n\n return detectedPort ?? getDefaultPort(integration);\n}\n"]}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { FrameworkConfig } from './framework-config.js';
|
|
2
|
+
import type { Language } from './language-detection.js';
|
|
3
|
+
import type { InstallerOptions } from '../utils/types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Standard exports from an integration module.
|
|
6
|
+
* Each `src/integrations/{name}/index.ts` must export these.
|
|
7
|
+
*/
|
|
8
|
+
export interface IntegrationModule {
|
|
9
|
+
config: FrameworkConfig;
|
|
10
|
+
run: (options: InstallerOptions) => Promise<string>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Registry that provides lookup, detection, and enumeration of integrations.
|
|
14
|
+
*/
|
|
15
|
+
export interface IntegrationRegistry {
|
|
16
|
+
/** All registered integrations */
|
|
17
|
+
all(): FrameworkConfig[];
|
|
18
|
+
/** Get config by integration name */
|
|
19
|
+
get(name: string): IntegrationModule | undefined;
|
|
20
|
+
/** Get integrations for a specific language, ordered by priority */
|
|
21
|
+
forLanguage(language: Language): FrameworkConfig[];
|
|
22
|
+
/** Get integration names for CLI choices */
|
|
23
|
+
choices(): Array<{
|
|
24
|
+
name: string;
|
|
25
|
+
value: string;
|
|
26
|
+
}>;
|
|
27
|
+
/** Detection order: all integrations sorted by priority (higher = checked first) */
|
|
28
|
+
detectionOrder(): FrameworkConfig[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Build the integration registry by discovering all integration modules.
|
|
32
|
+
* Scans `src/integrations/` (or `dist/integrations/` at runtime) for directories
|
|
33
|
+
* with an index.js/index.ts file and dynamically imports them.
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildRegistry(): Promise<IntegrationRegistry>;
|
|
36
|
+
/**
|
|
37
|
+
* Get the integration registry (builds once, caches thereafter).
|
|
38
|
+
*/
|
|
39
|
+
export declare function getRegistry(): Promise<IntegrationRegistry>;
|
|
40
|
+
/**
|
|
41
|
+
* Reset the registry cache. Used in tests.
|
|
42
|
+
*/
|
|
43
|
+
export declare function resetRegistry(): void;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { readdirSync, existsSync } from 'node:fs';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
/**
|
|
5
|
+
* Build the integration registry by discovering all integration modules.
|
|
6
|
+
* Scans `src/integrations/` (or `dist/integrations/` at runtime) for directories
|
|
7
|
+
* with an index.js/index.ts file and dynamically imports them.
|
|
8
|
+
*/
|
|
9
|
+
export async function buildRegistry() {
|
|
10
|
+
const modules = new Map();
|
|
11
|
+
// Resolve the integrations directory relative to this file
|
|
12
|
+
// In dev: src/lib/registry.ts -> src/integrations/
|
|
13
|
+
// In dist: dist/lib/registry.js -> dist/integrations/
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = dirname(__filename);
|
|
16
|
+
const integrationsDir = join(__dirname, '..', 'integrations');
|
|
17
|
+
if (!existsSync(integrationsDir)) {
|
|
18
|
+
throw new Error(`No integrations directory found at ${integrationsDir}. Is the build corrupt?`);
|
|
19
|
+
}
|
|
20
|
+
const entries = readdirSync(integrationsDir, { withFileTypes: true });
|
|
21
|
+
const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
22
|
+
if (dirs.length === 0) {
|
|
23
|
+
throw new Error('No integrations found. Is the build corrupt?');
|
|
24
|
+
}
|
|
25
|
+
for (const dir of dirs) {
|
|
26
|
+
// Skip directories starting with _ (convention for internal files like _manifest.ts)
|
|
27
|
+
if (dir.startsWith('_'))
|
|
28
|
+
continue;
|
|
29
|
+
const indexPath = join(integrationsDir, dir, 'index.js');
|
|
30
|
+
const indexTsPath = join(integrationsDir, dir, 'index.ts');
|
|
31
|
+
if (!existsSync(indexPath) && !existsSync(indexTsPath)) {
|
|
32
|
+
// Skip directories without an index file (not an integration)
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const mod = (await import(join(integrationsDir, dir, 'index.js')));
|
|
37
|
+
if (!mod.config || !mod.run) {
|
|
38
|
+
console.warn(`Integration ${dir} missing 'config' or 'run' export, skipping`);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const name = mod.config.metadata.integration;
|
|
42
|
+
if (modules.has(name)) {
|
|
43
|
+
throw new Error(`Duplicate integration name: '${name}' (found in both existing and '${dir}/')`);
|
|
44
|
+
}
|
|
45
|
+
modules.set(name, mod);
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
if (err instanceof Error && err.message.startsWith('Duplicate integration name')) {
|
|
49
|
+
throw err; // Re-throw duplicate name errors
|
|
50
|
+
}
|
|
51
|
+
console.warn(`Failed to load integration from ${dir}/: ${err}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Build sorted config array (by priority, descending)
|
|
55
|
+
const sortedConfigs = Array.from(modules.values())
|
|
56
|
+
.map((m) => m.config)
|
|
57
|
+
.sort((a, b) => b.metadata.priority - a.metadata.priority);
|
|
58
|
+
return {
|
|
59
|
+
all() {
|
|
60
|
+
return sortedConfigs;
|
|
61
|
+
},
|
|
62
|
+
get(name) {
|
|
63
|
+
return modules.get(name);
|
|
64
|
+
},
|
|
65
|
+
forLanguage(language) {
|
|
66
|
+
return sortedConfigs.filter((c) => c.metadata.language === language);
|
|
67
|
+
},
|
|
68
|
+
choices() {
|
|
69
|
+
return sortedConfigs.map((c) => ({
|
|
70
|
+
name: c.metadata.name,
|
|
71
|
+
value: c.metadata.integration,
|
|
72
|
+
}));
|
|
73
|
+
},
|
|
74
|
+
detectionOrder() {
|
|
75
|
+
return sortedConfigs;
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
// Singleton cache
|
|
80
|
+
let _registry = null;
|
|
81
|
+
/**
|
|
82
|
+
* Get the integration registry (builds once, caches thereafter).
|
|
83
|
+
*/
|
|
84
|
+
export async function getRegistry() {
|
|
85
|
+
if (!_registry) {
|
|
86
|
+
_registry = await buildRegistry();
|
|
87
|
+
}
|
|
88
|
+
return _registry;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Reset the registry cache. Used in tests.
|
|
92
|
+
*/
|
|
93
|
+
export function resetRegistry() {
|
|
94
|
+
_registry = null;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/lib/registry.ts"],"names":[],"mappings":"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","sourcesContent":["import { readdirSync, existsSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { FrameworkConfig } from './framework-config.js';\nimport type { Language } from './language-detection.js';\nimport type { InstallerOptions } from '../utils/types.js';\n\n/**\n * Standard exports from an integration module.\n * Each `src/integrations/{name}/index.ts` must export these.\n */\nexport interface IntegrationModule {\n config: FrameworkConfig;\n run: (options: InstallerOptions) => Promise<string>;\n}\n\n/**\n * Registry that provides lookup, detection, and enumeration of integrations.\n */\nexport interface IntegrationRegistry {\n /** All registered integrations */\n all(): FrameworkConfig[];\n\n /** Get config by integration name */\n get(name: string): IntegrationModule | undefined;\n\n /** Get integrations for a specific language, ordered by priority */\n forLanguage(language: Language): FrameworkConfig[];\n\n /** Get integration names for CLI choices */\n choices(): Array<{ name: string; value: string }>;\n\n /** Detection order: all integrations sorted by priority (higher = checked first) */\n detectionOrder(): FrameworkConfig[];\n}\n\n/**\n * Build the integration registry by discovering all integration modules.\n * Scans `src/integrations/` (or `dist/integrations/` at runtime) for directories\n * with an index.js/index.ts file and dynamically imports them.\n */\nexport async function buildRegistry(): Promise<IntegrationRegistry> {\n const modules = new Map<string, IntegrationModule>();\n\n // Resolve the integrations directory relative to this file\n // In dev: src/lib/registry.ts -> src/integrations/\n // In dist: dist/lib/registry.js -> dist/integrations/\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n const integrationsDir = join(__dirname, '..', 'integrations');\n\n if (!existsSync(integrationsDir)) {\n throw new Error(`No integrations directory found at ${integrationsDir}. Is the build corrupt?`);\n }\n\n const entries = readdirSync(integrationsDir, { withFileTypes: true });\n const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);\n\n if (dirs.length === 0) {\n throw new Error('No integrations found. Is the build corrupt?');\n }\n\n for (const dir of dirs) {\n // Skip directories starting with _ (convention for internal files like _manifest.ts)\n if (dir.startsWith('_')) continue;\n\n const indexPath = join(integrationsDir, dir, 'index.js');\n const indexTsPath = join(integrationsDir, dir, 'index.ts');\n\n if (!existsSync(indexPath) && !existsSync(indexTsPath)) {\n // Skip directories without an index file (not an integration)\n continue;\n }\n\n try {\n const mod = (await import(join(integrationsDir, dir, 'index.js'))) as IntegrationModule;\n\n if (!mod.config || !mod.run) {\n console.warn(`Integration ${dir} missing 'config' or 'run' export, skipping`);\n continue;\n }\n\n const name = mod.config.metadata.integration;\n\n if (modules.has(name)) {\n throw new Error(`Duplicate integration name: '${name}' (found in both existing and '${dir}/')`);\n }\n\n modules.set(name, mod);\n } catch (err) {\n if (err instanceof Error && err.message.startsWith('Duplicate integration name')) {\n throw err; // Re-throw duplicate name errors\n }\n console.warn(`Failed to load integration from ${dir}/: ${err}`);\n }\n }\n\n // Build sorted config array (by priority, descending)\n const sortedConfigs = Array.from(modules.values())\n .map((m) => m.config)\n .sort((a, b) => b.metadata.priority - a.metadata.priority);\n\n return {\n all() {\n return sortedConfigs;\n },\n\n get(name: string) {\n return modules.get(name);\n },\n\n forLanguage(language: Language) {\n return sortedConfigs.filter((c) => c.metadata.language === language);\n },\n\n choices() {\n return sortedConfigs.map((c) => ({\n name: c.metadata.name,\n value: c.metadata.integration,\n }));\n },\n\n detectionOrder() {\n return sortedConfigs;\n },\n };\n}\n\n// Singleton cache\nlet _registry: IntegrationRegistry | null = null;\n\n/**\n * Get the integration registry (builds once, caches thereafter).\n */\nexport async function getRegistry(): Promise<IntegrationRegistry> {\n if (!_registry) {\n _registry = await buildRegistry();\n }\n return _registry;\n}\n\n/**\n * Reset the registry cache. Used in tests.\n */\nexport function resetRegistry(): void {\n _registry = null;\n}\n"]}
|
|
@@ -6,7 +6,6 @@ import { installerMachine } from './installer-core.js';
|
|
|
6
6
|
import { createInstallerEventEmitter } from './events.js';
|
|
7
7
|
import { CLIAdapter } from './adapters/cli-adapter.js';
|
|
8
8
|
import { DashboardAdapter } from './adapters/dashboard-adapter.js';
|
|
9
|
-
import { Integration } from './constants.js';
|
|
10
9
|
import { parseEnvFile } from '../utils/env-parser.js';
|
|
11
10
|
import { enableDebugLogs, initLogFile, logInfo, logError } from '../utils/debug.js';
|
|
12
11
|
import { getAccessToken, getCredentials, saveCredentials, getStagingCredentials, saveStagingCredentials, } from './credentials.js';
|
|
@@ -21,30 +20,17 @@ import { isInGitRepo, getUncommittedOrUntrackedFiles } from '../utils/clack-util
|
|
|
21
20
|
import { getCurrentBranch, isProtectedBranch, createBranch as createGitBranch, branchExists, } from '../utils/git-utils.js';
|
|
22
21
|
import { detectChanges, stageAndCommit, pushBranch as pushGitBranch, createPullRequest } from './post-install.js';
|
|
23
22
|
import { generateCommitMessage as generateCommitMessageAi, generatePrDescription as generatePrDescriptionAi, } from './ai-content.js';
|
|
24
|
-
import { INTEGRATION_CONFIG, INTEGRATION_ORDER } from './config.js';
|
|
25
23
|
import { autoConfigureWorkOSEnvironment } from './workos-management.js';
|
|
26
24
|
import { detectPort, getCallbackPath } from './port-detection.js';
|
|
27
25
|
import { writeEnvLocal } from './env-writer.js';
|
|
28
|
-
import {
|
|
29
|
-
import { runReactInstallerAgent } from '../react/react-installer-agent.js';
|
|
30
|
-
import { runReactRouterInstallerAgent } from '../react-router/react-router-installer-agent.js';
|
|
31
|
-
import { runTanstackStartInstallerAgent } from '../tanstack-start/tanstack-start-installer-agent.js';
|
|
32
|
-
import { runVanillaJsInstallerAgent } from '../vanilla-js/vanilla-js-installer-agent.js';
|
|
26
|
+
import { getRegistry } from './registry.js';
|
|
33
27
|
async function runIntegrationInstallerFn(integration, options) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return runReactInstallerAgent(options);
|
|
39
|
-
case Integration.reactRouter:
|
|
40
|
-
return runReactRouterInstallerAgent(options);
|
|
41
|
-
case Integration.tanstackStart:
|
|
42
|
-
return runTanstackStartInstallerAgent(options);
|
|
43
|
-
case Integration.vanillaJs:
|
|
44
|
-
return runVanillaJsInstallerAgent(options);
|
|
45
|
-
default:
|
|
46
|
-
throw new Error(`Unknown integration: ${integration}`);
|
|
28
|
+
const registry = await getRegistry();
|
|
29
|
+
const mod = registry.get(integration);
|
|
30
|
+
if (!mod) {
|
|
31
|
+
throw new Error(`Unknown integration: ${integration}`);
|
|
47
32
|
}
|
|
33
|
+
return mod.run(options);
|
|
48
34
|
}
|
|
49
35
|
function readExistingCredentials(installDir) {
|
|
50
36
|
const envPath = join(installDir, '.env.local');
|
|
@@ -64,15 +50,73 @@ function readExistingCredentials(installDir) {
|
|
|
64
50
|
}
|
|
65
51
|
}
|
|
66
52
|
async function detectIntegrationFn(options) {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
53
|
+
const registry = await getRegistry();
|
|
54
|
+
const configs = registry.detectionOrder();
|
|
55
|
+
for (const config of configs) {
|
|
56
|
+
// Use the detect function from INTEGRATION_CONFIG in config.ts for JS integrations,
|
|
57
|
+
// or fall back to checking if the framework package is installed
|
|
58
|
+
const detected = await detectSingleIntegration(config.metadata.integration, options);
|
|
70
59
|
if (detected) {
|
|
71
|
-
return integration;
|
|
60
|
+
return config.metadata.integration;
|
|
72
61
|
}
|
|
73
62
|
}
|
|
74
63
|
return undefined;
|
|
75
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Detect if a single integration matches the project.
|
|
67
|
+
* Uses package.json detection for JS integrations, manifest files for others.
|
|
68
|
+
*/
|
|
69
|
+
async function detectSingleIntegration(integration, options) {
|
|
70
|
+
const { getPackageDotJson } = await import('../utils/clack-utils.js');
|
|
71
|
+
const { hasPackageInstalled } = await import('../utils/package-json.js');
|
|
72
|
+
const { existsSync } = await import('node:fs');
|
|
73
|
+
const { join } = await import('node:path');
|
|
74
|
+
const registry = await getRegistry();
|
|
75
|
+
const mod = registry.get(integration);
|
|
76
|
+
if (!mod)
|
|
77
|
+
return false;
|
|
78
|
+
const config = mod.config;
|
|
79
|
+
// For JS integrations, check package.json
|
|
80
|
+
if (config.metadata.language === 'javascript') {
|
|
81
|
+
const packageJson = await getPackageDotJson(options);
|
|
82
|
+
switch (integration) {
|
|
83
|
+
case 'nextjs':
|
|
84
|
+
return hasPackageInstalled('next', packageJson);
|
|
85
|
+
case 'tanstack-start':
|
|
86
|
+
return hasPackageInstalled('@tanstack/react-start', packageJson);
|
|
87
|
+
case 'react-router':
|
|
88
|
+
return hasPackageInstalled('react-router', packageJson);
|
|
89
|
+
case 'react': {
|
|
90
|
+
const hasReact = hasPackageInstalled('react', packageJson);
|
|
91
|
+
const hasNext = hasPackageInstalled('next', packageJson);
|
|
92
|
+
const hasReactRouter = hasPackageInstalled('react-router', packageJson);
|
|
93
|
+
const hasTanstack = hasPackageInstalled('@tanstack/react-start', packageJson);
|
|
94
|
+
const hasSvelteKit = hasPackageInstalled('@sveltejs/kit', packageJson);
|
|
95
|
+
return hasReact && !hasNext && !hasReactRouter && !hasTanstack && !hasSvelteKit;
|
|
96
|
+
}
|
|
97
|
+
case 'sveltekit':
|
|
98
|
+
return hasPackageInstalled('@sveltejs/kit', packageJson);
|
|
99
|
+
case 'node': {
|
|
100
|
+
const hasExpress = hasPackageInstalled('express', packageJson);
|
|
101
|
+
const hasFrontend = hasPackageInstalled('next', packageJson) ||
|
|
102
|
+
hasPackageInstalled('@sveltejs/kit', packageJson) ||
|
|
103
|
+
hasPackageInstalled('react', packageJson) ||
|
|
104
|
+
hasPackageInstalled('@tanstack/react-start', packageJson);
|
|
105
|
+
return hasExpress && !hasFrontend;
|
|
106
|
+
}
|
|
107
|
+
case 'vanilla-js':
|
|
108
|
+
return true; // Fallback
|
|
109
|
+
default:
|
|
110
|
+
// Unknown JS integration — try package name detection
|
|
111
|
+
return hasPackageInstalled(config.detection.packageName, packageJson);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// For non-JS integrations, check manifest files
|
|
115
|
+
if (config.metadata.manifestFile) {
|
|
116
|
+
return existsSync(join(options.installDir, config.metadata.manifestFile));
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
76
120
|
export async function runWithCore(options) {
|
|
77
121
|
// Initialize debug/logging early so we capture all failures
|
|
78
122
|
initLogFile();
|
|
@@ -143,14 +187,14 @@ export async function runWithCore(options) {
|
|
|
143
187
|
const port = detectPort(integration, installerOptions.installDir);
|
|
144
188
|
const callbackPath = getCallbackPath(integration);
|
|
145
189
|
const redirectUri = installerOptions.redirectUri || `http://localhost:${port}${callbackPath}`;
|
|
146
|
-
const requiresApiKey = [
|
|
190
|
+
const requiresApiKey = ['nextjs', 'tanstack-start', 'react-router'].includes(integration);
|
|
147
191
|
if (credentials.apiKey && requiresApiKey) {
|
|
148
192
|
await autoConfigureWorkOSEnvironment(credentials.apiKey, integration, port, {
|
|
149
193
|
homepageUrl: installerOptions.homepageUrl,
|
|
150
194
|
redirectUri: installerOptions.redirectUri,
|
|
151
195
|
});
|
|
152
196
|
}
|
|
153
|
-
const redirectUriKey = integration ===
|
|
197
|
+
const redirectUriKey = integration === 'nextjs' ? 'NEXT_PUBLIC_WORKOS_REDIRECT_URI' : 'WORKOS_REDIRECT_URI';
|
|
154
198
|
writeEnvLocal(installerOptions.installDir, {
|
|
155
199
|
...(credentials.apiKey ? { WORKOS_API_KEY: credentials.apiKey } : {}),
|
|
156
200
|
WORKOS_CLIENT_ID: credentials.clientId,
|